1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
#include "privateAir.h"
#include <teemQnanhibit.h>
/*
** all this is based on a reading of
** Hennessy + Patterson "Computer Architecture, A Quantitative Approach"
** pages A-13 - A-17
**
** and some assorted web pages, such as:
** http://en.wikipedia.org/wiki/NaN#Encoding
** which explains what Teem calls qnanhibit, and
** http://grouper.ieee.org/groups/754/email/msg04192.html
** which includes some discussion on signal-vs-quiet nan
*/
/*
** _airFloatEndian{Little, Big}, _airDoubleEndian{Little, Big}
**
** these unions facilitate converting amongst
** i: unsigned integral type
** c: ( sign, exp, frac ) triples of unsigned integral components
** v: the floating point numbers these bit-patterns represent
*/
typedef union {
unsigned int i;
struct {
unsigned int mant : 23;
unsigned int expo : 8;
unsigned int sign : 1;
} c;
float v;
} _airFloatEndianLittle;
/* HEY COPY AND PASTE */
typedef union {
unsigned int i;
struct {
unsigned int sign : 1;
unsigned int expo : 8;
unsigned int mant : 23;
} c;
float v;
} _airFloatEndianBig;
typedef union {
airULLong i;
/* these next two members are used for printing in airFPFprintf_d */
struct { /* access to whole double as two unsigned ints */
unsigned int half0 : 32;
unsigned int half1 : 32;
} h;
struct { /* access to fraction with two unsigned ints */
unsigned int mant1 : 32;
unsigned int mant0 : 20;
unsigned int expo : 11;
unsigned int sign : 1;
} c;
double v;
} _airDoubleEndianLittle;
/* HEY COPY AND PASTE */
typedef union {
airULLong i;
/* these next two members are used for printing in airFPFprintf_d */
struct { /* access to whole double as two unsigned ints */
unsigned int half1 : 32;
unsigned int half0 : 32;
} h;
struct { /* access to fraction with two unsigned ints */
unsigned int sign : 1;
unsigned int expo : 11;
unsigned int mant0 : 20;
unsigned int mant1 : 32;
} c;
double v;
} _airDoubleEndianBig;
/*
** The hex numbers in braces are examples of C's "initial member of a union"
** aggregate initialization.
*/
#if TEEM_QNANHIBIT == 1
const int airMyQNaNHiBit = 1;
const airFloat airFloatQNaN = {0x7fffffff};
const airFloat airFloatSNaN = {0x7fbfffff};
#else
const int airMyQNaNHiBit = 0;
const airFloat airFloatQNaN = {0x7fbfffff};
const airFloat airFloatSNaN = {0x7fffffff};
#endif
const airFloat airFloatPosInf = {0x7f800000};
const airFloat airFloatNegInf = {0xff800000}; /* why does solaris whine? */
/*
** these shouldn't be needed, but here they are if need be:
in this file:
const airFloat airFloatMax = {0x7f7fffff};
const airFloat airFloatMin = {0x00800000};
const airDouble airDoubleMax = {AIR_ULLONG( 0x7fefffffffffffff )};
const airDouble airDoubleMin = {AIR_ULLONG( 0x0010000000000000 )};
in air.h:
extern air_export const airFloat airFloatMax;
extern air_export const airFloat airFloatMin;
extern air_export const airDouble airDoubleMax;
extern air_export const airDouble airDoubleMin;
#define AIR_FLT_MIN ( airFloatMin.f )
#define AIR_FLT_MAX ( airFloatMax.f )
#define AIR_DBL_MIN ( airDoubleMin.d )
#define AIR_DBL_MAX ( airDoubleMax.d )
*/
/* the bit-masking done here quiets gcc -Wconversion warnings */
#define FP_SET_F( flit, fbig, s, e, m ) \
flit.c.sign = 1u & ( s ); \
flit.c.expo = ( ( 1u<8 )-1 ) & ( e ); \
flit.c.mant = ( ( 1u<23 )-1 ) & ( m ); \
fbig.c.sign = 1u & ( s ); \
fbig.c.expo = ( ( 1u<8 )-1 ) & ( e ); \
fbig.c.mant = ( ( 1u<23 )-1 ) & ( m )
#define FP_GET_F( s, e, m, flit, fbig ) \
if ( airEndianLittle == airMyEndian( ) ) { \
149 ( s ) = flit.c.sign; \
( e ) = flit.c.expo; \
( m ) = flit.c.mant; \
} else { \
( s ) = fbig.c.sign; \
( e ) = fbig.c.expo; \
( m ) = fbig.c.mant; \
}
#define FP_SET_D( dlit, dbig, s, e, m0, m1 ) \
dlit.c.sign = 1u & ( s ); \
dlit.c.expo = ( ( 1u<<11 )-1 ) & ( e ); \
dlit.c.mant0 = ( ( 1u<<20 )-1 ) & ( m0 ); \
162 dlit.c.mant1 = ( m1 ); \
dbig.c.sign = 1u & ( s ); \
dbig.c.expo = ( ( 1u<<11 )-1 ) & ( e ); \
dbig.c.mant0 = ( ( 1u<<20 )-1 ) & ( m0 ); \
dbig.c.mant1 = ( m1 )
#define FP_GET_D( s, e, m0, m1, dlit, dbig ) \
if ( airEndianLittle == airMyEndian( ) ) { \
( s ) = dlit.c.sign; \
( e ) = dlit.c.expo; \
( m0 ) = dlit.c.mant0; \
173 ( m1 ) = dlit.c.mant1; \
} else { \
( s ) = dbig.c.sign; \
( e ) = dbig.c.expo; \
( m0 ) = dbig.c.mant0; \
( m1 ) = dbig.c.mant1; \
}
float
airFPPartsToVal_f( unsigned int sign,
unsigned int expo,
unsigned int mant ) {
_airFloatEndianLittle flit;
_airFloatEndianBig fbig;
FP_SET_F( flit, fbig, sign, expo, mant );
return ( airEndianLittle == airMyEndian( )
? flit.v
: fbig.v );
}
195 void
airFPValToParts_f( unsigned int *signP,
unsigned int *expoP,
unsigned int *mantP, float v ) {
_airFloatEndianLittle flit;
_airFloatEndianBig fbig;
flit.v = fbig.v = v;
FP_GET_F( *signP, *expoP, *mantP, flit, fbig );
}
double
airFPPartsToVal_d( unsigned int sign,
unsigned int expo,
unsigned int mant0,
unsigned int mant1 ) {
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
FP_SET_D( dlit, dbig, sign, expo, mant0, mant1 );
215 return ( airEndianLittle == airMyEndian( )
? dlit.v
: dbig.v );
}
/*
** Disable the 'local variable used without having been initialized'
** warning produced by the MSVC compiler
*/
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4700 )
#endif
void
airFPValToParts_d( unsigned int *signP,
unsigned int *expoP,
unsigned int *mant0P,
unsigned int *mant1P, double v ) {
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
dlit.v = dbig.v = v;
FP_GET_D( *signP, *expoP, *mant0P, *mant1P, dlit, dbig );
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
/*
******** airFPGen_f( )
**
** generates a floating point value which is a member of the given class
*/
float
airFPGen_f( int cls ) {
_airFloatEndianLittle flit;
_airFloatEndianBig fbig;
switch( cls ) {
case airFP_SNAN:
/* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */
FP_SET_F( flit, fbig, 0, 0xff, ( !TEEM_QNANHIBIT << 22 ) | 0x3fffff );
break;
case airFP_QNAN:
/* sgn: anything, mant: anything non-zero with high bit TEEM_QNANHIBIT */
FP_SET_F( flit, fbig, 0, 0xff, ( TEEM_QNANHIBIT << 22 ) | 0x3fffff );
break;
case airFP_POS_INF:
FP_SET_F( flit, fbig, 0, 0xff, 0 );
break;
case airFP_NEG_INF:
FP_SET_F( flit, fbig, 1, 0xff, 0 );
break;
case airFP_POS_NORM:
269 /* exp: anything non-zero but < 0xff, mant: anything */
FP_SET_F( flit, fbig, 0, 0x80, 0x7ff000 );
break;
case airFP_NEG_NORM:
/* exp: anything non-zero but < 0xff, mant: anything */
FP_SET_F( flit, fbig, 1, 0x80, 0x7ff000 );
break;
case airFP_POS_DENORM:
/* mant: anything non-zero */
FP_SET_F( flit, fbig, 0, 0, 0xff );
break;
case airFP_NEG_DENORM:
/* mant: anything non-zero */
FP_SET_F( flit, fbig, 1, 0, 0xff );
break;
case airFP_NEG_ZERO:
FP_SET_F( flit, fbig, 1, 0, 0 );
break;
case airFP_POS_ZERO:
default:
FP_SET_F( flit, fbig, 0, 0, 0 );
break;
}
return ( airEndianLittle == airMyEndian( )
? flit.v
: fbig.v );
}
/*
******** airFPGen_d( )
**
** generates a floating point value which is a member of the given class
*/
double
airFPGen_d( int cls ) {
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
switch( cls ) {
case airFP_SNAN:
/* sgn: anything, mant: anything non-zero with high bit !TEEM_QNANHIBIT */
FP_SET_D( dlit, dbig, 0, 0x7ff, ( !TEEM_QNANHIBIT << 19 ) | 0x7ffff, 0xffffffff );
break;
case airFP_QNAN:
/* sgn: anything, mant anything non-zero with high bit TEEM_QNANHIBIT */
FP_SET_D( dlit, dbig, 0, 0x7ff, ( TEEM_QNANHIBIT << 19 ) | 0x7ffff, 0xffffffff );
break;
case airFP_POS_INF:
FP_SET_D( dlit, dbig, 0, 0x7ff, 0, 0 );
break;
case airFP_NEG_INF:
FP_SET_D( dlit, dbig, 1, 0x7ff, 0, 0 );
break;
case airFP_POS_NORM:
323 /* exp: anything non-zero but < 0xff, mant: anything */
FP_SET_D( dlit, dbig, 0, 0x400, 0x0ff00, 0 );
break;
case airFP_NEG_NORM:
/* exp: anything non-zero but < 0xff, mant: anything */
FP_SET_D( dlit, dbig, 1, 0x400, 0x0ff00, 0 );
break;
case airFP_POS_DENORM:
/* mant: anything non-zero */
FP_SET_D( dlit, dbig, 0, 0, 0xff, 0 );
break;
case airFP_NEG_DENORM:
/* mant: anything non-zero */
FP_SET_D( dlit, dbig, 1, 0, 0xff, 0 );
break;
case airFP_NEG_ZERO:
FP_SET_D( dlit, dbig, 1, 0, 0, 0 );
break;
case airFP_POS_ZERO:
default:
FP_SET_D( dlit, dbig, 0, 0, 0, 0 );
break;
}
return ( airEndianLittle == airMyEndian( )
? dlit.v
: dbig.v );
}
/*
******** airFPClass_f( )
**
** given a floating point number, tells which class its in
*/
int
airFPClass_f( float val ) {
_airFloatEndianLittle flit;
_airFloatEndianBig fbig;
unsigned int sign, expv, mant;
int indexv, ret = 0;
flit.v = fbig.v = val;
FP_GET_F( sign, expv, mant, flit, fbig );
indexv = ( ( !!sign ) << 2 ) | ( ( !!expv ) << 1 ) | ( !!mant );
switch( indexv ) {
case 0:
/* all fields are zero */
ret = airFP_POS_ZERO;
break;
case 1:
/* only mantissa is non-zero */
ret = airFP_POS_DENORM;
break;
case 2:
/* only exponent field is non-zero */
if ( 0xff == expv ) {
ret = airFP_POS_INF;
} else {
ret = airFP_POS_NORM;
}
break;
case 3:
/* exponent and mantissa fields are non-zero */
if ( 0xff == expv ) {
if ( TEEM_QNANHIBIT == mant >> 22 ) {
ret = airFP_QNAN;
} else {
ret = airFP_SNAN;
}
} else {
ret = airFP_POS_NORM;
}
break;
case 4:
/* only sign field is non-zero */
ret = airFP_NEG_ZERO;
break;
case 5:
/* sign and mantissa fields are non-zero */
ret = airFP_NEG_DENORM;
break;
case 6:
/* sign and exponent fields are non-zero */
if ( 0xff > expv ) {
ret = airFP_NEG_NORM;
407 } else {
ret = airFP_NEG_INF;
}
break;
case 7:
/* all fields are non-zero */
if ( 0xff > expv ) {
ret = airFP_NEG_NORM;
} else {
if ( TEEM_QNANHIBIT == mant >> 22 ) {
ret = airFP_QNAN;
} else {
ret = airFP_SNAN;
}
}
break;
}
return ret;
}
/*
** Disable the 'local variable used without having been initialized'
** warning produced by the MSVC compiler
*/
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : 4700 )
#endif
/*
******** airFPClass_d( )
**
** given a double, tells which class its in
*/
int
airFPClass_d( double val ) {
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
unsigned int sign, expo, mant0, mant1;
int indexv, ret=0;
unsigned char hibit;
dlit.v = dbig.v = val;
/* "expo = d.c.expo" had been annotated with: "this seems to be a
WIN32 bug: on a quiet-NaN, d.c.exp should be non-zero, but it was
completely zero, so that this function returned airFP_NEG_DENORM
instead of airFP_QNAN" */
FP_GET_D( sign, expo, mant0, mant1, dlit, dbig );
hibit = AIR_CAST( unsigned char, mant0 >> 19 ); /* mant0 20 bits wide: ok */
indexv = ( ( !!sign ) << 2 ) | ( ( !!expo ) << 1 ) | ( !!mant0 || !!mant1 );
switch( indexv ) {
case 0:
/* all fields are zero */
ret = airFP_POS_ZERO;
break;
case 1:
/* only fractional field is non-zero */
ret = airFP_POS_DENORM;
break;
case 2:
/* only exponent field is non-zero */
if ( 0x7ff > expo ) {
ret = airFP_POS_NORM;
} else {
ret = airFP_POS_INF;
}
break;
case 3:
/* exponent and fractional fields are non-zero */
if ( 0x7ff > expo ) {
ret = airFP_POS_NORM;
} else {
if ( TEEM_QNANHIBIT == hibit ) {
ret = airFP_QNAN;
} else {
ret = airFP_SNAN;
}
}
break;
case 4:
/* only sign field is non-zero */
ret = airFP_NEG_ZERO;
break;
case 5:
/* sign and fractional fields are non-zero */
ret = airFP_NEG_DENORM;
break;
case 6:
/* sign and exponent fields are non-zero */
if ( 0x7ff > expo ) {
497 ret = airFP_NEG_NORM;
} else {
ret = airFP_NEG_INF;
}
break;
case 7:
/* all fields are non-zero */
if ( 0x7ff > expo )
ret = airFP_NEG_NORM;
else {
if ( TEEM_QNANHIBIT == hibit ) {
ret = airFP_QNAN;
} else {
ret = airFP_SNAN;
}
}
break;
}
return ret;
}
#ifdef _MSC_VER
#pragma warning( pop )
519 #endif
/*
******** airIsNaN( )
**
** returns 1 if input is either kind of NaN, 0 otherwise. It is okay
** to only have a double version of this function, as opposed to
** having one for float and one for double, because Section 6.2 of the
** 754 spec tells us that that NaN is to be preserved across precision
** changes ( and airSanity( ) explicitly checks for this ).
*/
int
airIsNaN( double g ) {
_airFloatEndianLittle flit;
533 _airFloatEndianBig fbig;
unsigned int sign, expo, mant;
flit.v = fbig.v = AIR_CAST( float, g );
FP_GET_F( sign, expo, mant, flit, fbig );
AIR_UNUSED( sign );
return ( 0xff == expo && mant );
}
/*
******** airIsInf_f( ), airIsInf_d( )
**
** returns 1 if input is positive infinity,
** -1 if negative infinity,
** or 0 otherwise ( including NaN )
**
** thus the non-zero-ness of the return is an easy way to do a
** boolean check of whether the value is infinite
*/
int
airIsInf_f( float f ) {
int c, ret;
556 c = airFPClass_f( f );
if ( airFP_POS_INF == c ) {
ret = 1;
} else if ( airFP_NEG_INF == c ) {
ret = -1;
} else {
ret = 0;
}
return ret;
}
int
airIsInf_d( double d ) {
int c, ret;
c = airFPClass_d( d );
if ( airFP_POS_INF == c ) {
572 ret = 1;
} else if ( airFP_NEG_INF == c ) {
ret = -1;
} else {
ret = 0;
}
return ret;
}
/* airExists_f( ) airExists_d( ) were nixed because they weren't used-
you can just use AIR_EXISTS_F and AIR_EXISTS_D directly */
583
/*
******** airExists( )
**
** an optimization-proof alternative to AIR_EXISTS
*/
int
airExists( double val ) {
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
dbig.v = dlit.v = val;
return ( airEndianLittle == airMyEndian( )
? 0x7ff != dlit.c.expo
: 0x7ff != dbig.c.expo );
}
/*
******** airNaN( )
**
** returns a float quiet NaN
*/
float
airNaN( void ) {
return airFPGen_f( airFP_QNAN );
}
/*
******** airFPFprintf_f( )
**
** prints out the bits of a "float", indicating the three different fields
615 */
void
airFPFprintf_f( FILE *file, float val ) {
int i;
unsigned int sign, expo, mant;
_airFloatEndianLittle flit;
_airFloatEndianBig fbig;
if ( file ) {
flit.v = fbig.v = val;
FP_GET_F( sign, expo, mant, flit, fbig );
fprintf( file, "%f: class %d; 0x%08x = ", val, airFPClass_f( val ),
airEndianLittle == airMyEndian( ) ? flit.i : fbig.i );
fprintf( file, "sign:0x%x, expo:0x%02x, mant:0x%06x = \n",
sign, expo, mant );
fprintf( file, " S [ . . Exp . . ] "
"[ . . . . . . . . . Mant. . . . . . . . . . ]\n" );
fprintf( file, " %d ", sign );
for ( i=7; i>=0; i-- ) {
fprintf( file, "%d ", ( expo >> i ) & 1 );
}
for ( i=22; i>=0; i-- ) {
fprintf( file, "%d ", ( mant >> i ) & 1 );
}
fprintf( file, "\n" );
}
}
/*
******** airFPFprintf_d( )
**
** prints out the bits of a "double", indicating the three different fields
*/
void
airFPFprintf_d( FILE *file, double val ) {
int i;
_airDoubleEndianLittle dlit;
_airDoubleEndianBig dbig;
unsigned int sign, expo, mant0, mant1;
if ( file ) {
dlit.v = dbig.v = val;
fprintf( file, "%f: class %d; 0x%08x %08x = \n",
val, airFPClass_d( val ),
( airEndianLittle == airMyEndian( ) ? dlit.h.half1 : dbig.h.half1 ),
( airEndianLittle == airMyEndian( ) ? dlit.h.half0 : dbig.h.half0 ) );
FP_GET_D( sign, expo, mant0, mant1, dlit, dbig );
fprintf( file, "sign:0x%x, expo:0x%03x, mant:0x%05x %08x = \n",
sign, expo, mant0, mant1 );
fprintf( file, "S[...Exp...][.......................Mant.......................]\n" );
fprintf( file, "%d", sign );
for ( i=10; i>=0; i-- ) {
fprintf( file, "%d", ( expo >> i ) & 1 );
}
for ( i=19; i>=0; i-- ) {
fprintf( file, "%d", ( mant0 >> i ) & 1 );
}
for ( i=31; i>=0; i-- ) {
fprintf( file, "%d", ( mant1 >> i ) & 1 );
}
fprintf( file, "\n" );
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
static void
27 _airLenSet( airArray *a, unsigned int len ) {
a->len = len;
/* printf( " HEY: len = %d\n", a->len ); */
if ( a->lenP ) {
*( a->lenP ) = len;
/* printf( " HEY: *( a->lenP ) = *( %lu ) = %d\n",
( unsigned long )a->lenP, *( a->lenP ) ); */
}
}
static void
39 _airSetData( airArray *a, void *data ) {
a->data = data;
if ( a->dataP ) {
*( a->dataP ) = data;
}
}
/*
******** airArrayNew( )
**
** creates a new airArray struct and returns a pointer to it.
** dataP is a pointer to the user's data pointer
** lenP is a pointer to the user's array length variable ( optional )
** unit is the size ( in bytes ) of one element in the array
** incr is the number of units by which the array will grow or shrink
**
** returns NULL on error, or the new airArray pointer if okay
** errors: bogus arguments, or couldn't alloc airArray struct
**
** --> The user CAN NOT change the pointer variable ( of which *dataP
** is the address ) after this is called, or else everything will
** get all bolloxed up. The same goes for the array length
** variable, if its address is passed- though in that case the
** correct value will over-write any other.
*/
airArray *
66 airArrayNew( void **dataP, unsigned int *lenP, size_t unit, unsigned int incr ) {
airArray *a;
if ( !unit || !incr ) {
return NULL;
}
a = AIR_CALLOC( 1, airArray );
if ( !a ) {
return NULL;
}
a->dataP = dataP;
_airSetData( a, NULL );
a->lenP = lenP;
_airLenSet( a, 0 );
a->incr = incr;
a->unit = unit;
a->noReallocWhenSmaller = AIR_FALSE;
a->allocCB = NULL;
a->freeCB = NULL;
a->initCB = NULL;
a->doneCB = NULL;
return a;
}
/*
******** airArrayStructCB( )
**
** set callbacks to maintain array of structs
*/
void
100 airArrayStructCB( airArray *a,
void ( *initCB )( void * ), void ( *doneCB )( void * ) ) {
if ( a ) {
a->initCB = initCB;
a->doneCB = doneCB;
a->allocCB = NULL;
a->freeCB = NULL;
}
}
/*
******** airArrayPointerCB( )
**
** set callbacks to maintain array of pointers
*/
void
117 airArrayPointerCB( airArray *a,
void *( *allocCB )( void ), void *( *freeCB )( void * ) ) {
if ( a ) {
a->initCB = NULL;
a->doneCB = NULL;
a->allocCB = allocCB;
a->freeCB = freeCB;
}
}
/* ---- BEGIN non-NrrdIO */
/*
******** airArrayLenPreSet( )
**
** allocates the array to hold up to given length, without
** actually changing the length. In order for this to be
** useful, this also turns on noReallocWhenSmaller
**
** NB: this used to have a "boolean" return to indicate allocation
** error, but nothing in Teem actually did the error checking. Now
** conscientious users can look at NULL-ity of a->data to detect such
** an error.
*/
void
142 airArrayLenPreSet( airArray *a, unsigned int newlen ) {
/* char me[]="airArrayLenPreSet"; */
unsigned int newsize;
void *newdata;
if ( !a ) {
return;
}
if ( newlen == 0 ) {
/* there is no pre-set length, turn off noReallocWhenSmaller */
a->noReallocWhenSmaller = AIR_FALSE;
} else {
newsize = ( newlen-1 )/a->incr + 1;
/*
fprintf( stderr, "!%s: newlen = %u, incr = %u -> newsize = %u\n", me,
newlen, a->incr, newsize );
fprintf( stderr, "!%s: a->size = %u, a->len = %u, a->unit = %u\n", me,
a->size, a->len, a->unit );
*/
if ( newsize > a->size ) {
newdata = calloc( newsize*a->incr, a->unit );
/*
fprintf( stderr, "!%s: a->data = %p, newdata = %p\n", me,
a->data, newdata );
*/
if ( !newdata ) {
free( a->data );
_airSetData( a, NULL );
return;
}
if ( a->data ) {
memcpy( newdata, a->data, AIR_MIN( a->len*a->unit,
newsize*a->incr*a->unit ) );
free( a->data );
}
_airSetData( a, newdata );
a->size = newsize;
}
a->noReallocWhenSmaller = AIR_TRUE;
}
/* fprintf( stderr, "!%s: returning data %p\n", me, a->data ); */
return;
}
/* ---- END non-NrrdIO */
/*
******** airArrayLenSet( )
**
** Set the length of the array, allocating or freeing as needed
**
** returns 1 on error, otherwise 0 if okay
** possible errors: bogus arguments, or couldn't allocate new memory segment
**
** In case we can't allocate the new space, the old space is left untouched,
** however if the new length is smaller, the free/done callbacks will
** have been called on invalidated elements
**
** NB: this used to have a "boolean" return to indicate allocation
** error, but almost nothing in Teem actually did the error checking.
** Now conscientious users can look at NULL-ity of a->data to detect
** such an error.
*/
void
207 airArrayLenSet( airArray *a, unsigned int newlen ) {
/* char me[]="airArrayLenSet"; */
unsigned int ii, newsize;
void *addr, *newdata;
if ( !a ) {
/* user is a moron, what can you do */
return;
}
if ( newlen == a->len ) {
/* nothing to do */
return;
}
/* call freeCB/doneCB on all the elements which are going bye-bye */
/* Wed Sep 12 14:40:45 EDT 2007: the order in which these called is
now ascending, instead of descending ( as was the way before ) */
if ( newlen < a->len && ( a->freeCB || a->doneCB ) ) {
for ( ii=newlen; ii<a->len; ii++ ) {
addr = ( char* )( a->data ) + ii*a->unit;
if ( a->freeCB ) {
( a->freeCB )( *( ( void** )addr ) );
} else {
( a->doneCB )( addr );
}
}
}
newsize = newlen ? ( newlen-1 )/a->incr + 1 : 0;
if ( newsize != a->size ) {
/* we have to change the size of the array */
if ( newsize ) {
/* array should be bigger or smaller, but not zero-length */
if ( newsize > a->size
|| ( newsize < a->size && !( a->noReallocWhenSmaller ) ) ) {
newdata = calloc( newsize*a->incr, a->unit );
if ( !newdata ) {
free( a->data );
_airSetData( a, NULL );
return;
}
memcpy( newdata, a->data, AIR_MIN( a->len*a->unit,
newsize*a->incr*a->unit ) );
free( a->data );
_airSetData( a, newdata );
a->size = newsize;
}
} else {
/* array should be zero-length */
free( a->data );
_airSetData( a, NULL );
a->size = newsize;
}
}
/* else new size is still within current allocated length,
and neither "size" nor "data" need to change */
/* call allocCB/initCB on newly created elements */
if ( newlen > a->len && ( a->allocCB || a->initCB ) ) {
for ( ii=a->len; ii<newlen; ii++ ) {
addr = ( char* )( a->data ) + ii*a->unit;
if ( a->allocCB ) {
*( ( void** )addr ) = ( a->allocCB )( );
} else {
( a->initCB )( addr );
}
}
}
_airLenSet( a, newlen );
return;
}
/*
******** airArrayLenIncr( )
**
** Like airArrayLenSet, but works with an increment instead of an
** absolute length. Return value is different:
** got NULL: return 0
** allocation error: return 0, and a->data set to NULL
** no error, delta > 0: return index of 1st element in newly allocated
** segment ( a->len before length was increased )
** no error, delta <= 0: return 0, and a->data unchanged
**
** HEY: it is apparently not clear how to do error checking ( aside from
** looking at a->data ) when there was NO data previously allocated, and the
** first index of the newly allocated data is zero.
*/
unsigned int
297 airArrayLenIncr( airArray *a, int delta ) {
/* char me[]="airArrayLenIncr"; */
unsigned int oldlen, ret, negdel;
if ( !a ) {
return 0;
}
negdel = ( delta < 0
? AIR_UINT( -delta )
: 0 );
if ( delta < 0 && negdel > a->len ) {
/* error: asked for newlength to be negative */
airArrayLenSet( a, 0 );
return 0;
}
oldlen = a->len;
airArrayLenSet( a, ( delta >= 0
? oldlen + AIR_UINT( delta )
: oldlen - negdel ) );
if ( !a->data ) {
/* allocation error */
ret = 0;
} else {
ret = ( delta <= 0 ? 0 : oldlen );
}
return ret;
}
/*
******** airArrayNuke( )
**
** free both the memory pointed to by the struct and the struct itself
*/
airArray *
332 airArrayNuke( airArray *a ) {
if ( a ) {
airArrayLenSet( a, 0 );
free( a );
}
return NULL;
}
/*
******** airArrayNix( )
**
** frees just the struct, leaving the memory it points to untouched
*/
airArray *
347 airArrayNix( airArray *a ) {
if ( a ) {
free( a );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
#include <teemDio.h>
#if TEEM_DIO == 0
#else
/* HEY: these may be SGI-specific */
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#if TEEM_DIO == 0
const int airMyDio = 0;
#else
const int airMyDio = 1;
#endif
int airDisableDio = AIR_FALSE;
static const char
_airNoDioErr[AIR_NODIO_MAX+2][AIR_STRLEN_SMALL] = {
"( invalid noDio value )",
"CAN TOO do direct I/O!",
"direct I/O apparently not available on this architecture",
"direct I/O apparently not suitable for given file format",
"won't do direct I/O on std{in|out|err}",
"got -1 as file descriptor",
"fcntl( F_DIOINFO ) to learn direct I/O specifics failed",
"requested transfer size is too small",
"requested transfer size not a multiple of d_miniosz",
"data memory address not multiple of d_mem",
"current file position not multiple of d_miniosz",
"fcntl( F_SETFL, FDIRECT ) to turn on direct I/O failed",
"memalign( ) test ( on a small chuck of memory ) failed",
"direct I/O ( in air library ) has been disabled with airDisableDio"
};
const char *
62 airNoDioErr( int noDio ) {
if ( AIR_IN_CL( 0, noDio, AIR_NODIO_MAX ) ) {
return _airNoDioErr[noDio+1];
}
else {
return _airNoDioErr[0];
}
}
/*
******** airDioTest
**
** does everything necessary to assess whether direct IO can be used
** to read a data segment of a given size, from a given file
** descriptor, into a given pointer. The given pointer ptr can be
** NULL, and/or the size can be 0, in order to test the other aspects
** of direct IO. The return value of this is from the airNoDio_* enum.
** Note that airNoDio_okay means, "actually, direct IO *does* seem to
** be possible here".
*/
#if TEEM_DIO == 0
int
85 airDioTest( int fd, const void *ptr, size_t size ) {
AIR_UNUSED( fd );
AIR_UNUSED( ptr );
AIR_UNUSED( size );
/* Teem makefiles think no direct IO is possible on this architecture */
return airNoDio_arch;
}
#else
int
95 airDioTest( int fd, const void *ptr, size_t size ) {
struct dioattr dioinfo;
void *tmp;
int flags;
if ( airDisableDio ) {
/* user turned direct I/O off */
return airNoDio_disable;
}
if ( 0 == fd || 1 == fd || 2 == fd ) {
/* This was added because I was noticing a problem with piping
between unrrdu programs- sometimes the fread( ) of the receiving
data through a unix pipe ( "|" ) failed to read all the data. If
the body of this function was bypassed ( with "return
airNoDio_disable;", for instance ), then the problem went away.
The problematic call seemed to be the fflush( ) below ( Tue Feb 1
06:47:33 EST 2005: which has since been removed with the change
of this function's argument from a FILE * to an integral file
descriptor ). I don't think direct I/O is possible on stdin,
stdout, or stdout, since the fcntl( ) call below fails on stdin
and stdout. However, something about making that fcntl( ) call
changes something which means that about half the time, the
read( ) on a piped stdin fails ( on an irix6.n32 O2, at
least ). So, seems to be safest to just explicitly say that
direct I/O is unavailable, based solely on the file descriptor
number ( 0, 1, 2 ). */
return airNoDio_std;
}
if ( -1 == fd ) {
/* caller probably couldn't get the underlying file descriptor */
return airNoDio_fd;
}
if ( 0 != fcntl( fd, F_DIOINFO, &dioinfo ) ) {
/* couldn't learn direct I/O specifics */
return airNoDio_dioinfo;
}
if ( size ) {
/*
** direct I/O requirements:
** 1 ) xfer size between d_miniosz and d_maxiosz
** 2 ) xfer size a multiple of d_miniosz
** 3 ) memory buffer on d_mem-byte boundary
** 4 ) file position on d_miniosz-byte boundary
**
** As long as xfer size is >= d_miniosz and meets req. #2, then
** we can break the xfer into d_maxiosz-size pieces of need be.
** We can test #3 here if we're given non-NULL ptr
** We can always test #4
*/
if ( size < dioinfo.d_miniosz ) {
/* fails req. #1 above */
return airNoDio_small;
}
/* we don't actually check for being too large, since we can always
do IO on d_maxiosz-sized pieces */
if ( size % dioinfo.d_miniosz ) {
/* fails req. #2 above */
return airNoDio_size;
}
}
if ( ptr ) {
if ( ( unsigned long )( ptr ) % dioinfo.d_mem ) {
/* fails req. #3 above */
return airNoDio_ptr;
}
} else {
tmp = memalign( dioinfo.d_mem, dioinfo.d_miniosz );
if ( !tmp ) {
/* couldn't even alloc ( via memalign ) the minimum size */
return airNoDio_test;
}
free( tmp );
}
if ( lseek( fd, 0, SEEK_CUR ) % dioinfo.d_miniosz ) {
/* fails req. #4 above */
return airNoDio_fpos;
}
flags = fcntl( fd, F_GETFL );
if ( -1 == fcntl( fd, F_SETFL, flags | FDIRECT ) ) {
/* couln't turn on direct I/O */
return airNoDio_setfl;
}
/* put things back the way they were */
fcntl( fd, F_SETFL, flags );
/* as far as we know, direct I/O seems workable */
return airNoDio_okay;
}
#endif
/*
******** airDioInfo
**
** does the fcntl stuff to learn the direct IO parameters:
** align: required alignment of memory ( pointer must be multiple of this )
** min: minimum size of dio transfer
** max: maximum size of dio transfer
**
** NOTE: this does not try to do any error checking, because it assumes
** that you've already called airDioTest without incident.
*/
#if TEEM_DIO == 0
void
199 airDioInfo( int *align, int *min, int *max, int fd ) {
AIR_UNUSED( align );
AIR_UNUSED( min );
AIR_UNUSED( max );
AIR_UNUSED( fd );
return;
}
#else
void
208 airDioInfo( int *align, int *min, int *max, int fd ) {
struct dioattr dioinfo;
if ( align && min && max && !fcntl( fd, F_DIOINFO, &dioinfo ) ) {
*align = dioinfo.d_mem;
*min = dioinfo.d_miniosz;
*max = dioinfo.d_maxiosz;
}
return;
}
#endif
/*
******** airDioMalloc
**
** does direct IO compatible memory allocation.
**
** NOTE: like airDioInfo, this assumes that you've called airDioTest
** without incident
*/
#if TEEM_DIO == 0
void *
230 airDioMalloc( size_t size, int fd ) {
AIR_UNUSED( size );
AIR_UNUSED( fd );
return NULL;
}
#else
void *
238 airDioMalloc( size_t size, int fd ) {
int align, min, max;
airDioInfo( &align, &min, &max, fd );
return memalign( align, size );
}
#endif
/*
******** airDioRead
**
** like read( ), but for direct IO. The idea is that you call this on as
** big a chunk of memory as possible.
**
** NOTE: like airDioInfo, this assumes that you've called airDioTest
** without incident
*/
#if TEEM_DIO == 0
size_t
257 airDioRead( int fd, void *_ptr, size_t size ) {
AIR_UNUSED( fd );
AIR_UNUSED( _ptr );
AIR_UNUSED( size );
return 0;
}
#else
size_t
266 airDioRead( int fd, void *_ptr, size_t size ) {
size_t red, totalred;
int align, min, max, flags;
size_t remain, part;
char *ptr;
if ( !( _ptr && airNoDio_okay == airDioTest( fd, _ptr, size ) ) ) {
return 0;
}
flags = fcntl( fd, F_GETFL );
fcntl( fd, F_SETFL, flags | FDIRECT );
airDioInfo( &align, &min, &max, fd );
remain = size;
totalred = 0;
ptr = ( char* )_ptr;
do {
part = AIR_MIN( remain, max );
red = read( fd, ptr, part );
totalred += red;
if ( red != part ) {
break;
}
ptr += red;
remain -= red;
} while ( remain );
fcntl( fd, F_SETFL, flags );
return totalred;
}
#endif
/*
******** airDioWrite
**
** like write( ), but for direct IO. The idea is that you call this on as
** big a chunk of memory as possible.
**
** NOTE: like airDioInfo, this assumes that you've called airDioTest
** without incident
*/
#if TEEM_DIO == 0
size_t
309 airDioWrite( int fd, const void *_ptr, size_t size ) {
AIR_UNUSED( fd );
AIR_UNUSED( _ptr );
AIR_UNUSED( size );
return 0;
}
#else
size_t
318 airDioWrite( int fd, const void *_ptr, size_t size ) {
size_t rit, totalrit;
int align, min, max, flags;
size_t remain, part;
char *ptr;
if ( !( _ptr && ( airNoDio_okay == airDioTest( fd, _ptr, size ) ) ) ) {
return 0;
}
flags = fcntl( fd, F_GETFL );
fcntl( fd, F_SETFL, flags | FDIRECT );
airDioInfo( &align, &min, &max, fd );
remain = size;
totalrit = 0;
ptr = ( char* )_ptr;
do {
part = AIR_MIN( remain, max );
rit = write( fd, ptr, part );
totalrit += rit;
if ( rit != part ) {
break;
}
ptr += rit;
remain -= rit;
} while ( remain );
fcntl( fd, F_SETFL, flags );
return totalrit;
}
#endif
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/*
******** airMyEndian( )
**
** determine at run-time if we are little ( 1234 ) or big ( 4321 ) endian
*/
int
32 airMyEndian( void ) {
int tmpI, ret;
char leastbyte;
/* set int to 1:
least signficant byte will be 1,
most signficant byte will be 0 */
tmpI = 1;
/* cast address of ( 4-byte ) int to char*, and dereference,
which retrieves the byte at the low-address-end of int
( the "first" byte in memory ordering ).
On big endian, we're getting the most significant byte ( 0 );
on little endian, we're getting least significant byte ( 1 ) */
leastbyte = *( AIR_CAST( char*, &tmpI ) );
if ( leastbyte ) {
ret = airEndianLittle;
} else {
ret = airEndianBig;
}
return ret;
}
static const char *
_airEndianStr[] = {
"( unknown endian )",
"little",
"big"
};
static const char *
_airEndianDesc[] = {
"unknown endianness",
"Intel and compatible",
"Everyone besides Intel and compatible"
};
static const int
_airEndianVal[] = {
airEndianUnknown,
airEndianLittle,
airEndianBig,
};
static const airEnum
_airEndian = {
"endian",
2,
_airEndianStr, _airEndianVal,
_airEndianDesc,
NULL, NULL,
AIR_FALSE
};
85 const airEnum *const
airEndian = &_airEndian;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/*
** Until Teem has its own printf implementation, this will have to do;
** it is imperfect because these are not functionally identical.
*/
#if defined( WIN32 ) || defined( _WIN32 )
# define snprintf _snprintf
#endif
/*
******** airEnumUnknown
**
** return the value representing "unknown" in an enum
*/
int
40 airEnumUnknown( const airEnum *enm ) {
if ( enm && enm->val ) {
return enm->val[0];
} else {
return 0;
}
}
/*
** _airEnumIndex( )
**
** given an enum "enm" and value "val", return the index into enm->str[]
** and enm->desc[] which correspond to that value. To be safe, when
** given an invalid enum value, we return zero.
*/
static unsigned int
57 _airEnumIndex( const airEnum *enm, int val ) {
unsigned int ii, ret;
ret = 0;
if ( enm->val ) {
for ( ii=1; ii<=enm->M; ii++ ) {
if ( val == enm->val[ii] ) {
ret = ii;
break;
}
}
} else {
unsigned int uval;
uval = AIR_UINT( val );
ret = ( 0 <= val && uval <= enm->M ) ? uval : 0;
}
return ret;
}
/*
** returns non-zero if there is an error: given "val" is *not*
** a valid value of the airEnum "enm"
*/
int
81 airEnumValCheck( const airEnum *enm, int val ) {
return ( 0 == _airEnumIndex( enm, val ) );
}
const char *
87 airEnumStr( const airEnum *enm, int val ) {
unsigned int idx;
idx = _airEnumIndex( enm, val );
return enm->str[idx];
}
const char *
95 airEnumDesc( const airEnum *enm, int val ) {
unsigned int idx;
idx = _airEnumIndex( enm, val );
return enm->desc[idx];
}
int
103 airEnumVal( const airEnum *enm, const char *str ) {
char *strCpy, test[AIR_STRLEN_SMALL];
unsigned int ii;
if ( !str ) {
return airEnumUnknown( enm );
}
strCpy = airStrdup( str );
if ( !enm->sense ) {
airToLower( strCpy );
}
if ( enm->strEqv ) {
/* want strlen and not airStrlen here because the strEqv array
should be terminated by a non-null empty string */
for ( ii=0; strlen( enm->strEqv[ii] ); ii++ ) {
airStrcpy( test, AIR_STRLEN_SMALL, enm->strEqv[ii] );
if ( !enm->sense ) {
airToLower( test );
}
if ( !strcmp( test, strCpy ) ) {
free( strCpy );
return enm->valEqv[ii];
}
}
} else {
/* enm->strEqv NULL */
for ( ii=1; ii<=enm->M; ii++ ) {
airStrcpy( test, AIR_STRLEN_SMALL, enm->str[ii] );
if ( !enm->sense ) {
airToLower( test );
}
if ( !strcmp( test, strCpy ) ) {
free( strCpy );
return enm->val ? enm->val[ii] : ( int )ii; /* HEY scrutinize cast */
}
}
}
/* else we never matched a string */
free( strCpy );
return airEnumUnknown( enm );
}
/*
******** airEnumFmtDesc( )
**
** Formats a description line for one element "val" of airEnum "enm",
** and puts the result in a NEWLY ALLOCATED string which is the return
** of this function. The formatting is done via sprintf( ), as governed
** by "fmt", which should contain to "%s" conversion sequences, the
** first for the string version "val", and the second for the
** description If "canon", then the canonical string representation
** will be used ( the one in enm->str[] ), otherwise the shortest string
** representation will be used ( which differs from the canonical one
** when there is a strEqv[]/valEqv[] pair defining a shorter string )
*/
char *
162 airEnumFmtDesc( const airEnum *enm, int val, int canon, const char *fmt ) {
const char *desc;
char *buff, ident[AIR_STRLEN_SMALL];
const char *_ident;
int i;
size_t len;
if ( !( enm && enm->desc && fmt ) ) {
return airStrdup( "( airEnumDesc: invalid args )" );
}
if ( airEnumValCheck( enm, val ) ) {
val = airEnumUnknown( enm );
}
_ident = airEnumStr( enm, val );
if ( !canon && enm->strEqv ) {
len = airStrlen( _ident );
for ( i=0; airStrlen( enm->strEqv[i] ); i++ ) {
if ( val != enm->valEqv[i] ) {
/* this isn't a string representing the value we care about */
continue;
}
if ( airStrlen( enm->strEqv[i] ) < len ) {
/* this one is shorter */
len = airStrlen( enm->strEqv[i] );
_ident = enm->strEqv[i];
}
}
}
airStrcpy( ident, AIR_STRLEN_SMALL, _ident );
if ( !enm->sense ) {
airToLower( ident );
}
desc = enm->desc[_airEnumIndex( enm, val )];
buff = AIR_CALLOC( airStrlen( fmt ) + airStrlen( ident ) +
airStrlen( desc ) + 1, char );
if ( buff ) {
sprintf( buff, fmt, ident, desc );
}
return buff;
}
static void
204 _enumPrintVal( FILE *file, const airEnum *enm, int ii ) {
if ( enm->desc ) {
fprintf( file, "desc: %s\n", enm->desc[ii] );
}
if ( enm->strEqv ) {
unsigned int jj;
fprintf( file, "eqv:" ); fflush( file );
jj = 0;
while ( airStrlen( enm->strEqv[jj] ) ) {
if ( enm->valEqv[jj] == ( enm->val
? enm->val[ii]
: ii ) ) {
fprintf( file, " \"%s\"", enm->strEqv[jj] );
}
jj++;
}
fprintf( file, "\n" );
}
}
void
226 airEnumPrint( FILE *file, const airEnum *enm ) {
int ii; /* this should arguable be unsigned int, but
airEnum values were kept as "int", even after
the great unsigned conversion */
if ( !( file && enm ) ) {
return;
}
if ( airStrlen( enm->name ) ) {
fprintf( file, "airEnum \"%s\":\n", enm->name );
} else {
fprintf( file, "airEnum ( NO NAME! ):\n" );
}
fprintf( file, "( %s case sensitive )\n", ( enm->sense ? "yes, is" : "is not" ) );
if ( enm->val ) {
fprintf( file, "Values ( %u valid ) given explicitly\n", enm->M );
fprintf( file, "--- ( 0 ) %d: \"%s\"\n", enm->val[0], enm->str[0] );
for ( ii=1; ii<=AIR_CAST( int, enm->M ); ii++ ) {
fprintf( file, "--- ( %d ) %d: \"%s\" == \"%s\"\n", ii,
enm->val[ii], enm->str[ii],
airEnumStr( enm, enm->val[ii] ) );
_enumPrintVal( file, enm, ii );
}
} else {
/* enm->val NULL */
fprintf( file, "Values implicit; [1, %u] valid\n", enm->M );
fprintf( file, "--- 0: \"%s\"\n", enm->str[0] );
for ( ii=1; ii<=AIR_CAST( int, enm->M ); ii++ ) {
fprintf( file, "--- %d: %s == %s\n", ii, enm->str[ii],
airEnumStr( enm, ii ) );
_enumPrintVal( file, enm, ii );
}
}
return;
}
/* ---- BEGIN non-NrrdIO */
/*
******** airEnumCheck
**
** tries various things to check on the construction and internal
** consistency of an airEnum; returns 1 if there is a problem, and 0
** if all is well. we're in air, so there's no biff, but we sprintf a
** description of the error into "err", if given
**
** The requirement that the strings have strlen <= AIR_STRLEN_SMALL-1
** is a reflection of the cheap implementation of the airEnum
** functions in this file, rather than an actual restriction on what an
** airEnum could be.
*/
int
278 airEnumCheck( char err[AIR_STRLEN_LARGE], const airEnum *enm ) {
static const char me[]="airEnumCheck";
unsigned int ii, jj;
size_t slen, ASL;
ASL = AIR_STRLEN_LARGE;
if ( !enm ) {
if ( err ) {
snprintf( err, ASL, "%s: got NULL enm", me );
}
return 1;
}
if ( !enm->name ) {
if ( err ) {
snprintf( err, ASL, "%s: enm->name NULL", me );
}
return 1;
}
if ( 0 == enm->M ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): enm->M zero; no values in enum",
me, enm->name );
}
return 1;
}
for ( ii=0; ii<=enm->M; ii++ ) {
if ( !enm->str[ii] ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): enm->str[%u] NULL", me, enm->name, ii );
}
return 1;
}
slen = airStrlen( enm->str[ii] );
if ( !( slen >= 1 && slen <= AIR_STRLEN_SMALL-1 ) ) {
if ( err ) {
char stmp[AIR_STRLEN_SMALL];
snprintf( err, ASL, "%s( %s ): strlen( enm->str[%u] \"%s\" ) "
"%s not in range [1, %u]", me,
enm->name, ii, enm->str[ii],
airSprintSize_t( stmp, slen ), AIR_STRLEN_SMALL-1 );
}
return 1;
}
/* make sure there are no duplicates among the strings,
including remapping the case in case of case insensitivity */
for ( jj=ii+1; jj<=enm->M; jj++ ) {
if ( !enm->str[jj] ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): enm->str[%u] NULL", me, enm->name, jj );
}
return 1;
}
if ( !strcmp( enm->str[ii], enm->str[jj] ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): str[%d] and [%u] both \"%s\"",
me, enm->name, ii, jj, enm->str[jj] );
}
return 1;
}
if ( !enm->sense ) {
char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL];
strcpy( bb1, enm->str[ii] );
airToLower( bb1 );
strcpy( bb2, enm->str[jj] );
airToLower( bb2 );
if ( !strcmp( bb1, bb2 ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): after case-lowering, "
"str[%d] and [%u] both \"%s\"",
me, enm->name, ii, jj, bb1 );
}
return 1;
}
}
}
}
if ( enm->val ) {
for ( ii=1; ii<=enm->M; ii++ ) {
if ( enm->val[0] == enm->val[ii] ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): val[%u] %u same as "
"unknown/invalid val[0] %u",
me, enm->name, ii, enm->val[ii], enm->val[0] );
}
return 1;
}
for ( jj=ii+1; jj<=enm->M; jj++ ) {
if ( enm->val[jj] == enm->val[ii] ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): val[%u] %u same as val[%u] %u", me,
enm->name, ii, enm->val[ii], jj, enm->val[jj] );
}
return 1;
}
}
}
}
if ( enm->desc ) {
for ( ii=0; ii<=enm->M; ii++ ) {
if ( !enm->desc[ii] ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): enm->desc[%u] NULL", me, enm->name, ii );
}
return 1;
}
/* we don't really care about slen, but learning it will
hopefully produce some memory error if the array is not valid */
slen = airStrlen( enm->desc[ii] );
if ( !( slen > 0 ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): enm->desc[%u] empty", me, enm->name, ii );
}
return 1;
}
}
}
if ( enm->strEqv ) {
/* the strEqv array is one of the easiest ways to mess up an
airEnum definition; it deserves these tests and maybe more */
if ( !enm->valEqv ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): non-NULL strEqv but NULL valEqv",
me, enm->name );
}
return 1;
}
if ( !( airStrlen( enm->strEqv[0] ) ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): strEqv[0] is NULL or empty",
me, enm->name );
}
return 1;
}
/* check length and see if any string maps to an invalid value */
for ( ii=0; ( slen = strlen( enm->strEqv[ii] ) ); ii++ ) {
if ( !( slen <= AIR_STRLEN_SMALL-1 ) ) {
if ( err ) {
char stmp[AIR_STRLEN_SMALL];
snprintf( err, ASL, "%s( %s ): strlen( enm->strEqv[%u] \"%s\" ) "
"%s not <= %u", me, enm->name, ii, enm->strEqv[ii],
airSprintSize_t( stmp, slen ), AIR_STRLEN_SMALL-1 );
}
return 1;
}
/* see if a string maps to an invalid value */
if ( airEnumValCheck( enm, enm->valEqv[ii] ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): valEqv[%u] %u ( with strEqv[%u] \"%s\" )"
" not valid",
me, enm->name, ii, enm->valEqv[ii], ii, enm->strEqv[ii] );
}
return 1;
}
}
/* make sure eqv strings contain the canonical string */
for ( ii=1; ii<=enm->M; ii++ ) {
int eval, rval;
eval = ( enm->val ? enm->val[ii] : AIR_CAST( int, ii ) );
rval = airEnumVal( enm, enm->str[ii] );
if ( eval != rval ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): ii=%u->eval=%d->str=\"%s\"->%d != %d "
"( i.e. canonical string didn't map to its own value )",
me, enm->name, ii, eval, enm->str[ii], rval, eval );
}
return 1;
}
}
/* make sure there are no duplicates among the strEqv,
including remapping the case in case of case insensitivity */
for ( ii=0; strlen( enm->strEqv[ii] ); ii++ ) {
for ( jj=ii+1; strlen( enm->strEqv[jj] ); jj++ ) {
if ( !strcmp( enm->strEqv[ii], enm->strEqv[jj] ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): strEqv[%d] and [%u] both \"%s\"",
me, enm->name, ii, jj, enm->strEqv[jj] );
}
return 1;
}
if ( !enm->sense ) {
char bb1[AIR_STRLEN_SMALL], bb2[AIR_STRLEN_SMALL];
strcpy( bb1, enm->strEqv[ii] );
airToLower( bb1 );
strcpy( bb2, enm->strEqv[jj] );
airToLower( bb2 );
if ( !strcmp( bb1, bb2 ) ) {
if ( err ) {
snprintf( err, ASL, "%s( %s ): after case-lowering, "
"strEqv[%d] and [%u] both \"%s\"",
me, enm->name, ii, jj, bb1 );
}
return 1;
}
}
}
}
}
return 0;
}
/* ---- END non-NrrdIO */
/* this is the end */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2010 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
#include <string.h>
/* internal helper routines */
/* restore heap property by pulling a single node up the tree */
27 static void upheap( airHeap *h, unsigned int i ) {
while ( i ) {
unsigned int parent = ( i-1 )/2;
if ( h->key[h->idx[parent]] > h->key[h->idx[i]] ) { /* swap */
unsigned int tmp = h->idx[parent];
h->idx[parent] = h->idx[i];
h->idx[i] = tmp;
h->invidx[h->idx[i]] = i;
h->invidx[h->idx[parent]] = parent;
i = parent;
} else
i = 0; /* break loop */
}
}
/* restore heap property by pushing a single node down the tree */
43 static void downheap ( airHeap *h, unsigned int i ) {
unsigned int len = h->key_a->len;
while ( 1 ) {
unsigned int left = 2*i+1;
unsigned int right = 2*i+2;
unsigned int minidx = 0;
double minval = 0;
if ( left>=len )
return;
if ( ( right>=len ) ||
( h->key[h->idx[left]] < h->key[h->idx[right]] ) ) {
minidx = left;
minval = h->key[h->idx[left]];
} else {
minidx = right;
minval = h->key[h->idx[right]];
}
if ( h->key[h->idx[i]] > minval ) { /* swap */
unsigned int tmp = h->idx[i];
h->idx[i] = h->idx[minidx];
h->idx[minidx] = tmp;
h->invidx[h->idx[i]] = i;
h->invidx[h->idx[minidx]] = minidx;
i = minidx;
} else
return;
}
}
/* Restore heap property "from scratch" ( without any prior assumptions ).
* Works in a bottom-up manner, which is more efficient than top-down */
74 static void heapify ( airHeap *h ) {
unsigned int len = h->key_a->len;
int maxi = len/2-1, i; /* above maxi, downheap has no effect */
for ( i=maxi; i>=0; i-- ) {
downheap( h, i );
}
}
/* Tries to increase or decrease the length of h. returns 0 upon success. */
83 static int heapLenIncr ( airHeap *h, int delta ) {
unsigned int oldlen=h->key_a->len;
unsigned int newlen=oldlen+delta;
if ( delta==0 )
return 0;
airArrayLenIncr( h->key_a, delta );
if ( h->data_a!=NULL ) airArrayLenIncr( h->data_a, delta );
airArrayLenIncr( h->idx_a, delta );
airArrayLenIncr( h->invidx_a, delta );
if ( h->key_a->len<newlen || ( h->data_a!=NULL && h->data_a->len<newlen ) ||
h->idx_a->len<newlen || h->invidx_a->len<newlen ) {
/* Error. Try to undo changes and return error code. */
if ( h->key_a->len>oldlen ) airArrayLenSet( h->key_a, oldlen );
if ( h->data_a!=NULL && h->data_a->len>oldlen )
airArrayLenSet( h->data_a, oldlen );
if ( h->idx_a->len>oldlen ) airArrayLenSet( h->idx_a, oldlen );
if ( h->invidx_a->len>oldlen ) airArrayLenSet( h->invidx_a, oldlen );
return 1;
}
return 0;
}
/* Creates a new heap, returning NULL upon error. If additional data
* is to be stored with each node, dataUnit needs to be set to the
* number of data bytes needed per element. incr is used for dynamic
* memory allocation ( an additional number of incr elements are
* allocated each time the heap grows past its current capacity ).
*/
111 airHeap *airHeapNew( size_t dataUnit, unsigned int incr ) {
airHeap *h;
airPtrPtrUnion appu;
h = AIR_CALLOC( 1, airHeap );
if ( h==NULL ) {
return NULL;
}
appu.d = &h->key;
h->key_a = airArrayNew( appu.v, NULL, sizeof( double ), incr );
if ( dataUnit>0 ) { /* data is optional */
h->data_a = airArrayNew( &h->data, NULL, dataUnit, incr );
}
appu.ui = &h->idx;
h->idx_a = airArrayNew( appu.v, NULL, sizeof( unsigned int ), incr );
appu.ui = &h->invidx;
h->invidx_a = airArrayNew( appu.v, NULL, sizeof( unsigned int ), incr );
if ( h->key_a==NULL || ( dataUnit>0 && h->data_a==NULL ) || h->idx_a==NULL ||
h->invidx_a==NULL ) { /* allocation failed ( partly ) */
airHeapNix( h );
return NULL;
}
return h;
}
/* Same as airHeapNew, but initializes the new heap with the keys from key
* and the optional data from data. If data is non-NULL, it needs to
* have the same length as key, or this function will fail. The incr
* field of the heap is copied from key ( rather than data ).
*/
142 airHeap *airHeapFromArray( const airArray *key, const airArray *data ) {
airHeap *h;
unsigned int i;
if ( key==NULL || ( data!=NULL && data->len!=key->len ) )
return NULL; /* unusable input */
h = airHeapNew( ( data==NULL )?0:data->unit, key->incr );
if ( heapLenIncr ( h, key->len ) ) { /* could not allocate memory */
airHeapNix( h );
return NULL;
}
memcpy( h->key, key->data, key->len*sizeof( double ) );
if ( h->data_a!=NULL ) memcpy( h->data, data->data, data->len*data->unit );
for ( i=0; i<key->len; i++ ) { /* initialize indices to identity */
h->idx[i]=i;
h->invidx[i]=i;
}
heapify( h );
return h;
}
/* Frees all memory associated with the heap and its data */
163 airHeap *airHeapNix( airHeap *h ) {
if ( h!=NULL ) {
airArrayNuke( h->key_a );
if ( h->data_a!=NULL ) airArrayNuke( h->data_a );
airArrayNuke( h->idx_a );
airArrayNuke( h->invidx_a );
free( h );
}
return NULL;
}
/* Returns the number of elements that are currently in heap h
* ( or 0 if h==NULL ) */
176 unsigned int airHeapLength( const airHeap *h ) {
if ( h!=NULL ) {
return h->key_a->len;
} else
return 0;
}
/* Inserts a key into the heap. data is copied over ( deep copy ) when the
* heap was initialized to hold additional data. Otherwise, it is ignored.
*
* Returns the new number of elements in h.
* Upon error, this can be the same as the old length. */
188 unsigned int airHeapInsert( airHeap *h, double key, const void *data ) {
unsigned int oldlen;
if ( h==NULL ) return 0;
oldlen = h->key_a->len;
/* make space for the new element */
if ( heapLenIncr( h, 1 ) )
return oldlen;
h->key[oldlen]=key;
if ( h->data_a!=NULL && data!=NULL ) {
memcpy( ( char* )h->data_a->data+oldlen*h->data_a->unit, data,
h->data_a->unit );
}
h->idx[oldlen]=oldlen;
h->invidx[oldlen]=oldlen;
upheap( h, oldlen ); /* restore the heap property */
return oldlen+1;
}
/* Merges the second heap into the first. Returns the new length of first,
* or zero upon error. */
208 unsigned int airHeapMerge( airHeap *first, const airHeap *second ) {
unsigned int first_len, second_len, i;
if ( first==NULL || second==NULL )
return 0;
if ( ( first->data_a==NULL ) ^ ( second->data_a==NULL ) )
return 0; /* incompatible data */
if ( first->data_a!=NULL &&
( first->data_a->unit!=second->data_a->unit ) )
return 0; /* incompatible data */
/* make sufficient space in first */
first_len = first->key_a->len;
second_len = second->key_a->len;
if ( heapLenIncr( first, second_len ) )
return 0;
/* concatenate and heapify */
memcpy( first->key+first_len, second->key, second_len*sizeof( double ) );
if ( first->data_a!=NULL )
memcpy( ( char* )first->data_a->data+first_len*first->data_a->unit,
second->data_a->data, second_len*second->data_a->unit );
for ( i=0; i<second_len; i++ ) {
first->idx[first_len+i] = first_len+second->idx[i];
first->invidx[first->idx[first_len+i]]=first_len+i;
}
heapify( first );
return first_len+second_len;
}
/* Returns the key of the front element in the heap and optionally copies the
* associated data to *data. Does not modify the heap. */
237 double airHeapFrontPeek( const airHeap *h, void *data ) {
if ( h==NULL || h->key_a->len==0 )
return 0.0;
if ( data!=NULL && h->data_a!=NULL )
memcpy( data, ( char* )h->data_a->data+h->idx[0]*h->data_a->unit,
h->data_a->unit );
return h->key[h->idx[0]];
}
/* Same as airHeapFrontPeek, but additionally removes the front element. */
247 double airHeapFrontPop( airHeap *h, void *data ) {
double retval = airHeapFrontPeek( h, data );
if ( h!=NULL && h->key_a->len>0 ) {
airHeapRemove( h, h->idx[0] );
}
return retval;
}
/* Assigns a new key ( and optionally, new data ) to the front element and
* re-sorts the heap if necessary. */
257 int airHeapFrontUpdate( airHeap *h, double newKey, const void *newData ) {
if ( h==NULL || h->key_a->len==0 )
return 1;
if ( newData!=NULL && h->data_a!=NULL )
memcpy( ( char* )h->data_a->data+h->idx[0]*h->data_a->unit, newData,
h->data_a->unit );
h->key[h->idx[0]]=newKey;
downheap( h, 0 );
return 0;
}
/* The following functions provide advanced functionality and should
* not be required in most applications. */
/* airHeapFind returns 0 if an element is found whose data is bitwise
* equal to the given data, and sets *ai to the index of this
* element. If more than one element matches the data, an arbitrary
* one of them is returned. The index can be used with airHeapRemove
* or airHeapUpdate, but will become invalid as soon as any element is
* removed from the heap. */
277 int airHeapFind( const airHeap *h, unsigned int *ai, const void *data ) {
unsigned int i;
if ( h==NULL || ai==NULL || data==NULL || h->data_a==NULL )
return 1;
for ( i=0; i<h->key_a->len; i++ ) {
if ( !memcmp( ( char* )h->data_a->data+i*h->data_a->unit, data,
h->data_a->unit ) ) {
*ai = i;
return 0;
}
}
return 1;
}
/* Removes element ai from the heap, returns 0 upon success. */
292 int airHeapRemove( airHeap *h, unsigned int ai ) {
unsigned int old_invidx_ai;
if ( h==NULL || h->key_a->len<=ai )
return 1;
/* in the tree, replace ai with last element */
old_invidx_ai=h->invidx[ai];
h->idx[h->invidx[ai]]=h->idx[h->key_a->len-1];
h->invidx[h->idx[h->key_a->len-1]]=h->invidx[ai];
/* remove ai - copy last element over, then drop it */
if ( ai!=h->key_a->len-1 ) {
h->key[ai]=h->key[h->key_a->len-1];
if ( h->data_a!=NULL ) {
memcpy( ( char* )h->data_a->data+ai*h->data_a->unit,
( char* )h->data_a->data+( h->key_a->len-1 )*h->data_a->unit,
h->data_a->unit );
}
h->idx[h->invidx[h->key_a->len-1]]=ai;
h->invidx[ai]=h->invidx[h->key_a->len-1];
}
heapLenIncr( h, -1 );
/* push moved element downheap */
if ( old_invidx_ai<h->key_a->len )
downheap( h, old_invidx_ai );
return 0;
}
/* Changes the key ( and optional data ) of the element ai, re-sorting
* the heap if necessary. Returns 0 upon success. */
320 int airHeapUpdate( airHeap *h, unsigned int ai, double newKey,
const void *newData ) {
double oldkey;
if ( h==NULL || h->key_a->len<=ai )
return 1;
oldkey = h->key[ai];
/* replace key and data */
h->key[ai] = newKey;
if ( h->data_a!=NULL && newData!=NULL ) {
memcpy( ( char* )h->data_a->data+ai*h->data_a->unit,
newData, h->data_a->unit );
}
if ( oldkey<newKey ) downheap( h, h->invidx[ai] );
else upheap( h, h->invidx[ai] );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/*
** by the way, the organization of functions into files is a little
** arbitrary around here
*/
/*
** from: Gavin C Cawley, "On a Fast, Compact Approximation of the
** Exponential Function" Neural Computation, 2000, 12( 9 ), 2009-2012.
**
** which in turn is based on: N N Schraudolph, "A fast, compact approximation
** of the exponential function." Neural Computation, 1999, 11( 4 ), 853-862.
** http://cnl.salk.edu/~schraudo/pubs/Schraudolph99.pdf
**
** some possible code which does not depend on endian
** http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html
*/
typedef union {
double dd;
int nn[2];
} eco_t;
#define EXPA ( 1048576/0.69314718055994530942 )
#define EXPC 60801
double
52 airFastExp( double val ) {
eco_t eco;
double ret;
int tmpI, EXPI;
/* HEY: COPY AND PASTE from airMyEndian */
tmpI = 1;
EXPI = *( AIR_CAST( char*, &tmpI ) );
eco.nn[EXPI] = AIR_CAST( int, ( EXPA*( val ) ) + ( 1072693248 - EXPC ) );
eco.nn[1-EXPI] = 0;
ret = ( eco.dd > 0.0
? eco.dd
/* seems that only times this happens is when the real exp( )
returns either 0 or +inf */
: ( val < 0 ? 0 : AIR_POS_INF ) );
return ret;
}
#undef EXPA
#undef EXPC
/*
** based on MiniMaxApproximation, but this has failed in its goal
** of being faster than libc's exp( ), so should be considered
** a work-in-progress
*/
double
78 airExp( double x ) {
double num, den, ee;
unsigned int pp;
if ( AIR_IN_CL( -1, x, 1 ) ) {
num = 1 + x*( 0.500241 + x*( 0.107193 + ( 0.0118938 + 0.000591457*x )*x ) );
den = 1 + x*( -0.499759 + x*( 0.106952 + ( -0.0118456 + 0.000587495*x )*x ) );
ee = num/den;
} else if ( x > 1 ) {
pp = 0;
while ( x > 2 ) {
x /= 2;
pp += 1;
}
num = 1 + x*( 0.552853 + x*( 0.135772 + ( 0.0183685 + 0.00130944*x )*x ) );
den = 1 + x*( -0.44714 + x*( 0.0828937 + ( -0.00759541 + 0.000291662*x )*x ) );
ee = num/den;
while ( pp ) {
ee *= ee;
pp -= 1;
}
} else if ( x < -1 ) {
pp = 0;
while ( x < -2 ) {
x /= 2;
pp += 1;
}
num = 0.999999+x*( 0.44726+x*( 0.0829439 + ( 0.00760326 + 0.000292122*x )*x ) );
den = 1 + x*( -0.552732 + x*( 0.135702 + ( -0.0183511 + 0.00130689*x )*x ) );
ee = num/den;
while ( pp ) {
ee *= ee;
pp -= 1;
}
} else {
/* x is probably not finite */
ee = exp( x );
}
return ee;
}
/*
******** airNormalRand
**
** generates two numbers with normal distribution ( mean 0, stdv 1 )
** using the Box-Muller transform.
**
** on ( seemingly sound ) advice of
** <http://www.taygeta.com/random/gaussian.html>,
** I'm using the polar form of the Box-Muller transform, instead of the
** Cartesian one described at
** <http://mathworld.wolfram.com/Box-MullerTransformation.html>
**
** this is careful to not write into given NULL pointers
*/
void
134 airNormalRand( double *z1, double *z2 ) {
double w, x1, x2;
do {
x1 = 2*airDrandMT( ) - 1;
x2 = 2*airDrandMT( ) - 1;
w = x1*x1 + x2*x2;
} while ( w >= 1 );
w = sqrt( ( -2*log( w ) )/w );
if ( z1 ) {
*z1 = x1*w;
}
if ( z2 ) {
*z2 = x2*w;
}
return;
}
void
153 airNormalRand_r( double *z1, double *z2, airRandMTState *state ) {
double w, x1, x2;
do {
x1 = 2*airDrandMT_r( state ) - 1;
x2 = 2*airDrandMT_r( state ) - 1;
w = x1*x1 + x2*x2;
} while ( w >= 1 );
w = sqrt( ( -2*log( w ) )/w );
if ( z1 ) {
*z1 = x1*w;
}
if ( z2 ) {
*z2 = x2*w;
}
return;
}
/*
******** airShuffle( )
**
** generates a random permutation of integers [0..N-1] if perm is non-zero,
** otherwise, just fills buff with [0..N-1] in order
**
** see http://en.wikipedia.org/wiki/Knuth_shuffle
*/
void
180 airShuffle( unsigned int *buff, unsigned int N, int perm ) {
unsigned int i;
if ( !( buff && N > 0 ) ) {
return;
}
for ( i=0; i<N; i++ ) {
buff[i] = i;
}
if ( perm ) {
unsigned int swp, tmp;
while ( N > 1 ) {
swp = airRandInt( N );
N--;
tmp = buff[N];
buff[N] = buff[swp];
buff[swp] = tmp;
}
}
}
void
203 airShuffle_r( airRandMTState *state,
unsigned int *buff, unsigned int N, int perm ) {
unsigned int i;
/* HEY !!! COPY AND PASTE !!!! */
if ( !( buff && N > 0 ) ) {
return;
}
for ( i=0; i<N; i++ ) {
buff[i] = i;
}
if ( perm ) {
unsigned int swp, tmp;
while ( N > 1 ) {
swp = airRandInt_r( state, N );
N--;
tmp = buff[N];
buff[N] = buff[swp];
buff[swp] = tmp;
}
}
/* HEY !!! COPY AND PASTE !!!! */
}
double
229 airSgnPow( double v, double p ) {
return ( p == 1
? v
: ( v >= 0
? pow( v, p )
: -pow( -v, p ) ) );
}
double
239 airFlippedSgnPow( double vv, double pp ) {
double sn=1.0;
if ( 1.0 == pp ) {
return vv;
}
if ( vv < 0 ) {
sn = -1.0;
vv = -vv;
}
return sn*( 1.0 - pow( 1.0 - AIR_MIN( 1.0, vv ), pp ) );
}
double
253 airIntPow( double v, int p ) {
double sq, ret;
if ( p > 0 ) {
sq = v;
while ( !( p & 1 ) ) {
/* while the low bit is zero */
p >>= 1;
sq *= sq;
}
/* must terminate because we know p != 0, and when
it terminates we know that the low bit is 1 */
ret = sq;
while ( p >>= 1 ) {
/* while there are any non-zero bits in p left */
sq *= sq;
if ( p & 1 ) {
ret *= sq;
}
}
} else if ( p < 0 ) {
ret = airIntPow( 1.0/v, -p );
} else {
ret = 1.0;
}
return ret;
}
/*
******** airLog2( )
**
** silly little function which returns log_2( n ) if n is exactly a power of 2,
** and -1 otherwise
*/
int
289 airLog2( size_t _nn ) {
size_t nn;
int ret;
nn = _nn;
if ( 0 == nn ) {
/* 0 is not a power of 2 */
ret = -1;
} else {
int alog = 0;
/* divide by 2 while its non-zero and the low bit is off */
while ( !!nn && !( nn & 1 ) ) {
alog += 1;
nn /= 2;
}
if ( 1 == nn ) {
ret = alog;
} else {
ret = -1;
}
}
return ret;
}
int
314 airSgn( double v ) {
return ( v > 0
? 1
: ( v < 0
? -1
: 0 ) );
}
/*
******** airCbrt
**
** cbrt( ) isn't in C89, so any hacks to create a stand-in for cbrt( )
** are done here.
*/
double
329 airCbrt( double v ) {
#if defined( _WIN32 ) || defined( __STRICT_ANSI__ )
return ( v < 0.0 ? -pow( -v, 1.0/3.0 ) : pow( v, 1.0/3.0 ) );
#else
return cbrt( v );
#endif
}
/*
** skewness of three numbers, scaled to fit in [-1, +1]
** -1: small, big, big
** +1: small, small, big
*/
double
343 airMode3_d( const double _v[3] ) {
double num, den, mean, v[3];
mean = ( _v[0] + _v[1] + _v[2] )/3;
v[0] = _v[0] - mean;
v[1] = _v[1] - mean;
v[2] = _v[2] - mean;
num = ( v[0] + v[1] - 2*v[2] )*( 2*v[0] - v[1] - v[2] )*( v[0] - 2*v[1] + v[2] );
den = v[0]*v[0] + v[1]*v[1] + v[2]*v[2] - v[1]*v[2] - v[0]*v[1] - v[0]*v[2];
den = sqrt( den );
return ( den ? num/( 2*den*den*den ) : 0 );
}
double
357 airMode3( double v0, double v1, double v2 ) {
double v[3];
v[0] = v0;
v[1] = v1;
v[2] = v2;
return airMode3_d( v );
}
double
367 airGaussian( double x, double mean, double stdv ) {
x = x - mean;
return exp( -( x*x )/( 2*stdv*stdv ) )/( stdv*sqrt( 2*AIR_PI ) );
}
/*
** The function approximations below were done by GLK in Mathematica,
** using its MiniMaxApproximation[] function. The functional forms
** used for the Bessel functions were copied from Numerical Recipes
** ( which were in turn copied from the "Handbook of Mathematical
** Functions with Formulas, Graphs, and Mathematical Tables" by
** Abramowitz and Stegun ), but the coefficients here represent
** an increase in accuracy.
**
** The rational functions ( crudely copy/paste from Mathematica into
** this file ) upon which the approximations are based have a relative
** error of less than 10^-9, at least on the intervals on which they
** were created ( in the case of the second branch of the
** approximation, the lower end of the interval was chosen as close to
** zero as possible ). The relative error of the total approximation
** may be greater.
*/
double
392 airErfc( double x ) {
double ax, y, b;
ax = AIR_ABS( x );
if ( ax < 0.9820789566638689 ) {
b = ( 0.9999999999995380997 + ax*( -1.0198241793287349401 +
ax*( 0.37030717279808919457 + ax*( -0.15987839763633308864 +
ax*( 0.12416682580357861661 + ( -0.04829622197742573233 +
0.0066094852952188890901*ax )*ax ) ) ) ) )/
( 1 + ax*( 0.1085549876246959456 + ax*( 0.49279836663925410323 +
ax*( 0.020058474597886988472 + ax*( 0.10597158000597863862 +
( -0.0012466514192679810876 + 0.0099475501252703646772*ax )*ax ) ) ) ) );
} else if ( ax < 2.020104167011169 ) {
y = ax - 1;
b = ( 0.15729920705029613528 + y*( -0.37677358667097191131 +
y*( 0.3881956571123873123 + y*( -0.22055886537359936478 +
y*( 0.073002666454740425451 + ( -0.013369369366972563804 +
0.0010602024397541548717*y )*y ) ) ) ) )/
( 1 + y*( 0.243700597525225235 + y*( 0.47203101881562848941 +
y*( 0.080051054975943863026 + y*( 0.083879049958465759886 +
( 0.0076905426306038205308 + 0.0058528196473365970129*y )*y ) ) ) ) );
} else {
y = 2/ax;
b = ( -2.7460876468061399519e-14 + y*( 0.28209479188874503125 +
y*( 0.54260398586720212019 + y*( 0.68145162781305697748 +
( 0.44324741856237800393 + 0.13869182273440856508*y )*y ) ) ) )/
( 1 + y*( 1.9234811027995435174 + y*( 2.5406810534399069812 +
y*( 1.8117409273494093139 + ( 0.76205066033991530997 +
0.13794679143736608167*y )*y ) ) ) );
b *= exp( -x*x );
}
if ( x < 0 ) {
b = 2-b;
}
return b;
}
double
430 airErf( double x ) {
return 1.0 - airErfc( x );
}
/*
******** airBesselI0
**
** modified Bessel function of the first kind, order 0
*/
double
440 airBesselI0( double x ) {
double b, ax, y;
ax = AIR_ABS( x );
if ( ax < 5.664804810929075 ) {
y = x/5.7;
y *= y;
b = ( 0.9999999996966272 + y*( 7.7095783675529646 +
y*( 13.211021909077445 + y*( 8.648398832703904 +
( 2.5427099920536578 + 0.3103650754941674*y )*y ) ) ) )/
( 1 + y*( -0.41292170755003793 + ( 0.07122966874756179
- 0.005182728492608365*y )*y ) );
} else {
y = 5.7/ax;
b = ( 0.398942280546057 + y*( -0.749709626164583 +
y*( 0.507462772839054 + y*( -0.0918770649691261 +
( -0.00135238228377743 - 0.0000897561853670307*y )*y ) ) ) )/
( 1 + y*( -1.90117313211089 + ( 1.31154807540649
- 0.255339661975509*y )*y ) );
b *= ( exp( ax )/sqrt( ax ) );
}
return b;
}
/*
******** airBesselI0ExpScaled
**
** modified Bessel function of the first kind, order 0,
** scaled by exp( -abs( x ) ).
*/
double
471 airBesselI0ExpScaled( double x ) {
double b, ax, y;
ax = AIR_ABS( x );
if ( ax < 5.664804810929075 ) {
y = x/5.7;
y *= y;
b = ( 0.9999999996966272 + y*( 7.7095783675529646 +
y*( 13.211021909077445 + y*( 8.648398832703904 +
( 2.5427099920536578 + 0.3103650754941674*y )*y ) ) ) )/
( 1 + y*( -0.41292170755003793 + ( 0.07122966874756179
- 0.005182728492608365*y )*y ) );
b *= exp( -ax );
} else {
y = 5.7/ax;
b = ( 0.398942280546057 + y*( -0.749709626164583 +
y*( 0.507462772839054 + y*( -0.0918770649691261 +
( -0.00135238228377743 - 0.0000897561853670307*y )*y ) ) ) )/
( 1 + y*( -1.90117313211089 + ( 1.31154807540649
- 0.255339661975509*y )*y ) );
b *= ( 1/sqrt( ax ) );
}
return b;
}
/*
******** airBesselI1
**
** modified Bessel function of the first kind, order 1
*/
double
503 airBesselI1( double x ) {
double b, ax, y;
ax = AIR_ABS( x );
if ( ax < 6.449305566387246 ) {
y = x/6.45;
y *= y;
b = ax*( 0.4999999998235554 + y*( 2.370331499358438 +
y*( 3.3554331305863787 + y*( 2.0569974969268707 +
( 0.6092719473097832 + 0.0792323006694466*y )*y ) ) ) )/
( 1 + y*( -0.4596495788370524 + ( 0.08677361454866868 \
- 0.006777712190188699*y )*y ) );
} else {
y = 6.45/ax;
b = ( 0.398942280267484 + y*( -0.669339325353065 +
y*( 0.40311772245257 + y*( -0.0766281832045885 +
( 0.00248933264397244 + 0.0000703849046144657*y )*y ) ) ) )/
( 1 + y*( -1.61964537617937 + ( 0.919118239717915 -
0.142824922601647*y )*y ) );
b *= exp( ax )/sqrt( ax );
}
return x < 0.0 ? -b : b;
}
/*
******** airBesselI1ExpScaled
**
** modified Bessel function of the first kind, order 1,
** scaled by exp( -abs( x ) )
*/
double
534 airBesselI1ExpScaled( double x ) {
double b, ax, y;
ax = AIR_ABS( x );
if ( ax < 6.449305566387246 ) {
y = x/6.45;
y *= y;
b = ax*( 0.4999999998235554 + y*( 2.370331499358438 +
y*( 3.3554331305863787 + y*( 2.0569974969268707 +
( 0.6092719473097832 + 0.0792323006694466*y )*y ) ) ) )/
( 1 + y*( -0.4596495788370524 + ( 0.08677361454866868 \
- 0.006777712190188699*y )*y ) );
b *= exp( -ax );
} else {
y = 6.45/ax;
b = ( 0.398942280267484 + y*( -0.669339325353065 +
y*( 0.40311772245257 + y*( -0.0766281832045885 +
( 0.00248933264397244 + 0.0000703849046144657*y )*y ) ) ) )/
( 1 + y*( -1.61964537617937 + ( 0.919118239717915 -
0.142824922601647*y )*y ) );
b *= 1/sqrt( ax );
}
return x < 0.0 ? -b : b;
}
/*
******** airLogBesselI0
**
** natural logarithm of airBesselI0
*/
double
565 airLogBesselI0( double x ) {
double b, ax, y;
ax = AIR_ABS( x );
if ( ax < 4.985769687853781 ) {
y = x/5.0;
y *= y;
b = ( 5.86105592521167098e-27 + y*( 6.2499999970669017 +
y*( 41.1327842713925212 + y*( 80.9030404787605539 +
y*( 50.7576267390706893 + 6.88231907401413133*y ) ) ) ) )/
( 1 + y*( 8.14374525361325784 + y*( 21.3288286560361152 +
y*( 20.0880670952325953 + ( 5.5138982784800139 +
0.186784275148079847*y )*y ) ) ) );
} else {
y = 5.0/ax;
b = ( -0.91893853280169872884 + y*( 2.7513907055333657679 +
y*( -3.369024122613176551 + y*( 1.9164545708124343838 +
( -0.46136261965797010108 + 0.029092365715948197066*y )*y ) ) ) )/
( 1 + y*( -2.9668913151685312745 + y*( 3.5882191453626541066 +
y*( -1.9954040017063882247 + ( 0.45606687718126481603 -
0.0231678041994100784*y )*y ) ) ) );
b += ax - log( ax )/2;
}
return b;
}
/*
******** airLogRician
**
** natural logarithm of Rician distribution
** mes is measured value
** tru is "true" underlying value
** sig is sigma of 2-D Gaussian
*/
double
600 airLogRician( double mes, double tru, double sig ) {
double lb, ss;
ss = sig*sig;
lb = airLogBesselI0( mes*tru/ss );
return lb + log( mes/ss ) - ( mes*mes + tru*tru )/( 2*ss );
}
double
609 airRician( double mes, double tru, double sig ) {
return exp( airLogRician( mes, tru, sig ) );
}
/*
******** airBesselI1By0
**
** the quotient airBesselI1( x )/airBesselI0( x )
*/
double
619 airBesselI1By0( double x ) {
double q, ax, y;
ax = AIR_ABS( x );
if ( ax < 2.2000207427754046 ) {
y = ax/2.2;
q = ( 1.109010375603908e-29 + y*( 1.0999999994454934 +
y*( 0.05256560007682146 + y*( 0.3835178789165919 +
( 0.011328636410807382 + 0.009066934622942833*y )*y ) ) ) )/
( 1 + y*( 0.047786822784523904 + y*( 0.9536550439261017 +
( 0.03918380275938573 + 0.09730715527121027*y )*y ) ) );
} else if ( ax < 5.888258985638512 ) {
y = ( ax-2.2 )/3.68;
q = ( 0.7280299135046744 + y*( 2.5697382341657002 +
y*( 3.69819451510548 + y*( 3.131374238190916 +
( 1.2811958061688737 + 0.003601218043466571*y )*y ) ) ) )/
( 1 + y*( 2.8268553393021527 + y*( 4.164742157157812 +
y*( 3.2377768820326756 + 1.3051900460060342*y ) ) ) );
} else {
y = 5.88/ax;
q = ( 1.000000000646262020372530870790956088593 +
y*( -2.012513842496824157039372120680781513697 +
y*( 1.511644590219033259220408231325838531123 +
( -0.3966391319921114140077576390415605232003 +
0.02651815520696779849352690755529178056941*y )*y ) ) )/
( 1 + y*( -1.927479858946526082413004924812844224781 +
y*( 1.351359456116228102988125069310621733956 +
( -0.288087717540546638165144937495654019162 +
0.005906535730887518966127383058238522133819*y )*y ) ) );
}
return x < 0.0 ? -q : q;
}
/*
******** airBesselIn
**
** modified Bessel function of the first kind, order n.
**
*/
double
659 airBesselIn( int nn, double xx ) {
double tax, bb, bi, bim, bip;
int ii, an, top;
an = AIR_ABS( nn );
if ( 0 == an ) {
return airBesselI0( xx );
} else if ( 1 == an ) {
return airBesselI1( xx );
}
if ( 0.0 == xx ) {
return 0.0;
}
tax = 2.0/AIR_ABS( xx );
bip = bb = 0.0;
bi = 1.0;
top = 2*( an + AIR_CAST( int, sqrt( 40.0*an ) ) );
for ( ii=top; ii > 0; ii-- ) {
bim = bip + ii*tax*bi;
bip = bi;
bi = bim;
if ( AIR_ABS( bi ) > 1.0e10 ) {
bb *= 1.0e-10;
bi *= 1.0e-10;
bip*= 1.0e-10;
}
if ( ii == an ) {
bb = bip;
}
}
bb *= airBesselI0( xx )/bi;
return ( xx < 0.0 ? -bb : bb );
}
/*
******** airBesselInExpScaled
**
** modified Bessel function of the first kind, order n,
** scaled by exp( -abs( x ) )
**
*/
double
703 airBesselInExpScaled( int nn, double xx ) {
double tax, bb, bi, bim, bip, eps;
int top, ii, an;
an = AIR_ABS( nn );
if ( 0 == an ) {
return airBesselI0ExpScaled( xx );
} else if ( 1 == an ) {
return airBesselI1ExpScaled( xx );
}
if ( 0 == xx ) {
return 0.0;
}
tax = 2.0/AIR_ABS( xx );
bip = bb = 0.0;
bi = 1.0;
/* HEY: GLK tried to increase sqrt( 40.0*an ) to sqrt( 100.0*an ) to avoid
jagged discontinuities in ( e.g. ) airBesselInExpScaled( n, 17*17 ); the
problem was detected because of glitches in the highest blurring
levels for scale-space feature detection; but that didn't quite
work either; this will have to be debugged further! */
top = 2*( an + AIR_CAST( int, sqrt( 40.0*an ) ) );
eps = 1.0e-10;
for ( ii=top; ii > 0; ii-- ) {
bim = bip + ii*tax*bi;
bip = bi;
bi = bim;
if ( AIR_ABS( bi ) > 1.0/eps ) {
bb *= eps;
bi *= eps;
bip*= eps;
}
if ( ii == an ) {
bb = bip;
}
}
bb *= airBesselI0ExpScaled( xx )/bi;
return ( xx < 0.0 ? -bb : bb );
}
/*
** based on: T. Lindeberg. "Effective Scale: A Natural Unit For
** Measuring Scale-Space Lifetime" IEEE PAMI, 15:1068-1074 ( 1993 )
**
** which includes tau( tee ) as equation ( 29 ),
** here taking A'' == 0 and B'' == 1, with
** a0 and a1 as defined by eqs ( 22 ) and ( 23 )
**
** Used MiniMaxApproximation[] from Mathematica ( see
** ~gk/papers/ssp/nb/effective-scale-TauOfTee.nb ) to get functions,
** but the specific functions and their domains could certainly be
** improved upon. Also, the absence of conversions directly between
** tau and sigma is quite unfortunate: going through tee loses
** precision and takes more time.
**
** ACCURATE: can set this to 0 or 1:
** 0: use a quick-and-dirty approximation to tau( tee ), which
** uses a straight line segment for small scales, and then
** has a C^1-continuous transition to the large-scale approximation eq ( 33 )
** 1: careful approximation based on the MiniMaxApproximation[]s
*/
#define ACCURATE 1
double
768 airTauOfTime( double tee ) {
double tau;
if ( tee < 0 ) {
tau = 0;
#if ACCURATE
} else if ( tee < 1.49807 ) {
/* mmtau0tweaked */
tau = ( tee*( 0.2756644487429131 + tee*( 0.10594329088466668 + tee*( 0.05514331911165778 + ( 0.021449249669475232 + 0.004417835440932558*tee )*tee ) ) ) )/
( 1.0 + tee*( -0.08684532328108877 + tee*( 0.1792830876099199 + tee*( 0.07468999631784223 + ( 0.012123550696192354 + 0.0021535864222409365*tee )*tee ) ) ) );
} else if ( tee < 4.96757 ) {
/* mmtau1 */
tau = ( 0.0076145275813930356 + tee*( 0.24811886965997867 + tee*( 0.048329025380584194 +
tee*( 0.04227260554167517 + ( 0.0084221516844712 + 0.0092075782656669*tee )*tee ) ) ) )/
( 1.0 + tee*( -0.43596678272093764 + tee*( 0.38077975530585234 + tee*( -0.049133766853683175 + ( 0.030319379462443567 + 0.0034126333151669654*tee )*tee ) ) ) );
} else if ( tee < 15.4583 ) {
/* mmtau2 */
tau = ( -0.2897145176074084 + tee*( 1.3527948686285203 + tee*( -0.47099157589904095 +
tee*( -0.16031981786376195 + ( -0.004820970155881798 - 4.149777202275125e-6*tee )*tee ) ) ) )/
( 1.0 + tee*( 0.3662508612514773 + tee*( -0.5357849572367938 + ( -0.0805122462310566 - 0.0015558889784971902*tee )*tee ) ) );
} else if ( tee < 420.787 ) {
/* mmtau3 */
tau = ( -4.2037874383990445e9 + tee*( 2.838805157541766e9 + tee*( 4.032410315406513e8 + tee*( 5.392017876788518e6 + tee*( 9135.49750298428 + tee ) ) ) ) )/
( tee*( 2.326563899563907e9 + tee*( 1.6920560224321905e8 + tee*( 1.613645012626063e6 + ( 2049.748257887103 + 0.1617034516398788*tee )*tee ) ) ) );
#else /* quick and dirty approximation */
} else if ( tee < 1.3741310015234351 ) {
tau = 0.600069568241882*tee/1.3741310015234351;
#endif
} else {
/* lindtau = eq ( 33 ) in paper */
tau = 0.53653222368715360118 + log( tee )/2.0 + log( 1.0 - 1.0/( 8.0*tee ) );
}
return tau;
}
double
804 airTimeOfTau( double tau ) {
double tee;
/* the number of branches here is not good; needs re-working */
if ( tau < 0 ) {
tee = 0;
#if ACCURATE
} else
if ( tau < 0.611262 ) {
/* mmtee0tweaked */
tee = ( tau*( 3.6275987317285265 + tau*( 11.774700160760132 + tau*( 4.52406587856803 + tau*( -14.125688866786549 + tau*( -0.725387283317479 + 3.5113122862478865*tau ) ) ) ) ) )/
( 1.0 + tau*( 4.955066250765395 + tau*( 4.6850073321973404 + tau*( -6.407987550661679 + tau*( -6.398430668865182 + 5.213709282093169*tau ) ) ) ) );
} else if ( tau < 1.31281 ) {
/* mmtee1 */
tee = ( 1.9887378739371435e49 + tau*( -2.681749984485673e50 + tau*( -4.23360463718195e50 + tau*( 2.09694591123974e51 + tau*( -2.7561518523389087e51 + ( 1.661629137055526e51 - 4.471073383223687e50*tau )*tau ) ) ) ) )/
( 1.0 + tau*( -5.920734745050949e50 + tau*( 1.580953446553531e51 + tau*( -1.799463907469813e51 + tau*( 1.0766702953985062e51 + tau*( -3.57278667155516e50 + 5.008335824520649e49*tau ) ) ) ) ) );
} else if ( tau < 1.64767 ) {
/* mmtee2 */
tee = ( 7.929177830383403 + tau*( -26.12773195115971 + tau*( 40.13296225515305 + tau*( -25.041659428733585 + 11.357596970027744*tau ) ) ) )/
( 1.0 + tau*( -2.3694595653302377 + tau*( 7.324354882915464 + ( -3.5335141717471314 + 0.4916661013041915*tau )*tau ) ) );
} else if ( tau < 1.88714 ) {
/* mmtee3 */
tee = ( 0.8334252264680793 + tau*( -0.2388940380698891 + ( 0.6057616935583752 - 0.01610044688317929*tau )*tau ) )/( 1.0 + tau*( -0.7723301124908083 + ( 0.21283962841683607 - 0.020834957466407206*tau )*tau ) );
} else if ( tau < 2.23845 ) {
/* mmtee4 */
tee = ( 0.6376900379835665 + tau*( 0.3177131886056259 + ( 0.1844114646774132 + 0.2001613331260136*tau )*tau ) )/( 1.0 + tau*( -0.6685635461372561 + ( 0.15860524381878136 - 0.013304300252332686*tau )*tau ) );
} else if ( tau < 2.6065 ) {
/* mmtee5 */
tee = ( 1.3420027677612982 + ( -0.939215712453483 + 0.9586140009249253*tau )*tau )/( 1.0 + tau*( -0.6923014141351673 + ( 0.16834190074776287 - 0.014312833444962668*tau )*tau ) );
} else if ( tau < 3.14419 ) {
/* mmtee6 */
tee = ( tau*( 190.2181493338235 + tau*( -120.16652155353106 + 60.*tau ) ) )/( 76.13355144582292 + tau*( -42.019121363472614 + ( 8.023304636521623 - 0.5281725039404653*tau )*tau ) );
#else /* quick and dirty approximation */
} else if ( tau < 0.600069568241882 ) {
tee = 1.3741310015234351*tau/0.600069568241882;
#endif
} else {
/* lindtee = lindtau^{-1} */
double ee;
ee = exp( 2.0*tau );
tee = 0.0063325739776461107152*( 27.0*ee + 2*AIR_PI*AIR_PI + 3.0*sqrt( 81.0*ee*ee + 12*ee*AIR_PI*AIR_PI ) );
}
return tee;
}
double
850 airSigmaOfTau( double tau ) {
return sqrt( airTimeOfTau( tau ) );
}
double
856 airTauOfSigma( double sig ) {
return airTauOfTime( sig*sig );
}
/* http://en.wikipedia.org/wiki/Halton_sequences */
double
863 airVanDerCorput( unsigned int indx, unsigned int base ) {
double result=0.0, ff;
unsigned int ii;
ff = 1.0/base;
ii = indx;
while ( ii ) {
result += ff*( ii % base );
ii /= base;
ff /= base;
}
return result;
}
void
878 airHalton( double *out, unsigned int indx,
const unsigned int *base, unsigned int num ) {
unsigned int nn, bb;
for ( nn=0; nn<num; nn++ ) {
double ff, result = 0.0;
unsigned int ii;
bb = base[nn];
ff = 1.0/bb;
ii = indx;
while ( ii ) {
result += ff*( ii % bb );
ii /= bb;
ff /= bb;
}
out[nn] = result;
}
return;
}
/* via "Table[Prime[n], {n, 1000}]" in Mathematica */
const unsigned int airPrimeList[AIR_PRIME_NUM] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211,
223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379,
383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461,
463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563,
569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643,
647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739,
743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937,
941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021,
1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093,
1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181,
1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259,
1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321,
1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433,
1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493,
1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579,
1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741,
1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831,
1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913,
1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003,
2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087,
2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161,
2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269,
2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347,
2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417,
2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531,
2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621,
2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693,
2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767,
2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851,
2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953,
2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041,
3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163,
3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251,
3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329,
3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413,
3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517,
3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583,
3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673,
3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767,
3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853,
3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931,
3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027,
4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129,
4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229,
4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297,
4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421,
4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513,
4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603,
4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691,
4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793,
4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909,
4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987,
4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077,
5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171,
5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279,
5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393,
5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471,
5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557,
5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653,
5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741,
5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839,
5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903,
5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043,
6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131,
6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221,
6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311,
6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379,
6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521,
6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607,
6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703,
6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803,
6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899,
6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983,
6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079,
7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207,
7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307,
7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433,
7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523,
7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589,
7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687,
7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789,
7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883,
7901, 7907, 7919};
/*
** CRC code available in various places, including:
** http://pubs.opengroup.org/onlinepubs/007904875/utilities/cksum.html
** which curiously has no copyright declaration?
*/
static unsigned int
crcTable[] = {
0x00000000,
0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};
/* note "c" is used once */
#define CRC32( crc, c ) ( crc ) = ( ( ( crc ) << 8 ) ^ crcTable[( ( crc ) >> 24 ) ^ ( c )] )
unsigned int
1055 airCRC32( const unsigned char *cdata, size_t len, size_t unit, int swap ) {
unsigned int crc=0;
size_t ii, jj, nn, mm;
const unsigned char *crev;
if ( !( cdata && len ) ) {
return 0;
}
if ( swap ) {
/* if doing swapping, we need make sure we have a unit size,
and that it divides into len */
if ( !( unit && !( len % unit ) ) ) {
return 0;
}
}
nn = len;
if ( !swap ) {
/* simple case: feed "len" bytes from "cdata" into CRC32 */
for ( ii=0; ii<nn; ii++ ) {
CRC32( crc, *( cdata++ ) );
}
} else {
/* have to swap, work "unit" bytes at a time, working down
the bytes within each unit */
mm = len / unit;
for ( jj=0; jj<mm; jj++ ) {
crev = cdata + jj*unit + unit-1;
for ( ii=0; ii<unit; ii++ ) {
CRC32( crc, *( crev-- ) );
}
}
}
/* include length of data in result */
for ( ; nn; nn >>= 8 ) {
CRC32( crc, ( nn & 0xff ) );
}
return ~crc;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
#include "privateAir.h"
/* timer functions */
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#include <time.h>
#else
#include <sys/time.h>
#endif
/*
******** airTeemVersion
******** airTeemReleaseDone
******** airTeemReleaseDate
**
** updated with each release to contain a string representation of
** the Teem version number and release date. Originated in version 1.5;
** use of TEEM_VERSION #defines started in 1.9
*/
const char *
airTeemVersion = TEEM_VERSION_STRING;
const int
airTeemReleaseDone = AIR_FALSE;
const char *
airTeemReleaseDate = "maybe 2014 or 2015";
/*
******** airTeemVersionSprint
**
** uniform way of printing information about the Teem version
*/
void
57 airTeemVersionSprint( char buff[AIR_STRLEN_LARGE] ) {
sprintf( buff, "Teem version %s, %s%s%s",
airTeemVersion,
airTeemReleaseDone ? "released on " : "",
airTeemReleaseDate,
airTeemReleaseDone ? "" : " ( not yet released )" );
return;
}
double
67 _airSanityHelper( double val ) {
return val*val*val;
}
/*
******** airNull( )
**
** returns NULL
*/
void *
77 airNull( void ) {
return NULL;
}
/*
******** airSetNull
**
** dereferences and sets to NULL, returns NULL
*/
void *
88 airSetNull( void **ptrP ) {
if ( ptrP ) {
*ptrP = NULL;
}
return NULL;
}
/*
******** airFree( )
**
** to facilitate setting a newly free( )'d pointer; always returns NULL.
** also makes sure that NULL is not passed to free( ).
*/
void *
103 airFree( void *ptr ) {
if ( ptr ) {
free( ptr );
}
return NULL;
}
/*
******** airFopen( )
**
** encapsulates that idea that "-" is either standard in or stardard
** out, and does McRosopht stuff required to make piping work
**
** Does not error checking. If fopen fails, then C' errno and strerror are
** left untouched for the caller to access.
*/
FILE *
121 airFopen( const char *name, FILE *std, const char *mode ) {
FILE *ret;
if ( !strcmp( name, "-" ) ) {
ret = std;
#ifdef _WIN32
if ( strchr( mode, 'b' ) ) {
_setmode( _fileno( ret ), _O_BINARY );
}
#endif
} else {
ret = fopen( name, mode );
}
return ret;
}
/*
******** airFclose( )
**
** just to facilitate setting a newly fclose( )'d file pointer to NULL
** also makes sure that NULL is not passed to fclose( ), and won't close
** stdin, stdout, or stderr ( its up to the user to open these correctly )
*/
FILE *
145 airFclose( FILE *file ) {
if ( file ) {
if ( !( stdin == file || stdout == file || stderr == file ) ) {
fclose( file );
}
}
return NULL;
}
/*
******** airSinglePrintf
**
** a complete stand-in for {f|s}printf( ), as long as the given format
** string contains exactly one conversion sequence. The utility of
** this is to standardize the printing of IEEE 754 special values:
** QNAN, SNAN -> "NaN"
** POS_INF -> "+inf"
** NEG_INF -> "-inf"
** The format string can contain other things besides just the
** conversion sequence: airSingleFprintf( f, " ( %f )\n", AIR_NAN )
** will be the same as fprintf( f, " ( %s )\n", "NaN" );
**
** To get fprintf behavior, pass "str" as NULL
** to get sprintf bahavior, pass "file" as NULL
**
** Finding a complete {f|s|}printf replacement is a priority for Teem 2.0
*/
int
174 airSinglePrintf( FILE *file, char *str, const char *_fmt, ... ) {
char *fmt, buff[AIR_STRLEN_LARGE];
double val=0, gVal, fVal;
int ret, isF, isD, cls;
char *conv=NULL, *p0, *p1, *p2, *p3, *p4, *p5;
va_list ap;
va_start( ap, _fmt );
fmt = airStrdup( _fmt );
/* this is needlessly complicated; the "l" modifier is a no-op */
p0 = strstr( fmt, "%e" );
p1 = strstr( fmt, "%f" );
p2 = strstr( fmt, "%g" );
p3 = strstr( fmt, "%le" );
p4 = strstr( fmt, "%lf" );
p5 = strstr( fmt, "%lg" );
isF = p0 || p1 || p2;
isD = p3 || p4 || p5;
/* the code here says "isF" and "isD" as if it means "is float" or
"is double". It really should be "is2" or "is3", as in,
"is 2-character conv. seq., or "is 3-character conv. seq." */
if ( isF ) {
conv = p0 ? p0 : ( p1 ? p1 : p2 );
}
if ( isD ) {
conv = p3 ? p3 : ( p4 ? p4 : p5 );
}
if ( isF || isD ) {
/* use "double" instead of "float" because var args are _always_
subject to old-style C type promotions: float promotes to double */
val = va_arg( ap, double );
cls = airFPClass_d( val );
switch ( cls ) {
case airFP_SNAN:
case airFP_QNAN:
case airFP_POS_INF:
case airFP_NEG_INF:
if ( isF ) {
memcpy( conv, "%s", 2 );
} else {
/* this sneakiness allows us to replace a 3-character conversion
sequence for a double ( such as %lg ) with a 3-character conversion
for a string, which we know has at most 4 characters */
memcpy( conv, "%4s", 3 );
}
break;
}
#define PRINT( F, S, C, V ) ( ( F ) ? fprintf( ( F ), ( C ), ( V ) ) : sprintf( ( S ), ( C ), ( V ) ) )
switch ( cls ) {
case airFP_SNAN:
case airFP_QNAN:
ret = PRINT( file, str, fmt, "NaN" );
break;
case airFP_POS_INF:
ret = PRINT( file, str, fmt, "+inf" );
break;
case airFP_NEG_INF:
ret = PRINT( file, str, fmt, "-inf" );
break;
default:
if ( p2 || p5 ) {
/* got "%g" or "%lg", see if it would be better to use "%f" */
sprintf( buff, "%f", val );
sscanf( buff, "%lf", &fVal );
sprintf( buff, "%g", val );
sscanf( buff, "%lf", &gVal );
if ( fVal != gVal ) {
/* using %g ( or %lg ) lost precision!! Use %f ( or %lf ) instead */
if ( p2 ) {
memcpy( conv, "%f", 2 );
} else {
memcpy( conv, "%lf", 3 );
}
}
}
ret = PRINT( file, str, fmt, val );
break;
}
} else {
/* conversion sequence is neither for float nor double */
ret = file ? vfprintf( file, fmt, ap ) : vsprintf( str, fmt, ap );
}
va_end( ap );
free( fmt );
return ret;
}
/*
******** airSprintSize_t
**
** sprints a single size_t to a given string, side-stepping
** non-standardized format specifier confusion with printf
*/
char *
270 airSprintSize_t( char _str[AIR_STRLEN_SMALL], size_t val ) {
char str[AIR_STRLEN_SMALL];
unsigned int si;
if ( !_str ) {
return NULL;
}
si = AIR_STRLEN_SMALL-1;
str[si] = '\0';
do {
str[--si] = AIR_CAST( char, ( val % 10 ) + '0' );
val /= 10;
} while ( val );
strcpy( _str, str + si );
return _str;
}
/*
******** airSprintPtrdiff_t
**
** sprints a single ptrdiff_t to a given string, side-stepping
** non-standardized format specifier confusion with printf
*/
char *
294 airSprintPtrdiff_t( char _str[AIR_STRLEN_SMALL], ptrdiff_t val ) {
char str[AIR_STRLEN_SMALL];
unsigned int si;
int sign;
if ( !_str ) {
return NULL;
}
si = AIR_STRLEN_SMALL-1;
str[si] = '\0';
sign = ( val < 0 ? -1 : 1 );
do {
ptrdiff_t dig;
dig = val % 10;
str[--si] = AIR_CAST( char, dig > 0 ? dig + '0' : -dig + '0' );
val /= 10;
} while ( val );
if ( -1 == sign ) {
str[--si] = '-';
}
strcpy( _str, str + si );
return _str;
}
/* ---- BEGIN non-NrrdIO */
const int
airPresent = 42;
/*
** sprints a length-"len" vector "vec" of size_t values into "dst", which is
** simply assumed to be big enough to hold this. Vector enclosed in "[]" and
** values separated by ", "
*/
char *
329 airSprintVecSize_t( char *dst, const size_t *vec, unsigned int len ) {
char stmp[AIR_STRLEN_SMALL];
unsigned int axi;
/* if we got NULL to sprint into, there's nothing to do but return it */
if ( !dst ) {
return dst;
}
strcpy( dst, "[" );
for ( axi=0; axi<len; axi++ ) {
if ( axi ) {
strcat( dst, ", " );
}
airSprintSize_t( stmp, vec[axi] );
strcat( dst, stmp );
}
strcat( dst, "]" );
return dst;
}
/*
******** airPrettySprintSize_t
**
** sprints a single size_t in a way that is human readable as
** bytes, kilobytes ( KB ), etc
*/
char *
356 airPrettySprintSize_t( char str[AIR_STRLEN_SMALL], size_t val ) {
static const char suff[][7] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB"};
unsigned int suffIdx, suffNum;
double dval;
if ( !str ) {
return NULL;
}
suffIdx = 0;
dval = AIR_CAST( double, val );
suffNum = AIR_UINT( sizeof( suff )/sizeof( suff[0] ) );
while ( suffIdx < suffNum-1 ) { /* while we can go to a bigger suffix */
if ( dval > 1024 ) {
dval /= 1024;
suffIdx++;
} else {
break;
}
}
sprintf( str, "%g %s", dval, suff[suffIdx] );
return str;
}
/*
******** airStderr, airStdout, airStdin
**
** these exist only to give uniform access to FILE *s that might be
** annoying to access in higher-level language wrappings around Teem.
*/
FILE *
386 airStderr( void ) {
return stderr;
}
FILE *
391 airStdout( void ) {
return stdout;
}
FILE *
396 airStdin( void ) {
return stdin;
}
unsigned int
401 airBitsSet( unsigned int vv ) {
/* http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan */
unsigned int cc;
for ( cc=0; vv; cc++ ) {
/* wherever lowest bit is on in vv; vv-1 will have it off, and leave
unchanged all the higher bits */
vv &= vv - 1;
}
return cc;
}
/*
******** AIR_INDEX( i, x, I, L, t )
**
** READ CAREFULLY!!
**
** Utility for mapping a floating point x in given range [i, I] to the
** index of an array with L elements, AND SAVES THE INDEX INTO GIVEN
** VARIABLE t, WHICH MUST BE OF SOME INTEGER TYPE because this relies
** on the implicit cast of an assignment to truncate away the
** fractional part. ALSO, t must be of a type large enough to hold
** ONE GREATER than L. So you can't pass a variable of type unsigned
** char if L is 256
**
** DOES NOT DO BOUNDS CHECKING: given an x which is not inside [i, I],
** this may produce an index not inside [0, L-1] ( but it won't always
** do so: the output being outside range [0, L-1] is not a reliable
** test of the input being outside range [i, I] ). The mapping is
** accomplished by dividing the range from i to I into L intervals,
** all but the last of which is half-open; the last one is closed.
** For example, the number line from 0 to 3 would be divided as
** follows for a call with i = 0, I = 4, L = 4:
**
** index: 0 1 2 3 = L-1
** intervals: [ )[ )[ )[ ]
** |----|----|----|----|
** value: 0 1 2 3 4
**
** The main point of the diagram above is to show how I made the
** arbitrary decision to orient the half-open interval, and which
** end has the closed interval.
**
** Note that AIR_INDEX( 0, 3, 4, 4, t ) and AIR_INDEX( 0, 4, 4, 4, t ) both set t = 3
**
** The reason that this macro requires a argument for saving the
** result is that this is the easiest way to avoid extra conditionals.
** Otherwise, we'd have to do some check to see if x is close enough
** to I so that the generated index would be L and not L-1. "Close
** enough" because due to precision problems you can have an x < I
** such that ( x-i )/( I-i ) == 1, which was a bug with the previous version
** of this macro. It is far simpler to just do the index generation
** and then do the sneaky check to see if the index is too large by 1.
** We are relying on the fact that C _defines_ boolean true to be exactly 1.
**
** Note also that we are never explicity casting to one kind of int or
** another-- the given t can be any integral type, including long long.
*/
/*
#define AIR_INDEX( i, x, I, L, t ) ( \
( t ) = ( L ) * ( ( double )( x )-( i ) ) / ( ( double )( I )-( i ) ), \
( t ) -= ( ( t ) == ( L ) ) )
*/
/*
******* airIndex
**
** Given a value range [min, max], a single input value val inside the range,
** and a number of intervals N spanning and evenly dividing that range,
** airIndex returns the index of the interval containing val:
**
** input value range: min max
** val somewhere in: |-----|-----|-----|-----|
** N intervals: [ )[ )[ )[ ] ( for N=4 )
** output index: 0 1 2 3
**
** This can be used ( as in nrrdHisto and other histogramming functions ) to
** represent the range [min, max] with N samples between 0 and N-1. Those
** samples are cell-centered, because "0" is logically located ( in the
** continuous input range ) in the *middle* of the first of the N intervals.
** In contrast, the *endpoints* of the N intervals ( the 5 "|" in the picture
** above ) form N+1 *node*-centered samples from min to max.
**
** If max < min, then "min" and "max" would be swapped in the diagram
** above and their roles are switched. This overdue fix was only added
** in version 1.11.
**
** NOTE: This does not do bounds checking; for that use airIndexClamp
*/
unsigned int
489 airIndex( double min, double val, double max, unsigned int N ) {
unsigned int idx;
double mnm;
mnm = max - min;
if ( mnm > 0 ) {
idx = AIR_UINT( N*( val - min )/mnm );
idx -= ( idx == N );
} else if ( mnm < 0 ) {
idx = AIR_UINT( N*( val - max )/( -mnm ) );
idx -= ( idx == N );
idx = N-1-idx;
} else {
idx = 0;
}
return idx;
}
unsigned int
508 airIndexClamp( double min, double val, double max, unsigned int N ) {
unsigned int idx;
double mnm;
mnm = max - min;
if ( mnm > 0 ) {
val = AIR_MAX( min, val );
idx = AIR_UINT( N*( val - min )/mnm );
idx = AIR_MIN( idx, N-1 );
} else if ( mnm < 0 ) {
val = AIR_MAX( max, val );
idx = AIR_UINT( N*( val - max )/( -mnm ) );
idx = AIR_MIN( idx, N-1 );
idx = N-1-idx;
} else {
idx = 0;
}
return idx;
}
airULLong
529 airIndexULL( double min, double val, double max, airULLong N ) {
airULLong idx;
if ( min == max ) {
return 0;
}
if ( min > max ) {
return N-1-airIndexULL( max, val, min, N );
}
#if defined( _WIN32 ) && !defined( __CYGWIN__ ) && !defined( __MINGW32__ )
/* compile error on Win32-vs60: "error C2520: conversion from
unsigned __int64 to double not implemented, use signed __int64 */
airLLong sidx;
sidx = AIR_CAST( airLLong, AIR_CAST( double, N )*( val - min )/( max - min ) );
idx = AIR_CAST( airULLong, sidx );
#else
idx = AIR_CAST( airULLong, AIR_CAST( double, N )*( val - min )/( max - min ) );
#endif
idx -= ( idx == N );
return idx;
}
airULLong
551 airIndexClampULL( double min, double val, double max, airULLong N ) {
airULLong idx;
if ( min == max ) {
return 0;
}
if ( min > max ) {
return N-1-airIndexULL( max, val, min, N );
}
#if defined( _WIN32 ) && !defined( __CYGWIN__ ) && !defined( __MINGW32__ )
airLLong sidx;
val = AIR_MAX( min, val ); /* see note in airIndexClamp */
sidx = AIR_CAST( airLLong, AIR_CAST( double, N )*( val - min )/( max - min ) );
idx = AIR_CAST( airULLong, sidx );
#else
val = AIR_MAX( min, val ); /* see note in airIndexClamp */
idx = AIR_CAST( airULLong, AIR_CAST( double, N )*( val - min )/( max - min ) );
#endif
idx = AIR_MIN( idx, N-1 );
return idx;
}
/*
******* airDoneStr( )
**
** dinky little utility for generating progress messages of the form
** " 1.9%" or " 35.3%" or "100.0%"
**
** The message will ALWAYS be six characters, and will ALWAYS be
** preceeded by six backspaces. Thus, you pass in a string to print
** into, and it had better be allocated for at least 6+6+1 = 13 chars.
*/
char *
583 airDoneStr( double start, double here, double end, char *str ) {
int perc=0;
if ( str ) {
if ( end != start )
perc = ( int )( 1000*( here-start )/( end-start ) + 0.5 );
else
perc = 1000;
if ( perc < 0 ) {
sprintf( str, "\b\b\b\b\b\b ---- " );
} else if ( perc < 1000 ) {
sprintf( str, "\b\b\b\b\b\b% 3d.%d%%", perc/10, perc%10 );
}
else if ( perc == 1000 ) {
/* the "% 3d" formatting sequence should have taken care
of this, but whatever */
sprintf( str, "\b\b\b\b\b\b100.0%%" );
}
else {
sprintf( str, "\b\b\b\b\b\b done." );
}
}
return str;
}
/*
******** airTime( )
**
** returns current time in seconds ( with millisecond resolution only
** when not on Windows ) as a double. From "man gettimeofday": The
** time is expressed in seconds and microseconds since midnight ( 0
** hour ), January 1, 1970.
*/
double
618 airTime( ) {
#ifdef _WIN32
/* HEY: this has crummy precision */
return ( double )clock( )/CLOCKS_PER_SEC;
#else
double sec, usec;
struct timeval tv;
gettimeofday( &tv, NULL );
sec = AIR_CAST( double, tv.tv_sec );
usec = AIR_CAST( double, tv.tv_usec );
return sec + usec*1.0e-6;
#endif
}
const char
airTypeStr[AIR_TYPE_MAX+1][AIR_STRLEN_SMALL] = {
"( unknown )",
"bool",
"int",
"unsigned int",
"long int",
"unsigned long int",
"size_t",
"float",
"double",
"char",
"string",
"enum",
"other",
};
const size_t
airTypeSize[AIR_TYPE_MAX+1] = {
0,
sizeof( int ),
sizeof( int ),
sizeof( unsigned int ),
sizeof( long int ),
sizeof( unsigned long int ),
sizeof( size_t ),
sizeof( float ),
sizeof( double ),
sizeof( char ),
sizeof( char* ),
sizeof( int ),
0 /* we don't know anything about type "other" */
};
/*
******** airEqvSettle( )
**
** takes a mapping map[i], i in [0..len-1], and shifts the range of the
** mapping downward so that the range is a contiguous set of uints
** starting at 0.
**
** returns the number of different uints; previous version returned one
** less than this ( the highest mapping output value, after the settling )
**
** honestly this doesn't necessarily have anything to do with processing
** equivalence classes, but its an operation that is nice to have in
** those cases
*/
unsigned int
682 airEqvSettle( unsigned int *map, unsigned int len ) {
unsigned int i, j, count, max, *hit;
max = 0;
for ( i=0; i<len; i++ ) {
max = AIR_MAX( max, map[i] );
}
hit = ( unsigned int * )calloc( 1+max, sizeof( unsigned int ) );
for ( i=0; i<len; i++ ) {
hit[map[i]] = 1;
}
count = 0;
for ( j=0; j<=max; j++ ) {
if ( hit[j] ) {
hit[j] = count;
count += 1;
}
}
for ( i=0; i<len; i++ ) {
map[i] = hit[map[i]];
}
free( hit );
return count;
}
/*
******** airEqvMap
**
** takes the equivalence pairs in eqvArr, and an array of uints map of
** length len, and puts in map[i] the uint that class i's value should
** be changed to.
**
** based on numerical recipes, C edition, pg. 346
** modifications:
** - when resolving ancestors, map to the one with the lower index.
** - applies settling to resulting map
**
** returns the number of different class IDs
*/
unsigned int
722 airEqvMap( airArray *eqvArr, unsigned int *map, unsigned int len ) {
unsigned int *eqv, j, k, t, eqi;
for ( j=0; j<len; j++ ) {
map[j] = j;
}
eqv = ( unsigned int* )( eqvArr->data );
for ( eqi=0; eqi<eqvArr->len; eqi++ ) {
j = eqv[0 + 2*eqi];
k = eqv[1 + 2*eqi];
while ( map[j] != j ) {
j = map[j];
}
while ( map[k] != k ) {
k = map[k];
}
if ( j != k ) {
if ( j < k ) {
t = j; j = k; k = t;
}
map[j] = k;
}
}
for ( j=0; j<len; j++ ) {
while ( map[j] != map[map[j]] ) {
map[j] = map[map[j]];
}
}
return airEqvSettle( map, len );
}
/*
******** airEqvAdd
**
** adds another equivalence ( which may or may not amount to adding
** a new class; that will be determined later )
*/
void
760 airEqvAdd( airArray *eqvArr, unsigned int j, unsigned int k ) {
unsigned int *eqv, eqi;
/* HEY: would it speed things up at all to enforce j < k? */
if ( eqvArr->len ) {
/* we have some equivalences, but we're only going to check against
the last one in an effort to reduce duplicate entries */
eqv = AIR_CAST( unsigned int*, eqvArr->data );
eqi = eqvArr->len-1;
if ( ( eqv[0 + 2*eqi] == j && eqv[1 + 2*eqi] == k ) ||
( eqv[0 + 2*eqi] == k && eqv[1 + 2*eqi] == j ) ) {
/* don't add a duplicate */
return;
}
}
eqi = airArrayLenIncr( eqvArr, 1 );
eqv = AIR_CAST( unsigned int*, eqvArr->data );
eqv[0 + 2*eqi] = j;
eqv[1 + 2*eqi] = k;
return;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/*
learned: using these functions correctly to manage even simple
memory usage can be very tricky.
problem 0: even trying to write airMopPrint, I foolishly thought:
"print the string, then free it". But the print callback clobbered
the free callback, because of the semantics of airMopAdd( ). So,
I had to add _airMopAdd( ).
problem 1: debugging hest with purify, on case of hitting error after
parsing multiple variable parameter option of strings: so, I allocated
an array of strings ( arrays ), and registered all the strings with
airMopMem( ), and registered the array itself also with airMopMem( ).
Again, got clobbered. airSetNull( &( ( *vP )[0] ) ) clobbered
airFree( *vP ). So, I gave up on using airMopMem( ) for the individual
elements, and am using simply airMopAdd( airFree ). The alternative
was to change the airMopAdd( )s in airMopMem( ) to _airMopAdd( )s, but
I didn't feel confident that this would be safe.
----------- SO: as a result of all that:
airMopAdd( ) will no longer over-write a callback based on the pointer
It will only over-write the "when" of a ( pointer, callback ) pair, so that
you can't register multiple copies of a ( pointer, callback ) pair ( regardless
of differences, if any, in "when" ). Therefore, there will be AT MOST ONE
instance of a ( pointer, callback ) pair in a mop.
_airMopAdd( ) was nixed.
airMopSub( ) and airMopUnMem were created
*/
#define AIR_MOP_INCR 10
airArray *
62 airMopNew( ) {
return airArrayNew( NULL, NULL, sizeof( airMop ), AIR_MOP_INCR );
}
/*
** except for allocation error, this always returns 0,
** to facilitate this weird idiom:
**
** if ( !( nmeasr = nrrdNew( ) )
** || airMopAdd( mop, nmeasr, ( airMopper )nrrdNuke, airMopAlways )
** || !( nsize = nrrdNew( ) )
** || airMopAdd( mop, nsize, ( airMopper )nrrdNuke, airMopAlways )
** || !( pair = AIR_CAST( ccpair *, calloc( pctx->CCNum, sizeof( ccpair ) ) ) )
** || airMopAdd( mop, pair, airFree, airMopAlways ) ) {
**
** GLK may regret this.
*/
int
81 airMopAdd( airArray *arr, void *ptr, airMopper mop, int when ) {
static const char me[]="airMopAdd";
airMop *mops;
unsigned int ii;
if ( !arr ) {
return 0;
}
mops = ( airMop * )arr->data;
/* first see if this is something we already set a callback for */
for ( ii=0; ii<arr->len; ii++ ) {
if ( mops[ii].ptr == ptr && mops[ii].mop == mop ) {
mops[ii].when = when;
/* we're done */
return 0;
}
}
/* this is a new ptr */
ii = airArrayLenIncr( arr, 1 );
if ( !arr->data ) {
fprintf( stderr, "%s: PANIC: can't re-allocate mop array\n", me );
return 1;
}
mops = ( airMop * )arr->data;
mops[ii].ptr = ptr;
mops[ii].mop = mop;
mops[ii].when = when;
return 0;
}
void
113 airMopSub( airArray *arr, void *ptr, airMopper mop ) {
airMop *mops;
unsigned int ii;
if ( !arr ) {
return;
}
mops = ( airMop * )arr->data;
/* first see if this is something we already set a callback for */
for ( ii=0; ii<arr->len; ii++ ) {
if ( mops[ii].ptr == ptr && mops[ii].mop == mop ) {
mops[ii].ptr = NULL;
mops[ii].mop = NULL;
mops[ii].when = airMopNever;
return;
}
}
/* else we've never seen this before, user is a moron */
return;
}
void
136 airMopMem( airArray *arr, void *_ptrP, int when ) {
void **ptrP;
if ( !( arr && _ptrP ) ) {
return;
}
ptrP = ( void ** )_ptrP;
airMopAdd( arr, ptrP, ( airMopper )airSetNull, when );
airMopAdd( arr, *ptrP, airFree, when );
/*
printf( "airMopMem( 0x%p ): will free( ) 0x%p\n",
( void* )arr, ( void* )( *ptrP ) );
printf( "airMopMem( 0x%p ): will set 0x%p to NULL\n",
( void* )arr, ( void* )ptrP );
*/
return;
}
void
156 airMopUnMem( airArray *arr, void *_ptrP ) {
void **ptrP;
if ( !( arr && _ptrP ) ) {
return;
}
ptrP = ( void ** )_ptrP;
airMopSub( arr, ptrP, ( airMopper )airSetNull );
airMopSub( arr, *ptrP, airFree );
return;
}
static void *
170 _airMopPrint( void *_str ) {
char *str;
str = ( char * )_str;
if ( str ) {
printf( "%s\n", str );
}
return NULL;
}
void
181 airMopPrint( airArray *arr, const void *_str, int when ) {
char *copy;
if ( !( arr && _str ) )
return;
copy = airStrdup( AIR_CAST( const char*, _str ) );
airMopAdd( arr, copy, airFree, airMopAlways );
airMopAdd( arr, copy, _airMopPrint, when );
return;
}
static const char
_airMopWhenStr[4][128] = {
" never",
" error",
" okay",
"always",
};
/*
** This is to overcome the warning about
** "ISO C forbids conversion of function pointer to object pointer type";
** the result here is thus implementation-dependent
*/
typedef union {
airMopper m;
void *v;
} mvunion;
void
212 airMopDebug( airArray *arr ) {
airMop *mops;
unsigned int ii;
mvunion mvu;
if ( !arr )
return;
mops = ( airMop * )arr->data;
printf( "airMopDebug: _________________________ mop stack for 0x%p:\n",
AIR_VOIDP( arr ) );
if ( arr->len ) {
ii = arr->len;
do {
ii--;
printf( "%4u: ", ii );
if ( NULL == mops[ii].mop && NULL == mops[ii].ptr
&& airMopNever == mops[ii].when ) {
printf( "no-op\n" );
continue;
}
/* else */
printf( "%s: ", _airMopWhenStr[mops[ii].when] );
if ( airFree == mops[ii].mop ) {
printf( "airFree( 0x%p )\n", AIR_VOIDP( mops[ii].ptr ) );
continue;
}
if ( ( airMopper )airSetNull == mops[ii].mop ) {
printf( "airSetNull( 0x%p )\n", AIR_VOIDP( mops[ii].ptr ) );
continue;
}
if ( _airMopPrint == mops[ii].mop ) {
printf( "_airMopPrint( \"%s\" == 0x%p )\n",
AIR_CAST( char*, mops[ii].ptr ), AIR_VOIDP( mops[ii].ptr ) );
continue;
}
if ( ( airMopper )airFclose == mops[ii].mop ) {
printf( "airFclose( 0x%p )\n", AIR_VOIDP( mops[ii].ptr ) );
continue;
}
/* else */
mvu.m = mops[ii].mop;
printf( "0x%p( 0x%p )\n", AIR_VOIDP( mvu.v ), AIR_VOIDP( mops[ii].ptr ) );
} while ( ii );
}
printf( "airMopDebug: ^^^^^^^^^^^^^^^^^^^^^^^^^\n" );
}
void
261 airMopDone( airArray *arr, int error ) {
airMop *mops;
unsigned int ii;
/*
printf( "airMopDone( %p ): hello, %s\n", ( void* )arr, error ? "error" : "okay" );
*/
if ( arr ) {
mops = ( airMop * )arr->data;
if ( arr->len ) {
ii = arr->len;
do {
ii--;
if ( mops[ii].ptr
&& ( airMopAlways == mops[ii].when
|| ( airMopOnError == mops[ii].when && error )
|| ( airMopOnOkay == mops[ii].when && !error ) ) ) {
mops[ii].mop( mops[ii].ptr );
}
} while ( ii );
}
airArrayNuke( arr );
/*
printf( "airMopDone( %p ): done!\n", ( void* )arr );
*/
}
return;
}
void
291 airMopError( airArray *arr ) {
airMopDone( arr, AIR_TRUE );
}
void
297 airMopOkay( airArray *arr ) {
airMopDone( arr, AIR_FALSE );
}
/* ---- BEGIN non-NrrdIO */
/*
** like airMopSub but calls the mopper first
*/
void
308 airMopSingleDone( airArray *arr, void *ptr, int error ) {
airMop *mops;
unsigned int ii;
if ( !arr || !( arr->len ) ) {
return;
}
mops = ( airMop * )arr->data;
ii = arr->len;
do {
ii--;
if ( ptr == mops[ii].ptr
&& ( airMopAlways == mops[ii].when
|| ( airMopOnError == mops[ii].when && error )
|| ( airMopOnOkay == mops[ii].when && !error ) ) ) {
mops[ii].mop( mops[ii].ptr );
mops[ii].ptr = NULL;
mops[ii].mop = NULL;
mops[ii].when = airMopNever;
}
} while ( ii );
return;
}
void
333 airMopSingleError( airArray *arr, void *ptr ) {
airMopSingleDone( arr, ptr, AIR_TRUE );
}
void
339 airMopSingleOkay( airArray *arr, void *ptr ) {
airMopSingleDone( arr, ptr, AIR_FALSE );
}
/* ---- END non-NrrdIO */
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
static const char *
_airBoolStr[] = {
"( unknown bool )",
"false",
"true"
};
static const char *
_airBoolDesc[] = {
"unknown boolean",
"false",
"true"
};
static const int
_airBoolVal[] = {
-1,
AIR_FALSE,
AIR_TRUE
};
static const char *
_airBoolStrEqv[] = {
"0", "no", "n", "false", "f", "off", "nope",
"1", "yes", "y", "true", "t", "on", "yea",
""
};
static const int
_airBoolValEqv[] = {
AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE, AIR_FALSE,
AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE, AIR_TRUE
};
static const airEnum
_airBool = {
"boolean",
2,
_airBoolStr,
_airBoolVal,
_airBoolDesc,
_airBoolStrEqv,
_airBoolValEqv,
AIR_FALSE
};
72 const airEnum *const
airBool = &_airBool;
double
76 airAtod( const char *str ) {
double val = 0.0;
airSingleSscanf( str, "%lf", &val );
return val;
}
int
84 airSingleSscanf( const char *str, const char *fmt, void *ptr ) {
char *tmp;
double val;
int ret;
if ( !strcmp( fmt, "%e" ) || !strcmp( fmt, "%f" ) || !strcmp( fmt, "%g" )
|| !strcmp( fmt, "%le" ) || !strcmp( fmt, "%lf" ) || !strcmp( fmt, "%lg" ) ) {
tmp = airStrdup( str );
if ( !tmp ) {
return 0;
}
airToLower( tmp );
if ( strstr( tmp, "nan" ) ) {
val = AIR_NAN;
}
/* ---- BEGIN non-NrrdIO */
else if ( strstr( tmp, "-pi" ) ) {
val = -AIR_PI;
}
else if ( strstr( tmp, "pi" ) ) {
val = AIR_PI;
}
/* ---- END non-NrrdIO */
else if ( strstr( tmp, "-inf" ) ) {
val = AIR_NEG_INF;
}
else if ( strstr( tmp, "inf" ) ) {
val = AIR_POS_INF;
}
else {
/* nothing special matched; pass it off to sscanf( ) */
/* ( save setlocale here ) */
ret = sscanf( str, fmt, ptr );
/* ( return setlocale here ) */
free( tmp );
return ret;
}
/* else we matched "nan", "-inf", or "inf", and set val accordingly */
if ( !strncmp( fmt, "%l", 2 ) ) {
/* we were given a double pointer */
*( ( double * )( ptr ) ) = val;
}
else {
/* we were given a float pointer */
*( ( float * )( ptr ) ) = AIR_CAST( float, val );
}
free( tmp );
return 1;
} else if ( !strcmp( fmt, "%z" ) ) {
/* its a size_t */
size_t tsz = 0; /* tmp size_t */
const char *chh = str; /* char here */
while ( chh ) {
int dig;
dig = AIR_CAST( int, *chh - '0' );
if ( AIR_IN_CL( 0, dig, 9 ) ) {
tsz = 10*tsz + AIR_CAST( size_t, dig );
} else {
break;
}
chh++;
}
*( ( size_t * )( ptr ) ) = tsz;
return 1;
} else {
/* not a float, double, or size_t, let sscanf handle it */
return sscanf( str, fmt, ptr );
}
}
#define _PARSE_STR_ARGS( type ) type *out, const char *_s, \
const char *ct, unsigned int n, ...
#define _PARSE_STR_BODY( format ) \
unsigned int i; \
char *tmp, *s, *last; \
\
/* if we got NULL, there's nothing to do */ \
if ( !( out && _s && ct ) ) \
return 0; \
\
/* copy the input so that we don't change it */ \
s = airStrdup( _s ); \
\
/* keep calling airStrtok( ) until we have everything */ \
for ( i=0; i<n; i++ ) { \
tmp = airStrtok( i ? NULL : s, ct, &last ); \
if ( !tmp ) { \
free( s ); \
return i; \
} \
if ( 1 != airSingleSscanf( tmp, format, out+i ) ) { \
free( s ); \
return i; \
} \
} \
free( s ); \
return n; \
/*
******* airParse*( )
**
** parse ints, floats, doubles, or single characters, from some
** given string "s"; try to parse "n" of them, as delimited by
** characters in "ct", and put the results in "out".
**
** Returns the number of things succesfully parsed- should be n;
** there's been an error if return is < n.
**
** The embarrassing reason for the var-args ( "..." ) is that I want the
** type signature of all these functions to be the same, and I have a function
** for parsing airEnums, in case the airEnum must be supplied as a final
** argument.
**
** This uses air's thread-safe strtok( ) replacement: airStrtok( )
*/
unsigned int
airParseStrI( _PARSE_STR_ARGS( int ) ) { _PARSE_STR_BODY( "%d" ) }
unsigned int
airParseStrUI( _PARSE_STR_ARGS( unsigned int ) ) { _PARSE_STR_BODY( "%u" ) }
unsigned int
airParseStrLI( _PARSE_STR_ARGS( long int ) ) { _PARSE_STR_BODY( "%ld" ) }
unsigned int
airParseStrULI( _PARSE_STR_ARGS( unsigned long int ) ) { _PARSE_STR_BODY( "%lu" ) }
unsigned int
airParseStrZ( _PARSE_STR_ARGS( size_t ) ) { _PARSE_STR_BODY( "%z" ) }
unsigned int
airParseStrF( _PARSE_STR_ARGS( float ) ) { _PARSE_STR_BODY( "%f" ) }
unsigned int
airParseStrD( _PARSE_STR_ARGS( double ) ) { _PARSE_STR_BODY( "%lf" ) }
unsigned int
airParseStrB( int *out, const char *_s, const char *ct, unsigned int n, ... ) {
unsigned int i;
char *tmp, *s, *last;
/* if we got NULL, there's nothing to do */
if ( !( out && _s && ct ) )
return 0;
/* copy the input so that we don't change it */
s = airStrdup( _s );
/* keep calling airStrtok( ) until we have everything */
for ( i=0; i<n; i++ ) {
tmp = airStrtok( i ? NULL : s, ct, &last );
if ( !tmp ) {
free( s );
return i;
}
out[i] = airEnumVal( airBool, tmp );
if ( airEnumUnknown( airBool ) == out[i] ) {
free( s );
return i;
}
}
free( s );
return n;
}
unsigned int
airParseStrC( char *out, const char *_s, const char *ct, unsigned int n, ... ) {
unsigned int i;
char *tmp, *s, *last;
/* if we got NULL, there's nothing to do */
if ( !( out && _s && ct ) )
return 0;
/* copy the input so that we don't change it */
s = airStrdup( _s );
/* keep calling airStrtok( ) until we have everything */
for ( i=0; i<n; i++ ) {
tmp = airStrtok( i ? NULL : s, ct, &last );
if ( !tmp ) {
free( s );
return i;
}
out[i] = tmp[0];
}
free( s );
return n;
}
unsigned int
airParseStrS( char **out, const char *_s, const char *ct, unsigned int n, ... ) {
unsigned int i;
int greedy;
char *tmp, *s, *last;
airArray *mop;
va_list ap;
/* grab "greedy" every time, prior to error checking */
va_start( ap, n );
greedy = va_arg( ap, int );
va_end( ap );
/* if we got NULL, there's nothing to do */
if ( !( out && _s && ct ) )
return 0;
mop = airMopNew( );
/* copy the input so that we don't change it */
s = airStrdup( _s );
airMopMem( mop, &s, airMopAlways );
/* keep calling airStrtok( ) until we have everything */
for ( i=0; i<n; i++ ) {
/* if n == 1, then with greediness, the whole string is used,
and without greediness, we use airStrtok( ) to get only
the first part of it */
if ( n > 1 || !greedy ) {
tmp = airStrtok( i ? NULL : s, ct, &last );
}
else {
tmp = s;
}
if ( !tmp ) {
airMopError( mop );
return i;
}
out[i] = airStrdup( tmp );
if ( !out[i] ) {
airMopError( mop );
return i;
}
airMopMem( mop, out+i, airMopOnError );
}
airMopOkay( mop );
return n;
}
unsigned int
airParseStrE( int *out, const char *_s, const char *ct, unsigned int n, ... ) {
unsigned int i;
char *tmp, *s, *last;
airArray *mop;
va_list ap;
airEnum *enm;
/* grab the enum every time, prior to error checking */
va_start( ap, n );
enm = va_arg( ap, airEnum * );
va_end( ap );
/* if we got NULL, there's nothing to do */
if ( !( out && _s && ct ) ) {
return 0;
}
mop = airMopNew( );
/* copy the input so that we don't change it */
s = airStrdup( _s );
airMopMem( mop, &s, airMopAlways );
if ( 1 == n ) {
/* Because it should be permissible to have spaces in what is
intended to be only a single string from an airEnum, we treat
1==n differently, and do NOT use airStrtok to tokenize the
input string s into spaces. We check the whole s string */
out[0] = airEnumVal( enm, s );
if ( airEnumUnknown( enm ) == out[0] ) {
airMopError( mop );
return 0;
}
} else {
/* keep calling airStrtok( ) until we have everything */
for ( i=0; i<n; i++ ) {
tmp = airStrtok( i ? NULL : s, ct, &last );
if ( !tmp ) {
airMopError( mop );
return i;
}
out[i] = airEnumVal( enm, tmp );
if ( airEnumUnknown( enm ) == out[i]
/* getting the unknown value is not a parse failure if the
string was actually the string for the unknown value! */
&& strcmp( tmp, enm->str[0] ) ) {
airMopError( mop );
return i;
}
}
}
airMopOkay( mop );
return n;
}
unsigned int
( *airParseStr[AIR_TYPE_MAX+1] )( void *, const char *,
const char *, unsigned int, ... ) = {
NULL,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrB,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrI,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrUI,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrLI,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrULI,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrZ,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrF,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrD,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrC,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrS,
( unsigned int ( * )( void *, const char *, const char *,
unsigned int, ... ) )airParseStrE,
NULL /* no standard way of parsing type "other" */
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
This file is a modified version of the MersenneTwister.h source file
written by Richard J. Wagner. The original copyright notice follows.
Mersenne Twister random number generator -- a C++ class MTRand
Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
The Mersenne Twister is an algorithm for generating random numbers. It
was designed with consideration of the flaws in various other generators.
The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
are far greater. The generator is also fast; it avoids multiplication and
division, and it benefits from caches and pipelines. For more information
see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
Reference
M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
Copyright ( C ) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
Copyright ( C ) 2000 - 2003, Richard J. Wagner
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
The original code included the following notice:
When you use this, send an email to: matumoto@math.keio.ac.jp
with an appropriate reference to your work.
It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu
when you write.
*/
#include "air.h"
#include "privateAir.h"
#define AIR_RANDMT_M 397
#define AIR_RANDMT_DEFAULT_SEED 42
/* Inlined class member functions that I made macros */
#define HIBIT( u ) ( ( u ) & 0x80000000UL )
#define LOBIT( u ) ( ( u ) & 0x00000001UL )
#define LOBITS( u ) ( ( u ) & 0x7fffffffUL )
#define MIXBITS( u, v ) ( HIBIT( u ) | LOBITS( v ) )
#define TWOSCOMP( u ) ( ~( u )+1 )
#define TWIST( m, s0, s1 ) \
( ( m ) ^ ( MIXBITS( s0, s1 )>>1 ) ^ ( TWOSCOMP( LOBIT( s1 ) ) & 0x9908b0dfUL ) )
/* airRandMTStateGlobal is not allocated at compile-time because of
weirdness of where non-initialized global objects go in shared
libraries. Its allocation and initialization are controlled by
_airRandMTStateGlobal_{allocated, initialized}. Users who want to
ensure thread safety should be using airRandMTStateNew and
airDrandMT_r, not the global state.
*/
airRandMTState *airRandMTStateGlobal = NULL;
static int _airRandMTStateGlobal_allocated = AIR_FALSE;
static int _airRandMTStateGlobal_initialized = AIR_FALSE;
111 static void
_airRandMTInitialize( airRandMTState *rng, unsigned int seed ) {
/* Initialize generator state with seed See Knuth TAOCP Vol 2, 3rd
Ed, p.106 for multiplier. In previous versions, most significant
bits ( MSBs ) of the seed affect only MSBs of the state array.
Modified 9 Jan 2002 by Makoto Matsumoto.
*/
register unsigned int *s = rng->state;
register unsigned int *r = rng->state;
register int i = 1;
*s++ = seed & 0xffffffffUL;
for( ; i < AIR_RANDMT_N; ++i ) {
*s++ = ( 1812433253UL * ( *r ^ ( *r >> 30 ) ) + i ) & 0xffffffffUL;
r++;
}
}
128 static void
_airRandMTReload( airRandMTState *rng ) {
/* Generate N new values in state. Made clearer and faster by
Matthew Bellew ( matthew.bellew@home.com ) */
register int i;
register unsigned int *p = rng->state;
for ( i=AIR_RANDMT_N - AIR_RANDMT_M; i--; ++p ) {
*p = TWIST( p[AIR_RANDMT_M], p[0], p[1] );
}
for ( i=AIR_RANDMT_M; --i; ++p ) {
*p = TWIST( p[AIR_RANDMT_M-AIR_RANDMT_N], p[0], p[1] );
}
*p = TWIST( p[AIR_RANDMT_M-AIR_RANDMT_N], p[0], rng->state[0] );
rng->left = AIR_RANDMT_N;
rng->pNext = rng->state;
}
146 airRandMTState *
airRandMTStateNew( unsigned int seed ) {
airRandMTState* ret;
ret = AIR_CAST( airRandMTState*, malloc( sizeof( airRandMTState ) ) );
airSrandMT_r( ret, seed );
return ret;
}
155 airRandMTState *
airRandMTStateNix( airRandMTState *state ) {
airFree( state );
return NULL;
}
161 void
airSrandMT_r( airRandMTState *rng, unsigned int seed ) {
/* Seed the generator with a simple uint32 */
_airRandMTInitialize( rng, seed );
_airRandMTReload( rng );
}
/* Pull a 32-bit integer from the generator state. Every other access
** function simply transforms the numbers extracted here.
*/
171 unsigned int
airUIrandMT_r( airRandMTState *rng ) {
register unsigned int s1;
if ( rng->left == 0 ) {
_airRandMTReload( rng );
}
--rng->left;
s1 = *rng->pNext++;
s1 ^= ( s1 >> 11 );
s1 ^= ( s1 << 7 ) & 0x9d2c5680UL;
s1 ^= ( s1 << 15 ) & 0xefc60000UL;
return ( s1 ^ ( s1 >> 18 ) );
}
/* This generates the closed interval [0, 1] */
188 double
airDrandMT_r( airRandMTState *rng ) {
double result = airUIrandMT_r( rng );
return result * ( 1.0/4294967295.0 );
}
/* [0, 1 ) w/ 53 bit precision ( capacity of IEEE double precision ) */
195 double
airDrandMT53_r( airRandMTState *rng ) {
unsigned int a, b;
a = airUIrandMT_r( rng ) >> 5;
b = airUIrandMT_r( rng ) >> 6;
return ( a * 67108864.0 + b ) * ( 1.0/9007199254740992.0 ); /* by Isaku Wada */
}
#define _GLOBAL_ALLOC \
if ( !_airRandMTStateGlobal_allocated ) { \
airRandMTStateGlobal = airRandMTStateNew( 0 ); \
_airRandMTStateGlobal_allocated = AIR_TRUE; \
}
#define _GLOBAL_INIT \
if ( !_airRandMTStateGlobal_initialized ) { \
airSrandMT( AIR_RANDMT_DEFAULT_SEED ); \
} \
/*
******** airRandMTStateGlobalInit
215 **
** Allocates and initializes airRandMTStateGlobal if it was not already
** allocated and initialized. However, this does not need to be called
** except if the user wants to use the airRandMTStateGlobal pointer.
** The allocation and initialization is done as necessary inside the
** the airSrandMT( ), airDrandMT( ), etc functions ( all the functions
221 ** that use airRandMTStateGlobal
*/
void
airRandMTStateGlobalInit( ) {
_GLOBAL_ALLOC;
_GLOBAL_INIT;
}
228
void
airSrandMT( unsigned int seed ) {
_GLOBAL_ALLOC;
airSrandMT_r( airRandMTStateGlobal, seed );
_airRandMTStateGlobal_initialized = AIR_TRUE;
}
double
airDrandMT( ) {
_GLOBAL_ALLOC;
_GLOBAL_INIT;
240 return airDrandMT_r( airRandMTStateGlobal );
}
/*
******** airRandInt
**
** returns a random integer in range [0, N-1]
247 */
unsigned int
airRandInt( unsigned int N ) {
_GLOBAL_ALLOC;
_GLOBAL_INIT;
return airUIrandMT_r( airRandMTStateGlobal )%N;
}
unsigned int
airRandInt_r( airRandMTState *state, unsigned int N ) {
return airUIrandMT_r( state )%N;
259 }
/* This checks to see if the sequence of numbers we get is what we
expect. It should return 1 if all is well, 0 if not.
One thing to check for if it fails is the presence of twos
complement interger representation. The code here relies on it.
*/
int
airRandMTSanity( void ) {
int result = 0;
/* Create a new generator with our seed */
airRandMTState* rng = airRandMTStateNew( AIR_RANDMT_DEFAULT_SEED );
if ( !rng ) {
/* Couldn't allocate memory */
return 0;
}
/* Now check against a predetermined list of values; any inequality
will set result to be non-zero */
result |= airUIrandMT_r( rng ) != 1608637542U;
result |= airUIrandMT_r( rng ) != 3421126067U;
result |= airUIrandMT_r( rng ) != 4083286876U;
result |= airUIrandMT_r( rng ) != 787846414U;
result |= airUIrandMT_r( rng ) != 3143890026U;
result |= airUIrandMT_r( rng ) != 3348747335U;
result |= airUIrandMT_r( rng ) != 2571218620U;
result |= airUIrandMT_r( rng ) != 2563451924U;
result |= airUIrandMT_r( rng ) != 670094950U;
result |= airUIrandMT_r( rng ) != 1914837113U;
airRandMTStateNix( rng );
return !result;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
#include "privateAir.h"
/*
******** airSanity( )
**
** Does run-time checks to see if the compile-time constants are correct.
** Returns a value from the airInsane* enum; airInsane_not means all
** the checks came back without detecting any problems.
*/
int
35 airSanity( void ) {
double nanValue, pinf, ninf;
float nanF, pinfF, ninfF;
unsigned int sign, expvalue, mant;
int tmpI;
char endian;
unsigned char uc0, uc1;
static int _airSanity=0;
if ( _airSanity ) {
return airInsane_not;
}
/* now that there is no more compile-time endian info, this is
merely double checking that airMyEndian( ) works, and returns
the constants ( either 1234, pronounced "little endian", or
4321, "big endian" ) that are defined in air.h */
tmpI = 1;
endian = !( *( ( char* )( &tmpI ) ) );
if ( endian ) {
/* big endian */
if ( 4321 != airMyEndian( ) ) {
return airInsane_endian;
}
} else {
if ( 1234 != airMyEndian( ) ) {
return airInsane_endian;
}
}
/* checks on sizes of uchar, float, int, double, airLLong */
uc0 = 255;
uc1 = AIR_CAST( unsigned char, AIR_INT( uc0 ) + 1 ); /* want to overflow */
if ( !( 255 == uc0 && 0 == uc1 ) ) {
return airInsane_UCSize;
}
/* these justify the AIR_EXISTS_F and AIR_EXISTS_D macros */
if ( !( ( sizeof( float ) == sizeof( int ) ) && ( 4 == sizeof( int ) ) ) ) {
return airInsane_FISize;
}
if ( !( ( sizeof( double ) == sizeof( airLLong ) ) && ( 8 == sizeof( airLLong ) ) ) ) {
return airInsane_DLSize;
}
/* run-time NaN checks */
pinf = DBL_MAX;
pinf = _airSanityHelper( pinf );
pinf = _airSanityHelper( pinf );
pinf = _airSanityHelper( pinf );
if ( AIR_EXISTS( pinf ) ) {
return airInsane_pInfExists;
}
ninf = -pinf;
if ( AIR_EXISTS( ninf ) ) {
return airInsane_nInfExists;
}
nanValue = pinf / pinf;
if ( AIR_EXISTS( nanValue ) ) {
return airInsane_NaNExists;
}
nanF = ( float )nanValue;
pinfF = ( float )pinf;
ninfF = ( float )ninf;
airFPValToParts_f( &sign, &expvalue, &mant, nanF );
mant >>= 22;
if ( AIR_QNANHIBIT != ( int )mant ) {
return airInsane_QNaNHiBit;
}
if ( !( airFP_QNAN == airFPClass_f( AIR_NAN )
&& airFP_QNAN == airFPClass_f( AIR_QNAN )
/*
As of July 4 2012 GLK decides that the signalling NaN tests are
more trouble than they're worth: the signal-ness of the NaN is not
preserved in double-float conversion for some platforms ( so
airFP_SNAN == airFPClass_d( AIR_SNAN ) has never been enforced ), and
there are more platforms for which ( apparently ) passing AIR_SNAN to
airFPClass_d changes it to a quiet NaN, which defeats the purpose
of the test. To summarize, given that:
** AIR_NAN and AIR_QNAN are checked here to be quiet NaN, after
casting to both float and double,
** quiet NaN "hi bit" is tested above, and that
** quiet and signalling NaN are mutually exclusive,
skipping the signalling NaN tests is unlikely to undermine knowing
the correctness of the compile-time representation of NaNs. So the
following line is now commented out for all platforms.
*/
/* && airFP_SNAN == airFPClass_f( AIR_SNAN ) */
&& airFP_QNAN == airFPClass_d( AIR_NAN )
&& airFP_QNAN == airFPClass_d( AIR_QNAN ) ) ) {
return airInsane_AIR_NAN;
}
if ( !( airFP_QNAN == airFPClass_f( nanF )
&& airFP_POS_INF == airFPClass_f( pinfF )
&& airFP_NEG_INF == airFPClass_f( ninfF ) ) ) {
/* really, this is verifying that assigning from a double to a
float maintains the FPClass for non-existent values */
return airInsane_FltDblFPClass;
}
/* just make sure AIR_DIO is reasonably set
( actually, this should be done by include/teemDio.h ) */
switch ( AIR_DIO ) {
case 0: break;
case 1: break;
default:
return airInsane_dio;
}
_airSanity = 1;
return airInsane_not;
}
static const char
_airInsaneErr[AIR_INSANE_MAX+1][AIR_STRLEN_MED] = {
"sanity checked PASSED!",
"airMyEndian( ) is wrong",
"AIR_EXISTS( +inf ) was true",
"AIR_EXISTS( -inf ) was true",
"AIR_EXISTS( NaN ) was true",
"air_FPClass_f( ) wrong after double->float assignment",
"TEEM_QNANHIBIT is wrong",
"airFPClass( AIR_QNAN ) wrong",
"TEEM_DIO has invalid value",
"unsigned char isn't 8 bits",
"sizeof( float ), sizeof( int ) not both == 4",
"sizeof( double ), sizeof( airLLong ) not both == 8",
};
static const char _airBadInsane[] = "( invalid insane value )";
const char *
167 airInsaneErr( int insane ) {
if ( AIR_IN_CL( 0, insane, AIR_INSANE_MAX ) ) {
return _airInsaneErr[insane];
}
else {
return _airBadInsane;
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/* this has to default to false in order for airStrtok to be a
functional substitute for strtok( ) */
int airStrtokQuoting = AIR_FALSE;
/*
******** airStrdup( )
**
** because they didn't put strdup( ) in ANSI.
** This will return NULL if given NULL.
*/
char *
37 airStrdup( const char *s ) {
char *ret;
if ( !s ) {
ret = NULL;
}
else {
ret = ( char * )malloc( strlen( s )+1 );
if ( ret ) {
strcpy( ret, s );
}
}
return ret;
}
/*
******** airStrlen( )
**
** just like strlen, but safe to call on NULL ( for which return is 0 )
*/
size_t
58 airStrlen( const char *s ) {
size_t ret;
if ( !s ) {
ret = 0;
}
else {
ret = strlen( s );
}
return ret;
}
/* ---- BEGIN non-NrrdIO */
/*
******** airStrcmp
**
** like strcmp, but:
** safe to call with NULL strings,
** and, a NULL string is treated same as an empty string
*/
int
79 airStrcmp( const char *_s1, const char *_s2 ) {
static const char empty[]="";
const char *s1, *s2;
int ret;
s1 = _s1 ? _s1 : empty;
s2 = _s2 ? _s2 : empty;
/*
fprintf( stderr, "airStrcmp: _s1=|%s|->s1=|%s| _s2=|%s|->s2=|%s|\n",
_s1 ? _s1 : "( NULL )", s1,
_s2 ? _s2 : "( NULL )", s2 );
*/
ret = strcmp( s1, s2 );
return ret;
}
/* ---- END non-NrrdIO */
/*
******** airStrtok( )
**
** thread-safe strtok( ) replacement. Use just like strtok( ), but on
** each call to parse a given string, pass as the last argument the
** address of a char*, to be used for saving state while the string is
** traversed. Like strtok( ), this will alter the "s" array passed to
** it on the first call, and like strtok( ), this returns pointers into
** this string ( rather than allocating new strings for each token ).
*/
char *
107 airStrtok( char *s, const char *ct, char **last ) {
char *h, *e, *q;
if ( !( ct && last ) ) {
/* can't do any work, bail */
return NULL;
}
h = s ? s : *last;
if ( !airStrlen( h ) )
return NULL;
h += strspn( h, ct );
if ( '\"' == *h && airStrtokQuoting ) {
/* something is trying to be quoted, and, we'll respect that */
/* have to find the next un-escaped '\"' */
h++;
q = h;
while ( *q && !( '\"' == *q && '\\' != q[-1] ) ) {
q++;
}
if ( *q ) {
/* we found an unescaped '\"' */
e = q;
} else {
/* give up; pretend we never tried to do this quoting stuff */
e = h + strcspn( h, ct );
}
} else {
e = h + strcspn( h, ct );
}
if ( '\0' == *e ) {
*last = e;
}
else {
*e = '\0';
*last = e + 1;
}
return h;
}
/*
******** airStrntok( )
**
** returns the number of tokens parsable by airStrtok( ), but does
** NOT alter the given string
*/
unsigned int
153 airStrntok( const char *_s, const char *ct ) {
char *s, *t, *l=NULL;
unsigned int n = 0;
if ( _s && ct ) {
s = airStrdup( _s );
t = airStrtok( s, ct, &l );
while ( t ) {
n++;
t = airStrtok( NULL, ct, &l );
}
airFree( s ); /* no NULL assignment to s, else compile warnings */
}
return n;
}
char *
170 airStrtrans( char *s, char from, char to ) {
size_t i, l;
if ( s ) {
l = strlen( s );
for ( i=0; i<l; i++ ) {
if ( s[i] == from ) {
s[i] = to;
}
}
}
return s;
}
/*
******** airStrcpy
**
** Like strncpy but logic is different ( and perhaps more useful ), being:
** "dst is allocated for dstSize chars. Copy as much of src as can
** "fit in dst, and always 0-terminate the resulting dst.",
** instead of strncpy's "Copy at most n characters, blah blah blah,
** and you still have to 0-terminate the rest yourself".
**
** E.g. with declaration buff[AIR_STRLEN_SMALL], you call
** airStrcpy( buff, AIR_STRLEN_SMALL, src ), and know that then
** strlen( buff ) <= AIR_STRLEN_SMALL-1. ( see note in air.h about
** the meaning of the STRLEN #defines ).
**
** Returns NULL if there was a problem ( NULL dst or dstSize zero ),
** otherwise returns dst
*/
char *
202 airStrcpy( char *dst, size_t dstSize, const char *src ) {
size_t ii, copyLen, srcLen;
if ( !( dst && dstSize > 0 ) ) {
return NULL;
}
srcLen = airStrlen( src );
if ( 1 == dstSize || !srcLen ) {
dst[0] = '\0';
return dst;
}
/* else dstSize > 1 AND src is a non-empy string */
copyLen = AIR_MIN( dstSize-1, srcLen );
for ( ii=0; ii<copyLen; ii++ ) {
dst[ii] = src[ii];
}
dst[copyLen] = '\0';
return dst;
}
/*
******** airEndsWith
**
** if "s" ends with "suff", then returns 1, 0 otherwise
*/
int
228 airEndsWith( const char *s, const char *suff ) {
if ( !( s && suff ) )
return 0;
if ( !( strlen( s ) >= strlen( suff ) ) )
return 0;
if ( !strncmp( s + strlen( s ) - strlen( suff ), suff, strlen( suff ) ) )
return 1;
else
return 0;
}
/*
******** airUnescape( )
**
** unescapes \\ and \n in place in a given string.
** Always returns the same pointer as given
*/
char *
247 airUnescape( char *s ) {
size_t i, j, len;
int found=0;
len = airStrlen( s );
if ( !len )
return s;
for ( i=1, j=0; i<len; i++, j++ ) {
if ( s[i-1] == '\\' && s[i] == '\\' ) {
s[j] = '\\'; i++; found = 1;
} else if ( s[i-1] == '\\' && s[i] == 'n' ) {
s[j] = '\n'; i++; found = 1;
} else {
s[j] = s[i-1]; found = 0;
}
}
if ( i == len || !found ) s[j++] = s[len-1];
s[j] = 0;
return s;
}
/*
******** airOneLinify( )
**
** converts all contiguous white space ( as determined by isspace( ) ) to
** a single ' ', entirely removes non-printable ( as determined by
** isprint( ) ) characters, and entirely removes white space contiguous
** with the end of the string, even if that means shrinking the string
** to "".
**
** Useful for cleaning up lines of text to be saved as strings in
** fields of other structs.
**
** Whatever happens, this returns the pointer passed to it
*/
char *
285 airOneLinify( char *s ) {
size_t i, j, len;
len = airStrlen( s );
if ( !len )
return s;
/* convert white space to space ( ' ' ), and delete unprintables */
for ( i=0; i<len && s[i]; i++ ) {
if ( isspace( AIR_CAST( int, s[i] ) ) ) {
s[i] = ' ';
continue;
}
if ( !isprint( AIR_CAST( int, s[i] ) ) ) {
for ( j=i; j<len; j++ ) {
/* this will copy the '\0' at the end */
s[j] = s[j+1];
}
i--;
continue;
}
}
/* compress all contiguous spaces into one */
for ( i=0; i<len; i++ ) {
while ( ' ' == s[i] && ' ' == s[i+1] ) {
for ( j=i+1; j<len; j++ ) {
s[j] = s[j+1];
}
}
}
/* lose trailing white space */
i = airStrlen( s );
if ( ' ' == s[i-1] ) {
s[i-1] = '\0';
}
return s;
}
/*
******** airToLower( )
**
** calls tolower( ) on all characters in a string, and returns the same
** pointer that it was given
*/
char *
333 airToLower( char *str ) {
char *c;
if ( str ) {
c = str;
while ( *c ) {
*c = AIR_CAST( char, tolower( AIR_INT( *c ) ) );
c++;
}
}
return str;
}
/*
******** airToUpper( )
**
** calls toupper( ) on all characters in a string, and returns the same
** pointer that it was given
*/
char *
353 airToUpper( char *str ) {
char *c;
if ( str ) {
c = str;
while ( *c ) {
*c = AIR_CAST( char, toupper( AIR_INT( *c ) ) );
c++;
}
}
return str;
}
/*
******** airOneLine( )
**
** gets one line from "file", putting it into an array of given size.
** "size" must be the size of line buffer "line": the size which
** "line" was allocated for, not the number of non-null characters it
** was meant to hold. "size" must be at least 3. Always
** null-terminates the contents of the array ( except if the arguments
** are invalid ). The idea is that the null-termination replaces the
** line termination.
**
** 0: if saw EOF before seeing a newline, or arguments are invalid
** 1: if line was a single newline
** n; n <= size: if line was n-1 characters followed by newline
** size+1: if didn't see a newline within size-1 characters
**
** So except for returns of -1 and size+1, the return is the number of
** characters comprising the line, including the newline character.
**
** For all you DOS\Windows\Cygwin users, this will quietly pretend that
** a "\r\n" pair is really just "\n", including the way that characters
** comprising the line are counted. However, there is no pretension
** that on those platforms, "\n" by itself does not actually count as
** a newline.
**
** Finally, for those trafficking in legacy Mac text files ( for which
** the line termination is only "\r", the same applies- these are also
** effectively treated the same as a newline.
*/
unsigned int
396 airOneLine( FILE *file, char *line, unsigned int size ) {
int cc=0;
unsigned int ii;
if ( !( size >= 3 /* need room for a character and a Windows newline */
&& line && file ) ) {
return 0;
}
/* c is always set at least once, but not so for any char in line[] */
for ( ii=0;
( ii <= size-2 /* room for line[ii] and \0 after that */
&& EOF != ( cc=getc( file ) ) /* didn't hit EOF trying to read char */
&& cc != '\n' /* char isn't newline */
&& cc != '\r' ); /* char isn't carriage return */
++ii ) {
line[ii] = AIR_CAST( char, cc );
}
if ( EOF == cc ) {
/* for-loop terminated because we hit EOF */
line[0] = '\0';
return 0;
} else if ( '\r' == cc || '\n' == cc ) {
/* for-loop terminated because we hit '\n' or '\r' */
/* if it was '\r', see if next character is '\n' */
if ( '\r' == cc ) {
cc = getc( file );
if ( EOF != cc && '\n' != cc ) {
/* oops, we got something, and it was not a '\n'; put it back */
ungetc( cc, file );
}
}
line[ii] = '\0';
return ii+1;
} else {
/* for-loop terminated because we got to end of buffer ( ii == size-1 ) */
cc = getc( file );
/* but see if we were about to get '\r', "\r\n", or '\n' */
if ( '\r' == cc ) {
int dd;
dd = getc( file );
if ( EOF != dd && '\n' != dd ) {
/* oops, put it back */
ungetc( dd, file );
}
line[ii] = '\0';
return ii+1;
} else if ( '\n' == cc ) {
line[ii] = '\0';
return ii+1;
} else {
/* weren't about to get a line termination,
we really did run out of buffer */
if ( EOF != cc ) {
ungetc( cc, file ); /* we're allowed one ungetc on ANY stream */
}
line[size-1] = '\0';
return size+1;
}
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
/*
** 0 1 ( 2 )
** bessy <double>
*/
int
32 main( int argc, char *argv[] ) {
char *me;
double x;
int i;
me = argv[0];
if ( 2 != argc || 1 != sscanf( argv[1], "%lg", &x ) ) {
fprintf( stderr, "%s: need one double as argument\n", me );
exit( 1 );
}
printf( "BesselI( 0, %g ) = %g\n", x, airBesselI0( x ) );
printf( "log( BesselI( 0, %g ) ) = %g ?=? %g\n", x, airLogBesselI0( x ),
log( airBesselI0( x ) ) );
printf( "BesselI( 1, %g ) = %g\n", x, airBesselI1( x ) );
printf( "BesselI1By0( %g ) = %g ?=? %g\n", x,
airBesselI1By0( x ), airBesselI1( x )/airBesselI0( x ) );
printf( "BesselIExpScaled( 0, %g ) = %f\n", x, airBesselI0ExpScaled( x ) );
printf( "BesselIExpScaled( 1, %g ) = %f\n", x, airBesselI1ExpScaled( x ) );
printf( "erfc, erf( %g ) = %g %g\n", x, airErfc( x ), airErf( x ) );
printf( " n BesselIn( n, %g ) BesselInExpScaled( n, %g )\n", x, x );
for ( i = -10; i<= 10; i++ ) {
printf( "%d %g %g\n", i,
airBesselIn( i, x ), airBesselInExpScaled( i, x ) );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
char *me;
int
30 main( int argc, char *argv[] ) {
char *fS, buff[128];
float f;
double d, sd;
int ret;
me = argv[0];
if ( 2 != argc ) {
fprintf( stderr, "usage: %s <double>\n", me );
exit( 1 );
}
fS = argv[1];
ret = sscanf( fS, "%lf", &sd );
if ( !ret ) {
printf( "%s: sscanf( %s, \"%%lf\" ) failed\n", me, fS );
printf( "\n" );
}
if ( 1 != airSingleSscanf( fS, "%lf", &d ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as double\n", me, fS );
exit( 1 );
}
if ( ret && ( sd != d ) ) {
printf( "%s: sscanf result ( %f ) != airSingleSscanf ( %f )!!!\n", me, sd, d );
printf( "\n" );
}
f = d;
airSinglePrintf( NULL, buff, "%f", f );
printf( "%s: printf/airSinglePrintf as float:\n%f\n%s\n", me, f, buff );
airSinglePrintf( NULL, buff, "%lf", d );
printf( "\n" );
printf( "%s: printf/airSinglePrintf as double:\n%f\n%s\n", me, d, buff );
printf( "\n" );
printf( "%s: airFPFprintf_d:\n", me );
airFPFprintf_d( stderr, d );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
char *me;
int
30 main( int argc, char *argv[] ) {
char *fS, buff[128];
float f, sf;
double d;
int ret;
me = argv[0];
if ( 2 != argc ) {
fprintf( stderr, "usage: %s <float>\n", me );
exit( 1 );
}
fS = argv[1];
ret = sscanf( fS, "%f", &sf );
if ( !ret ) {
printf( "%s: sscanf( %s, \"%%f\" ) failed\n", me, fS );
printf( "\n" );
}
if ( 1 != airSingleSscanf( fS, "%f", &f ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as float\n", me, fS );
exit( 1 );
}
if ( ret && ( sf != f ) ) {
printf( "%s: sscanf result ( %f ) != airSingleSscanf ( %f ) !!!\n", me, sf, f );
printf( "\n" );
}
d = f;
airSinglePrintf( NULL, buff, "%f", f );
printf( "%s: printf/airSinglePrintf as float:\n%f\n%s\n", me, f, buff );
airSinglePrintf( NULL, buff, "%f", d );
printf( "\n" );
printf( "%s: printf/airSinglePrintf as double:\n%f\n%s\n", me, d, buff );
printf( "\n" );
printf( "%s: airFPFprintf_f:\n", me );
airFPFprintf_f( stderr, f );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
#include <teemQnanhibit.h>
char *me;
int
30 main( int argc, char *argv[] ) {
int c, hibit, ret;
float f, g, parsed_f;
double d, parsed_d;
char str[128];
AIR_UNUSED( argc );
me = argv[0];
g = 0.0;
g = g/g;
printf( "0.0/0.0 = %f\n", g );
airFPFprintf_f( stdout, g );
hibit = ( *( ( int* )&g ) >> 22 ) & 1;
printf( "hi bit of 23-bit fraction field = %d\n", hibit );
if ( hibit == airMyQNaNHiBit ) {
printf( "( agrees with airMyQNaNHiBit )\n" );
}
else {
printf( "%s: !!!!\n", me );
printf( "%s: !!!! PROBLEM: nan's hi bit is NOT airMyQNaNHiBit ( %d )\n",
me, airMyQNaNHiBit );
printf( "%s: !!!!\n", me );
}
printf( " - - - - - - - - - - - - - - - -\n" );
printf( " - - - - - FLOATS - - - - - - -\n" );
printf( " - - - - - - - - - - - - - - - -\n" );
for( c=airFP_Unknown+1; c<airFP_Last; c++ ) {
f = airFPGen_f( c );
sprintf( str, "%f", f );
ret = airSingleSscanf( str, "%d", &parsed_f );
printf( "********** airFPGen_f( %d ) = %f ( -> %f( %d ) ) ( AIR_EXISTS = %d )\n",
c, f, parsed_f, ret, AIR_EXISTS( f ) );
airSinglePrintf( stdout, NULL, "--<%f>--\n", f );
if ( c != airFPClass_f( f ) ) {
printf( "\n\n%s: Silly hardware!!!\n", me );
printf( "%s: can't return a float of class %d %sfrom a function\n\n\n",
me, c, airFP_SNAN == c ? "( signaling NaN ) " : "" );
}
airFPFprintf_f( stdout, f );
d = f;
/* I think solaris turns the SNAN into a QNAN */
printf( "to double and back:\n" );
airFPFprintf_f( stdout, d );
printf( "AIR_ISNAN_F = %d\n", AIR_ISNAN_F( f ) );
}
printf( " - - - - - - - - - - - - - - - -\n" );
printf( " - - - - - DOUBLES - - - - - - -\n" );
printf( " - - - - - - - - - - - - - - - -\n" );
for( c=airFP_Unknown+1; c<airFP_Last; c++ ) {
d = airFPGen_d( c );
sprintf( str, "%f", d );
ret = airSingleSscanf( str, "%lf", &parsed_d );
printf( "********** airFPGen_d( %d ) = %f ( -> %f( %d ) ) ( AIR_EXISTS = %d )\n",
c, d, parsed_d, ret, AIR_EXISTS( d ) );
airSinglePrintf( stdout, NULL, "--<%f>--\n", d );
if ( c != airFPClass_d( d ) ) {
printf( "\n\n%s: Silly hardware!!!\n", me );
printf( "%s: can't return a double of class %d %sfrom a function\n\n\n",
me, c, airFP_SNAN == c ? "( signaling NaN ) " : "" );
}
airFPFprintf_d( stdout, d );
}
printf( " - - - - - - - - - - - - - - - -\n" );
printf( " - - - - - - - - - - - - - - - -\n" );
f = AIR_SNAN;
printf( "SNaN test: f = SNaN = float( 0x%x ) = %f; ( QNaNHiBit = %d )\n",
airFloatSNaN.i, f, airMyQNaNHiBit );
airFPFprintf_f( stdout, f );
g = f*f;
printf( "g = f*f = %f\n", g );
airFPFprintf_f( stdout, g );
g = sin( f );
printf( "g = sin( f ) = %f\n", g );
airFPFprintf_f( stdout, g );
printf( "\n" );
printf( "FLT_MAX:\n" ); airFPFprintf_f( stdout, FLT_MAX ); printf( "\n" );
printf( "FLT_MIN:\n" ); airFPFprintf_f( stdout, FLT_MIN ); printf( "\n" );
printf( "DBL_MAX:\n" ); airFPFprintf_d( stdout, DBL_MAX ); printf( "\n" );
printf( "DBL_MIN:\n" ); airFPFprintf_d( stdout, DBL_MIN ); printf( "\n" );
printf( "AIR_NAN = %f; AIR_EXISTS( AIR_NAN ) = %d\n",
AIR_NAN, AIR_EXISTS( AIR_NAN ) );
printf( "AIR_POS_INF = %f; AIR_EXISTS( AIR_POS_INF ) = %d\n",
AIR_POS_INF, AIR_EXISTS( AIR_POS_INF ) );
printf( "AIR_NEG_INF = %f; AIR_EXISTS( AIR_NEG_INF ) = %d\n",
AIR_NEG_INF, AIR_EXISTS( AIR_NEG_INF ) );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
/*
** 0 1 2 3 ( 4 )
** logrice tru mes sig
*/
int
32 main( int argc, char *argv[] ) {
char *me;
double tru, mes, sig;
me = argv[0];
if ( 4 != argc
|| 1 != sscanf( argv[1], "%lg", &tru )
|| 1 != sscanf( argv[2], "%lg", &mes )
|| 1 != sscanf( argv[3], "%lg", &sig ) ) {
fprintf( stderr, "%s: need three doubles\n", me );
exit( 1 );
}
printf( "log( Rician( %g, %g, %g ) ) = %g\n",
tru, mes, sig, airLogRician( tru, mes, sig ) );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
int
28 main( int argc, char *argv[] ) {
char *me, *fname, *incrS;
airArray *mop, *dataArr;
FILE *file;
unsigned int incr, numRed;
unsigned char *data;
int datum; /* must be int, so it can store EOF */
airPtrPtrUnion appu;
me = argv[0];
if ( 3 != argc ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <filename> <incr>\n", me );
return 1;
}
fname = argv[1];
incrS = argv[2];
/* the "mop" is for management of dynamically allocated resources
cleanly in combination with error handling, its not important
for understanding how airArrays work ( although as you can tell
from the declaration, the "mop" is built on an airArray ) */
mop = airMopNew( );
if ( !( file = fopen( fname, "rb" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for reading\n", me, fname );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( 1 != sscanf( incrS, "%ud", &incr ) ) {
fprintf( stderr, "%s: couln't parse incr \"%s\" as unsigned int\n",
me, incrS );
airMopError( mop ); return 1;
}
/* now "file" is the open file that we read from, and "incr is the
size increment ( the granularity ) for re-allocating the data */
/* the arguments here are as follows:
1 ) &data: the address of the array ( itself a pointer ) into which
we'll copy the data. Whenever the airArray re-allocates the array,
it will update the value of the array variable to the new location.
So, while it seems a little weird at first, the value of the "data"
variable can change as a side-effect of calling the airArray functions.
2 ) NULL: we could pass the address of a variable to record the current
allocated length of the array, and this might be useful, but isn't
necessary
3 ) sizeof( unsigned char ): this is the size of the individual elements
that we are saving in the array. Because memory is allocated and
addressed at the level of individual bytes ( and files can be read
one byte at-a-time ), we manage the buffer as an array of unsigned chars.
4 ) incr: when the array length is a multiple of incr, the memory
segment is re-allocated, so this determines how often the re-allocation
happens ( we want it to happen fairly infrequently ) */
/* dataArr = airArrayNew( &data, NULL, sizeof( unsigned char ), incr ); */
/* but wait: to play well with type checking, we have to use a stupid
union to pass in the address of the array. So, appu.v == &data,
but the types are right. We don't do a cast because recent versions
of gcc will complain about breaking "strict-aliasing rules". */
appu.uc = &data;
dataArr = airArrayNew( appu.v, NULL, sizeof( unsigned char ), incr );
if ( !dataArr ) {
fprintf( stderr, "%s: couldn't allocate airArray\n", me );
airMopError( mop ); return 1;
}
/* numRed will keep track of the number of bytes that have been
successfully read from the file AND stored in data[] */
numRed = 0;
/* try to get the first byte of data */
datum = fgetc( file );
if ( EOF == datum ) {
fprintf( stderr, "%s: hit EOF trying to get first byte\n", me );
airMopError( mop ); return 1;
}
while ( EOF != datum ) {
airArrayLenSet( dataArr, numRed+1 );
if ( !data ) {
fprintf( stderr, "%s: couldn't re-allocated data buffer\n", me );
airMopError( mop ); return 1;
}
/* now "data" is the address of a sufficiently large array */
data[numRed++] = datum;
datum = fgetc( file );
}
/* loop finishes when there's nothing more to read from file */
printf( "%s: read %u bytes into memory\n", me, numRed );
/* destroy the airArray, but keep the data allocated */
airArrayNix( dataArr );
printf( "%s: first value was %u\n", me, data[0] );
/* free up the data array itself */
free( data );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
#if TEEM_DIO == 0
#else
/* HEY: these may be SGI-specific */
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#endif
int
36 main( int argc, char *argv[] ) {
#if TEEM_DIO == 0
AIR_UNUSED( argc );
fprintf( stderr, "%s: no direct-io testing for you\n", argv[0] );
return 1;
#else
char *me, *fname, *multS, *data;
FILE *file;
double time0, time1, time2;
int fd, align, mult, min, max, ret;
size_t size;
airArray *mop;
me = argv[0];
if ( 3 != argc ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <filename> <mult>\n", me );
return 1;
}
fname = argv[1];
multS = argv[2];
if ( 1 != sscanf( multS, "%d", &mult ) ) {
fprintf( stderr, "%s: couln't parse mult %s as int\n", me, multS );
return 1;
}
mop = airMopNew( );
if ( !( file = fopen( fname, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing\n", me, fname );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
fd = fileno( file );
if ( -1 == fd ) {
fprintf( stderr, "%s: couldn't get underlying descriptor\n", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: fd( %s ) = %d\n", me, fname, fd );
ret = airDioTest( fd, NULL, 0 );
if ( airNoDio_okay != ret ) {
fprintf( stderr, "%s: no good: \"%s\"\n", me, airNoDioErr( ret ) );
airMopError( mop ); return 1;
}
airDioInfo( &align, &min, &max, fd );
fprintf( stderr, "%s: --> align=%d, min=%d, max=%d\n", me, align, min, max );
size = ( size_t )max*mult;
data = airDioMalloc( size, fd );
if ( !data ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: airDioMalloc( %s ) failed\n", me,
airSprintSize_t( stmp, size ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, data, airFree, airMopAlways );
fprintf( stderr, "\ndata size = %g MB\n", ( double )size/( 1024*1024 ) );
/* -------------------------------------------------------------- */
fprintf( stderr, "( 1 ) non-aligned memory, regular write:\n" );
time0 = airTime( );
if ( size-1 != write( fd, data+1, size-1 ) ) {
fprintf( stderr, "%s: write failed\n", me );
airMopError( mop ); return 1;
}
time1 = airTime( );
fsync( fd );
time2 = airTime( );
fprintf( stderr, " time = %g + %g = %g ( %g MB/sec )\n",
time1 - time0, time2 - time1, time2 - time0,
( size/( 1024*1024 ) ) / ( time2 - time0 ) );
airMopSub( mop, file, ( airMopper )airFclose );
fclose( file );
/* -------------------------------------------------------------- */
/* -------------------------------------------------------------- */
fprintf( stderr, "( 2 ) aligned memory, regular write:\n" );
file = fopen( fname, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
fd = fileno( file );
time0 = airTime( );
if ( size != write( fd, data, size ) ) {
fprintf( stderr, "%s: write failed\n", me );
airMopError( mop ); return 1;
}
time1 = airTime( );
fsync( fd );
time2 = airTime( );
fprintf( stderr, " time = %g + %g = %g ( %g MB/sec )\n",
time1 - time0, time2 - time1, time2 - time0,
( size/( 1024*1024 ) ) / ( time2 - time0 ) );
airMopSub( mop, file, ( airMopper )airFclose );
fclose( file );
/* -------------------------------------------------------------- */
/* -------------------------------------------------------------- */
fprintf( stderr, "( 3 ) aligned memory, air's direct IO:\n" );
file = fopen( fname, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
fd = fileno( file );
time0 = airTime( );
if ( size != airDioWrite( fd, data, size ) ) {
fprintf( stderr, "%s: write failed\n", me );
airMopError( mop ); return 1;
}
time1 = airTime( );
fsync( fd );
time2 = airTime( );
fprintf( stderr, " time = %g + %g = %g ( %g MB/sec )\n",
time1 - time0, time2 - time1, time2 - time0,
( size/( 1024*1024 ) ) / ( time2 - time0 ) );
airMopSub( mop, file, ( airMopper )airFclose );
fclose( file );
/* -------------------------------------------------------------- */
/* -------------------------------------------------------------- */
fprintf( stderr, "( 4 ) aligned memory, direct IO by hand:\n" );
{
/* "input": fname, size, data */
int flags;
struct dioattr dio;
char *ptr;
size_t remain, totalrit, rit, part;
file = fopen( fname, "w" );
if ( -1 == ( fd = fileno( file ) ) ) {
fprintf( stderr, "%s: couldn't get underlying descriptor\n", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
flags = fcntl( fd, F_GETFL );
if ( -1 == fcntl( fd, F_SETFL, flags | FDIRECT ) ) {
fprintf( stderr, "%s: couldn't turn on direct IO\n", me );
airMopError( mop ); return 1;
}
if ( 0 != fcntl( fd, F_DIOINFO, &dio ) ) {
fprintf( stderr, "%s: couldn't learn direct IO specifics", me );
airMopError( mop ); return 1;
}
remain = size;
totalrit = 0;
ptr = data;
time0 = airTime( );
do {
part = remain > dio.d_maxiosz ? dio.d_maxiosz : remain;
rit = write( fd, ptr, part );
if ( rit != part ) {
fprintf( stderr, "%s: write failed\n", me );
airMopError( mop ); return 1;
}
totalrit += rit;
ptr += rit;
remain -= rit;
} while ( remain );
time1 = airTime( );
fsync( fd );
time2 = airTime( );
fprintf( stderr, " time = %g + %g = %g ( %g MB/sec )\n",
time1 - time0, time2 - time1, time2 - time0,
( size/( 1024*1024 ) ) / ( time2 - time0 ) );
airMopSub( mop, file, ( airMopper )airFclose );
fclose( file );
}
/* -------------------------------------------------------------- */
airMopError( mop );
exit( 0 );
#endif
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
/*
** 0 1 ( 2 )
** texp N
*/
int
32 main( int argc, char *argv[] ) {
char *me;
unsigned int ii, NN;
me = argv[0];
if ( 2 != argc || 1 != sscanf( argv[1], "%u", &NN ) ) {
fprintf( stderr, "%s: need one uint as argument\n", me );
exit( 1 );
}
for ( ii=0; ii<NN; ii++ ) {
double xx;
xx = AIR_AFFINE( 0.0, airDrandMT( ), 1.0, -10, 10 );
printf( "%f %f\n", exp( xx ), airFastExp( xx ) );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
FILE *
28 myopen( char *name ) {
if ( !strcmp( name, "-" ) ) {
return stdin;
} else {
return fopen( name, "r" );
}
}
void
38 myclose( FILE *file ) {
if ( file != stdin ) {
fclose( file );
}
return;
}
int
48 main( int argc, char *argv[] ) {
char *me, *fileS, *line;
FILE *file;
int size, maxed, ret;
me = argv[0];
if ( 2 != argc ) {
/* 0 1 ( 2 ) */
fprintf( stderr, "usage: %s <file>\n", me );
exit( 1 );
}
fileS = argv[1];
if ( !( file = myopen( fileS ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for reading\n", me, fileS );
exit( 1 );
}
myclose( file );
size = 30000;
do {
maxed = 0;
file = myopen( fileS );
line = ( char * )calloc( size, sizeof( char ) );
printf( " ----------------- size = %d\n", size );
do {
ret = airOneLine( file, line, size );
if ( ret ) {
printf( "%2d |%s|\n", ret, line );
maxed |= ( ret == size+1 );
}
} while( ret > 0 );
myclose( file );
free( line );
size++;
} while( maxed );
printf( "\n\nsize = %d\n", size );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
void *
28 print( void *_iP ) {
int *iP;
iP = ( int * )_iP;
printf( "%d\n", *iP );
return NULL;
}
int
37 main( int argc, char *argv[] ) {
void *ptr;
int i = 111, j = 222, k = 333, l = 444;
airArray *mop;
char *str, *me;
AIR_UNUSED( argc );
me = argv[0];
printf( "%s: -------------------------------------\n", me );
mop = airMopNew( );
str = airStrdup( "this is just a test" );
printf( "%s: str = \"%s\", str = 0x%p, &str = 0x%p\n", me,
str, str, AIR_CAST( void*, &str ) );
airMopMem( mop, &str, airMopAlways );
airMopDebug( mop );
airMopOkay( mop );
printf( "%s: -------------------------------------\n", me );
mop = airMopNew( );
ptr = calloc( 1024, sizeof( char ) );
airMopMem( mop, &ptr, airMopAlways );
airMopAdd( mop, &i, print, airMopNever );
airMopAdd( mop, &j, print, airMopOnError );
airMopAdd( mop, &k, print, airMopOnOkay );
airMopAdd( mop, &l, print, airMopAlways );
airMopPrint( mop, "this is a joke", airMopOnError );
airMopDebug( mop );
airMopError( mop );
printf( "%s: -------------------------------------\n", me );
mop = airMopNew( );
ptr = calloc( 1024, sizeof( char ) );
airMopMem( mop, &ptr, airMopAlways );
airMopAdd( mop, &i, print, airMopNever );
airMopAdd( mop, &j, print, airMopOnError );
airMopAdd( mop, &k, print, airMopOnOkay );
airMopPrint( mop, "this is a joke", airMopOnError );
airMopAdd( mop, &l, print, airMopAlways );
airMopDebug( mop );
airMopOkay( mop );
printf( "%s: -------------------------------------\n", me );
mop = airMopNew( );
ptr = calloc( 1024, sizeof( char ) );
airMopMem( mop, &ptr, airMopAlways );
free( ptr );
airMopUnMem( mop, &ptr );
airMopDebug( mop );
airMopOkay( mop );
printf( "%s: -------------------------------------\n", me );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
int
28 main( int argc, char *argv[] ) {
char *s, *ct, *last, *ret;
if ( 3 != argc ) {
/* 0 1 2 ( 3 ) */
printf( "usage: %s <s> <ct>\n", argv[0] );
exit( 1 );
}
s = argv[1];
ct = argv[2];
printf( "s = |%s|\n", s );
printf( "ct = |%s|\n", ct );
ret = airStrtok( s, ct, &last );
while ( ret ) {
printf( "|%s|\n", ret );
ret = airStrtok( NULL, ct, &last );
}
printf( "--------------\n" );
printf( "hey, why doesn't this work?!?!?\n" );
printf( "s = |%s|\n", s );
ret = strtok( s, ct );
while ( ret ) {
printf( "ret = |%s|\n", ret );
ret = strtok( NULL, ct );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
int
28 main( int argc, char *argv[] ) {
char *me;
char str[AIR_STRLEN_SMALL];
size_t sz;
ptrdiff_t pd;
int change;
AIR_UNUSED( argc );
me = argv[0];
#define PRINT printf( "%s: %lu %s\n", me, AIR_CAST( unsigned long, sz ), airSprintSize_t( str, sz ) )
sz = 1; PRINT;
do {
sz = 2.4*sz; PRINT;
} while ( sz );
*( ( long int * )( &sz ) ) = -1; PRINT;
sz += 1; PRINT;
sz -= 1; PRINT;
sz -= 1; PRINT;
sz -= 1; PRINT;
#undef PRINT
#define PRINT printf( "%s: %ld %s\n", me, AIR_CAST( long, pd ), airSprintPtrdiff_t( str, pd ) )
pd = 1; PRINT;
do {
ptrdiff_t od;
od = pd;
pd = 2.4*pd; PRINT;
change = ( od != pd );
} while ( change );
pd = -1; PRINT;
do {
ptrdiff_t od;
od = pd;
pd = 2.4*pd; PRINT;
change = ( od != pd );
} while ( change );
pd -= 5;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
pd += 1; PRINT;
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../air.h"
int
28 main( int argc, char *argv[] ) {
char *me, *NS;
int ii, N;
me = argv[0];
if ( 2 != argc ) {
/* 0 1 ( 2 ) */
fprintf( stderr, "usage: %s <N>\n", me );
exit( 1 );
}
NS = argv[1];
if ( 1 != sscanf( NS, "%d", &N ) ) {
fprintf( stderr, "%s: couldn't parse %s as int N\n", me, NS );
exit( 1 );
}
for ( ii=0; ii<N; ii++ ) {
printf( "%g\n", airDrandMT( ) );
}
exit( 0 );
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "air.h"
/* HEY: the whole matter of function returns has to be standardized. */
int airThreadNoopWarning = AIR_TRUE;
/* ------------------------------------------------------------------ */
#if TEEM_PTHREAD /* ----------------------------------------- PTHREAD */
/* ------------------------------------------------------------------ */
#include <pthread.h>
const int airThreadCapable = AIR_TRUE;
struct _airThread {
pthread_t id;
};
struct _airThreadMutex {
pthread_mutex_t id;
};
struct _airThreadCond {
pthread_cond_t id;
};
airThread *
51 airThreadNew( void ) {
airThread *thread;
thread = AIR_CALLOC( 1, airThread );
/* HEY: not sure if this can be usefully initialized */
return thread;
}
int
60 airThreadStart( airThread *thread, void *( *threadBody )( void * ), void *arg ) {
pthread_attr_t attr;
pthread_attr_init( &attr );
#ifdef __sgi
pthread_attr_setscope( &attr, PTHREAD_SCOPE_BOUND_NP );
#endif
return pthread_create( &( thread->id ), &attr, threadBody, arg );
}
int
71 airThreadJoin( airThread *thread, void **retP ) {
return pthread_join( thread->id, retP );
}
airThread *
77 airThreadNix( airThread *thread ) {
airFree( thread );
return NULL;
}
airThreadMutex *
84 airThreadMutexNew( void ) {
airThreadMutex *mutex;
mutex = AIR_CALLOC( 1, airThreadMutex );
if ( mutex ) {
if ( pthread_mutex_init( &( mutex->id ), NULL ) ) {
mutex = ( airThreadMutex * )airFree( mutex );
}
}
return mutex;
}
int
97 airThreadMutexLock( airThreadMutex *mutex ) {
return pthread_mutex_lock( &( mutex->id ) );
}
int
103 airThreadMutexUnlock( airThreadMutex *mutex ) {
return pthread_mutex_unlock( &( mutex->id ) );
}
airThreadMutex *
109 airThreadMutexNix( airThreadMutex *mutex ) {
if ( mutex ) {
if ( !pthread_mutex_destroy( &( mutex->id ) ) ) {
/* there was no error */
mutex = ( airThreadMutex * )airFree( mutex );
}
}
return mutex;
}
airThreadCond *
121 airThreadCondNew( void ) {
airThreadCond *cond;
cond = AIR_CALLOC( 1, airThreadCond );
if ( cond ) {
if ( pthread_cond_init( &( cond->id ), NULL ) ) {
/* there was an error */
cond = ( airThreadCond * )airFree( cond );
}
}
return cond;
}
int
135 airThreadCondWait( airThreadCond *cond, airThreadMutex *mutex ) {
return pthread_cond_wait( &( cond->id ), &( mutex->id ) );
}
int
141 airThreadCondSignal( airThreadCond *cond ) {
return pthread_cond_signal( &( cond->id ) );
}
int
147 airThreadCondBroadcast( airThreadCond *cond ) {
return pthread_cond_broadcast( &( cond->id ) );
}
airThreadCond *
153 airThreadCondNix( airThreadCond *cond ) {
if ( cond ) {
if ( !pthread_cond_destroy( &( cond->id ) ) ) {
/* there was no error */
cond = ( airThreadCond * )airFree( cond );
}
}
return cond;
}
/* ------------------------------------------------------------------ */
#elif defined( _WIN32 ) /* ------------------------------------- WIN 32 */
/* ------------------------------------------------------------------ */
#if defined( _WIN32 )
/* SignalObjectAndWait supported by NT4.0 and greater only */
# define _WIN32_WINNT 0x400
# include <windows.h>
#endif
const int airThreadCapable = AIR_TRUE;
struct _airThread {
HANDLE handle;
void *( *body )( void * );
void *arg;
void *ret;
};
struct _airThreadMutex {
HANDLE handle;
};
struct _airThreadCond {
int count;
CRITICAL_SECTION lock;
HANDLE sema;
HANDLE done;
size_t broadcast;
};
airThread *
196 airThreadNew( void ) {
airThread *thread;
thread = AIR_CALLOC( 1, airThread );
/* HEY: any useful way to initialized a HANDLE? */
thread->handle = NULL;
thread->body = NULL;
thread->arg = thread->ret = NULL;
return thread;
}
#if defined( __BORLANDC__ )
unsigned long
#else
int
#endif /* defined( __BORLANDC__ ) */
WINAPI _airThreadWin32Body( void *_thread ) {
airThread *thread;
215 thread = ( airThread * )_thread;
216 thread->ret = thread->body( thread->arg );
return 0;
}
int
airThreadStart( airThread *thread, void *( *threadBody )( void * ), void *arg ) {
thread->body = threadBody;
thread->arg = arg;
thread->handle = CreateThread( 0, 0, _airThreadWin32Body,
( void * )thread, 0, 0 );
return NULL == thread->handle;
}
int
airThreadJoin( airThread *thread, void **retP ) {
int err;
err = ( WAIT_FAILED == WaitForSingleObject( thread->handle, INFINITE ) );
*retP = thread->ret;
return err;
}
airThread *
airThreadNix( airThread *_thread ) {
char me[] = "airThreadNix";
if ( 0 == CloseHandle( _thread->handle ) ) {
fprintf( stderr, "%s: CloseHandle failed, something is wrong\n", me );
}
return airFree( _thread );
}
airThreadMutex *
airThreadMutexNew( ) {
airThreadMutex *mutex;
mutex = AIR_CALLOC( 1, airThreadMutex );
if ( mutex ) {
if ( !( mutex->handle = CreateMutex( NULL, FALSE, NULL ) ) ) {
return airFree( mutex );
}
}
return mutex;
}
int
airThreadMutexLock( airThreadMutex *mutex ) {
return WAIT_FAILED == WaitForSingleObject( mutex->handle, INFINITE );
}
int
airThreadMutexUnlock( airThreadMutex *mutex ) {
return 0 == ReleaseMutex( mutex->handle );
}
airThreadMutex *
airThreadMutexNix( airThreadMutex *mutex ) {
CloseHandle( mutex->handle );
mutex = airFree( mutex );
return mutex;
}
airThreadCond *
airThreadCondNew( void ) {
airThreadCond *cond;
cond = AIR_CALLOC( 1, airThreadCond );
if ( cond ) {
cond->count = 0;
cond->broadcast = 0;
cond->sema = CreateSemaphore( NULL, 0, 0x7fffffff, NULL );
if ( NULL == cond->sema ) {
return airFree( cond );
}
InitializeCriticalSection( &( cond->lock ) );
cond->done = CreateEvent( NULL, FALSE, FALSE, NULL );
if ( NULL == cond->done ) {
CloseHandle( cond->sema );
return airFree( cond );
}
}
return cond;
}
int
airThreadCondWait( airThreadCond *cond, airThreadMutex *mutex ) {
int last;
/* increment count */
EnterCriticalSection( &( cond->lock ) ); /* avoid race conditions */
cond->count++;
LeaveCriticalSection( &( cond->lock ) );
/* atomically release the mutex and wait on the
semaphore until airThreadCondSignal or airThreadCondBroadcast
are called by another thread */
if ( WAIT_FAILED == SignalObjectAndWait( mutex->handle, cond->sema,
INFINITE, FALSE ) ) {
return 1;
}
/* reacquire lock to avoid race conditions */
EnterCriticalSection( &( cond->lock ) );
/* we're no longer waiting... */
cond->count--;
/* check to see if we're the last waiter after airThreadCondBroadcast */
last = ( cond->broadcast && 0 == cond->count );
LeaveCriticalSection( &( cond->lock ) );
/* if we're the last waiter thread during this particular broadcast
then let all the other threads proceed */
if ( last ) {
/* atomically signal the done event and waits until
we can acquire the mutex ( this is required to ensure fairness ) */
if ( WAIT_FAILED == SignalObjectAndWait( cond->done, mutex->handle,
INFINITE, FALSE ) ) {
return 1;
}
} else {
/* regain the external mutex since that's the guarantee to our callers */
if ( WAIT_FAILED == WaitForSingleObject( mutex->handle, INFINITE ) ) {
return 1;
}
}
return 0;
}
int
airThreadCondSignal( airThreadCond *cond ) {
int waiters;
EnterCriticalSection( &( cond->lock ) );
waiters = cond->count > 0;
LeaveCriticalSection( &( cond->lock ) );
/* if there aren't any waiters, then this is a no-op */
if ( waiters ) {
if ( 0 == ReleaseSemaphore( cond->sema, 1, NULL ) ) {
return 1;
}
}
return 0;
}
int
airThreadCondBroadcast( airThreadCond *cond ) {
int waiters;
/* need to ensure that cond->count and cond->broadcast are consistent */
EnterCriticalSection( &( cond->lock ) );
waiters = 0;
if ( cond->count > 0 ) {
/* we are broadcasting, even if there is just one waiter...
record that we are broadcasting, which helps optimize
airThreadCondWait for the non-broadcast case */
cond->broadcast = 1;
waiters = 1;
}
if ( waiters ) {
/* wake up all the waiters atomically */
if ( 0 == ReleaseSemaphore( cond->sema, cond->count, 0 ) ) {
return 1;
}
LeaveCriticalSection( &( cond->lock ) );
/* wait for all the awakened threads to acquire the counting semaphore */
if ( WAIT_FAILED == WaitForSingleObject( cond->done, INFINITE ) ) {
return 1;
}
/* this assignment is okay, even without the lock held
because no other waiter threads can wake up to access it */
cond->broadcast = 0;
} else {
LeaveCriticalSection( &( cond->lock ) );
}
return 0;
}
airThreadCond *
airThreadCondNix( airThreadCond *cond ) {
airThreadCond *ret=NULL;
if ( cond ) {
cond->count = 0;
cond->broadcast = 0;
if ( 0 == CloseHandle( cond->done ) ) {
ret = cond;
}
DeleteCriticalSection( &( cond->lock ) );
if ( 0 == CloseHandle( cond->sema ) ) {
ret = cond;
}
ret = airFree( cond );
}
return ret;
}
/* ------------------------------------------------------------------ */
#else /* --------------------------------------- ( no multi-threading ) */
/* ------------------------------------------------------------------ */
const int airThreadCapable = AIR_FALSE;
struct _airThread {
void *ret;
};
struct _airThreadMutex {
int dummy;
};
struct _airThreadCond {
int dummy;
};
airThread *
airThreadNew( void ) {
airThread *thread;
thread = AIR_CALLOC( 1, airThread );
thread->ret = NULL;
return thread;
}
int
airThreadStart( airThread *thread, void *( *threadBody )( void * ), void *arg ) {
/* run the threadBody callback, which will return only when the
task has decided to call it quits */
thread->ret = ( *threadBody )( arg );
return 0;
}
int
airThreadJoin( airThread *thread, void **retP ) {
*retP = thread->ret;
return 0;
}
airThread *
airThreadNix( airThread *thread ) {
airFree( thread );
return NULL;
}
airThreadMutex *
airThreadMutexNew( void ) {
airThreadMutex *mutex;
mutex = AIR_CALLOC( 1, airThreadMutex );
return mutex;
}
int
airThreadMutexLock( airThreadMutex *mutex ) {
char me[]="airThreadMutexLock";
AIR_UNUSED( mutex );
if ( airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: all mutex usage is a no-op!\n", me );
}
return 0;
}
int
airThreadMutexUnlock( airThreadMutex *mutex ) {
char me[]="airThreadMutexUnlock";
AIR_UNUSED( mutex );
if ( airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: all mutex usage is a no-op!\n", me );
}
return 0;
}
airThreadMutex *
airThreadMutexNix( airThreadMutex *mutex ) {
airFree( mutex );
return NULL;
}
airThreadCond *
airThreadCondNew( void ) {
airThreadCond *cond;
cond = AIR_CALLOC( 1, airThreadCond );
return cond;
}
int
airThreadCondWait( airThreadCond *cond, airThreadMutex *mutex ) {
char me[]="airThreadCondWait";
AIR_UNUSED( cond );
AIR_UNUSED( mutex );
if ( airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: all cond usage is a no-op!\n", me );
}
return 0;
}
int
airThreadCondSignal( airThreadCond *cond ) {
char me[]="airThreadCondSignal";
AIR_UNUSED( cond );
if ( airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: all cond usage is a no-op!\n", me );
}
return 0;
}
int
airThreadCondBroadcast( airThreadCond *cond ) {
char me[]="airThreadCondBroadcast";
AIR_UNUSED( cond );
if ( airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: all cond usage is a no-op!\n", me );
}
return 0;
}
airThreadCond *
airThreadCondNix( airThreadCond *cond ) {
airFree( cond );
return NULL;
}
/* ------------------------------------------------------------------ */
#endif /* ----------------------------------------------------------- */
/* ------------------------------------------------------------------ */
airThreadBarrier *
airThreadBarrierNew( unsigned int numUsers ) {
airThreadBarrier *barrier;
barrier = AIR_CALLOC( 1, airThreadBarrier );
if ( barrier ) {
barrier->numUsers = numUsers;
barrier->numDone = 0;
if ( !( barrier->doneMutex = airThreadMutexNew( ) ) ) {
airFree( barrier );
return NULL;
}
if ( !( barrier->doneCond = airThreadCondNew( ) ) ) {
barrier->doneMutex = airThreadMutexNix( barrier->doneMutex );
airFree( barrier );
return NULL;
}
}
return barrier;
}
int
airThreadBarrierWait( airThreadBarrier *barrier ) {
airThreadMutexLock( barrier->doneMutex );
barrier->numDone += 1;
if ( barrier->numDone < barrier->numUsers ) {
airThreadCondWait( barrier->doneCond, barrier->doneMutex );
} else {
barrier->numDone = 0;
airThreadCondBroadcast( barrier->doneCond );
}
airThreadMutexUnlock( barrier->doneMutex );
return 0;
}
airThreadBarrier *
airThreadBarrierNix( airThreadBarrier *barrier ) {
barrier->doneMutex = airThreadMutexNix( barrier->doneMutex );
barrier->doneCond = airThreadCondNix( barrier->doneCond );
airFree( barrier );
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
** learned: valgrind is sometimes far off when reporting the line
** number for an invalid read- have to comment out various lines in
** order to find the real offending line
*/
#include "alan.h"
int
33 _alanCheck( alanContext *actx ) {
static const char me[]="alanCheck";
if ( !actx ) {
biffAddf( ALAN, "%s: got NULL pointer", me );
return 1;
}
if ( 0 == actx->dim ) {
biffAddf( ALAN, "%s: dimension of texture not set", me );
return 1;
}
if ( alanTextureTypeUnknown == actx->textureType ) {
biffAddf( ALAN, "%s: texture type not set", me );
return 1;
}
if ( !( actx->size[0] > 0 && actx->size[1] > 0
&& ( 2 == actx->dim || actx->size[2] > 0 ) ) ) {
biffAddf( ALAN, "%s: texture sizes invalid", me );
return 1;
}
if ( 0 == actx->deltaT ) {
biffAddf( ALAN, "%s: deltaT == 0", me );
return 1;
}
return 0;
}
int
62 alanUpdate( alanContext *actx ) {
static const char me[]="alanUpdate";
int ret;
if ( _alanCheck( actx ) ) {
biffAddf( ALAN, "%s: ", me );
return 1;
}
if ( actx->_nlev[0] || actx->_nlev[0] ) {
biffAddf( ALAN, "%s: confusion: _nlev[0, 1] already allocated?", me );
return 1;
}
actx->_nlev[0] = nrrdNew( );
actx->_nlev[1] = nrrdNew( );
actx->nparm = nrrdNew( );
if ( 2 == actx->dim ) {
ret = ( nrrdMaybeAlloc_va( actx->_nlev[0], alan_nt, 3,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, actx->size[0] ),
AIR_CAST( size_t, actx->size[1] ) )
|| nrrdCopy( actx->_nlev[1], actx->_nlev[0] )
|| nrrdMaybeAlloc_va( actx->nparm, alan_nt, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, actx->size[0] ),
AIR_CAST( size_t, actx->size[1] ) ) );
} else {
ret = ( nrrdMaybeAlloc_va( actx->_nlev[0], alan_nt, 4,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, actx->size[0] ),
AIR_CAST( size_t, actx->size[1] ),
AIR_CAST( size_t, actx->size[2] ) )
|| nrrdCopy( actx->_nlev[1], actx->_nlev[0] )
|| nrrdMaybeAlloc_va( actx->nparm, alan_nt, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, actx->size[0] ),
AIR_CAST( size_t, actx->size[1] ),
AIR_CAST( size_t, actx->size[2] ) ) );
}
if ( ret ) {
biffMovef( ALAN, NRRD, "%s: trouble allocating buffers", me );
return 1;
}
return 0;
}
int
109 alanInit( alanContext *actx, const Nrrd *nlevInit, const Nrrd *nparmInit ) {
static const char me[]="alanInit";
alan_t *levInit=NULL, *lev0, *parmInit=NULL, *parm;
size_t I, N;
if ( _alanCheck( actx ) ) {
biffAddf( ALAN, "%s: ", me );
return 1;
}
if ( !( actx->_nlev[0] && actx->_nlev[0] && actx->nparm ) ) {
biffAddf( ALAN, "%s: _nlev[0, 1] not allocated: call alanUpdate", me );
return 1;
}
if ( nlevInit ) {
if ( nrrdCheck( nlevInit ) ) {
biffMovef( ALAN, NRRD, "%s: given nlevInit has problems", me );
return 1;
}
if ( !( alan_nt == nlevInit->type
&& nlevInit->dim == 1 + actx->dim
&& actx->_nlev[0]->axis[0].size == nlevInit->axis[0].size
&& actx->size[0] == nlevInit->axis[1].size
&& actx->size[1] == nlevInit->axis[2].size
&& ( 2 == actx->dim || actx->size[2] == nlevInit->axis[3].size ) ) ) {
biffAddf( ALAN, "%s: type/size mismatch with given nlevInit", me );
return 1;
}
levInit = ( alan_t* )( nlevInit->data );
}
if ( nparmInit ) {
if ( nrrdCheck( nparmInit ) ) {
biffMovef( ALAN, NRRD, "%s: given nparmInit has problems", me );
return 1;
}
if ( !( alan_nt == nparmInit->type
&& nparmInit->dim == 1 + actx->dim
&& 3 == nparmInit->axis[0].size
&& actx->size[0] == nparmInit->axis[1].size
&& actx->size[1] == nparmInit->axis[2].size
&& ( 2 == actx->dim || actx->size[2] == nparmInit->axis[3].size ) ) ) {
biffAddf( ALAN, "%s: type/size mismatch with given nparmInit", me );
return 1;
}
parmInit = ( alan_t* )( nparmInit->data );
}
#define RAND AIR_AFFINE( 0, airDrandMT( ), 1, -actx->randRange, actx->randRange )
N = nrrdElementNumber( actx->_nlev[0] )/actx->_nlev[0]->axis[0].size;
lev0 = ( alan_t* )( actx->_nlev[0]->data );
parm = ( alan_t* )( actx->nparm->data );
for ( I=0; I<N; I++ ) {
if ( levInit ) {
lev0[0 + 2*I] = levInit[0 + 2*I];
lev0[1 + 2*I] = levInit[1 + 2*I];
} else {
/* NOTE: the random number stuff here is OUTSIDE the multi-threaded
segment of the program- only the init thread does this */
lev0[0 + 2*I] = AIR_CAST( alan_t, actx->initA + RAND );
lev0[1 + 2*I] = AIR_CAST( alan_t, actx->initB + RAND );
}
if ( parmInit ) {
parm[0 + 3*I] = parmInit[0 + 3*I];
parm[1 + 3*I] = parmInit[1 + 3*I];
parm[2 + 3*I] = parmInit[2 + 3*I];
} else {
parm[0 + 3*I] = actx->deltaT;
parm[1 + 3*I] = actx->alpha;
parm[2 + 3*I] = actx->beta;
}
}
return 0;
}
int
185 _alanPerIteration( alanContext *actx, int iter ) {
static const char me[]="_alanPerIteration";
char fname[AIR_STRLEN_MED];
Nrrd *nslc, *nimg;
if ( !( actx->saveInterval || actx->frameInterval ) ) {
if ( actx->verbose && !( iter % 100 ) ) {
fprintf( stderr, "%s: iter = %d, averageChange = %g\n",
me, iter, actx->averageChange );
}
}
if ( actx->saveInterval && !( iter % actx->saveInterval ) ) {
sprintf( fname, "%06d.nrrd", actx->constFilename ? 0 : iter );
nrrdSave( fname, actx->_nlev[( iter+1 ) % 2], NULL );
fprintf( stderr, "%s: iter = %d, averageChange = %g, saved %s\n",
me, iter, actx->averageChange, fname );
}
if ( actx->frameInterval && !( iter % actx->frameInterval ) ) {
nrrdSlice( nslc=nrrdNew( ), actx->_nlev[( iter+1 ) % 2], 0, 0 );
nrrdQuantize( nimg=nrrdNew( ), nslc, NULL, 8 );
sprintf( fname, ( 2 == actx->dim ? "%06d.png" : "%06d.nrrd" ),
actx->constFilename ? 0 : iter );
nrrdSave( fname, nimg, NULL );
fprintf( stderr, "%s: iter = %d, averageChange = %g, saved %s\n",
me, iter, actx->averageChange, fname );
nrrdNuke( nslc );
nrrdNuke( nimg );
}
return 0;
}
typedef struct {
/* these two are genuine input to each worker thread */
alanContext *actx;
int idx;
/* this is just a convenient place to put airThread ( so that alanRun( )
doesn't have to make multiple arrays of per-thread items ) */
airThread *thread;
/* pointless: a pointer to this is passed to airThreadJoin for its
return, and currently that will just end up pointing back to this
struct */
void *me;
} alanTask;
void *
231 _alanTuringWorker( void *_task ) {
alan_t *tendata, *ten, react,
conf, Dxx, Dxy, Dyy, /* Dxz, Dyz, */
*tpx, *tmx, *tpy, *tmy, /* *tpz, *tmz, */
*lev0, *lev1, *parm, deltaT, alpha, beta, A, B,
*v[27], lapA, lapB, corrA, corrB,
deltaA, deltaB, diffA, diffB, change;
int dim, iter, stop, startW, endW, idx,
px, mx, py, my, pz, mz,
startY, endY, startZ, endZ, sx, sy, sz, x, y, z;
alanTask *task;
task = ( alanTask * )_task;
dim = task->actx->dim;
sx = task->actx->size[0];
sy = task->actx->size[1];
sz = ( 2 == dim ? 1 : task->actx->size[2] );
parm = ( alan_t* )( task->actx->nparm->data );
diffA = AIR_CAST( alan_t, task->actx->diffA/pow( task->actx->deltaX, dim ) );
diffB = AIR_CAST( alan_t, task->actx->diffB/pow( task->actx->deltaX, dim ) );
startW = task->idx*sy/task->actx->numThreads;
endW = ( task->idx+1 )*sy/task->actx->numThreads;
tendata = task->actx->nten ? ( alan_t * )task->actx->nten->data : NULL;
react = task->actx->react;
if ( 2 == dim ) {
startZ = 0;
endZ = 1;
startY = startW;
endY = endW;
} else {
startZ = startW;
endZ = endW;
startY = 0;
endY = sy;
}
for ( iter = 0;
( alanStopNot == task->actx->stop
&& ( 0 == task->actx->maxIteration
|| iter < task->actx->maxIteration ) );
iter++ ) {
if ( 0 == task->idx ) {
task->actx->iter = iter;
task->actx->nlev = task->actx->_nlev[( iter+1 ) % 2];
}
lev0 = ( alan_t* )( task->actx->_nlev[iter % 2]->data );
lev1 = ( alan_t* )( task->actx->_nlev[( iter+1 ) % 2]->data );
stop = alanStopNot;
change = 0;
conf = 1; /* if you have no data; this will stay 1 */
for ( z = startZ; z < endZ; z++ ) {
if ( task->actx->wrap ) {
pz = AIR_MOD( z+1, sz );
mz = AIR_MOD( z-1, sz );
} else {
pz = AIR_MIN( z+1, sz-1 );
mz = AIR_MAX( z-1, 0 );
}
for ( y = startY; y < endY; y++ ) {
if ( task->actx->wrap ) {
py = AIR_MOD( y+1, sy );
my = AIR_MOD( y-1, sy );
} else {
py = AIR_MIN( y+1, sy-1 );
my = AIR_MAX( y-1, 0 );
}
for ( x = 0; x < sx; x++ ) {
if ( task->actx->wrap ) {
px = AIR_MOD( x+1, sx );
mx = AIR_MOD( x-1, sx );
} else {
px = AIR_MIN( x+1, sx-1 );
mx = AIR_MAX( x-1, 0 );
}
idx = x + sx*( y + sy*z );
A = lev0[0 + 2*idx];
B = lev0[1 + 2*idx];
deltaT = parm[0 + 3*idx];
alpha = parm[1 + 3*idx];
beta = parm[2 + 3*idx];
lapA = lapB = corrA = corrB = 0;
if ( 2 == dim ) {
/*
** 0 1 2 ----> X
** 3 4 5
** 6 7 8
** |
** v Y
*/
v[1] = lev0 + 2*( x + sx*( my ) );
v[3] = lev0 + 2*( mx + sx*( y ) );
v[5] = lev0 + 2*( px + sx*( y ) );
v[7] = lev0 + 2*( x + sx*( py ) );
if ( tendata ) {
/*
** 0 1 2 Dxy/2 Dyy -Dxy/2
** 3 4 5 Dxx -2*( Dxx + Dyy ) Dxx
** 6 7 8 -Dxy/2 Dyy Dxy/2
*/
v[0] = lev0 + 2*( mx + sx*( my ) );
v[2] = lev0 + 2*( px + sx*( my ) );
v[6] = lev0 + 2*( mx + sx*( py ) );
v[8] = lev0 + 2*( px + sx*( py ) );
ten = tendata + 4*idx;
conf = AIR_CAST( alan_t, ( AIR_CLAMP( 0.3, ten[0], 1 ) - 0.3 )/0.7 );
if ( conf ) {
Dxx = ten[1];
Dxy = ten[2];
Dyy = ten[3];
lapA = ( Dxy*( v[0][0] + v[8][0] - v[2][0] - v[6][0] )/2
+ Dxx*( v[3][0] + v[5][0] ) + Dyy*( v[1][0] + v[7][0] )
- 2*( Dxx + Dyy )*A );
lapB = ( Dxy*( v[0][1] + v[8][1] - v[2][1] - v[6][1] )/2
+ Dxx*( v[3][1] + v[5][1] ) + Dyy*( v[1][1] + v[7][1] )
- 2*( Dxx + Dyy )*B );
if ( !( task->actx->homogAniso ) ) {
tpx = tendata + 4*( px + sx*( y + sy*( z ) ) );
tmx = tendata + 4*( mx + sx*( y + sy*( z ) ) );
tpy = tendata + 4*( x + sx*( py + sy*( z ) ) );
tmy = tendata + 4*( x + sx*( my + sy*( z ) ) );
corrA = ( ( tpx[1]-tmx[1] )*( v[5][0]-v[3][0] )/4+ /* Dxx, x*A, x */
( tpx[2]-tmx[2] )*( v[7][0]-v[1][0] )/4+ /* Dxy, x*A, y */
( tpy[2]-tmy[2] )*( v[5][0]-v[3][0] )/4+ /* Dxy, y*A, x */
( tpy[3]-tmy[3] )*( v[7][0]-v[1][0] ) ); /* Dyy, y*A, y */
corrB = ( ( tpx[1]-tmx[1] )*( v[5][1]-v[3][1] )/4+ /* Dxx, x*B, x */
( tpx[2]-tmx[2] )*( v[7][1]-v[1][1] )/4+ /* Dxy, x*B, y */
( tpy[2]-tmy[2] )*( v[5][1]-v[3][1] )/4+ /* Dxy, y*B, x */
( tpy[3]-tmy[3] )*( v[7][1]-v[1][1] ) ); /* Dyy, y*B, y */
}
} else {
/* no confidence; you diffuse */
lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A;
lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B;
}
} else {
/* no data; you diffuse */
lapA = v[1][0] + v[3][0] + v[5][0] + v[7][0] - 4*A;
lapB = v[1][1] + v[3][1] + v[5][1] + v[7][1] - 4*B;
}
} else {
/* 3 == dim */
/*
** 0 1 2 ---- X
** 3 4 5
** 6 7 8
** /
** / 9 10 11
** Y 12 13 14
** 15 16 17
**
** 18 19 20
** 21 22 23
** 24 25 26
** |
** |
** Z
*/
v[ 4] = lev0 + 2*( x + sx*( y + sy*( mz ) ) );
v[10] = lev0 + 2*( x + sx*( my + sy*( z ) ) );
v[12] = lev0 + 2*( mx + sx*( y + sy*( z ) ) );
v[14] = lev0 + 2*( px + sx*( y + sy*( z ) ) );
v[16] = lev0 + 2*( x + sx*( py + sy*( z ) ) );
v[22] = lev0 + 2*( x + sx*( y + sy*( pz ) ) );
if ( tendata ) {
if ( !( task->actx->homogAniso ) ) {
}
} else {
lapA = ( v[ 4][0] + v[10][0] + v[12][0]
+ v[14][0] + v[16][0] + v[22][0] - 6*A );
lapB = ( v[ 4][1] + v[10][1] + v[12][1]
+ v[14][1] + v[16][1] + v[22][1] - 6*B );
}
}
deltaA = deltaT*( react*conf*task->actx->K*( alpha - A*B )
+ diffA*( lapA + corrA ) );
if ( AIR_ABS( deltaA ) > task->actx->maxPixelChange ) {
stop = alanStopDiverged;
}
change += AIR_ABS( deltaA );
deltaB = deltaT*( react*conf*task->actx->K*( A*B - B - beta )
+ diffB*( lapB + corrB ) );
if ( !( AIR_EXISTS( deltaA ) && AIR_EXISTS( deltaB ) ) ) {
stop = alanStopNonExist;
}
A += deltaA;
B = AIR_MAX( 0, B + deltaB );
lev1[0 + 2*idx] = A;
lev1[1 + 2*idx] = B;
}
}
}
/* add change to global sum in a threadsafe way */
airThreadMutexLock( task->actx->changeMutex );
task->actx->averageChange += change/( sx*sy*sz );
task->actx->changeCount += 1;
if ( task->actx->changeCount == task->actx->numThreads ) {
/* I must be the last thread to reach this point; all
others must have passed the mutex unlock, and are
sitting at the barrier */
if ( alanStopNot != stop ) {
/* there was some problem in going from lev0 to lev1, which
we deal with now by setting actx->stop */
task->actx->stop = stop;
} else if ( task->actx->averageChange < task->actx->minAverageChange ) {
/* we converged */
task->actx->stop = alanStopConverged;
} else {
/* we keep going */
_alanPerIteration( task->actx, iter );
if ( task->actx->perIteration ) {
task->actx->perIteration( task->actx, iter );
}
}
task->actx->averageChange = 0;
task->actx->changeCount = 0;
}
airThreadMutexUnlock( task->actx->changeMutex );
/* force all threads to line up here, once per iteration */
airThreadBarrierWait( task->actx->iterBarrier );
}
if ( iter == task->actx->maxIteration ) {
/* HEY: all threads will agree on this, right? */
task->actx->stop = alanStopMaxIteration;
}
/* else: the non-alanStopNot value of task->actx->stop made us stop */
return _task;
}
int
469 alanRun( alanContext *actx ) {
static const char me[]="alanRun";
int tid, hack=AIR_FALSE;
alanTask task[ALAN_THREAD_MAX];
if ( _alanCheck( actx ) ) {
biffAddf( ALAN, "%s: ", me );
return 1;
}
if ( !( actx->_nlev[0] && actx->_nlev[0] ) ) {
biffAddf( ALAN, "%s: _nlev[0, 1] not allocated: "
"call alanUpdate + alanInit", me );
return 1;
}
if ( !airThreadCapable && 1 == actx->numThreads ) {
hack = airThreadNoopWarning;
airThreadNoopWarning = AIR_FALSE;
}
actx->changeMutex = airThreadMutexNew( );
actx->iterBarrier = airThreadBarrierNew( actx->numThreads );
actx->averageChange = 0;
actx->changeCount = 0;
actx->stop = alanStopNot;
for ( tid=0; tid<actx->numThreads; tid++ ) {
task[tid].actx = actx;
task[tid].idx = tid;
task[tid].thread = airThreadNew( );
airThreadStart( task[tid].thread, _alanTuringWorker,
( void * )&( task[tid] ) );
}
for ( tid=0; tid<actx->numThreads; tid++ ) {
airThreadJoin( task[tid].thread, &( task[tid].me ) );
task[tid].thread = airThreadNix( task[tid].thread );
}
actx->iterBarrier = airThreadBarrierNix( actx->iterBarrier );
actx->changeMutex = airThreadMutexNix( actx->changeMutex );
if ( !airThreadCapable && 1 == actx->numThreads ) {
airThreadNoopWarning = hack;
}
/* we assume that someone set actx->stop */
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "alan.h"
const char *
_alanStopStr[ALAN_STOP_MAX+1] = {
"( unknown_stop )",
"not",
"iter",
"nonexist",
"converged",
"diverged"
};
const char *
_alanStopDesc[ALAN_STOP_MAX+1] = {
"unknown_stop",
"there is no reason to stop",
"hit the maximum number of iterations",
"got non-existent values",
"simulation converged",
"simulation hit divergent instability"
};
const airEnum
_alanStop = {
"stop",
ALAN_STOP_MAX,
_alanStopStr, NULL,
_alanStopDesc,
NULL, NULL,
AIR_FALSE
};
55 const airEnum *const
alanStop = &_alanStop;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "alan.h"
const int
alanPresent = 42;
const char *
alanBiffKey = "alan";
void
34 alanContextInit( alanContext *actx ) {
if ( actx ) {
actx->verbose = 0;
actx->wrap = AIR_FALSE;
actx->textureType = alanTextureTypeUnknown;
actx->dim = 0;
ELL_3V_SET( actx->size, 0, 0, 0 );
actx->oversample = 0;
actx->homogAniso = AIR_FALSE;
actx->numThreads = 1;
actx->frameInterval = 10;
actx->saveInterval = 100;
actx->maxIteration = 1000000;
actx->minAverageChange = 0.00002f;
actx->maxPixelChange = 6;
actx->K = AIR_NAN;
actx->F = AIR_NAN;
actx->deltaX = 1.25;
actx->alpha = 16;
actx->beta = 12;
actx->deltaT = 1.0;
actx->react = 1.0;
actx->initA = actx->initB = 0;
actx->diffA = actx->diffB = 0;
actx->perIteration = NULL;
actx->randRange = 3;
actx->_nlev[0] = nrrdNuke( actx->_nlev[0] );
actx->_nlev[1] = nrrdNuke( actx->_nlev[1] );
actx->nlev = actx->_nlev[0];
actx->nparm = nrrdNuke( actx->nparm );
actx->nten = nrrdNuke( actx->nten );
actx->constFilename = AIR_FALSE;
}
return;
}
alanContext *
71 alanContextNew( void ) {
alanContext *actx;
actx = ( alanContext * )calloc( 1, sizeof( alanContext ) );
actx->_nlev[0] = actx->_nlev[1] = NULL;
actx->nlev = NULL;
actx->nparm = NULL;
actx->nten = NULL;
alanContextInit( actx );
return actx;
}
alanContext *
84 alanContextNix( alanContext *actx ) {
if ( actx ) {
actx->_nlev[0] = nrrdNuke( actx->_nlev[0] );
actx->_nlev[1] = nrrdNuke( actx->_nlev[1] );
actx->nparm = nrrdNuke( actx->nparm );
actx->nten = nrrdNuke( actx->nten );
free( actx );
}
return NULL;
}
#define GOT_NULL \
if ( !actx ) { \
biffAddf( ALAN, "%s: got NULL pointer", me ); \
return 1; \
}
101
#define DIM_SET \
if ( 0 == actx->dim ) { \
biffAddf( ALAN, "%s: dimension of texture not set", me ); \
return 1; \
}
int
alanDimensionSet( alanContext *actx, int dim ) {
static const char me[]="alanDimensionSet";
GOT_NULL;
if ( !( dim == 2 || dim == 3 ) ) {
biffAddf( ALAN, "%s: dimension must be 2 or 3, not %d", me, dim );
return 1;
116 }
actx->dim = dim;
return 0;
}
int
alan2DSizeSet( alanContext *actx, int sizeX, int sizeY ) {
static const char me[]="alan2DSizeSet";
GOT_NULL;
DIM_SET;
if ( 2 != actx->dim ) {
biffAddf( ALAN, "%s: texture not two-dimensional", me );
return 1;
}
if ( !( sizeX >= 10 && sizeY >= 10 ) ) {
biffAddf( ALAN, "%s: sizes ( %d, %d ) invalid ( too small? )", me, sizeX, sizeY );
return 1;
136 }
actx->size[0] = sizeX;
actx->size[1] = sizeY;
return 0;
}
int
alan3DSizeSet( alanContext *actx, int sizeX, int sizeY, int sizeZ ) {
static const char me[]="alan2DSizeSet";
GOT_NULL;
DIM_SET;
if ( 3 != actx->dim ) {
biffAddf( ALAN, "%s: texture not three-dimensional", me );
return 1;
}
if ( !( sizeX >= 10 && sizeY >= 10 && sizeZ >= 10 ) ) {
biffAddf( ALAN, "%s: sizes ( %d, %d, %d ) invalid ( too small? )",
me, sizeX, sizeY, sizeZ );
return 1;
}
158
actx->size[0] = sizeX;
actx->size[1] = sizeY;
actx->size[2] = sizeZ;
return 0;
}
int
alanTensorSet( alanContext *actx, Nrrd *nten, int oversample ) {
static const char me[]="alanTensorSet";
if ( !( actx && nten ) ) {
biffAddf( ALAN, "%s: got NULL pointer", me );
return 1;
}
DIM_SET;
if ( !( oversample > 0 ) ) {
biffAddf( ALAN, "%s: oversample %d invalid", me, oversample );
return 1;
}
if ( 2 == actx->dim ) {
if ( !( 3 == nten->dim && 4 == nten->axis[0].size ) ) {
biffAddf( ALAN, "%s: didn't get 3-D ( 4, X, Y ) nrrd", me );
return 1;
}
} else {
if ( !( 4 == nten->dim && 7 == nten->axis[0].size ) ) {
biffAddf( ALAN, "%s: didn't get 4-D ( 7, X, Y, Z ) nrrd", me );
return 1;
}
}
if ( 1 != oversample ) {
biffAddf( ALAN, "%s: sorry, can only handle oversample==1 now", me );
return 1;
}
actx->nten = nrrdNuke( actx->nten );
actx->nten = nrrdNew( );
if ( nrrdConvert( actx->nten, nten, alan_nt ) ) {
biffMovef( ALAN, NRRD, "%s: trouble converting tensors to alan_t", me );
return 1;
}
actx->size[0] = AIR_UINT( oversample*nten->axis[1].size );
actx->size[1] = AIR_UINT( oversample*nten->axis[2].size );
if ( 3 == actx->dim ) {
actx->size[2] = AIR_UINT( oversample*nten->axis[3].size );
205 } else {
actx->size[2] = 1;
}
return 0;
}
int
alanParmSet( alanContext *actx, int whichParm, double parm ) {
static const char me[]="alanParmSet";
int parmI;
GOT_NULL;
DIM_SET;
switch ( whichParm ) {
case alanParmVerbose:
parmI = !!parm;
actx->verbose = parmI;
break;
case alanParmTextureType:
parmI = !!parm;
switch( parmI ) {
case alanTextureTypeTuring:
actx->initA = 4.0;
actx->initB = 4.0;
actx->diffA = 0.25;
actx->diffB = 0.0625;
break;
case alanTextureTypeGrayScott:
actx->initA = 1;
actx->initB = 0;
actx->diffA = 0.00002f;
actx->diffB = 0.00002f;
break;
default:
biffAddf( ALAN, "%s: texture type %d invalid", me, parmI );
return 1;
break;
}
actx->textureType = parmI;
break;
case alanParmNumThreads:
parmI = !!parm;
if ( !airThreadCapable ) {
fprintf( stderr, "%s: WARNING: no multi-threading available, so 1 thread "
"will be used, not %d\n", me, parmI );
parmI = 1;
}
actx->numThreads = parmI;
break;
case alanParmHomogAniso:
parmI = !!parm;
actx->homogAniso = parmI;
break;
case alanParmSaveInterval:
parmI = !!parm;
actx->saveInterval = parmI;
break;
case alanParmFrameInterval:
parmI = !!parm;
actx->frameInterval = parmI;
break;
case alanParmMaxIteration:
parmI = !!parm;
actx->maxIteration = parmI;
break;
case alanParmConstantFilename:
parmI = !!parm;
actx->constFilename = parmI;
break;
case alanParmDeltaT:
actx->deltaT = AIR_CAST( alan_t, parm );
break;
case alanParmDeltaX:
actx->deltaX = AIR_CAST( alan_t, parm );
break;
case alanParmReact:
actx->react = AIR_CAST( alan_t, parm );
break;
case alanParmDiffA:
actx->diffA = AIR_CAST( alan_t, parm );
break;
case alanParmDiffB:
actx->diffB = AIR_CAST( alan_t, parm );
break;
case alanParmRandRange:
actx->randRange = AIR_CAST( alan_t, parm );
break;
case alanParmK:
actx->K = AIR_CAST( alan_t, parm );
break;
case alanParmF:
actx->F = AIR_CAST( alan_t, parm );
break;
case alanParmMinAverageChange:
actx->minAverageChange = AIR_CAST( alan_t, parm );
break;
case alanParmMaxPixelChange:
actx->maxPixelChange = AIR_CAST( alan_t, parm );
break;
case alanParmAlpha:
actx->alpha = AIR_CAST( alan_t, parm );
break;
case alanParmBeta:
actx->beta = AIR_CAST( alan_t, parm );
break;
case alanParmWrapAround:
parmI = !!parm;
actx->wrap = parmI;
break;
default:
biffAddf( ALAN, "%s: parameter %d invalid", me, whichParm );
return 1;
break;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../alan.h"
int
27 main( int argc, char *argv[] ) {
alanContext *actx;
char *err, *me;
Nrrd *ninit, *nparm, *npri;
me = argv[0];
ninit = nrrdNew( );
if ( nrrdLoad( ninit, "init.nrrd", NULL ) ) {
fprintf( stderr, "%s: load( init.nrrd ) failed\n", me );
free( biffGetDone( NRRD ) );
ninit = nrrdNuke( ninit );
}
nparm = nrrdNew( );
if ( nrrdLoad( nparm, "parm.nrrd", NULL ) ) {
fprintf( stderr, "%s: load( parm.nrrd ) failed\n", me );
free( biffGetDone( NRRD ) );
nparm = nrrdNuke( nparm );
}
npri = nrrdNew( );
if ( nrrdLoad( npri, "pri.nrrd", NULL ) ) {
fprintf( stderr, "%s: load( pri.nrrd ) failed\n", me );
free( biffGetDone( NRRD ) );
npri = nrrdNuke( npri );
}
actx = alanContextNew( );
if ( alanDimensionSet( actx, 2 )
|| alan2DSizeSet( actx, 100, 100 )
|| alanParmSet( actx, alanParmMaxIteration, 100000 )
|| alanParmSet( actx, alanParmVerbose, 1 )
|| alanParmSet( actx, alanParmTextureType, alanTextureTypeTuring )
|| alanParmSet( actx, alanParmRandRange, 4.0 )
|| alanParmSet( actx, alanParmK, 0.0125 )
|| alanParmSet( actx, alanParmH, 1.2 )
|| alanParmSet( actx, alanParmAlpha, 16.0+0.07 )
|| alanParmSet( actx, alanParmBeta, 12.0-0.07 )
|| alanParmSet( actx, alanParmSpeed, 1.38 )
|| alanParmSet( actx, alanParmMinAverageChange, 0.00002 )
|| alanParmSet( actx, alanParmSaveInterval, 500 )
|| alanParmSet( actx, alanParmFrameInterval, 500 )
|| alanParmSet( actx, alanParmConstantFilename, AIR_TRUE )
|| alanParmSet( actx, alanParmWrapAround, AIR_TRUE )
|| alanParmSet( actx, alanParmNumThreads, 10 )
) {
err = biffGetDone( ALAN );
fprintf( stderr, "%s: trouble: %s\n", me, err );
free( err ); return 1;
}
if ( alanUpdate( actx )
|| alanInit( actx, ninit, nparm ) ) {
err = biffGetDone( ALAN );
fprintf( stderr, "%s: trouble: %s\n", me, err );
free( err ); return 1;
}
fprintf( stderr, "%s: going to run ( %d threads ) . . .\n",
me, actx->numThreads );
alanRun( actx );
fprintf( stderr, "%s: stop = %d: %s\n", me, actx->stop,
airEnumDesc( alanStop, actx->stop ) );
/*
nrrdSave( "lev0.nrrd", actx->nlev[0], NULL );
nrrdSave( "lev1.nrrd", actx->nlev[1], NULL );
*/
actx = alanContextNix( actx );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
int
30 _baneClipAnswer_Absolute( int *countP, Nrrd *hvol, double *clipParm ) {
AIR_UNUSED( hvol );
*countP = ( int )( clipParm[0] );
return 0;
}
int
38 _baneClipAnswer_PeakRatio( int *countP, Nrrd *hvol, double *clipParm ) {
int *hits, maxhits;
size_t idx, num;
hits = ( int * )hvol->data;
maxhits = 0;
num = nrrdElementNumber( hvol );
for ( idx=0; idx<num; idx++ ) {
maxhits = AIR_MAX( maxhits, hits[idx] );
}
*countP = ( int )( maxhits*clipParm[0] );
return 0;
}
int
54 _baneClipAnswer_Percentile( int *countP, Nrrd *hvol, double *clipParm ) {
static const char me[]="_baneClipAnswer_Percentile";
Nrrd *ncopy;
int *hits, clip;
size_t num, sum, out, outsofar, hi;
if ( nrrdCopy( ncopy=nrrdNew( ), hvol ) ) {
biffMovef( BANE, NRRD, "%s: couldn't create copy of histovol", me );
return 1;
}
hits = ( int * )ncopy->data;
num = nrrdElementNumber( ncopy );
qsort( hits, num, sizeof( int ), nrrdValCompare[nrrdTypeInt] );
sum = 0;
for ( hi=0; hi<num; hi++ ) {
sum += hits[hi];
}
out = ( size_t )( sum*clipParm[0]/100 );
outsofar = 0;
hi = num-1;
do {
outsofar += hits[hi--];
} while ( outsofar < out );
clip = hits[hi];
nrrdNuke( ncopy );
*countP = clip;
return 0;
}
int
85 _baneClipAnswer_TopN( int *countP, Nrrd *hvol, double *clipParm ) {
static const char me[]="_baneClipAnwer_TopN";
Nrrd *copy;
int *hits, tmp;
size_t num;
if ( nrrdCopy( copy=nrrdNew( ), hvol ) ) {
biffMovef( BANE, NRRD, "%s: couldn't create copy of histovol", me );
return 1;
}
hits = ( int * )copy->data;
num = nrrdElementNumber( copy );
qsort( hits, num, sizeof( int ), nrrdValCompare[nrrdTypeInt] );
tmp = AIR_CLAMP( 0, ( int )clipParm[0], ( int )num-1 );
*countP = hits[num-tmp-1];
nrrdNuke( copy );
return 0;
}
baneClip *
106 baneClipNew( int type, double *parm ) {
static const char me[]="baneClipNew";
baneClip *clip;
if ( !( AIR_IN_OP( baneClipUnknown, type, baneClipLast ) ) ) {
biffAddf( BANE, "%s: baneClip %d invalid", me, type );
return NULL;
}
if ( !parm ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return NULL;
}
if ( !( AIR_EXISTS( parm[0] ) ) ) {
biffAddf( BANE, "%s: parm[0] doesn't exist", me );
return NULL;
}
clip = ( baneClip* )calloc( 1, sizeof( baneClip ) );
if ( !clip ) {
biffAddf( BANE, "%s: couldn't allocate baneClip!", me );
return NULL;
}
clip->parm[0] = parm[0];
clip->type = type;
switch( type ) {
case baneClipAbsolute:
sprintf( clip->name, "absolute" );
clip->answer = _baneClipAnswer_Absolute;
break;
case baneClipPeakRatio:
sprintf( clip->name, "peak ratio" );
clip->answer = _baneClipAnswer_PeakRatio;
break;
case baneClipPercentile:
sprintf( clip->name, "percentile" );
clip->answer = _baneClipAnswer_Percentile;
break;
case baneClipTopN:
sprintf( clip->name, "top N" );
clip->answer = _baneClipAnswer_TopN;
break;
default:
biffAddf( BANE, "%s: sorry, baneClip %d not implemented", me, type );
baneClipNix( clip ); return NULL;
break;
}
return clip;
}
int
155 baneClipAnswer( int *countP, baneClip *clip, Nrrd *hvol ) {
static const char me[]="baneClipAnswer";
if ( !( countP && clip && hvol ) ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 0;
}
if ( clip->answer( countP, hvol, clip->parm ) ) {
biffAddf( BANE, "%s: trouble", me );
return 0;
}
return 0;
}
baneClip *
170 baneClipCopy( baneClip *clip ) {
static const char me[]="baneClipCopy";
baneClip *ret = NULL;
ret = baneClipNew( clip->type, clip->parm );
if ( !ret ) {
biffAddf( BANE, "%s: couldn't make new clip", me );
return NULL;
}
return ret;
}
baneClip *
183 baneClipNix( baneClip *clip ) {
if ( clip ) {
airFree( clip->name );
airFree( clip );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
const char *
baneBiffKey = "bane";
int
baneDefVerbose = 0;
int
baneDefMakeMeasrVol = AIR_TRUE;
double
baneDefIncLimit = 0.80; /* throwing away more than 20% is too much */
int
baneDefRenormalize = AIR_TRUE;
int
baneDefPercHistBins = 1024;
int
baneStateHistEqBins = 4096;
int
baneStateHistEqSmart = 2;
int
baneHack = 0;
const char
baneDefLHKernel00[] = "cubic:0.333, 0.333";
const char
baneDefLHKernel11[] = "cubicd:0.333, 0.333";
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/*
** baneGkmsParseIncStrategy
**
** inc[0]: member of baneInc* enum
** inc[1], inc[2] ... : incParm[0], incParm[1] ...
*/
int
38 baneGkmsParseIncStrategy( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="baneGkmsParseIncStrategy";
double *inc, *incParm;
int i, bins;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
inc = ( double * )ptr;
incParm = inc + 1;
for ( i=0; i<BANE_PARM_NUM; i++ ) {
incParm[i] = AIR_NAN;
}
if ( 1 == sscanf( str, "f:%lg", incParm+0 )
|| 2 == sscanf( str, "f:%lg, %lg", incParm+0, incParm+1 ) ) {
inc[0] = baneIncRangeRatio;
return 0;
}
if ( 1 == sscanf( str, "p:%lg", incParm+1 )
|| 2 == sscanf( str, "p:%lg, %lg", incParm+1, incParm+2 ) ) {
inc[0] = baneIncPercentile;
incParm[0] = baneDefPercHistBins;
return 0;
}
if ( 3 == sscanf( str, "p:%d, %lg, %lg", &bins, incParm+1, incParm+2 ) ) {
inc[0] = baneIncPercentile;
incParm[0] = bins;
return 0;
}
if ( 2 == sscanf( str, "a:%lg, %lg", incParm+0, incParm+1 ) ) {
inc[0] = baneIncAbsolute;
return 0;
}
if ( 1 == sscanf( str, "s:%lg", incParm+0 )
|| 2 == sscanf( str, "s:%lg, %lg", incParm+1, incParm+2 ) ) {
inc[0] = baneIncStdv;
return 0;
}
/* else its no good */
sprintf( err, "%s: \"%s\" not recognized", me, str );
return 1;
}
hestCB _baneGkmsHestIncStrategy = {
( 1+BANE_PARM_NUM )*sizeof( double ),
"inclusion strategy",
baneGkmsParseIncStrategy,
NULL
};
hestCB *
baneGkmsHestIncStrategy = &_baneGkmsHestIncStrategy;
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
int
99 baneGkmsParseBEF( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="baneGkmsParseBEF", mesg[AIR_STRLEN_MED], *nerr;
float cent, width, shape, alpha, off, *bef;
Nrrd **nrrdP;
airArray *mop;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
nrrdP = ( Nrrd ** )ptr;
airMopAdd( mop, *nrrdP=nrrdNew( ), ( airMopper )nrrdNuke, airMopOnError );
if ( 4 == sscanf( str, "%g, %g, %g, %g", &shape, &width, ¢, &alpha ) ) {
/* its a valid BEF specification, we make the nrrd ourselves */
if ( nrrdMaybeAlloc_va( *nrrdP, nrrdTypeFloat, 2,
AIR_CAST( size_t, 2 ), AIR_CAST( size_t, 6 ) ) ) {
airMopAdd( mop, nerr = biffGetDone( NRRD ), airFree, airMopOnError );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
airMopError( mop );
return 1;
}
bef = ( float * )( ( *nrrdP )->data );
off = AIR_CAST( float, AIR_AFFINE( 0.0, shape, 1.0, 0.0, width/2 ) );
/* positions */
bef[0 + 2*0] = cent - 2*width;
bef[0 + 2*1] = cent - width/2 - off;
bef[0 + 2*2] = cent - width/2 + off;
bef[0 + 2*3] = cent + width/2 - off;
bef[0 + 2*4] = cent + width/2 + off;
bef[0 + 2*5] = cent + 2*width;
if ( bef[0 + 2*1] == bef[0 + 2*2] ) bef[0 + 2*1] -= 0.001f;
if ( bef[0 + 2*2] == bef[0 + 2*3] ) bef[0 + 2*2] -= 0.001f;
if ( bef[0 + 2*3] == bef[0 + 2*4] ) bef[0 + 2*4] += 0.001f;
/* opacities */
bef[1 + 2*0] = 0.0;
bef[1 + 2*1] = 0.0;
bef[1 + 2*2] = alpha;
bef[1 + 2*3] = alpha;
bef[1 + 2*4] = 0.0;
bef[1 + 2*5] = 0.0;
/* to tell gkms opac that this came from four floats */
( *nrrdP )->ptr = *nrrdP;
} else {
if ( nrrdLoad( *nrrdP, str, NULL ) ) {
airMopAdd( mop, nerr = biffGetDone( NRRD ), airFree, airMopOnError );
sprintf( mesg, "%s: couldn't parse \"%s\" as four-parameter BEF or "
"as a nrrd filename\n", me, str );
strcpy( err, mesg );
strncat( err, nerr, AIR_STRLEN_HUGE-1-strlen( mesg )-1 );
airMopError( mop );
return 1;
}
( *nrrdP )->ptr = NULL;
}
airMopOkay( mop );
return 0;
}
hestCB _baneGkmsHestBEF = {
sizeof( Nrrd * ),
"boundary emphasis function",
baneGkmsParseBEF,
( airMopper )nrrdNuke
};
hestCB *
baneGkmsHestBEF = &_baneGkmsHestBEF;
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/*
** gthr[0] = 1: some scaling of max grad "x<float>"
** 0: absolute "<float>"
** gthr[1] = the scaling, or the absolute
*/
int
179 baneGkmsParseGthresh( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="baneGkmsParseGthresh";
float *gthr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
gthr = ( float * )ptr;
if ( 'x' == str[0] ) {
if ( 1 != sscanf( str+1, "%f", gthr+1 ) ) {
sprintf( err, "%s: can't parse \"%s\" as x<float>", me, str );
return 1;
}
gthr[0] = 1;
} else {
if ( 1 != sscanf( str, "%f", gthr+1 ) ) {
sprintf( err, "%s: can't parse \"%s\" as float", me, str );
return 1;
}
gthr[0] = 0;
}
return 0;
}
hestCB _baneGkmsHestGthresh = {
2*sizeof( float ),
"gthresh specification",
baneGkmsParseGthresh,
NULL
};
hestCB *
baneGkmsHestGthresh = &_baneGkmsHestGthresh;
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/* ----------------------------------------------------------- */
/*
******** baneGkmsCmdList[]
**
** NULL-terminated array of unrrduCmd pointers, as ordered by
** BANE_GKMS_MAP macro
*/
unrrduCmd *
baneGkmsCmdList[] = {
BANE_GKMS_MAP( BANE_GKMS_LIST )
NULL
};
/*
******** baneGkmsUsage
**
** prints out a little banner, and a listing of all available commands
** with their one-line descriptions
*/
void
238 baneGkmsUsage( const char *me, hestParm *hparm ) {
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE];
unsigned int ci, si, len, maxlen;
maxlen = 0;
for ( ci=0; baneGkmsCmdList[ci]; ci++ ) {
maxlen = AIR_MAX( maxlen, AIR_UINT( strlen( baneGkmsCmdList[ci]->name ) ) );
}
sprintf( buff, "--- Semi-Automatic Generation of Transfer Functions ---" );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stderr, fmt, buff );
for ( ci=0; baneGkmsCmdList[ci]; ci++ ) {
len = AIR_UINT( strlen( baneGkmsCmdList[ci]->name ) );
strcpy( buff, "" );
for ( si=len; si<maxlen; si++ )
strcat( buff, " " );
strcat( buff, me );
strcat( buff, " " );
strcat( buff, baneGkmsCmdList[ci]->name );
strcat( buff, " ... " );
len = AIR_UINT( strlen( buff ) );
fprintf( stderr, "%s", buff );
_hestPrintStr( stderr, len, len, hparm->columns,
baneGkmsCmdList[ci]->info, AIR_FALSE );
}
}
static const char *
_baneGkmsMeasrStr[] = {
"( unknown measr )",
"min",
"max",
"mean",
"median",
"mode"
};
const int
_baneGkmsMeasrVal[] = {
nrrdMeasureUnknown,
nrrdMeasureHistoMin,
nrrdMeasureHistoMax,
nrrdMeasureHistoMean,
nrrdMeasureHistoMedian,
nrrdMeasureHistoMode
};
const airEnum
_baneGkmsMeasr = {
"measurement",
5,
_baneGkmsMeasrStr, _baneGkmsMeasrVal,
NULL,
NULL, NULL,
AIR_FALSE
};
298 const airEnum *const
baneGkmsMeasr = &_baneGkmsMeasr;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define HVOL_INFO "Make histogram volume"
static const char *_baneGkms_hvolInfoL =
( HVOL_INFO
". The histogram volume is a three-dimensional histogram recording "
"the relationship between data value, gradient magnitude, and the "
"second directional derivative along the gradient direction. Creating "
"it is the first step in semi-automatic transfer function generation. " );
int
36 baneGkms_hvolMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *perr;
Nrrd *nin, *nout;
airArray *mop;
int pret, dim[3], lapl, slow, gz = AIR_FALSE;
double inc[3*( 1+BANE_PARM_NUM )];
baneHVolParm *hvp;
NrrdIoState *nio;
NrrdKernelSpec *ksp00, *ksp11, *ksp22;
hestOptAdd( &opt, "s", "incV incG incH", airTypeOther, 3, 3, inc,
"f:1.0 p:0.005 p:0.015",
"Strategies for determining how much of the range "
"of a quantity should be included and quantized in its axis "
"of the histogram volume. Possibilities include:\n "
"\b\bo \"f:<F>\": included range is some fraction of the "
"total range, as scaled by F\n "
"\b\bo \"p:<P>\": exclude the extremal P percent of "
"the values\n "
"\b\bo \"s:<S>\": included range is S times the standard "
"deviation of the values\n "
"\b\bo \"a:<min>, <max>\": range is from <min> to <max>",
NULL, NULL, baneGkmsHestIncStrategy );
hestOptAdd( &opt, "d", "dimV dimG dimH", airTypeInt, 3, 3, dim,
"256 256 256",
"Dimensions of histogram volume; number of samples along "
"each axis" );
hestOptAdd( &opt, "k00", "kernel", airTypeOther, 1, 1, &ksp00, "tent",
"value reconstruction kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "k11", "kernel", airTypeOther, 1, 1, &ksp11, "cubicd:1, 0",
"first derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "k22", "kernel", airTypeOther, 1, 1, &ksp22, "cubicdd:1, 0",
"second derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "l", NULL, airTypeInt, 0, 0, &lapl, NULL,
"Use Laplacian instead of Hessian to approximate second "
"directional derivative. No faster, less accurate." );
hestOptAdd( &opt, "slow", NULL, airTypeInt, 0, 0, &slow, NULL,
"Instead of allocating a floating point VGH volume and measuring "
"V, G, H once, measure V, G, H multiple times on separate passes "
"( slower, but needs less memory )" );
if ( nrrdEncodingGzip->available( ) ) {
hestOptAdd( &opt, "gz", NULL, airTypeInt, 0, 0, &gz, NULL,
"Use gzip compression for output histo-volume; "
"much less disk space, slightly slower to read/write" );
}
hestOptAdd( &opt, "i", "volumeIn", airTypeOther, 1, 1, &nin, NULL,
"input scalar volume for which a transfer function is needed",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "hvolOut", airTypeString, 1, 1, &out, NULL,
"output histogram volume, used by \"gkms scat\" and "
"\"gkms info\"" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_hvolInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
hvp = baneHVolParmNew( );
airMopAdd( mop, hvp, ( airMopper )baneHVolParmNix, airMopAlways );
baneHVolParmGKMSInit( hvp );
hvp->makeMeasrVol = !slow;
fprintf( stderr, "!%s: need to be using baneHVolParmAxisSet\n", me );
/*
hvp->axis[0].res = dim[perm[0]];
hvp->axis[1].res = dim[perm[1]];
hvp->axis[2].res = dim[perm[2]];
hvp->axis[1].measr = lapl ? baneMeasrLapl : baneMeasrHess;
for ( i=0; i<=2; i++ ) {
hvp->ax[i].inc = baneIncArray[( int )inc[( 1+BANE_INC_PARM_NUM )*perm[i]]];
for ( j=0; j<BANE_INC_PARM_NUM; j++ ) {
hvp->ax[i].incParm[j] = inc[1 + j + ( 1+BANE_INC_PARM_NUM )*perm[i]];
}
}
*/
hvp->k3pack = AIR_TRUE;
nrrdKernelParmSet( &hvp->k[gageKernel00], hvp->kparm[gageKernel00], ksp00 );
nrrdKernelParmSet( &hvp->k[gageKernel11], hvp->kparm[gageKernel11], ksp11 );
nrrdKernelParmSet( &hvp->k[gageKernel22], hvp->kparm[gageKernel22], ksp22 );
if ( baneMakeHVol( nout, nin, hvp ) ) {
biffAddf( BANE, "%s: trouble making histogram volume", me );
airMopError( mop ); return 1;
}
nio->encoding = gz ? nrrdEncodingGzip : nrrdEncodingRaw;
if ( nrrdSave( out, nout, nio ) ) {
biffMovef( BANE, NRRD, "%s: error saving histogram volume", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
137 BANE_GKMS_CMD( hvol, HVOL_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define INFO_INFO "Project histogram volume for opacity function generation"
static const char *_baneGkms_infoInfoL =
( INFO_INFO
". This distills the histogram volume down to the information required "
"to create either 1-D or 2-D opacity functions." );
int
34 baneGkms_infoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *outS, *perr;
Nrrd *hvol, *nout;
airArray *mop;
int pret, one, measr;
hestOptAdd( &opt, "m", "measr", airTypeEnum, 1, 1, &measr, "mean",
"How to project along the 2nd derivative axis. Possibilities "
"include:\n "
"\b\bo \"mean\": average value\n "
"\b\bo \"median\": value at 50th percentile\n "
"\b\bo \"mode\": most common value\n "
"\b\bo \"min\", \"max\": probably not useful",
NULL, baneGkmsMeasr );
hestOptAdd( &opt, "one", NULL, airTypeInt, 0, 0, &one, NULL,
"Create 1-dimensional info file; default is 2-dimensional" );
hestOptAdd( &opt, "i", "hvolIn", airTypeOther, 1, 1, &hvol, NULL,
"input histogram volume ( from \"gkms hvol\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "infoOut", airTypeString, 1, 1, &outS, NULL,
"output info file, used by \"gkms pvg\" and \"gkms opac\"" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_infoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( baneOpacInfo( nout, hvol, one ? 1 : 2, measr ) ) {
biffAddf( BANE, "%s: trouble distilling histogram info", me );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving info file", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
79 BANE_GKMS_CMD( info, INFO_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define MITE_INFO "Modify opacity function to work with \"mite\""
static const char *_baneGkms_miteInfoL =
( MITE_INFO
". Useful when using the \"mite\" Teem library, or the \"miter\" "
"command-line renderer. This adds a \"stub\" axis 0, and setting the "
"axis labels to identify the domain and range of the opacity function. "
"The underlying opacity function is not modified." );
int
35 baneGkms_miteMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *perr;
Nrrd *nin, *nout;
airArray *mop;
int pret, E;
hestOptAdd( &opt, "i", "opacIn", airTypeOther, 1, 1, &nin, NULL,
"input opacity function ( 1 or 2 dimensional ), from "
"\"gkms opac\"",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL,
"output opacity function filename" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_miteInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( 1 == nin->axis[0].size && nin->axis[0].label &&
!strcmp( "A", nin->axis[0].label ) ) {
fprintf( stderr, "%s: already\n", me );
nout = nin;
} else {
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( !E ) E |= nrrdAxesInsert( nout, nin, 0 );
if ( !E ) E |= !( nout->axis[0].label = airStrdup( "A" ) );
if ( !E ) E |= !( nout->axis[1].label = airStrdup( "gage( v )" ) );
if ( 3 == nout->dim ) {
if ( !E ) E |= !( nout->axis[2].label = airStrdup( "gage( gm )" ) );
}
if ( E ) {
biffMovef( BANE, NRRD,
"%s: trouble modifying opacity function nrrd", me );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( out, nout, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving opacity function", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
84 BANE_GKMS_CMD( mite, MITE_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define OPAC_INFO "Generate opacity functions"
static const char *_baneGkms_opacInfoL =
( OPAC_INFO
". Takes information from an \"info\" file and from a \"boundary "
"emphasis function\" to generate 1D or 2D ( depending on info file ) "
"opacity functions. " );
int
34 baneGkms_opacMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *outS, *perr, *befS;
Nrrd *ninfo, *nbef, *nout, *nmax, *npos, *nopac;
airArray *mop;
int pret, radius, idim;
float sigma, gthrInfo[2], gthresh;
hestOptAdd( &opt, "b", "bef", airTypeOther, 1, 1, &nbef, "1, 1, 0, 1",
"boundary emphasis function mapping from \"position\" to "
"opacity. Can be either:\n "
"\b\bo filename of nrrd suitable for \"unu imap\", or:\n "
"\b\bo comma-separated list of four floats, with no spaces: "
"\"s, w, c, a\", where\n "
"s = shape of function, between 0.0 for box and "
"1.0 for tent\n "
"w = full-width half-max of function support\n "
"c = where to center function support\n "
"a = maximum opacity\n "
"If all goes well, the units for \"w\" and \"c\" are voxels.",
NULL, NULL, baneGkmsHestBEF );
hestOptAdd( &opt, "s", "sigma", airTypeFloat, 1, 1, &sigma, "nan",
"scaling in position calculation, accounts for thickness "
"of transition region between materials. Lower sigmas lead to "
"wider peaks in opacity function. "
"Calculated automatically by default." );
hestOptAdd( &opt, "g", "gthresh", airTypeOther, 1, 1, gthrInfo, "x0.04",
"minimum significant gradient magnitude. Can be given "
"in two different ways:\n "
"\b\bo \"<float>\": specify gthresh as <float> exactly.\n "
"\b\bo \"x<float>\": gthresh is a scaling, by <float>, of "
"the maximum gradient magnitude in the info file.",
NULL, NULL, baneGkmsHestGthresh );
hestOptAdd( &opt, "r", "radius", airTypeInt, 1, 1, &radius, "0",
"radius of median filtering to apply to opacity function, "
"use \"0\" to signify no median filtering" );
hestOptAdd( &opt, "m", "befOut", airTypeString, 1, 1, &befS, "",
"if boundary emphasis function given via \"-b\" "
"is in the \"s, w, c, a\" form, then save out the "
"corresponding nrrd to <befOut>, suitable for use in this "
"command or \"unu imap\"" );
hestOptAdd( &opt, "i", "infoIn", airTypeOther, 1, 1, &ninfo, NULL,
"input info file ( from \"gkms info\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "opacOut", airTypeString, 1, 1, &outS, NULL,
"output 1D or 2D opacity function" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_opacInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
airMopAdd( mop, nmax=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, npos=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nopac=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( baneInfoCheck( ninfo, AIR_FALSE ) ) {
biffAddf( BANE, "%s: didn't get a valid histogram info file", me );
airMopError( mop ); return 1;
}
idim = ninfo->dim-1;
if ( nbef->ptr && airStrlen( befS ) ) {
if ( nrrdSave( befS, nbef, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving boundary emphasis", me );
airMopError( mop ); return 1;
}
}
if ( !AIR_EXISTS( sigma ) ) {
if ( baneSigmaCalc( &sigma, ninfo ) ) {
biffAddf( BANE, "%s: trouble calculating sigma", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: calculated sigma = %g\n", me, sigma );
}
if ( 0 == gthrInfo[0] ) {
gthresh = gthrInfo[1];
} else {
if ( 2 == idim ) {
gthresh = AIR_CAST( float, gthrInfo[1]*ninfo->axis[2].max );
}
else {
if ( nrrdProject( nmax, ninfo, 1, nrrdMeasureMax, nrrdTypeDefault ) ) {
biffAddf( BANE, "%s: couldn't do max projection of 1D histo-info", me );
airMopError( mop ); return 1;
}
gthresh = gthrInfo[1]*nrrdFLookup[nmax->type]( nmax->data, 0 );
}
fprintf( stderr, "%s: calculated gthresh = %g\n", me, gthresh );
}
if ( banePosCalc( npos, sigma, gthresh, ninfo )
|| baneOpacCalc( nopac, nbef, npos ) ) {
biffAddf( BANE, "%s: trouble calculating position or opacity", me );
airMopError( mop ); return 1;
}
if ( radius ) {
if ( nrrdCheapMedian( nout, nopac, AIR_TRUE, AIR_FALSE, radius, 1.0, 2048 ) ) {
biffMovef( BANE, NRRD, "%s: error in median filtering", me );
airMopError( mop ); return 1;
}
} else {
if ( nrrdCopy( nout, nopac ) ) {
biffMovef( BANE, NRRD, "%s: error in copying output", me );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving opacity function", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
149 BANE_GKMS_CMD( opac, OPAC_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
float _baneGkmsDonData[] = {0 /* AIR_NEG_INF */ , 0, 0, 0,
0 /* AIR_NAN */ , 0, 0, 0,
0 /* AIR_POS_INF */ , 0, 0, 0,
-9.5, 0, 107, 255, /* ( 3 ) start: blue */
-8.5, 51, 104, 255,
-7.5, 103, 117, 255,
-6.5, 123, 124, 255,
-5.5, 141, 130, 255,
-4.5, 156, 132, 255,
-3.5, 166, 131, 245,
-2.5, 174, 131, 231,
-1.5, 181, 130, 216,
-0.5, 255, 255, 255, /* ( 12 ) middle */
0.5, 255, 255, 255, /* ( 13 ) middle */
1.5, 192, 129, 186,
2.5, 197, 128, 172,
3.5, 200, 128, 158,
4.5, 204, 127, 142,
5.5, 210, 126, 113,
6.5, 212, 126, 98,
7.5, 213, 126, 84,
8.5, 216, 126, 49,
9.5, 220, 133, 0}; /* ( 22 ) end: orange */
#define INVERT( d, i ) \
( d )[1 + ( i )*4] = 255 - ( d )[1 + ( i )*4]; \
( d )[2 + ( i )*4] = 255 - ( d )[2 + ( i )*4]; \
( d )[3 + ( i )*4] = 255 - ( d )[3 + ( i )*4]
54 #define PVG_HISTEQ_BINS 2048
Nrrd *
_baneGkmsDonNew( int invert ) {
static const char me[]="_baneGkmsDonNew";
Nrrd *ret;
float *data;
if ( nrrdMaybeAlloc_va( ret=nrrdNew( ), nrrdTypeFloat, 2,
AIR_CAST( size_t, 4 ), AIR_CAST( size_t, 23 ) ) ) {
biffAddf( BANE, "%s: can't create output", me );
return NULL;
}
data = ( float * )ret->data;
memcpy( data, _baneGkmsDonData, 4*23*sizeof( float ) );
data[0 + 4*0] = AIR_NEG_INF;
data[0 + 4*1] = AIR_NAN;
data[0 + 4*2] = AIR_POS_INF;
if ( invert ) {
INVERT( data, 0 );
INVERT( data, 1 );
INVERT( data, 2 );
INVERT( data, 12 );
INVERT( data, 13 );
}
return ret;
}
#define PVG_INFO "Create color-mapped pictures of p( v, g )"
static const char *_baneGkms_pvgInfoL =
( PVG_INFO
". This produces a qualitative visualization of the boundary information "
"that was captured in the histogram volume. The quantity shown is called "
"the \"position function\" in GK's published work, but a better term "
88 "would be \"distance map\", as a function of value ( v ) and gradient "
"magnitude ( g )." );
int
baneGkms_pvgMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *outS, *perr, *mapS;
Nrrd *ninfo, *nposA, *nposB, *ndon, *npvg;
NrrdIoState *nio;
airArray *mop;
int i, pret, invert, sv, sg, smlI;
float *pos, p, min, max, sml, newsml, newmin, newmax;
NrrdRange *range;
hestOptAdd( &opt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL,
"Draw on white background, instead of black" );
hestOptAdd( &opt, "m", "mapOut", airTypeString, 1, 1, &mapS, "",
"save out the colormap used here, so that it can be applied "
"to other nrrds with \"unu imap -r\"" );
hestOptAdd( &opt, "i", "infoIn", airTypeOther, 1, 1, &ninfo, NULL,
"input info file ( from \"gkms info\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "imageOut", airTypeString, 1, 1, &outS, NULL,
"output image, in PPM format" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_pvgInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
airMopAdd( mop, ndon=_baneGkmsDonNew( invert ),
( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nposA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nposB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, npvg=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nio=nrrdIoStateNew( ), ( airMopper )nrrdIoStateNix,
airMopAlways );
if ( airStrlen( mapS ) ) {
if ( nrrdSave( mapS, ndon, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving colormap", me );
airMopError( mop ); return 1;
}
}
/* we use sigma = 1.0: different sigmas will scale the position,
which will not change the coloring
gthresh = 0.0: we want to see everything, and Simian has taught
us that there can be boundaries at extremely low gradients */
if ( banePosCalc( nposA, 1.0, 0.0, ninfo ) ) {
biffAddf( BANE, "%s: trouble calculating position", me );
airMopError( mop ); return 1;
}
sv = nposA->axis[0].size;
sg = nposA->axis[1].size;
pos = ( float * )nposA->data;
/* find min, max, sml, smlI: histo-eq will warp values around such
that min-->min and max-->max, but 0-->??. So, find smallest
magnitide position ( sml ) as a stand-in for 0.0 and its index
( smlI ) */
sml = 0;
smlI = 0;
min = max = AIR_NAN;
for ( i=0; i<sv*sg; i++ ) {
p = pos[i];
if ( !AIR_EXISTS( p ) )
continue;
if ( !AIR_EXISTS( min ) ) {
min = max = p;
sml = AIR_ABS( p );
smlI = i;
continue;
}
min = AIR_MIN( p, min );
max = AIR_MAX( p, max );
if ( AIR_ABS( p ) < sml ) {
sml = AIR_ABS( p );
smlI = i;
}
}
if ( !AIR_EXISTS( min ) ) {
biffAddf( BANE, "%s: didn't see any real data in position array", me );
airMopError( mop ); return 1;
}
if ( nrrdHistoEq( nposB, nposA, NULL, PVG_HISTEQ_BINS, 3, 1.0 ) ) {
biffMovef( BANE, NRRD, "%s: trouble doing histo-eq on p( v, g )", me );
airMopError( mop ); return 1;
}
/* warp position values that pos[smlI] gets mapped back to zero,
and so that [newmin, newmax] is centered on zero */
pos = ( float * )nposB->data;
newsml = pos[smlI];
if ( min < -max ) {
newmin = min;
newmax = -min;
} else {
newmin = -max;
newmax = max;
}
for ( i=0; i<sv*sg; i++ ) {
if ( !AIR_EXISTS( pos[i] ) ) {
continue;
}
if ( pos[i] < newsml ) {
pos[i] = AIR_CAST( float, AIR_AFFINE( min, pos[i], newsml, newmin, 0.0 ) );
} else {
pos[i] = AIR_CAST( float, AIR_AFFINE( newsml, pos[i], max, 0.0, newmax ) );
}
}
range = nrrdRangeNew( newmin, newmax );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( nrrdFlip( nposA, nposB, 1 ) ||
nrrdApply1DIrregMap( npvg, nposA, range, ndon,
NULL, nrrdTypeUChar, AIR_TRUE ) ) {
biffMovef( BANE, NRRD, "%s: trouble applying colormap", me );
airMopError( mop ); return 1;
}
nio->format = nrrdFormatPNM;
if ( nrrdSave( outS, npvg, nio ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving pvg image", me );
airMopError( mop ); return 1;
}
214 airMopOkay( mop );
return 0;
}
BANE_GKMS_CMD( pvg, PVG_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define SCAT_INFO "Make V-G and V-H scatterplots"
static const char *_baneGkms_scatInfoL =
( SCAT_INFO
". These provide a quick way to inspect a histogram volume, in order to "
"verify that the derivative inclusion ranges were appropriate, and to "
"get an initial sense of what sorts of boundaries were present in the "
"original volume." );
int
35 baneGkms_scatMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out[2], *perr;
Nrrd *hvol, *nvgRaw, *nvhRaw, *nvgQuant, *nvhQuant;
NrrdRange *vgRange, *vhRange;
airArray *mop;
int pret, E;
double _gamma;
hestOptAdd( &opt, "g", "gamma", airTypeDouble, 1, 1, &_gamma, "1.0",
"gamma used to brighten/darken scatterplots. "
"gamma > 1.0 brightens; gamma < 1.0 darkens. "
"Negative gammas invert values ( like in xv ). " );
hestOptAdd( &opt, "i", "hvolIn", airTypeOther, 1, 1, &hvol, NULL,
"input histogram volume ( from \"gkms hvol\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "o", "vgOut vhOut", airTypeString, 2, 2, out, NULL,
"Filenames to use for two output scatterplots, ( gradient "
"magnitude versus value, and 2nd derivative versus value ); "
"can use PGM or PNG format" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_scatInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nvgRaw = nrrdNew( );
nvhRaw = nrrdNew( );
nvgQuant = nrrdNew( );
nvhQuant = nrrdNew( );
airMopAdd( mop, nvgRaw, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nvhRaw, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nvgQuant, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nvhQuant, ( airMopper )nrrdNuke, airMopAlways );
if ( baneRawScatterplots( nvgRaw, nvhRaw, hvol, AIR_TRUE ) ) {
biffAddf( BANE, "%s: trouble creating raw scatterplots", me );
airMopError( mop ); return 1;
}
vgRange = nrrdRangeNewSet( nvgRaw, nrrdBlind8BitRangeFalse );
vhRange = nrrdRangeNewSet( nvhRaw, nrrdBlind8BitRangeFalse );
airMopAdd( mop, vgRange, ( airMopper )nrrdRangeNix, airMopAlways );
airMopAdd( mop, vhRange, ( airMopper )nrrdRangeNix, airMopAlways );
E = 0;
if ( !E ) E |= nrrdArithGamma( nvgRaw, nvgRaw, vgRange, _gamma );
if ( !E ) E |= nrrdArithGamma( nvhRaw, nvhRaw, vhRange, _gamma );
if ( !E ) E |= nrrdQuantize( nvgQuant, nvgRaw, vgRange, 8 );
if ( !E ) E |= nrrdQuantize( nvhQuant, nvhRaw, vhRange, 8 );
if ( E ) {
biffMovef( BANE, NRRD, "%s: trouble doing gamma or quantization", me );
airMopError( mop ); return 1;
}
if ( !E ) E |= nrrdSave( out[0], nvgQuant, NULL );
if ( !E ) E |= nrrdSave( out[1], nvhQuant, NULL );
if ( E ) {
biffMovef( BANE, NRRD, "%s: trouble saving scatterplot images", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
99 BANE_GKMS_CMD( scat, SCAT_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define TXF_INFO "Create Levoy-style triangular 2D opacity functions"
static const char *_baneGkms_txfInfoL =
( TXF_INFO
". The triangles are in the 2D space of data value and gradient "
"magnitude. They can be tilted sideways and clipped at the bottom. "
"This doesn't strictly speaking belong in \"gkms\" but there's no "
"other good place in Teem." );
int
35 baneGkms_txfMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *perr;
Nrrd *nout;
airArray *mop;
int pret, E, res[2], vi, gi, step;
float min[2], max[2], top[2], v0, g0, *data, v, g,
gwidth, width, mwidth,
tvl, tvr, vl, vr, tmp, maxa;
hestOptAdd( &opt, "r", "Vres Gres", airTypeInt, 2, 2, res, "256 256",
"resolution of the transfer function in value and gradient "
"magnitude" );
hestOptAdd( &opt, "min", "Vmin Gmin", airTypeFloat, 2, 2, min, "0.0 0.0",
"minimum value and grad mag in txf" );
hestOptAdd( &opt, "max", "Vmax Gmax", airTypeFloat, 2, 2, max, NULL,
"maximum value and grad mag in txf" );
hestOptAdd( &opt, "v", "base value", airTypeFloat, 1, 1, &v0, NULL,
"data value at which to position bottom of triangle" );
hestOptAdd( &opt, "g", "gthresh", airTypeFloat, 1, 1, &g0, "0.0",
"lowest grad mag to receive opacity" );
hestOptAdd( &opt, "gw", "gwidth", airTypeFloat, 1, 1, &gwidth, "0.0",
"range of grad mag values over which to apply threshold "
"at low gradient magnitudes" );
hestOptAdd( &opt, "top", "Vtop Gtop", airTypeFloat, 2, 2, top, NULL,
"data value and grad mag at center of top of triangle" );
hestOptAdd( &opt, "w", "value width", airTypeFloat, 1, 1, &width, NULL,
"range of values to be spanned at top of triangle" );
hestOptAdd( &opt, "mw", "value width", airTypeFloat, 1, 1, &mwidth, "0",
"range of values to be spanned at BOTTOM of triangle" );
hestOptAdd( &opt, "step", NULL, airTypeInt, 0, 0, &step, NULL,
"instead of assigning opacity inside a triangular region, "
"make it more like a step function, in which opacity never "
"decreases in increasing data value" );
hestOptAdd( &opt, "a", "max opac", airTypeFloat, 1, 1, &maxa, "1.0",
"highest opacity to assign" );
hestOptAdd( &opt, "o", "opacOut", airTypeString, 1, 1, &out, NULL,
"output opacity function filename" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _baneGkms_txfInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( !E ) E |= nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3,
AIR_CAST( size_t, 1 ),
AIR_CAST( size_t, res[0] ),
AIR_CAST( size_t, res[1] ) );
if ( !E ) E |= !( nout->axis[0].label = airStrdup( "A" ) );
if ( !E ) E |= !( nout->axis[1].label = airStrdup( "gage( scalar:v )" ) );
if ( !E ) nrrdAxisInfoSet_va( nout, nrrdAxisInfoMin,
AIR_NAN, ( double )min[0], ( double )min[1] );
if ( !E ) nrrdAxisInfoSet_va( nout, nrrdAxisInfoMax,
AIR_NAN, ( double )max[0], ( double )max[1] );
if ( !E ) E |= !( nout->axis[2].label = airStrdup( "gage( scalar:gm )" ) );
if ( E ) {
biffMovef( BANE, NRRD, "%s: trouble creating opacity function nrrd", me );
airMopError( mop ); return 1;
}
data = ( float * )nout->data;
tvl = top[0] - width/2;
tvr = top[0] + width/2;
mwidth /= 2;
for ( gi=0; gi<res[1]; gi++ ) {
g = AIR_CAST( float, NRRD_CELL_POS( min[1], max[1], res[1], gi ) );
for ( vi=0; vi<res[0]; vi++ ) {
v = AIR_CAST( float, NRRD_CELL_POS( min[0], max[0], res[0], vi ) );
vl = AIR_CAST( float, AIR_AFFINE( 0, g, top[1], v0-mwidth, tvl ) );
vr = AIR_CAST( float, AIR_AFFINE( 0, g, top[1], v0+mwidth, tvr ) );
if ( g > top[1] ) {
data[vi + res[0]*gi] = 0;
continue;
}
tmp = AIR_CAST( float, ( v - vl )/( 0.00001 + vr - vl ) );
tmp = 1 - AIR_ABS( 2*tmp - 1 );
if ( step && v > ( vr + vl )/2 ) {
tmp = 1;
}
tmp = AIR_MAX( 0, tmp );
data[vi + res[0]*gi] = tmp*maxa;
tmp = AIR_CAST( float, AIR_AFFINE( g0 - gwidth/2, g, g0 + gwidth/2,
0.0, 1.0 ) );
tmp = AIR_CLAMP( 0, tmp, 1 );
data[vi + res[0]*gi] *= tmp;
}
}
if ( nrrdSave( out, nout, NULL ) ) {
biffMovef( BANE, NRRD, "%s: trouble saving opacity function", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
134 BANE_GKMS_CMD( txf, TXF_INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
/* hz is defined in the aix system in /usr/include/sys/m_param.h .
This causes compilation errors for later functions which want to use
this as a variable.
*/
#ifdef hz
# undef hz
#endif
/*
** learned: don't ever count on interleaved printfs to stdout
** and stderr to appear in the right order
*/
int
41 _baneAxisCheck ( baneAxis *ax ) {
static const char me[]="_baneAxisCheck";
if ( !( ax->res >= 2 ) ) {
biffAddf( BANE, "%s: need resolution at least 2 ( not %d )", me, ax->res );
return 1;
}
if ( !ax->measr ) {
biffAddf( BANE, "%s: have NULL baneMeasr", me );
return 1;
}
if ( !ax->inc ) {
biffAddf( BANE, "%s: have NULL baneInc", me );
return 1;
}
/* all okay */
return 0;
}
void
62 baneProbe( double val[3],
Nrrd *nin, baneHVolParm *hvp, gageContext *ctx,
unsigned int x, unsigned int y, unsigned int z ) {
float *data=NULL;
if ( hvp->makeMeasrVol ) {
data = ( ( float* )( hvp->measrVol->data )
+ 3*( x + nin->axis[0].size*( y + nin->axis[1].size*z ) ) );
}
if ( !hvp->makeMeasrVol || !hvp->measrVolDone ) {
gageProbe( ctx, x, y, z );
val[0] = baneMeasrAnswer( hvp->axis[0].measr, ctx );
val[1] = baneMeasrAnswer( hvp->axis[1].measr, ctx );
val[2] = baneMeasrAnswer( hvp->axis[2].measr, ctx );
if ( hvp->makeMeasrVol ) {
data[0] = AIR_CAST( float, val[0] );
data[1] = AIR_CAST( float, val[1] );
data[2] = AIR_CAST( float, val[2] );
}
} else {
val[0] = data[0];
val[1] = data[1];
val[2] = data[2];
}
return;
}
int
90 baneFindInclusion( double min[3], double max[3],
Nrrd *nin, baneHVolParm *hvp, gageContext *ctx ) {
static const char me[]="baneFindInclusion";
char prog[13],
aname[3][AIR_STRLEN_SMALL] = {"grad-mag", "2nd deriv", "data value"};
int sx, sy, sz, x, y, z, E, ai;
baneInc *inc[3];
/* HEY HEY HEY: The variable "hist" is used before its value is set.
Nrrd *hist[3];
*/
double val[3];
/* conveniance copies */
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
inc[0] = hvp->axis[0].inc;
inc[1] = hvp->axis[1].inc;
inc[2] = hvp->axis[2].inc;
if ( hvp->verbose ) {
fprintf( stderr, "%s: inclusions: %s %s %s\n", me,
inc[0]->name, inc[1]->name, inc[2]->name );
fprintf( stderr, "%s: measures: %s %s %s\n", me,
hvp->axis[0].measr->name, hvp->axis[1].measr->name,
hvp->axis[2].measr->name );
/*
fprintf( stderr, "%s: gage query:\n", me );
ctx->pvl[0]->kind->queryPrint( stderr, ctx->pvl[0]->query );
*/
}
/* Determining the inclusion ranges for the histogram volume takes
some work- either finding the min and max values of some measure,
and/or making a histogram of them. The needed work for the three
measures should done simultaneously during a given pass through
the volume, so we break up the work into three stages- "passA",
"passB", and then the final determination of ranges, "ans". Here
we start with passA. If the chosen inclusion methods don't have
anything to do at this stage ( the callback is NULL ), we don't do
anything */
if ( hvp->verbose ) {
fprintf( stderr, "%s: pass A of inclusion initialization ... ", me );
fflush( stderr );
}
if ( inc[0]->process[0]
|| inc[1]->process[0]
|| inc[2]->process[0] ) {
/*
fprintf( stderr, "%s: inclusion pass CBs = %p %p %p \n", me,
incPass[0], incPass[1], incPass[2] );
*/
if ( hvp->makeMeasrVol && !hvp->measrVol ) {
if ( nrrdMaybeAlloc_va( hvp->measrVol=nrrdNew( ), nrrdTypeFloat, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) ) ) {
biffMovef( BANE, NRRD, "%s: couldn't allocate 3x%dx%dx%d VGH volume",
me, sx, sy, sz );
return 1;
}
}
for ( z=0; z<sz; z++ ) {
for ( y=0; y<sy; y++ ) {
if ( hvp->verbose && !( ( y+sy*z )%200 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, y+sy*z, sy*sz, prog ) );
fflush( stderr );
}
for ( x=0; x<sx; x++ ) {
baneProbe( val, nin, hvp, ctx, x, y, z );
if ( inc[0]->process[0] ) inc[0]->process[0]( inc[0], val[0] );
if ( inc[1]->process[0] ) inc[1]->process[0]( inc[1], val[1] );
if ( inc[2]->process[0] ) inc[2]->process[0]( inc[2], val[2] );
}
}
}
if ( hvp->makeMeasrVol ) {
hvp->measrVolDone = AIR_TRUE;
/* nrrdSave( "VGH.nrrd", hvp->measrVol, NULL ); */
}
}
if ( hvp->verbose )
fprintf( stderr, "\b\b\b\b\b\b done\n" );
/* HEY HEY HEY: The variable "hist" is used before its value is set.
if ( hvp->verbose > 1 ) {
fprintf( stderr, "%s: after pass A; ranges: [%g, %g] [%g, %g] [%g, %g]\n", me,
hist[0]->axis[0].min, hist[0]->axis[0].max,
hist[1]->axis[0].min, hist[1]->axis[0].max,
hist[2]->axis[0].min, hist[2]->axis[0].max );
}
*/
/* second stage of initialization, includes creating histograms */
if ( hvp->verbose ) {
fprintf( stderr, "%s: pass B of inclusion initialization ... ", me );
fflush( stderr );
}
if ( inc[0]->process[1]
|| inc[1]->process[1]
|| inc[2]->process[1] ) {
if ( hvp->makeMeasrVol && !hvp->measrVol ) {
if ( nrrdMaybeAlloc_va( hvp->measrVol=nrrdNew( ), nrrdTypeFloat, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) ) ) {
biffMovef( BANE, NRRD, "%s: couldn't allocate 3x%dx%dx%d VGH volume",
me, sx, sy, sz );
return 1;
}
}
for ( z=0; z<sz; z++ ) {
for ( y=0; y<sy; y++ ) {
if ( hvp->verbose && !( ( y+sy*z )%200 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, y+sy*z, sy*sz, prog ) );
fflush( stderr );
}
for ( x=0; x<sx; x++ ) {
baneProbe( val, nin, hvp, ctx, x, y, z );
if ( inc[0]->process[1] ) inc[0]->process[1]( inc[0], val[0] );
if ( inc[1]->process[1] ) inc[1]->process[1]( inc[1], val[1] );
if ( inc[2]->process[1] ) inc[2]->process[1]( inc[2], val[2] );
}
}
}
if ( hvp->makeMeasrVol ) {
hvp->measrVolDone = AIR_TRUE;
}
}
if ( hvp->verbose )
fprintf( stderr, "\b\b\b\b\b\b done\n" );
/* HEY HEY HEY: The variable "hist" is used before its value is set.
if ( hvp->verbose > 1 ) {
fprintf( stderr, "%s: after pass B; ranges: [%g, %g] [%g, %g] [%g, %g]\n", me,
hist[0]->axis[0].min, hist[0]->axis[0].max,
hist[1]->axis[0].min, hist[1]->axis[0].max,
hist[2]->axis[0].min, hist[2]->axis[0].max );
}
*/
/* now the real work of determining the inclusion */
if ( hvp->verbose ) {
fprintf( stderr, "%s: determining inclusion ... ", me );
fflush( stderr );
}
E = 0;
if ( !E ) {
ai = 0;
E |= baneIncAnswer( inc[0], 0 + min, 0 + max );
}
if ( !E ) {
ai = 1;
E |= baneIncAnswer( inc[1], 1 + min, 1 + max );
}
if ( !E ) {
ai = 2;
E |= baneIncAnswer( inc[2], 2 + min, 2 + max );
}
if ( E ) {
biffAddf( BANE, "%s: problem calculating inclusion for axis %d ( %s )",
me, ai, aname[ai] );
return 1;
}
if ( hvp->verbose )
fprintf( stderr, "done\n" );
/* HEY HEY HEY: The variable "hist" is used before its value is set.
nrrdNuke( hist[0] );
nrrdNuke( hist[1] );
nrrdNuke( hist[2] );
*/
return 0;
}
int
265 baneMakeHVol( Nrrd *hvol, Nrrd *nin, baneHVolParm *hvp ) {
static const char me[]="baneMakeHVol";
char prog[13];
gageContext *ctx;
gagePerVolume *pvl;
int E, sx, sy, sz, shx, shy, shz, x, y, z, hx, hy, hz,
*rhvdata, clipVal, hval, pad;
/* these are doubles because ultimately the inclusion functions
use doubles, because I wanted the most generality */
double val[3], min[3], max[3];
size_t hidx, included;
float fracIncluded;
unsigned char *nhvdata;
Nrrd *rawhvol;
airArray *mop;
if ( !( hvol && nin && hvp ) ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
if ( baneInputCheck( nin, hvp ) ) {
biffAddf( BANE, "%s: something wrong with input volume or parameters", me );
return 1;
}
/* set up */
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
mop = airMopNew( );
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
pvl = gagePerVolumeNew( ctx, nin, gageKindScl );
gageParmSet( ctx, gageParmVerbose, 0 );
gageParmSet( ctx, gageParmRenormalize, hvp->renormalize );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
if ( !hvp->k3pack ) {
biffAddf( BANE, "%s: code currently assumes k3pack", me );
airMopError( mop ); return 1;
}
gageParmSet( ctx, gageParmK3Pack, hvp->k3pack );
E = 0;
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, hvp->k[gageKernel00],
hvp->kparm[gageKernel00] );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, hvp->k[gageKernel11],
hvp->kparm[gageKernel11] );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, hvp->k[gageKernel22],
hvp->kparm[gageKernel22] );
if ( !E ) E |= gageQueryReset( ctx, pvl );
if ( !E ) E |= gageQueryAdd( ctx, pvl, hvp->axis[0].measr->query );
if ( !E ) E |= gageQueryAdd( ctx, pvl, hvp->axis[1].measr->query );
if ( !E ) E |= gageQueryAdd( ctx, pvl, hvp->axis[2].measr->query );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
biffMovef( BANE, GAGE, "%s: trouble setting up gage", me );
airMopError( mop ); return 1;
}
pad = ctx->radius;
if ( baneFindInclusion( min, max, nin, hvp, ctx ) ) {
biffAddf( BANE, "%s: trouble finding inclusion ranges", me );
airMopError( mop ); return 1;
}
if ( max[0] == min[0] ) {
max[0] += 1;
if ( hvp->verbose )
fprintf( stderr, "%s: fixing range 0 [%g, %g] --> [%g, %g]\n",
me, min[0], min[0], min[0], max[0] );
}
if ( max[1] == min[1] ) {
max[1] += 1;
if ( hvp->verbose )
fprintf( stderr, "%s: fixing range 1 [%g, %g] --> [%g, %g]\n",
me, min[1], min[1], min[1], max[1] );
}
if ( max[2] == min[2] ) {
max[2] += 1;
if ( hvp->verbose )
fprintf( stderr, "%s: fixing range 2 [%g, %g] --> [%g, %g]\n",
me, min[2], min[2], min[2], max[2] );
}
if ( hvp->verbose )
fprintf( stderr, "%s: inclusion: 0:[%g, %g], 1:[%g, %g], 2:[%g, %g]\n", me,
min[0], max[0], min[1], max[1], min[2], max[2] );
/* construct the "raw" ( un-clipped ) histogram volume */
if ( hvp->verbose ) {
fprintf( stderr, "%s: creating raw histogram volume ... ", me );
fflush( stderr );
}
shx = hvp->axis[0].res;
shy = hvp->axis[1].res;
shz = hvp->axis[2].res;
if ( nrrdMaybeAlloc_va( rawhvol=nrrdNew( ), nrrdTypeInt, 3,
AIR_CAST( size_t, shx ),
AIR_CAST( size_t, shy ),
AIR_CAST( size_t, shz ) ) ) {
biffMovef( BANE, NRRD,
"%s: couldn't allocate raw histovol ( %dx%dx%d )",
me, shx, shy, shz );
airMopError( mop ); return 1;
}
airMopAdd( mop, rawhvol, ( airMopper )nrrdNuke, airMopAlways );
rhvdata = ( int * )rawhvol->data;
included = 0;
for ( z=pad; z<sz-pad; z++ ) {
for ( y=pad; y<sy-pad; y++ ) {
if ( hvp->verbose && !( ( y-pad+( sy-2*pad )*( z-pad ) )%200 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, y-pad+( sy-2*pad )*( z-pad ),
( sy-2*pad )*( sz-2*pad ), prog ) );
fflush( stderr );
}
for ( x=pad; x<sx-pad; x++ ) {
baneProbe( val, nin, hvp, ctx, x, y, z );
if ( !( AIR_IN_CL( min[0], val[0], max[0] ) &&
AIR_IN_CL( min[1], val[1], max[1] ) &&
AIR_IN_CL( min[2], val[2], max[2] ) ) ) {
continue;
}
/* else this voxel will contribute to the histovol */
hx = airIndex( min[0], val[0], max[0], shx );
hy = airIndex( min[1], val[1], max[1], shy );
hz = airIndex( min[2], val[2], max[2], shz );
hidx = hx + shx*( hy + shy*hz );
if ( rhvdata[hidx] < INT_MAX ) {
++rhvdata[hidx];
}
++included;
}
}
}
fracIncluded = ( float )included/( ( sz-2*pad )*( sy-2*pad )*( sx-2*pad ) );
if ( fracIncluded < hvp->incLimit ) {
biffAddf( BANE, "%s: included only %g%% of data, wanted at least %g%%",
me, 100*fracIncluded, 100*hvp->incLimit );
airMopError( mop ); return 1;
}
if ( hvp->verbose ) {
fprintf( stderr, "\b\b\b\b\b\b done\n" );
fprintf( stderr, "%s: included %g%% of original voxels\n", me,
fracIncluded*100 );
}
/* determine the clipping value and produce the final histogram volume */
if ( baneClipAnswer( &clipVal, hvp->clip, rawhvol ) ) {
biffAddf( BANE, "%s: trouble determining clip value", me );
airMopError( mop ); return 1;
}
if ( hvp->verbose )
fprintf( stderr, "%s: will clip at %d\n", me, clipVal );
if ( hvp->verbose ) {
fprintf( stderr, "%s: creating 8-bit histogram volume ... ", me );
fflush( stderr );
}
if ( nrrdMaybeAlloc_va( hvol, nrrdTypeUChar, 3,
AIR_CAST( size_t, shx ),
AIR_CAST( size_t, shy ),
AIR_CAST( size_t, shz ) ) ) {
biffMovef( BANE, NRRD, "%s: couldn't alloc finished histovol", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, hvol, ( airMopper )nrrdEmpty, airMopOnError );
hvol->axis[0].min = min[0];
hvol->axis[1].min = min[1];
hvol->axis[2].min = min[2];
hvol->axis[0].max = max[0];
hvol->axis[1].max = max[1];
hvol->axis[2].max = max[2];
hvol->axis[0].label = airStrdup( hvp->axis[0].measr->name );
hvol->axis[1].label = airStrdup( hvp->axis[1].measr->name );
hvol->axis[2].label = airStrdup( hvp->axis[2].measr->name );
hvol->axis[0].center = nrrdCenterCell;
hvol->axis[1].center = nrrdCenterCell;
hvol->axis[2].center = nrrdCenterCell;
nhvdata = ( unsigned char * )hvol->data;
for ( hz=0; hz<shz; hz++ ) {
for ( hy=0; hy<shy; hy++ ) {
if ( hvp->verbose && !( ( hy+shy*hz )%200 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, hy+shy*hz, shy*shz, prog ) );
fflush( stderr );
}
for ( hx=0; hx<shx; hx++ ) {
hidx = hx + shx*( hy + shy*hz );
hval = airIndexClamp( 0, rhvdata[hidx], clipVal, 256 );
nhvdata[hidx] = hval;
}
}
}
if ( hvp->verbose )
fprintf( stderr, "\b\b\b\b\b\b done\n" );
airMopOkay( mop );
return 0;
}
Nrrd *
464 baneGKMSHVol( Nrrd *nin, float gradPerc, float hessPerc ) {
static const char me[]="baneGKMSHVol";
baneHVolParm *hvp;
Nrrd *hvol;
if ( !( hvp = baneHVolParmNew( ) ) ) {
biffAddf( BANE, "%s: couldn't get hvol parm struct", me );
return NULL;
}
baneHVolParmGKMSInit( hvp );
hvp->axis[0].inc->parm[1] = gradPerc;
hvp->axis[1].inc->parm[1] = hessPerc;
hvol = nrrdNew( );
if ( baneMakeHVol( hvol, nin, hvp ) ) {
biffAddf( BANE, "%s: trouble making GKMS histogram volume", me );
free( hvp ); return NULL;
}
baneHVolParmNix( hvp );
return hvol;
}
/*
int
baneApplyMeasr( Nrrd *nout, Nrrd *nin, int measr ) {
static const char me[]="baneApplyMeasr";
int sx, sy, sz, x, y, z, marg;
baneMeasrType msr;
nrrdBigInt idx;
float ( *insert )( void *, nrrdBigInt, float );
if ( 3 != nin->dim ) {
biffAddf( BANE, "%s: need a 3-dimensional nrrd ( not %d )", me, nin->dim );
return 1;
}
if ( !( AIR_IN_OP( nrrdTypeUnknown, nin->type, nrrdTypeLast ) &&
nin->type != nrrdTypeBlock ) ) {
biffAddf( BANE, "%s: must have a scalar type nrrd", me );
return 1;
}
if ( !( AIR_EXISTS( nin->axis[0].spacing ) && nin->axis[0].spacing > 0 &&
AIR_EXISTS( nin->axis[1].spacing ) && nin->axis[1].spacing > 0 &&
AIR_EXISTS( nin->axis[2].spacing ) && nin->axis[2].spacing > 0 ) ) {
biffAddf( BANE, "%s: must have positive spacing for all three axes", me );
return 1;
}
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
marg = baneMeasrMargin[measr];
msr = baneMeasr[measr];
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3,
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) ) ) {
biffMovef( BANE, NRRD, "%s: couldn't alloc output nrrd", me );
return 1;
}
nout->axis[0].spacing = nin->axis[0].spacing;
nout->axis[1].spacing = nin->axis[1].spacing;
nout->axis[2].spacing = nin->axis[2].spacing;
insert = nrrdFInsert[nrrdTypeFloat];
for ( z=marg; z<sz-marg; z++ ) {
for ( y=marg; y<sy-marg; y++ ) {
for ( x=marg; x<sx-marg; x++ ) {
idx = x + sx*( y + sy*z );
insert( nout->data, idx, msr( nin, idx ) );
}
}
}
return 0;
}
*/
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
void
28 _baneIncProcess_LearnMinMax( baneInc *inc, double val ) {
if ( AIR_EXISTS( inc->nhist->axis[0].min ) ) {
/* then both min and max have seen at least one valid value */
inc->nhist->axis[0].min = AIR_MIN( inc->nhist->axis[0].min, val );
inc->nhist->axis[0].max = AIR_MAX( inc->nhist->axis[0].max, val );
} else {
inc->nhist->axis[0].min = inc->nhist->axis[0].max = val;
}
/*
fprintf( stderr, "## _baneInc_LearnMinMax: ( %g, %g )\n",
inc->nhist->axis[0].min, inc->nhist->axis[0].max );
*/
return;
}
void
45 _baneIncProcess_Stdv( baneInc *inc, double val ) {
inc->S += val;
inc->SS += val*val;
inc->num += 1;
return;
}
void
54 _baneIncProcess_HistFill( baneInc *inc, double val ) {
int *hist;
unsigned int idx;
idx = airIndex( inc->nhist->axis[0].min, val, inc->nhist->axis[0].max,
inc->nhist->axis[0].size );
/*
fprintf( stderr, "## _baneInc_HistFill: ( %g, %g, %g ) %d ---> %d\n",
inc->nhist->axis[0].min, val, inc->nhist->axis[0].max,
inc->nhist->axis[0].size, idx );
*/
if ( idx < inc->nhist->axis[0].size ) {
hist = ( int* )inc->nhist->data;
hist[idx]++;
}
return;
}
/*
** _baneIncAnswer_Absolute
**
** incParm[0]: new min
** incParm[1]: new max
*/
int
79 _baneIncAnswer_Absolute( double *minP, double *maxP,
Nrrd *hist, double *incParm,
baneRange *range ) {
AIR_UNUSED( hist );
AIR_UNUSED( range );
*minP = incParm[0];
*maxP = incParm[1];
return 0;
}
/*
** _baneIncAnswer_RangeRatio
**
** incParm[0]: scales the size of the range after it has been
** sent through the associated range function.
*/
int
96 _baneIncAnswer_RangeRatio( double *minP, double *maxP,
Nrrd *hist, double *incParm,
baneRange *range ) {
static const char me[]="_baneIncAnwer_RangeRatio";
double mid;
if ( range->answer( minP, maxP, hist->axis[0].min, hist->axis[0].max ) ) {
biffAddf( BANE, "%s: trouble", me );
return 1;
}
if ( baneRangeAnywhere == range->type ) {
mid = AIR_EXISTS( range->center ) ? range->center : ( *minP + *maxP )/2;
*minP = AIR_AFFINE( -1, -incParm[0], 0, *minP, mid );
*maxP = AIR_AFFINE( 0, incParm[0], 1, mid, *maxP );
} else {
*minP *= incParm[0];
*maxP *= incParm[0];
}
return 0;
}
/*
** _baneIncAnswer_Percentile
**
** incParm[0]: resolution of histogram generated
** incParm[1]: PERCENT of hits to throw away, by nibbling away at
** lower and upper ends of range, in a manner dependant on the
** range type
*/
int
127 _baneIncAnswer_Percentile( double *minP, double *maxP,
Nrrd *nhist, double *incParm,
baneRange *range ) {
static const char me[]="_baneIncAnswer_Percentile";
int *hist, i, histSize, sum;
float minIncr, maxIncr, out, outsofar, mid, minIdx, maxIdx;
double min, max;
/* integrate histogram and determine how many hits to exclude */
sum = 0;
hist = ( int * )nhist->data;
histSize = nhist->axis[0].size;
for ( i=0; i<histSize; i++ ) {
sum += hist[i];
}
if ( !sum ) {
biffAddf( BANE, "%s: integral of histogram is zero", me );
return 1;
}
/*
sprintf( err, "%03d-histo.nrrd", baneHack ); nrrdSave( err, nhist, NULL );
baneHack++;
*/
out = AIR_CAST( float, sum*incParm[1]/100.0 );
fprintf( stderr, "##%s: hist's size=%d, sum=%d --> out = %g\n", me,
histSize, sum, out );
if ( range->answer( &min, &max, nhist->axis[0].min, nhist->axis[0].max ) ) {
biffAddf( BANE, "%s:", me ); return 1;
}
fprintf( stderr, "##%s: hist's min, max ( %g, %g ) ---%s---> %g, %g\n",
me, nhist->axis[0].min, nhist->axis[0].max,
range->name, min, max );
if ( baneRangeAnywhere == range->type ) {
mid = AIR_CAST( float, ( AIR_EXISTS( range->center )
? range->center
: ( min + max )/2 ) );
} else {
mid = 0;
/* yes, this is okay. The "mid" is the value we march towards
from both ends, but we control the rate of marching according
to the distance to the ends. So if min == mid == 0, then
there is no marching up from below
HOWEVER: the mode of histogram would probably be better. */
}
fprintf( stderr, "##%s: hist ( %g, %g ) --> min, max = ( %g, %g ) --> mid = %g\n",
me, nhist->axis[0].min, nhist->axis[0].max, min, max, mid );
if ( max-mid > mid-min ) {
/* the max is further from the mid than the min */
maxIncr = 1;
minIncr = AIR_CAST( float, ( mid-min )/( max-mid ) );
} else {
/* the min is further */
minIncr = 1;
maxIncr = AIR_CAST( float, ( max-mid )/( mid-min ) );
}
if ( !( AIR_EXISTS( minIncr ) && AIR_EXISTS( maxIncr ) ) ) {
biffAddf( BANE, "%s: minIncr, maxIncr don't both exist", me );
return 1;
}
fprintf( stderr, "##%s: --> {min, max}Incr = %g, %g\n", me, minIncr, maxIncr );
minIdx = AIR_CAST( float,
AIR_AFFINE( nhist->axis[0].min, min, nhist->axis[0].max,
0, histSize-1 ) );
maxIdx = AIR_CAST( float,
AIR_AFFINE( nhist->axis[0].min, max, nhist->axis[0].max,
0, histSize-1 ) );
outsofar = 0;
while ( outsofar < out ) {
if ( AIR_IN_CL( 0, minIdx, histSize-1 ) ) {
outsofar += minIncr*hist[AIR_ROUNDUP( minIdx )];
}
if ( AIR_IN_CL( 0, maxIdx, histSize-1 ) ) {
outsofar += maxIncr*hist[AIR_ROUNDUP( maxIdx )];
}
minIdx += minIncr;
maxIdx -= maxIncr;
if ( minIdx > maxIdx ) {
biffAddf( BANE, "%s: minIdx ( %g ) passed maxIdx ( %g ) during "
"histogram traversal", me, minIdx, maxIdx );
return 1;
}
}
*minP = AIR_AFFINE( 0, minIdx, histSize-1,
nhist->axis[0].min, nhist->axis[0].max );
*maxP = AIR_AFFINE( 0, maxIdx, histSize-1,
nhist->axis[0].min, nhist->axis[0].max );
fprintf( stderr, "##%s: --> output min, max = %g, %g\n", me, *minP, *maxP );
return 0;
}
/*
** _baneIncAnswer_Stdv( )
**
** incParm[0]: range is standard deviation times this
*/
int
223 _baneIncAnswer_Stdv( double *minP, double *maxP,
Nrrd *hist, double *incParm,
baneRange *range ) {
float SS, stdv, mid, mean, width;
int count;
count = hist->axis[1].size;
mean = AIR_CAST( float, hist->axis[1].min/count );
SS = AIR_CAST( float, hist->axis[1].max/count );
stdv = AIR_CAST( float, sqrt( SS - mean*mean ) );
width = AIR_CAST( float, incParm[0]*stdv );
fprintf( stderr, "##%s: mean=%g, stdv=%g --> width=%g\n",
"_baneIncAnswer_Stdv", mean, stdv, width );
switch ( range->type ) {
case baneRangePositive:
*minP = 0;
*maxP = width;
break;
case baneRangeNegative:
*minP = -width;
*maxP = 0;
break;
case baneRangeZeroCentered:
*minP = -width/2;
*maxP = width/2;
break;
case baneRangeAnywhere:
mid = AIR_CAST( float, AIR_EXISTS( range->center ) ? range->center : mean );
*minP = mid - width/2;
*maxP = mid + width/2;
break;
default:
*minP = *maxP = AIR_NAN;
break;
}
return 0;
}
baneInc *
262 baneIncNew( int type, baneRange *range, double *parm ) {
static const char me[]="baneIncNew";
baneInc *inc;
if ( !( AIR_IN_OP( baneIncUnknown, type, baneIncLast ) ) ) {
biffAddf( BANE, "%s: baneInc %d invalid", me, type );
return NULL;
}
if ( !( range && parm ) ) {
biffAddf( BANE, "%s: got NULL baneRange or parm", me );
return NULL;
}
inc = ( baneInc* )calloc( 1, sizeof( baneInc ) );
if ( !inc ) {
biffAddf( BANE, "%s: couldn't allocated baneInc!", me );
return NULL;
}
inc->S = inc->SS = 0;
inc->num = 0;
inc->range = baneRangeCopy( range );
if ( !inc->range ) {
biffAddf( BANE, "%s: couldn't copy baneRange!", me );
baneIncNix( inc ); return NULL;
}
inc->type = type;
switch ( type ) {
/* --------------------------------------------------------- */
case baneIncAbsolute:
sprintf( inc->name, "absolute" );
inc->nhist = NULL;
if ( !( AIR_EXISTS( parm[0] ) && AIR_EXISTS( parm[1] ) ) ) {
biffAddf( BANE, "%s: parm[0] and parm[1] don't both exist", me );
baneIncNix( inc ); return NULL;
}
inc->parm[0] = parm[0]; /* enforced min */
inc->parm[1] = parm[1]; /* enforced max */
inc->process[0] = NULL;
inc->process[1] = NULL;
inc->answer = _baneIncAnswer_Absolute;
break;
/* --------------------------------------------------------- */
case baneIncRangeRatio:
sprintf( inc->name, "range ratio" );
inc->nhist = nrrdNew( );
if ( !AIR_EXISTS( parm[0] ) ) {
biffAddf( BANE, "%s: parm[0] doesn't exist", me );
baneIncNix( inc ); return NULL;
}
inc->parm[0] = parm[0]; /* scaling on range */
inc->process[0] = NULL;
inc->process[1] = _baneIncProcess_LearnMinMax;
inc->answer = _baneIncAnswer_RangeRatio;
break;
/* --------------------------------------------------------- */
case baneIncPercentile:
sprintf( inc->name, "percentile" );
inc->nhist = nrrdNew( );
if ( !( AIR_EXISTS( parm[0] ) && AIR_EXISTS( parm[1] ) ) ) {
biffAddf( BANE, "%s: parm[0] and parm[1] don't both exist", me );
baneIncNix( inc ); return NULL;
}
inc->parm[0] = parm[0]; /* size of histogram */
if ( nrrdMaybeAlloc_va( inc->nhist, nrrdTypeInt, 1,
AIR_CAST( size_t, parm[0] ) ) ) {
biffMovef( BANE, NRRD, "%s: couldn't allocate histogram", me );
baneIncNix( inc ); return NULL;
}
inc->parm[1] = parm[1]; /* percentile to exclude */
inc->process[0] = _baneIncProcess_LearnMinMax;
inc->process[1] = _baneIncProcess_HistFill;
inc->answer = _baneIncAnswer_Percentile;
break;
/* --------------------------------------------------------- */
case baneIncStdv:
sprintf( inc->name, "stdv" );
inc->nhist = NULL;
if ( !AIR_EXISTS( parm[0] ) ) {
biffAddf( BANE, "%s: parm[0] doesn't exist", me );
baneIncNix( inc ); return NULL;
}
inc->parm[0] = parm[0]; /* multiple of standard dev to use */
inc->process[0] = NULL;
inc->process[1] = _baneIncProcess_Stdv;
inc->answer = _baneIncAnswer_Stdv;
break;
/* --------------------------------------------------------- */
default:
biffAddf( BANE, "%s: Sorry, baneInc %d not implemented", me, type );
baneIncNix( inc ); return NULL;
}
return inc;
}
void
356 baneIncProcess( baneInc *inc, int passIdx, double val ) {
if ( inc && ( 0 == passIdx || 1 == passIdx ) && inc->process[passIdx] ) {
inc->process[passIdx]( inc, val );
}
return;
}
int
365 baneIncAnswer( baneInc *inc, double *minP, double *maxP ) {
static const char me[]="baneIncAnswer";
if ( !( inc && minP && maxP ) ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
if ( inc->answer( minP, maxP, inc->nhist, inc->parm, inc->range ) ) {
biffAddf( BANE, "%s: trouble", me );
return 1;
}
return 0;
}
baneInc *
380 baneIncCopy( baneInc *inc ) {
static const char me[]="baneIncCopy";
baneInc *ret = NULL;
ret = baneIncNew( inc->type, inc->range, inc->parm );
if ( !ret ) {
biffAddf( BANE, "%s: couldn't make new inc", me );
return NULL;
}
return ret;
}
baneInc *
393 baneIncNix( baneInc *inc ) {
if ( inc ) {
baneRangeNix( inc->range );
nrrdNuke( inc->nhist );
airFree( inc );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
double
28 _baneMeasr_StockAnswer( baneMeasr *measr, double *san, double *parm ) {
AIR_UNUSED( parm );
return san[measr->offset0];
}
baneMeasr *
35 baneMeasrNew( int type, double *parm ) {
static const char me[]="baneMeasrNew";
baneMeasr *measr;
int item;
AIR_UNUSED( parm );
if ( !( AIR_IN_OP( baneMeasrUnknown, type, baneMeasrLast ) ) ) {
biffAddf( BANE, "%s: baneMeasr %d invalid", me, type );
return NULL;
}
/* for now, parm is ignored */
measr = ( baneMeasr* )calloc( 1, sizeof( baneMeasr ) );
if ( !measr ) {
biffAddf( BANE, "%s: couldn't allocate baneMeasr!", me );
return NULL;
}
measr->type = type;
measr->range = NULL;
GAGE_QUERY_RESET( measr->query );
switch( type ) {
/* --------------------------------------------------------------- */
case baneMeasrValuePositive:
item = gageSclValue;
sprintf( measr->name, "%s, positive", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangePositive );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrValueZeroCentered:
item = gageSclValue;
sprintf( measr->name, "%s, zero-centered", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangeZeroCentered );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrValueAnywhere:
item = gageSclValue;
sprintf( measr->name, "%s, anywhere", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangeAnywhere );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrGradMag:
item = gageSclGradMag;
sprintf( measr->name, "%s", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangePositive );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrLaplacian:
item = gageSclLaplacian;
sprintf( measr->name, "%s", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangeZeroCentered );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasr2ndDD:
item = gageScl2ndDD;
sprintf( measr->name, "%s", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangeZeroCentered );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrTotalCurv:
item = gageSclTotalCurv;
sprintf( measr->name, "%s", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangePositive );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
case baneMeasrFlowlineCurv:
item = gageSclFlowlineCurv;
sprintf( measr->name, "%s", airEnumStr( gageScl, item ) );
GAGE_QUERY_ITEM_ON( measr->query, item );
measr->range = baneRangeNew( baneRangePositive );
measr->offset0 = gageKindAnswerOffset( gageKindScl, item );
measr->answer = _baneMeasr_StockAnswer;
break;
/* --------------------------------------------------------------- */
default:
biffAddf( BANE, "%s: Sorry, baneMeasr %d not implemented", me, type );
baneMeasrNix( measr ); return NULL;
}
return measr;
}
double
136 baneMeasrAnswer( baneMeasr *measr, gageContext *gctx ) {
static const char me[]="baneMeasrAnswer";
double ret;
if ( measr && gctx && 1 == gctx->pvlNum ) {
ret = measr->answer( measr, gctx->pvl[0]->answer, measr->parm );
} else {
fprintf( stderr, "%s: something is terribly wrong\n", me );
ret = AIR_NAN;
}
return ret;
}
baneMeasr *
150 baneMeasrCopy( baneMeasr *measr ) {
static const char me[]="baneMeasrCopy";
baneMeasr *ret = NULL;
ret = baneMeasrNew( measr->type, measr->parm );
if ( !ret ) {
biffAddf( BANE, "%s: couldn't make new measr", me );
return NULL;
}
return ret;
}
baneMeasr *
163 baneMeasrNix( baneMeasr *measr ) {
if ( measr ) {
baneRangeNix( measr->range );
airFree( measr );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
const int
banePresent = 42;
void
32 _baneAxisInit( baneAxis *axis ) {
axis->res = 0;
axis->measr = NULL;
axis->inc = NULL;
}
void
40 _baneAxisEmpty( baneAxis *axis ) {
axis->measr = baneMeasrNix( axis->measr );
axis->inc = baneIncNix( axis->inc );
}
baneHVolParm *
47 baneHVolParmNew( ) {
baneHVolParm *hvp;
int i, j;
hvp = ( baneHVolParm * )calloc( 1, sizeof( baneHVolParm ) );
if ( hvp ) {
hvp->verbose = baneDefVerbose;
hvp->makeMeasrVol = baneDefMakeMeasrVol;
hvp->measrVol = NULL;
hvp->measrVolDone = AIR_FALSE;
_baneAxisInit( hvp->axis + 0 );
_baneAxisInit( hvp->axis + 1 );
_baneAxisInit( hvp->axis + 2 );
hvp->k3pack = AIR_TRUE;
for( i=gageKernelUnknown+1; i<gageKernelLast; i++ ) {
hvp->k[i] = NULL;
for ( j=0; j<NRRD_KERNEL_PARMS_NUM; j++ )
hvp->kparm[i][j] = AIR_NAN;
}
hvp->renormalize = baneDefRenormalize;
hvp->clip = NULL;
hvp->incLimit = baneDefIncLimit;
}
return hvp;
}
void
74 baneHVolParmAxisSet( baneHVolParm *hvp, unsigned int axisIdx,
unsigned int res, baneMeasr *measr, baneInc *inc ) {
if ( hvp && axisIdx <= 2 ) {
_baneAxisEmpty( hvp->axis + axisIdx );
hvp->axis[axisIdx].res = res;
hvp->axis[axisIdx].measr = baneMeasrCopy( measr );
hvp->axis[axisIdx].inc = baneIncCopy( inc );
}
return;
}
void
87 baneHVolParmClipSet( baneHVolParm *hvp, baneClip *clip ) {
if ( hvp && clip ) {
hvp->clip = baneClipNix( hvp->clip );
hvp->clip = baneClipCopy( clip );
}
return;
}
baneHVolParm *
97 baneHVolParmNix( baneHVolParm *hvp ) {
if ( hvp ) {
if ( hvp->measrVol ) {
nrrdNuke( hvp->measrVol );
}
_baneAxisEmpty( hvp->axis + 0 );
_baneAxisEmpty( hvp->axis + 1 );
_baneAxisEmpty( hvp->axis + 2 );
baneClipNix( hvp->clip );
free( hvp );
}
return NULL;
}
/*
******** baneHVolParmGKMSInit( )
**
** The way Gordon does it.
*/
void
118 baneHVolParmGKMSInit( baneHVolParm *hvp ) {
baneMeasr *measr;
baneInc *inc;
double parm[BANE_PARM_NUM];
if ( hvp ) {
/* no parms to set */
measr = baneMeasrNew( baneMeasrGradMag, parm );
parm[0] = 1024;
parm[1] = 0.15;
inc = baneIncNew( baneIncPercentile, measr->range, parm );
baneHVolParmAxisSet( hvp, 0, 256, measr, inc );
measr = baneMeasrNix( measr );
inc = baneIncNix( inc );
/* no parms to set */
measr = baneMeasrNew( baneMeasr2ndDD, parm );
parm[0] = 1024;
parm[1] = 0.25;
inc = baneIncNew( baneIncPercentile, measr->range, parm );
baneHVolParmAxisSet( hvp, 1, 256, measr, inc );
measr = baneMeasrNix( measr );
inc = baneIncNix( inc );
/* no parms to set */
measr = baneMeasrNew( baneMeasrValueAnywhere, parm );
parm[0] = 1.0;
inc = baneIncNew( baneIncRangeRatio, measr->range, parm );
baneHVolParmAxisSet( hvp, 2, 256, measr, inc );
measr = baneMeasrNix( measr );
inc = baneIncNix( inc );
nrrdKernelParse( &( hvp->k[gageKernel00] ), hvp->kparm[gageKernel00],
"cubic:0, 0.5" ); /* catmull-rom */
nrrdKernelParse( &( hvp->k[gageKernel11] ), hvp->kparm[gageKernel11],
"cubicd:1, 0" ); /* b-spline */
nrrdKernelParse( &( hvp->k[gageKernel22] ), hvp->kparm[gageKernel22],
"cubicdd:1, 0" ); /* b-spline */
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
int
28 _baneRangePositive_Answer( double *ominP, double *omaxP,
double imin, double imax ) {
static const char me[]="_baneRangePositive_Answer";
if ( !( AIR_EXISTS( imin ) && AIR_EXISTS( imax ) ) ) {
biffAddf( BANE, "%s: imin and imax don't both exist", me );
return 1;
}
*ominP = 0;
*omaxP = imax;
return 0;
}
int
42 _baneRangeNegative_Answer( double *ominP, double *omaxP,
double imin, double imax ) {
static const char me[]="_baneRangeNegative_Answer";
if ( !( AIR_EXISTS( imin ) && AIR_EXISTS( imax ) ) ) {
biffAddf( BANE, "%s: imin and imax don't both exist", me );
return 1;
}
*ominP = imin;
*omaxP = 0;
return 0;
}
/*
** _baneRangeZeroCentered_Answer
**
** Unlike the last version of this function, this is conservative: we
** choose the smallest zero-centered range that includes the original
** min and max. Previously the average of the min and max magnitude
** were used.
*/
int
64 _baneRangeZeroCentered_Answer( double *ominP, double *omaxP,
double imin, double imax ) {
static const char me[]="_baneRangeZeroCentered_Answer";
if ( !( AIR_EXISTS( imin ) && AIR_EXISTS( imax ) ) ) {
biffAddf( BANE, "%s: imin and imax don't both exist", me );
return 1;
}
imin = AIR_MIN( imin, 0 );
imax = AIR_MAX( imax, 0 );
/* now the signs of imin and imax aren't wrong */
*ominP = AIR_MIN( -imax, imin );
*omaxP = AIR_MAX( imax, -imin );
return 0;
}
int
81 _baneRangeAnywhere_Answer( double *ominP, double *omaxP,
double imin, double imax ) {
static const char me[]="_baneRangeAnywhere_Answer";
if ( !( AIR_EXISTS( imin ) && AIR_EXISTS( imax ) ) ) {
biffAddf( BANE, "%s: imin and imax don't both exist", me );
return 1;
}
*ominP = imin;
*omaxP = imax;
return 0;
}
baneRange *
95 baneRangeNew( int type ) {
static const char me[]="baneRangeNew";
baneRange *range = NULL;
if ( !AIR_IN_OP( baneRangeUnknown, type, baneRangeLast ) ) {
biffAddf( BANE, "%s: baneRange %d not valid", me, type );
return NULL;
}
range = ( baneRange * )calloc( 1, sizeof( baneRange ) );
if ( !range ) {
biffAddf( BANE, "%s: couldn't allocate baneRange!", me );
return NULL;
}
range->type = type;
range->center = AIR_NAN;
switch( type ) {
case baneRangePositive:
sprintf( range->name, "positive" );
range->answer = _baneRangePositive_Answer;
break;
case baneRangeNegative:
sprintf( range->name, "negative" );
range->answer = _baneRangeNegative_Answer;
break;
case baneRangeZeroCentered:
sprintf( range->name, "zero-centered" );
range->answer = _baneRangeZeroCentered_Answer;
break;
case baneRangeAnywhere:
sprintf( range->name, "anywhere" );
range->answer = _baneRangeAnywhere_Answer;
break;
default:
biffAddf( BANE, "%s: Sorry, baneRange %d not implemented", me, type );
baneRangeNix( range ); return NULL;
}
return range;
}
baneRange *
135 baneRangeCopy( baneRange *range ) {
static const char me[]="baneRangeCopy";
baneRange *ret = NULL;
ret = baneRangeNew( range->type );
if ( !ret ) {
biffAddf( BANE, "%s: couldn't make new range", me );
return NULL;
}
ret->center = range->center;
return ret;
}
int
149 baneRangeAnswer( baneRange *range,
double *ominP, double *omaxP,
double imin, double imax ) {
static const char me[]="baneRangeAnswer";
if ( !( range && ominP && omaxP ) ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
if ( range->answer( ominP, omaxP, imin, imax ) ) {
biffAddf( BANE, "%s: trouble", me );
return 1;
}
return 0;
}
baneRange *
166 baneRangeNix( baneRange *range ) {
if ( range ) {
airFree( range );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
int
29 baneRawScatterplots( Nrrd *nvg, Nrrd *nvh, Nrrd *hvol, int histEq ) {
Nrrd *gA, *hA, *gB, *hB;
static const char me[]="baneRawScatterplots";
int E;
if ( !( nvg && nvh && hvol ) ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
if ( baneHVolCheck( hvol ) ) {
biffAddf( BANE, "%s: didn't get a valid histogram volume", me );
return 1;
}
gA = nrrdNew( ); gB = nrrdNew( );
hA = nrrdNew( ); hB = nrrdNew( );
/* create initial projections */
E = 0;
if ( !E ) E |= nrrdProject( gA, hvol, 1, nrrdMeasureSum, nrrdTypeDefault );
if ( !E ) E |= nrrdProject( hA, hvol, 0, nrrdMeasureSum, nrrdTypeDefault );
if ( E ) {
biffMovef( BANE, NRRD, "%s: trouble creating raw scatterplots", me );
return 1;
}
/* do histogram equalization on them */
if ( histEq ) {
if ( !E ) E |= nrrdHistoEq( gB, gA, NULL, baneStateHistEqBins,
baneStateHistEqSmart, 1.0 );
if ( !E ) E |= nrrdHistoEq( hB, hA, NULL, baneStateHistEqBins,
baneStateHistEqSmart, 1.0 );
} else {
if ( !E ) E |= nrrdCopy( gB, gA );
if ( !E ) E |= nrrdCopy( hB, hA );
}
if ( E ) {
biffMovef( BANE, NRRD, "%s: couldn't histogram equalize or copy", me );
return 1;
}
/* re-orient them so they look correct on the screen */
if ( !E ) E |= nrrdAxesSwap( gA, gB, 0, 1 );
if ( !E ) E |= nrrdAxesSwap( hA, hB, 0, 1 );
if ( !E ) E |= nrrdFlip( gB, gA, 1 );
if ( !E ) E |= nrrdFlip( hB, hA, 1 );
if ( E ) {
biffMovef( BANE, NRRD, "%s: couldn't re-orient scatterplots", me );
return 1;
}
if ( !E ) E |= nrrdCopy( nvg, gB );
if ( !E ) E |= nrrdCopy( nvh, hB );
if ( E ) {
biffMovef( BANE, NRRD, "%s: trouble saving results to given nrrds", me );
return 1;
}
nrrdNuke( gA ); nrrdNuke( gB );
nrrdNuke( hA ); nrrdNuke( hB );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
29 usage( ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <nin> <measr> <nout>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
int measr;
char *iStr, *mStr, *oStr;
Nrrd *nin, *nout;
me = argv[0];
if ( 4 != argc )
usage( );
iStr = argv[1];
mStr = argv[2];
oStr = argv[3];
if ( nrrdLoad( nin=nrrdNew( ), iStr ) ) {
fprintf( stderr, "%s: trouble reading input nrrd:\n%s\n", me,
biffGet( NRRD ) );
usage( );
}
if ( 1 != sscanf( mStr, "%d", &measr ) ) {
fprintf( stderr, "%s: couldn't parse %s as int\n", me, mStr );
usage( );
}
if ( baneApplyMeasr( nout = nrrdNew( ), nin, measr ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, biffGet( BANE ) );
exit( 1 );
}
if ( nrrdSave( oStr, nout, NULL ) ) {
fprintf( stderr, "%s: trouble writing output nrrd:\n%s\n", me,
biffGet( NRRD ) );
usage( );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
28 usage( ) {
/* 0 1 2 3 4 5 ( 6 ) */
fprintf( stderr, "usage: %s <bIn> <sigma> <gthresh> <info2In> <trnsfOut>\n",
me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
char *iStr, *bStr, *pStr, *sStr, *gStr, *tStr;
FILE *file;
Nrrd *info2, *Bcpts;
dirtTrnsf *trnsf;
float sigma, gthresh;
me = argv[0];
if ( argc != 6 )
usage( );
bStr = argv[1];
sStr = argv[2];
gStr = argv[3];
iStr = argv[4];
tStr = argv[5];
if ( !( file = fopen( iStr, "r" ) ) ) {
fprintf( stderr, "%s: couldn't open info2 %s for reading\n", me, iStr );
usage( );
}
if ( !( info2 = nrrdNewRead( file ) ) ) {
fprintf( stderr, "%s: trouble reading info 2%s:\n%s\n", me, iStr,
biffGet( NRRD ) );
usage( );
}
fclose( file );
if ( !( file = fopen( bStr, "r" ) ) ) {
fprintf( stderr, "%s: couldn't open b( x ) %s for reading\n", me, bStr );
usage( );
}
if ( !( Bcpts = nrrdNewRead( file ) ) ) {
fprintf( stderr, "%s: trouble reading b( x ) %s:\n%s\n", me,
bStr, biffGet( NRRD ) );
usage( );
}
fclose( file );
if ( !strcmp( sStr, "nan" ) ) {
sigma = airNanf( );
}
else {
if ( 1 != sscanf( sStr, "%g", &sigma ) ) {
fprintf( stderr, "%s: couldn't parse sigma %s\n", me, sStr );
usage( );
}
}
if ( 1 != sscanf( gStr, "%g", >hresh ) ) {
fprintf( stderr, "%s: couldn't parse gthresh %s\n", me, gStr );
usage( );
}
trnsf = baneNewDirtTrnsf( Bcpts, sigma, gthresh, info2 );
if ( !trnsf ) {
fprintf( stderr, "%s: trouble computing opacity functions:\n%s\n", me,
biffGet( BANE ) );
exit( 1 );
}
if ( !( file = fopen( tStr, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing\n", me, tStr );
usage( );
}
if ( dirtTrnsfWrite( file, trnsf ) ) {
fprintf( stderr, "%s: trouble writing trnsf to %s\n:%s\n", me,
tStr, biffGet( DIRT ) );
usage( );
}
fclose( file );
nrrdNuke( info2 );
dirtNixTrnsf( trnsf );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
28 usage( ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <bIn> <pIn> <opacOut>\n", me );
exit( 1 );
}
int
35 main( int argc, char *argv[] ) {
char *bStr, *pStr, *oStr;
Nrrd *b, *p, *o;
me = argv[0];
if ( argc != 4 )
usage( );
bStr = argv[1];
pStr = argv[2];
oStr = argv[3];
if ( nrrdLoad( b=nrrdNew( ), bStr, NULL ) ) {
fprintf( stderr, "%s: trouble reading %s:\n%s\n", me, bStr, biffGet( NRRD ) );
usage( );
}
if ( nrrdLoad( p=nrrdNew( ), pStr, NULL ) ) {
fprintf( stderr, "%s: trouble reading %s:\n%s\n", me, pStr, biffGet( NRRD ) );
usage( );
}
if ( baneOpacCalc( o = nrrdNew( ), b, p ) ) {
fprintf( stderr, "%s: trouble calculating opac:\n%s", me, biffGet( BANE ) );
exit( 1 );
}
if ( nrrdSave( oStr, o, NULL ) ) {
fprintf( stderr, "%s: trouble writing %s:\n%s\n", me, oStr, biffGet( NRRD ) );
exit( 1 );
}
nrrdNuke( o );
nrrdNuke( b );
nrrdNuke( p );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
29 usage( ) {
/* 0 1 2 3 4 ( 5 ) */
fprintf( stderr, "usage: %s <infoIn> <sigma> <gthresh> <posOut>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
Nrrd *info, *pos;
float sigma, gthresh;
char *iStr, *oStr, *sigStr, *gthStr;
me = argv[0];
if ( argc != 5 ) {
usage( );
}
iStr = argv[1];
sigStr = argv[2];
gthStr = argv[3];
oStr = argv[4];
if ( 1 != sscanf( sigStr, "%g", &sigma ) ||
1 != sscanf( gthStr, "%g", >hresh ) ) {
fprintf( stderr, "%s: couldn't parse %s and %s as floats\n", me,
sigStr, gthStr );
usage( );
}
if ( nrrdLoad( info=nrrdNew( ), iStr, NULL ) ) {
fprintf( stderr, "%s: trouble reading \"%s\" :\n%s\n", me,
iStr, biffGet( NRRD ) );
exit( 1 );
}
if ( banePosCalc( pos = nrrdNew( ), sigma, gthresh, info ) ) {
fprintf( stderr, "%s: trouble calculating %s:\n%s\n", me,
2 == info->dim ? "p( v, g )" : "p( v )", biffGet( BANE ) );
exit( 1 );
}
if ( nrrdSave( oStr, pos, NULL ) ) {
fprintf( stderr, "%s: trouble writing output to \"%s\"\n", me, oStr );
exit( 1 );
}
nrrdNuke( info );
nrrdNuke( pos );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
29 usage( ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <pos2DIn> <ppmOut>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
FILE *file;
char *posStr, *ppmStr;
Nrrd *pos, *ppm;
float *posData, p, min, max, sml, cwght, cidxf;
unsigned char *ppmData, *rgb;
int v, g, sv, sg, idx, smlIdx, /* donLen = 23, */ cidx;
unsigned char don[] = {0, 0, 0, /* background: black */
/* 1 */ 0, 107, 255, /* start: blue */
51, 104, 255,
103, 117, 255,
123, 124, 255,
141, 130, 255,
156, 132, 255,
166, 131, 245,
174, 131, 231,
181, 130, 216,
187, 130, 201,
/* 11 */ 255, 255, 255, /* middle: white */
/* 12 */ 255, 255, 255,
187, 130, 201,
192, 129, 186,
197, 128, 172,
200, 128, 158,
204, 127, 142,
210, 126, 113,
212, 126, 98,
213, 126, 84,
216, 126, 49,
/* 22 */ 220, 133, 0}; /* end: orange */
me = argv[0];
if ( 3 != argc )
usage( );
posStr = argv[1];
ppmStr = argv[2];
if ( !( file = fopen( posStr, "r" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for reading\n", me, posStr );
usage( );
}
if ( !( pos = nrrdNewRead( file ) ) ) {
fprintf( stderr, "%s: couldn't read pos from %s:\n%s\n", me, posStr,
biffGet( NRRD ) );
usage( );
}
fclose( file );
if ( !baneValidPos( pos, 2 ) ) {
fprintf( stderr, "%s: %s isn't a valid p( v, g ) file:\n%s\n", me, posStr,
biffGet( BANE ) );
usage( );
}
sv = pos->size[0];
sg = pos->size[1];
posData = ( float* )( pos->data );
/* assert that min = -max; */
min = max = AIR_NAN;
for ( g=0; g<=sg-1; g++ ) {
for ( v=0; v<=sv-1; v++ ) {
idx = v + sv*g;
p = posData[idx];
if ( !AIR_EXISTS( p ) )
continue;
if ( !AIR_EXISTS( min ) ) {
min = max = p;
sml = AIR_ABS( p );
}
min = AIR_MIN( p, min );
max = AIR_MAX( p, max );
if ( AIR_ABS( p ) < sml ) {
sml = AIR_ABS( p );
smlIdx = idx;
}
}
}
printf( "%s: pos range: [%g, %g, %g]\n", me, min, sml, max );
posData[smlIdx] = 0;
if ( nrrdHistoEq( pos, NULL, 2048, 3 ) ) {
fprintf( stderr, "%s: trouble doing histeq on p( v, g ):\n%s\n", me,
biffGet( NRRD ) );
exit( 1 );
}
if ( !( ppm = nrrdNewPPM( sv, sg ) ) ) {
fprintf( stderr, "%s: couldn't make %dx%d PPM:\n%s\n", me, sv, sg,
biffGet( NRRD ) );
exit( 1 );
}
ppmData = ( unsigned char * )( ppm->data );
sml = posData[smlIdx];
for ( g=0; g<=sg-1; g++ ) {
for ( v=0; v<=sv-1; v++ ) {
idx = v + sv*g;
p = posData[idx];
rgb = ppmData + 3*( v + sv*( sg-1-g ) );
if ( !AIR_EXISTS( p ) ) {
rgb[0] = don[0];
rgb[1] = don[1];
rgb[2] = don[2];
continue;
}
if ( p > sml ) {
cidxf = AIR_AFFINE( sml, p, max, 11.5, 21.999 );
cidx = cidxf;
cwght = cidxf - cidx;
rgb[0] = AIR_AFFINE( 0, cwght, 1, don[0+3*cidx], don[0+3*( cidx+1 )] );
rgb[1] = AIR_AFFINE( 0, cwght, 1, don[1+3*cidx], don[1+3*( cidx+1 )] );
rgb[2] = AIR_AFFINE( 0, cwght, 1, don[2+3*cidx], don[2+3*( cidx+1 )] );
}
else {
cidxf = AIR_AFFINE( min, p, sml, 1, 11.5 );
cidx = cidxf;
cwght = cidxf - cidx;
rgb[0] = AIR_AFFINE( 0, cwght, 1, don[0+3*cidx], don[0+3*( cidx+1 )] );
rgb[1] = AIR_AFFINE( 0, cwght, 1, don[1+3*cidx], don[1+3*( cidx+1 )] );
rgb[2] = AIR_AFFINE( 0, cwght, 1, don[2+3*cidx], don[2+3*( cidx+1 )] );
}
}
}
if ( !( file = fopen( ppmStr, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing\n", me, ppmStr );
exit( 1 );
}
ppm->encoding = nrrdEncodingRaw;
if ( nrrdWritePNM( file, ppm ) ) {
fprintf( stderr, "%s: trouble writing ppm:\n%s\n", me, biffGet( NRRD ) );
exit( 1 );
}
fclose( file );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
int
27 main( int argc, char *argv[] ) {
Nrrd *info;
float sigma;
AIR_UNUSED( argc );
if ( nrrdLoad( info=nrrdNew( ), argv[1], NULL ) ) {
fprintf( stderr, "trouble:\n%s\n", biffGet( BANE ) );
}
if ( baneSigmaCalc( &sigma, info ) ) {
fprintf( stderr, "trouble:\n%s\n", biffGet( BANE ) );
}
printf( "%g\n", sigma );
nrrdNuke( info );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
28 rangeTest( char *me, double imin, double imax ) {
double omin, omax;
baneRange *range;
int i;
printf( "input range ( %g, %g ) ---------------------\n", imin, imax );
for ( i=1; i<baneRangeLast; i++ ) {
range = baneRangeNew( i );
range->answer( &omin, &omax, imin, imax );
printf( "%s: range %s --> ( %g, %g )\n",
me, range->name, omin, omax );
range = baneRangeNix( range );
}
}
void
44 incTest( char *me, int num, baneRange *range ) {
double *val, tmp, incParm[BANE_PARM_NUM], omin, omax, rmin, rmax;
baneInc *inc;
Nrrd *hist;
int i, j;
airSrand48( );
val = ( double* )malloc( num*sizeof( double ) );
/* from <http://www.itl.nist.gov/div898/handbook/index.htm>:
the standard dev of a uniform distribution between A and B is
sqrt( ( B-A )^2/12 ) */
for ( j=0; j<num; j++ ) {
tmp = AIR_AFFINE( 0.0, airDrand48( ), 1.0, -1.0, 1.0 );
/* val[j] = tmp*tmp*tmp; */
val[j] = tmp;
}
rmin = rmax = val[0];
for ( j=0; j<num; j++ ) {
rmin = AIR_MIN( rmin, val[j] );
rmax = AIR_MAX( rmax, val[j] );
}
fprintf( stderr, "incTest: real min, max = %g, %g\n", rmin, rmax );
for ( i=1; i<baneIncLast; i++ ) {
/* NOTE: THIS IS BROKEN !!! */
inc = baneIncNew( i, NULL, incParm );
printf( "%s: inclusion %s ------\n", me, inc->name );
switch( i ) {
case baneIncAbsolute:
ELL_3V_SET( incParm, -0.8, 1.5, AIR_NAN );
break;
case baneIncRangeRatio:
ELL_3V_SET( incParm, 0.99, AIR_NAN, AIR_NAN );
break;
case baneIncPercentile:
ELL_3V_SET( incParm, 1024, 10, AIR_NAN );
break;
case baneIncStdv:
ELL_3V_SET( incParm, 1.0, AIR_NAN, AIR_NAN );
break;
}
fprintf( stderr, "!%s: THIS IS BROKEN!!!\n", "incTest" );
/*
if ( inc->passA ) {
for ( j=0; j<num; j++ )
inc->process[0]( hist, val[j], incParm );
}
if ( inc->passB ) {
for ( j=0; j<num; j++ )
inc->process[1]( hist, val[j], incParm );
}
inc->ans( &omin, &omax, hist, incParm, range );
*/
printf( " --> ( %g, %g )\n", omin, omax );
}
free( val );
}
int
105 main( int argc, char *argv[] ) {
me = argv[0];
printf( "%s ================================ range testing\n", me );
rangeTest( me, -1, 2 );
rangeTest( me, -3, 2 );
printf( "%s ================================ inclusion testing\n", me );
incTest( me, 10000, baneRangeFloat );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../bane.h"
char *me;
void
29 usage( ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <hvolin> <dim> <nout>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
Nrrd *hvol, *info;
char *iStr, *dStr, *oStr;
int dim;
me = argv[0];
if ( argc != 4 )
usage( );
iStr = argv[1];
dStr = argv[2];
oStr = argv[3];
if ( nrrdLoad( hvol=nrrdNew( ), iStr, NULL ) ) {
fprintf( stderr, "%s: trouble reading hvol:\n%s\n", me, biffGet( NRRD ) );
usage( );
}
if ( 1 != sscanf( dStr, "%d", &dim ) ) {
fprintf( stderr, "%s: trouble parsing %s as an int\n", me, dStr );
usage( );
}
if ( baneOpacInfo( info = nrrdNew( ), hvol, dim, nrrdMeasureHistoMean ) ) {
fprintf( stderr, "%s: trouble calculting %d-D opacity info:\n%s\n",
me, dim, biffGet( BANE ) );
exit( 1 );
}
if ( nrrdSave( oStr, info, NULL ) ) {
fprintf( stderr, "%s: trouble saving nrrd to %s:\n%s\n", me, oStr,
biffGet( NRRD ) );
exit( 1 );
}
nrrdNuke( hvol );
nrrdNuke( info );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
/* learned:
** NEVER EVER EVER bypass your own damn pseudo-constructors!
** "npos" used to be a Nrrd ( not a pointer ), and Joe's
** trex stuff was crashing because the if data free( data ) in nrrd Alloc
** was freeing random stuff, but ( and this is the weird part )
** only on some 1-D nrrds of 256 floats ( pos1D info ), and not others.
*/
Nrrd *baneNpos=NULL;
#define TREX_LUTLEN 256
float _baneTesting[256]={0};
float *
41 _baneTRexRead( char *fname ) {
char me[]="_baneTRexRead";
if ( nrrdLoad( baneNpos=nrrdNew( ), fname, NULL ) ) {
fprintf( stderr, "%s: !!! trouble reading \"%s\":\n%s\n", me,
fname, biffGet( NRRD ) );
return NULL;
}
if ( banePosCheck( baneNpos, 1 ) ) {
fprintf( stderr, "%s: !!! didn't get a valid p( x ) file:\n%s\n", me,
biffGet( BANE ) );
return NULL;
}
if ( TREX_LUTLEN != baneNpos->axis[0].size ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: !!! need a length %d p( x ) ( not %s )\n", me,
TREX_LUTLEN, airSprintSize_t( stmp, baneNpos->axis[0].size ) );
return NULL;
}
return ( float * )baneNpos->data;
}
void
65 _baneTRexDone( ) {
nrrdNuke( baneNpos );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
#define BIFF_NULL "%s: got NULL pointer"
#define BIFF_NRRDALLOC "%s: couldn't allocate output nrrd"
int
32 baneOpacInfo( Nrrd *info, Nrrd *hvol, int dim, int measr ) {
static const char me[]="baneOpacInfo";
Nrrd *proj2, *proj1, *projT;
float *data2D, *data1D;
int i, len, sv, sg;
if ( !( info && hvol ) ) {
biffAddf( BANE, BIFF_NULL, me ); return 1;
}
if ( !( 1 == dim || 2 == dim ) ) {
biffAddf( BANE, "%s: got dimension %d, not 1 or 2", me, dim );
return 1;
}
if ( !( nrrdMeasureHistoMin == measr ||
nrrdMeasureHistoMax == measr ||
nrrdMeasureHistoMean == measr ||
nrrdMeasureHistoMedian == measr ||
nrrdMeasureHistoMode == measr ) ) {
biffAddf( BANE, "%s: measure %d doesn't make sense for histovolume",
me, dim );
return 1;
}
if ( baneHVolCheck( hvol ) ) {
biffAddf( BANE, "%s: given nrrd doesn't seem to be a histogram volume", me );
return 1;
}
if ( 1 == dim ) {
len = hvol->axis[2].size;
if ( nrrdMaybeAlloc_va( info, nrrdTypeFloat, 2,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, len ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me );
return 1;
}
info->axis[1].min = hvol->axis[2].min;
info->axis[1].max = hvol->axis[2].max;
data1D = ( float * )info->data;
/* sum up along 2nd deriv for each data value, grad mag */
if ( nrrdProject( proj2=nrrdNew( ), hvol, 1,
nrrdMeasureSum, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting out 2nd deriv. for g( v )", me );
return 1;
}
/* now determine average gradient at each value ( 0: grad, 1: value ) */
if ( nrrdProject( proj1=nrrdNew( ), proj2, 0, measr, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting along gradient for g( v )", me );
return 1;
}
for ( i=0; i<len; i++ ) {
data1D[0 + 2*i] = nrrdFLookup[proj1->type]( proj1->data, i );
}
nrrdNuke( proj1 );
nrrdNuke( proj2 );
/* sum up along gradient for each data value, 2nd deriv */
if ( nrrdProject( proj2 = nrrdNew( ), hvol, 0,
nrrdMeasureSum, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting out gradient for h( v )", me );
return 1;
}
/* now determine average gradient at each value ( 0: 2nd deriv, 1: value ) */
if ( nrrdProject( proj1 = nrrdNew( ), proj2, 0, measr, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting along 2nd deriv. for h( v )", me );
return 1;
}
for ( i=0; i<len; i++ ) {
data1D[1 + 2*i] = nrrdFLookup[proj1->type]( proj1->data, i );
}
nrrdNuke( proj1 );
nrrdNuke( proj2 );
}
else {
/* 2 == dim */
/* hvol axes: 0: grad, 1: 2nd deriv: 2: data value */
sv = hvol->axis[2].size;
sg = hvol->axis[0].size;
if ( nrrdMaybeAlloc_va( info, nrrdTypeFloat, 3,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, sv ),
AIR_CAST( size_t, sg ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me );
return 1;
}
info->axis[1].min = hvol->axis[2].min;
info->axis[1].max = hvol->axis[2].max;
info->axis[2].min = hvol->axis[0].min;
info->axis[2].max = hvol->axis[0].max;
data2D = ( float * )info->data;
/* first create h( v, g ) */
if ( nrrdProject( proj2=nrrdNew( ), hvol, 1, measr, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting ( step 1 ) to create h( v, g )", me );
return 1;
}
if ( nrrdAxesSwap( projT=nrrdNew( ), proj2, 0, 1 ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting ( step 2 ) to create h( v, g )", me );
return 1;
}
for ( i=0; i<sv*sg; i++ ) {
data2D[0 + 2*i] = nrrdFLookup[projT->type]( projT->data, i );
}
nrrdNuke( proj2 );
nrrdNuke( projT );
/* then create #hits( v, g ) */
if ( nrrdProject( proj2=nrrdNew( ), hvol, 1,
nrrdMeasureSum, nrrdTypeDefault ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting ( step 1 ) to create #( v, g )", me );
return 1;
}
if ( nrrdAxesSwap( projT=nrrdNew( ), proj2, 0, 1 ) ) {
biffMovef( BANE, NRRD,
"%s: trouble projecting ( step 2 ) to create #( v, g )", me );
return 1;
}
for ( i=0; i<sv*sg; i++ ) {
data2D[1 + 2*i] = nrrdFLookup[projT->type]( projT->data, i );
}
nrrdNuke( proj2 );
nrrdNuke( projT );
}
return 0;
}
int
165 bane1DOpacInfoFrom2D( Nrrd *info1D, Nrrd *info2D ) {
static const char me[]="bane1DOpacInfoFrom2D";
Nrrd *projH2=NULL, *projH1=NULL, *projN=NULL, *projG1=NULL;
float *data1D;
int i, len;
if ( !( info1D && info2D ) ) {
biffAddf( BANE, BIFF_NULL, me ); return 1;
}
if ( baneInfoCheck( info2D, 2 ) ) {
biffAddf( BANE, "%s: didn't get valid 2D info", me );
return 1;
}
len = info2D->axis[1].size;
if ( nrrdProject( projH2=nrrdNew( ), info2D, 0,
nrrdMeasureProduct, nrrdTypeDefault )
|| nrrdProject( projH1=nrrdNew( ), projH2, 1,
nrrdMeasureSum, nrrdTypeDefault )
|| nrrdProject( projN=nrrdNew( ), info2D, 2,
nrrdMeasureSum, nrrdTypeDefault )
|| nrrdProject( projG1=nrrdNew( ), info2D, 2,
nrrdMeasureHistoMean, nrrdTypeDefault ) ) {
biffAddf( BANE, "%s: trouble creating needed projections", me );
return 1;
}
if ( nrrdMaybeAlloc_va( info1D, nrrdTypeFloat, 2,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, len ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me );
return 1;
}
info1D->axis[1].min = info2D->axis[1].min;
info1D->axis[1].max = info2D->axis[1].max;
data1D = ( float * )info1D->data;
for ( i=0; i<len; i++ ) {
data1D[0 + 2*i] = nrrdFLookup[projG1->type]( projG1->data, 1 + 2*i );
data1D[1 + 2*i] = ( nrrdFLookup[projH1->type]( projH1->data, i ) /
nrrdFLookup[projN->type]( projN->data, 1 + 2*i ) );
}
nrrdNuke( projH2 );
nrrdNuke( projH1 );
nrrdNuke( projN );
nrrdNuke( projG1 );
return 0;
}
int
215 _baneSigmaCalc1D( float *sP, Nrrd *info1D ) {
static const char me[]="_baneSigmaCalc1D";
int i, len;
float maxg, maxh, minh, *data;
len = info1D->axis[1].size;
data = ( float * )info1D->data;
maxg = -1;
maxh = -1;
minh = 1;
for ( i=0; i<len; i++ ) {
if ( AIR_EXISTS( data[0 + 2*i] ) )
maxg = AIR_MAX( maxg, data[0 + 2*i] );
if ( AIR_EXISTS( data[1 + 2*i] ) ) {
minh = AIR_MIN( minh, data[1 + 2*i] );
maxh = AIR_MAX( maxh, data[1 + 2*i] );
}
}
if ( maxg == -1 || maxh == -1 ) {
biffAddf( BANE, "%s: saw only NaNs in 1D info!", me );
return 1;
}
/* here's the actual calculation: from page 54 of GK's MS */
/* This is after the typo report by Fernando Vega Higuera;
the previous version of the code had a bug caused by
mindless transcription of the erroneous equation 5.8 */
*sP = AIR_CAST( float, 2*maxg/( sqrt( AIR_E )*( maxh - minh ) ) );
return 0;
}
int
248 baneSigmaCalc( float *sP, Nrrd *_info ) {
static const char me[]="baneSigmaCalc";
Nrrd *info;
if ( !( sP && _info ) ) {
biffAddf( BANE, BIFF_NULL, me ); return 1;
}
if ( baneInfoCheck( _info, 0 ) ) {
biffAddf( BANE, "%s: didn't get a valid info", me );
return 1;
}
if ( 3 == _info->dim ) {
if ( bane1DOpacInfoFrom2D( info = nrrdNew( ), _info ) ) {
biffAddf( BANE, "%s: couldn't create 1D opac info from 2D", me );
return 1;
}
}
else {
info = _info;
}
if ( _baneSigmaCalc1D( sP, info ) ) {
biffAddf( BANE, "%s: trouble calculating sigma", me );
return 1;
}
if ( _info != info ) {
nrrdNuke( info );
}
return 0;
}
int
279 banePosCalc( Nrrd *pos, float sigma, float gthresh, Nrrd *info ) {
static const char me[]="banePosCalc";
int d, i, len, vi, gi, sv, sg;
float *posData, *infoData, h, g, p;
if ( !( pos && info ) ) {
biffAddf( BANE, BIFF_NULL, me ); return 1;
}
if ( baneInfoCheck( info, 0 ) ) {
biffAddf( BANE, "%s: didn't get a valid info", me );
return 1;
}
d = info->dim-1;
if ( 1 == d ) {
len = info->axis[1].size;
if ( nrrdMaybeAlloc_va( pos, nrrdTypeFloat, 1,
AIR_CAST( size_t, len ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me );
return 1;
}
pos->axis[0].min = info->axis[1].min;
pos->axis[0].max = info->axis[1].max;
posData = ( float * )pos->data;
infoData = ( float * )info->data;
for ( i=0; i<len; i++ ) {
/* from pg. 55 of GK's MS */
g = infoData[0+2*i];
h = infoData[1+2*i];
if ( AIR_EXISTS( g ) && AIR_EXISTS( h ) )
p = -sigma*sigma*h/AIR_MAX( 0, g-gthresh );
else
p = AIR_NAN;
p = airIsInf_f( p ) ? 10000 : p;
posData[i] = p;
}
}
else {
/* 2 == d */
sv = info->axis[1].size;
sg = info->axis[2].size;
if ( nrrdMaybeAlloc_va( pos, nrrdTypeFloat, 2,
AIR_CAST( size_t, sv ),
AIR_CAST( size_t, sg ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me ); return 1;
}
pos->axis[0].min = info->axis[1].min;
pos->axis[0].max = info->axis[1].max;
pos->axis[1].min = info->axis[2].min;
pos->axis[1].max = info->axis[2].max;
posData = ( float * )pos->data;
for ( gi=0; gi<sg; gi++ ) {
g = AIR_CAST( float, AIR_AFFINE( 0, gi, sg-1,
info->axis[2].min, info->axis[2].max ) );
for ( vi=0; vi<sv; vi++ ) {
h = nrrdFLookup[info->type]( info->data, 0 + 2*( vi + sv*gi ) );
/* from pg. 61 of GK's MS */
if ( AIR_EXISTS( h ) ) {
p = -sigma*sigma*h/AIR_MAX( 0, g-gthresh );
}
else {
p = AIR_NAN;
}
p = airIsInf_f( p ) ? AIR_NAN : p;
posData[vi + sv*gi] = p;
}
}
}
return 0;
}
void
350 _baneOpacCalcA( unsigned int lutLen, float *opacLut,
unsigned int numCpts, float *xo,
float *pos ) {
unsigned int i, j;
float p;
for ( i=0; i<lutLen; i++ ) {
p = pos[i];
if ( !AIR_EXISTS( p ) ) {
opacLut[i] = 0;
continue;
}
if ( p <= xo[0 + 2*0] ) {
opacLut[i] = xo[1 + 2*0];
continue;
}
if ( p >= xo[0 + 2*( numCpts-1 )] ) {
opacLut[i] = xo[1 + 2*( numCpts-1 )];
continue;
}
for ( j=1; j<numCpts; j++ )
if ( p < xo[0 + 2*j] )
break;
opacLut[i] = AIR_CAST( float, AIR_AFFINE( xo[0 + 2*( j-1 )], p, xo[0 + 2*j],
xo[1 + 2*( j-1 )], xo[1 + 2*j] ) );
}
/*
for ( i=0; i<numCpts; i++ )
printf( "b( %g ) = %g\n", xo[0+2*i], xo[1+2*i] );
for ( i=0; i<lutLen; i++ )
printf( "p[%d] = %g -> o = %g\n", i, pos[i], opacLut[i] );
*/
}
void
385 _baneOpacCalcB( unsigned int lutLen, float *opacLut,
unsigned int numCpts, float *x, float *o,
float *pos ) {
/* static const char me[]="_baneOpacCalcB"; */
unsigned int i, j;
double p, op;
/*
printf( "%s( %d, %lu, %d, %lu, %lu, %lu ): hello\n", me, lutLen,
( unsigned long )opacLut, numCpts,
( unsigned long )x, ( unsigned long )o,
( unsigned long )pos );
*/
/*
for ( i=0; i<numCpts; i++ ) {
printf( "%s: opac( %g ) = %g\n", me, x[i], o[i] );
}
printf( "----------\n" );
*/
for ( i=0; i<lutLen; i++ ) {
p = pos[i];
/*
printf( "%s: pos[%d] = %g -->", me, i, p ); fflush( stdout );
*/
if ( !AIR_EXISTS( p ) ) {
op = 0;
goto endloop;
}
if ( p <= x[0] ) {
op = o[0];
goto endloop;
}
if ( p >= x[numCpts-1] ) {
op = o[numCpts-1];
goto endloop;
}
for ( j=1; j<numCpts; j++ )
if ( p < x[j] )
break;
op = AIR_AFFINE( x[j-1], p, x[j], o[j-1], o[j] );
endloop:
opacLut[i] = AIR_CAST( float, op );
/*
printf( "opac[%d] = %g\n", i, op );
*/
}
/*
printf( "^^^^^^^^^\n" );
*/
}
int
437 baneOpacCalc( Nrrd *opac, Nrrd *Bcpts, Nrrd *pos ) {
static const char me[]="baneOpacCalc";
int dim, sv, sg, len, npts;
float *bdata, *odata, *pdata;
if ( !( opac && Bcpts && pos ) ) {
biffAddf( BANE, BIFF_NULL, me ); return 1;
}
if ( baneBcptsCheck( Bcpts ) ) {
biffAddf( BANE, "%s: didn't get valid control points for b( x )", me );
return 1;
}
if ( banePosCheck( pos, 0 ) ) {
biffAddf( BANE, "%s: didn't get valid position data", me );
return 1;
}
dim = pos->dim;
if ( 1 == dim ) {
len = pos->axis[0].size;
if ( nrrdMaybeAlloc_va( opac, nrrdTypeFloat, 1,
AIR_CAST( size_t, len ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me ); return 1;
}
opac->axis[0].min = pos->axis[0].min;
opac->axis[0].max = pos->axis[0].max;
odata = ( float * )opac->data;
bdata = ( float * )Bcpts->data;
pdata = ( float * )pos->data;
npts = Bcpts->axis[1].size;
_baneOpacCalcA( len, odata, npts, bdata, pdata );
}
else {
sv = pos->axis[0].size;
sg = pos->axis[1].size;
if ( nrrdMaybeAlloc_va( opac, nrrdTypeFloat, 2,
AIR_CAST( size_t, sv ),
AIR_CAST( size_t, sg ) ) ) {
biffMovef( BANE, NRRD, BIFF_NRRDALLOC, me ); return 1;
}
opac->axis[0].min = pos->axis[0].min;
opac->axis[0].max = pos->axis[0].max;
opac->axis[1].min = pos->axis[1].min;
opac->axis[1].max = pos->axis[1].max;
odata = ( float * )opac->data;
bdata = ( float * )Bcpts->data;
pdata = ( float * )pos->data;
npts = Bcpts->axis[1].size;
_baneOpacCalcA( sv*sg, odata, npts, bdata, pdata );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "bane.h"
#include "privateBane.h"
int
29 baneInputCheck ( Nrrd *nin, baneHVolParm *hvp ) {
static const char me[]="baneInputCheck";
int i;
if ( nrrdCheck( nin ) ) {
biffMovef( BANE, NRRD, "%s: basic nrrd validity check failed", me );
return 1;
}
if ( 3 != nin->dim ) {
biffAddf( BANE, "%s: need a 3-dimensional nrrd ( not %d )", me, nin->dim );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( BANE, "%s: can't operate on block type", me );
return 1;
}
if ( !( AIR_EXISTS( nin->axis[0].spacing ) && nin->axis[0].spacing != 0 &&
AIR_EXISTS( nin->axis[1].spacing ) && nin->axis[1].spacing != 0 &&
AIR_EXISTS( nin->axis[2].spacing ) && nin->axis[2].spacing != 0 ) ) {
biffAddf( BANE, "%s: must have non-zero existent spacing for all 3 axes",
me );
return 1;
}
for ( i=0; i<=2; i++ ) {
if ( _baneAxisCheck( hvp->axis + i ) ) {
biffAddf( BANE, "%s: trouble with axis %d", me, i );
return 1;
}
}
if ( !hvp->clip ) {
biffAddf( BANE, "%s: got NULL baneClip", me );
return 1;
}
/* all okay */
return 0;
}
int
68 baneHVolCheck ( Nrrd *hvol ) {
static const char me[]="baneHVolCheck";
if ( 3 != hvol->dim ) {
biffAddf( BANE, "%s: need dimension to be 3 ( not %d )", me, hvol->dim );
return 1;
}
if ( nrrdTypeUChar != hvol->type ) {
biffAddf( BANE, "%s: need type to be %s ( not %s )",
me, airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, hvol->type ) );
return 1;
}
if ( !( AIR_EXISTS( hvol->axis[0].min ) && AIR_EXISTS( hvol->axis[0].max ) &&
AIR_EXISTS( hvol->axis[1].min ) && AIR_EXISTS( hvol->axis[1].max ) &&
AIR_EXISTS( hvol->axis[2].min ) && AIR_EXISTS( hvol->axis[2].max ) ) ) {
biffAddf( BANE, "%s: axisMin and axisMax must be set for all axes", me );
return 1;
}
/*
** NOTE: For the time being, I'm giving up on enforcing a
** particular kind of histogram volume
if ( strcmp( hvol->axis[0].label, baneMeasrGradMag->name ) ) {
biffAddf( BANE, "%s: expected \"%s\" on axis 0 label",
me, baneMeasrGradMag->name );
return 1;
}
if ( strcmp( hvol->axis[1].label, baneMeasrLapl->name ) &&
strcmp( hvol->axis[1].label, baneMeasrHess->name ) ) {
biffAddf( BANE, "%s: expected a 2nd deriv. measr on axis 1 ( %s or %s )",
me, baneMeasrHess->name, baneMeasrLapl->name );
return 1;
}
if ( strcmp( hvol->axis[2].label, baneMeasrVal->name ) ) {
biffAddf( BANE, "%s: expected \"%s\" on axis 2",
me, baneMeasrVal->name );
return 1;
}
*/
return 0;
}
int
111 baneInfoCheck ( Nrrd *info, int wantDim ) {
static const char me[]="baneInfoCheck";
int gotDim;
if ( !info ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
gotDim = info->dim;
if ( wantDim ) {
if ( !( 1 == wantDim || 2 == wantDim ) ) {
biffAddf( BANE, "%s: wantDim should be 1 or 2, not %d", me, wantDim );
return 1;
}
if ( wantDim+1 != gotDim ) {
biffAddf( BANE, "%s: dim is %d, not %d", me, gotDim, wantDim+1 );
return 1;
}
}
else {
if ( !( 2 == gotDim || 3 == gotDim ) ) {
biffAddf( BANE, "%s: dim is %d, not 2 or 3", me, gotDim );
return 1;
}
}
if ( nrrdTypeFloat != info->type ) {
biffAddf( BANE, "%s: need data of type float", me );
return 1;
}
if ( 2 != info->axis[0].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( BANE, "%s: 1st axis needs size 2 ( not %s )", me,
airSprintSize_t( stmp, info->axis[0].size ) );
return 1;
}
return 0;
}
int
150 banePosCheck ( Nrrd *pos, int wantDim ) {
static const char me[]="banePosCheck";
int gotDim;
if ( !pos ) {
biffAddf( BANE, "%s: got NULL pointer", me );
return 1;
}
gotDim = pos->dim;
if ( wantDim ) {
if ( !( 1 == wantDim || 2 == wantDim ) ) {
biffAddf( BANE, "%s: wantDim should be 1 or 2, not %d", me, wantDim );
return 1;
}
if ( wantDim != gotDim ) {
biffAddf( BANE, "%s: dim is %d, not %d", me, gotDim, wantDim );
return 1;
}
}
else {
if ( !( 1 == gotDim || 2 == gotDim ) ) {
biffAddf( BANE, "%s: dim is %d, not 1 or 2", me, gotDim );
return 1;
}
}
if ( nrrdTypeFloat != pos->type ) {
biffAddf( BANE, "%s: need data of type float", me );
return 1;
}
/* HEY? check for values in axisMin[0] and axisMax[0] ? */
/* HEY? check for values in axisMin[0] and axisMax[0] ? */
/* HEY? check for values in axisMin[1] and axisMax[1] ? */
return 0;
}
int
186 baneBcptsCheck ( Nrrd *Bcpts ) {
static const char me[]="baneBcptsCheck";
int i, len;
float *data;
if ( 2 != Bcpts->dim ) {
biffAddf( BANE, "%s: need 2-dimensional ( not %d )", me, Bcpts->dim );
return 1;
}
if ( 2 != Bcpts->axis[0].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( BANE, "%s: axis#0 needs size 2 ( not %s )", me,
airSprintSize_t( stmp, Bcpts->axis[0].size ) );
return 1;
}
if ( nrrdTypeFloat != Bcpts->type ) {
biffAddf( BANE, "%s: need data of type float", me );
return 1;
}
len = Bcpts->axis[1].size;
data = ( float * )Bcpts->data;
for ( i=0; i<=len-2; i++ ) {
if ( !( data[0 + 2*i] <= data[0 + 2*( i+1 )] ) ) {
biffAddf( BANE, "%s: value coord %d ( %g ) not <= coord %d ( %g )", me,
i, data[0 + 2*i], i+1, data[0 + 2*( i+1 )] );
return 1;
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "biff.h"
#include "privateBiff.h"
/*
** Until Teem has its own printf implementation, this will have to do;
** it is imperfect because these are not functionally identical.
*/
#if defined( WIN32 ) || defined( _WIN32 )
# define snprintf _snprintf
#endif
static biffMsg **
_bmsg=NULL; /* master array of biffMsg pointers */
static unsigned int
_bmsgNum=0; /* length of _biffErr == # keys maintained */
static airArray *
_bmsgArr=NULL; /* air array of _biffErr and _biffNum */
#define __INCR 2
typedef union {
biffMsg ***b;
void **v;
} _beu;
/*
** _bmsgStart( )
**
** allocates data structers needed by biff. Panics if
** anything goes wrong.
**
** NOTE: Can be harmlessly called multiple times.
*/
static void
58 _bmsgStart( void ) {
static const char me[]="[biff] _bmsgStart";
_beu uu;
if ( _bmsgArr ) {
/* its non-NULL, must have been called already */
return;
}
uu.b = &_bmsg;
_bmsgArr = airArrayNew( uu.v, &_bmsgNum, sizeof( biffMsg* ), __INCR );
if ( !_bmsgArr ) {
fprintf( stderr, "%s: PANIC: couldn't allocate internal data\n", me );
/* exit( 1 ); */
}
/* airArrayPointerCB( _bmsgArr, NULL, ( airMopper )biffMsgNix );*/
return;
}
static void
77 _bmsgFinish( void ) {
if ( _bmsgArr ) {
/* setting _bmsgArr to NULL is needed to put biff back in initial state
so that next calls to biff re-trigger _bmsgStart( ) */
_bmsgArr = airArrayNuke( _bmsgArr );
}
return;
}
/*
** _bmsgFind( )
**
** returns the biffMsg ( in _bmsg ) of the entry with the given key, or
** NULL if it was not found
*/
static biffMsg *
94 _bmsgFind( const char *key ) {
static const char me[]="[biff] _bmsgFind";
biffMsg *msg;
unsigned int ii;
if ( !key ) {
fprintf( stderr, "%s: PANIC got NULL key", me );
return NULL; /* exit( 1 ); */
}
msg = NULL;
if ( _bmsgNum ) {
for ( ii=0; ii<_bmsgNum; ii++ ) {
if ( !strcmp( _bmsg[ii]->key, key ) ) {
msg = _bmsg[ii];
break;
}
}
}
return msg;
}
/*
** assumes that msg really is in _bmsg[]
*/
static unsigned int
119 _bmsgFindIdx( biffMsg *msg ) {
unsigned int ii;
for ( ii=0; ii<_bmsgNum; ii++ ) {
if ( msg == _bmsg[ii] ) {
break;
}
}
return ii;
}
/*
** _bmsgAdd( )
**
** if given key already has a biffMsg in _bmsg, returns that.
** otherise, adds a new biffMsg for given key to _bmsg, and returns it
** panics if there is a problem
*/
static biffMsg *
138 _bmsgAdd( const char *key ) {
static const char me[]="[biff] _bmsgAdd";
unsigned int ii;
biffMsg *msg;
msg = NULL;
/* find if key exists already */
for ( ii=0; ii<_bmsgNum; ii++ ) {
if ( !strcmp( key, _bmsg[ii]->key ) ) {
msg = _bmsg[ii];
break;
}
}
if ( !msg ) {
/* have to add new biffMsg */
ii = airArrayLenIncr( _bmsgArr, 1 );
if ( !_bmsg ) {
fprintf( stderr, "%s: PANIC: couldn't accommodate one more key\n", me );
return NULL; /* exit( 1 ); */
}
msg = _bmsg[ii] = biffMsgNew( key );
}
return msg;
}
/***********************************************************************/
/***********************************************************************/
/*
******** biffAdd( )
**
** Adds string "err" at key "key", whether or not there are any
** existing messages there. Since biffSet( ) was killed
** Wed Apr 20 11:11:51 EDT 2005, this has become the main biff
** function.
*/
void
175 biffAdd( const char *key, const char *err ) {
biffMsg *msg;
_bmsgStart( );
msg = _bmsgAdd( key );
biffMsgAdd( msg, err );
return;
}
static void
185 _biffAddVL( const char *key, const char *errfmt, va_list args ) {
biffMsg *msg;
_bmsgStart( );
msg = _bmsgAdd( key );
_biffMsgAddVL( msg, errfmt, args );
return;
}
/*
******** biffAddf( )
**
** Adds string "err" at key "key", whether or not there are any
** existing messages there. This version accepts a printf style
** format string as input.
*/
void
202 biffAddf( const char *key, const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
_biffAddVL( key, errfmt, args );
va_end( args );
return;
}
#if 0
/*
******** biffAddf_e
**
** calls ( eventually ) biffMsgAdd if msg is non-NULL, otherwise calls
** biffAdd if msg is NULL.
*/
void
219 biffAddf_e( biffMsg *msg, const char *key, const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
if ( msg ) {
_biffMsgAddVL( msg, errfmt, args );
} else {
_biffAddVL( key, errfmt, args );
}
va_end( args );
return;
}
#endif
/*
******** biffMaybeAdd( )
**
** wrapper around biffAdd( ) but doesn't actually do anything if !useBiff
*/
void
239 biffMaybeAdd( const char *key, const char *err, int useBiff ) {
if ( useBiff ) {
biffAdd( key, err );
}
return;
}
void
248 biffMaybeAddf( int useBiff, const char *key, const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
if ( useBiff ) {
_biffAddVL( key, errfmt, args );
}
va_end( args );
return;
}
/*
******** biffGet( )
**
** creates a string which records all the errors at given key and
** returns it. Returns NULL in case of error. This function should
** be considered a glorified strdup( ): it is the callers responsibility
** to free( ) this string later
*/
char * /*Teem: allocates char* */ /* this comment is an experiment */
269 biffGet( const char *key ) {
static const char me[]="biffGet";
char *ret;
biffMsg *msg;
_bmsgStart( );
msg = _bmsgFind( key );
if ( !msg ) {
static const char err[]="[%s] No information for this key!";
size_t errlen;
fprintf( stderr, "%s: WARNING: no information for key \"%s\"\n", me, key );
errlen = strlen( err )+strlen( key )+1;
ret = AIR_CALLOC( errlen, char );
if ( !ret ) {
fprintf( stderr, "%s: PANIC: unable to allocate buffer\n", me );
return NULL; /* exit( 1 ); */
}
snprintf( ret, errlen, err, key );
return ret;
}
ret = AIR_CALLOC( biffMsgStrlen( msg )+1, char );
if ( !ret ) {
fprintf( stderr, "%s: PANIC: unable to allocate buffer\n", me );
return NULL; /* exit( 1 ); */
}
biffMsgStrSet( ret, msg );
return ret;
}
/*
******** biffGetStrlen( )
**
** for when you want to allocate the buffer for the biff string, this is
** how you learn its length
*/
unsigned int
306 biffGetStrlen( const char *key ) {
static const char me[]="biffGetStrlen";
biffMsg *msg;
unsigned int len;
_bmsgStart( );
msg = _bmsgFind( key );
if ( !msg ) {
fprintf( stderr, "%s: WARNING: no information for key \"%s\"\n", me, key );
return 0;
}
len = biffMsgStrlen( msg );
len += 1; /* GLK forgets if the convention is that the caller allocates
for one more to include '\0'; this is safer */
return len;
}
/*
******** biffSetStr( )
**
** for when you want to allocate the buffer for the biff string, this is
** how you get the error message itself
*/
void
330 biffSetStr( char *str, const char *key ) {
static const char me[]="biffSetStr";
biffMsg *msg;
if ( !str ) {
fprintf( stderr, "%s: ERROR: got NULL buffer for \"%s\"\n", me, key );
return;
}
_bmsgStart( );
msg = _bmsgFind( key );
if ( !msg ) {
fprintf( stderr, "%s: WARNING: no information for key \"%s\"\n", me, key );
return;
}
biffMsgStrSet( str, msg );
return;
}
/*
******** biffCheck( )
**
** sees how many messages there are for a given key;
** Note that this is just a simple wrapper around biffMsgErrNum
*/
unsigned int
357 biffCheck( const char *key ) {
_bmsgStart( );
return biffMsgErrNum( _bmsgFind( key ) );
}
/*
******** biffDone( )
**
** frees everything associated with given key, and shrinks list of keys,
** and calls _bmsgFinish( ) if there are no keys left
*/
void
370 biffDone( const char *key ) {
static const char me[]="biffDone";
unsigned int idx;
biffMsg *msg;
_bmsgStart( );
msg = _bmsgFind( key );
if ( !msg ) {
fprintf( stderr, "%s: WARNING: no information for key \"%s\"\n", me, key );
return;
}
idx = _bmsgFindIdx( msg );
biffMsgNix( msg );
if ( _bmsgNum > 1 ) {
/* if we have more than one key in action, move the last biffMsg
to the position that was just cleared up */
_bmsg[idx] = _bmsg[_bmsgNum-1];
}
airArrayLenIncr( _bmsgArr, -1 );
/* if that was the last key, close shop */
if ( !_bmsgArr->len ) {
_bmsgFinish( );
}
return;
}
void
399 biffMove( const char *destKey, const char *err, const char *srcKey ) {
static const char me[]="biffMove";
biffMsg *dest, *src;
_bmsgStart( );
dest = _bmsgAdd( destKey );
src = _bmsgFind( srcKey );
if ( !src ) {
fprintf( stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey );
return;
}
biffMsgMove( dest, src, err );
return;
}
static void
415 _biffMoveVL( const char *destKey, const char *srcKey,
const char *errfmt, va_list args ) {
static const char me[]="biffMovev";
biffMsg *dest, *src;
_bmsgStart( );
dest = _bmsgAdd( destKey );
src = _bmsgFind( srcKey );
if ( !src ) {
fprintf( stderr, "%s: WARNING: key \"%s\" unknown\n", me, srcKey );
return;
}
_biffMsgMoveVL( dest, src, errfmt, args );
return;
}
void
432 biffMovef( const char *destKey, const char *srcKey,
const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
_biffMoveVL( destKey, srcKey, errfmt, args );
va_end( args );
return;
}
char *
443 biffGetDone( const char *key ) {
char *ret;
_bmsgStart( );
ret = biffGet( key );
biffDone( key ); /* will call _bmsgFinish if this is the last key */
return ret;
}
/* ---- BEGIN non-NrrdIO */
void
456 biffSetStrDone( char *str, const char *key ) {
_bmsgStart( );
biffSetStr( str, key );
biffDone( key ); /* will call _bmsgFinish if this is the last key */
return;
}
/* ---- END non-NrrdIO */
/* this is the end */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "biff.h"
#include "privateBiff.h"
/* ---- BEGIN non-NrrdIO */
const int
biffPresent = 42;
/* ---- END non-NrrdIO */
/*
** with the Nov'09 re-write of biff, this sourcefile becomes the only
** place where a static buffer is used for message handling; this
** should eventually be avoided by using things like asprintf and
** vasprintf which allocated the string as needed
*/
#define _HACK_STRLEN AIR_STRLEN_HUGE
#define _MSG_INCR 2
biffMsg *
42 biffMsgNew( const char *key ) {
static const char me[]="biffMsgNew";
biffMsg *msg;
if ( !key ) {
fprintf( stderr, "%s: PANIC got NULL key\n", me );
return NULL; /* exit( 1 ); */
}
msg = AIR_CALLOC( 1, biffMsg );
if ( msg ) {
airPtrPtrUnion appu;
msg->key = airStrdup( key );
msg->err = NULL;
msg->errNum = 0;
appu.cp = &( msg->err );
msg->errArr = airArrayNew( appu.v, &( msg->errNum ),
sizeof( char* ), _MSG_INCR );
if ( msg->errArr ) {
airArrayPointerCB( msg->errArr, NULL, airFree );
}
}
if ( !( msg && msg->key && msg->errArr ) ) {
fprintf( stderr, "%s: PANIC couldn't calloc new msg\n", me );
return NULL; /* exit( 1 ); */
}
return msg;
}
biffMsg *
72 biffMsgNix( biffMsg *msg ) {
if ( msg && msg != biffMsgNoop ) {
airFree( msg->key );
airArrayLenSet( msg->errArr, 0 ); /* frees all msg->err[i] */
airArrayNuke( msg->errArr );
airFree( msg );
}
return NULL;
}
/*
** adds a given message to the given entry. The message is processed to
** convert all whitespace into ' ', and to eliminate whitespace at the
** end of the message.
*/
void
89 biffMsgAdd( biffMsg *msg, const char *err ) {
static const char me[]="biffMsgAdd";
unsigned int idx;
if ( biffMsgNoop == msg ) {
return;
}
if ( !( msg && err ) ) {
fprintf( stderr, "%s: PANIC got NULL msg ( %p ) or err ( %p )\n", me,
AIR_VOIDP( msg ), AIR_CVOIDP( err ) );
return; /* exit( 1 ); */
}
idx = airArrayLenIncr( msg->errArr, 1 );
if ( !msg->err ) {
fprintf( stderr, "%s: PANIC: couldn't add message to %s\n", me, msg->key );
return; /* exit( 1 ); */
}
if ( !( msg->err[idx] = airOneLinify( airStrdup( err ) ) ) ) {
fprintf( stderr, "%s: PANIC: couldn't alloc message to %s\n", me, msg->key );
return; /* exit( 1 ); */
}
return;
}
void
114 _biffMsgAddVL( biffMsg *msg, const char *errfmt, va_list args ) {
char errstr[_HACK_STRLEN];
vsprintf( errstr, errfmt, args );
biffMsgAdd( msg, errstr );
return;
}
/* ---- BEGIN non-NrrdIO */
void
124 biffMsgAddf( biffMsg *msg, const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
_biffMsgAddVL( msg, errfmt, args );
va_end( args );
return;
}
/* ---- END non-NrrdIO */
void
135 biffMsgClear( biffMsg *msg ) {
if ( biffMsgNoop == msg ) {
return;
}
airArrayLenSet( msg->errArr, 0 ); /* frees all msg->err[i] */
/* but msg->key stays allocated */
return;
}
/*
** max length of line formatted "[<key>] <err>\n"
*/
unsigned int
149 biffMsgLineLenMax( const biffMsg *msg ) {
unsigned int ii, len, maxlen;
if ( biffMsgNoop == msg ) {
return 0;
}
maxlen = 0;
for ( ii=0; ii<msg->errNum; ii++ ) {
len = AIR_UINT( strlen( msg->err[ii] ) + strlen( msg->key ) + strlen( "[] \n" ) );
maxlen = AIR_MAX( maxlen, len );
}
return maxlen;
}
/*
******** biffMsgMove
**
** "src" is not const because we clear it after moving things out
*/
void
169 biffMsgMove( biffMsg *dest, biffMsg *src, const char *err ) {
static const char me[]="biffMsgMove";
unsigned int ii;
char *buff;
if ( biffMsgNoop == dest || biffMsgNoop == src ) {
return;
}
if ( !( dest && src ) ) {
fprintf( stderr, "%s: PANIC got NULL msg ( %p %p )\n", me,
AIR_VOIDP( dest ), AIR_VOIDP( src ) );
return; /* exit( 1 ); */
}
/* if src and dest are same, this degenerates to biffMsgAdd */
if ( dest == src && airStrlen( err ) ) {
biffMsgAdd( dest, err );
return;
}
buff = AIR_CALLOC( biffMsgLineLenMax( src )+1, char );
if ( !buff ) {
fprintf( stderr, "%s: PANIC: can't allocate buffer\n", me );
return; /* exit( 1 ); */
}
for ( ii=0; ii<src->errNum; ii++ ) {
sprintf( buff, "[%s] %s", src->key, src->err[ii] );
biffMsgAdd( dest, buff );
}
free( buff );
biffMsgClear( src );
if ( airStrlen( err ) ) {
biffMsgAdd( dest, err );
}
return;
}
void
206 _biffMsgMoveVL( biffMsg *dest, biffMsg *src,
const char *errfmt, va_list args ) {
char errstr[_HACK_STRLEN];
vsprintf( errstr, errfmt, args );
biffMsgMove( dest, src, errstr );
return;
}
void
216 biffMsgMovef( biffMsg *dest, biffMsg *src, const char *errfmt, ... ) {
va_list args;
va_start( args, errfmt );
_biffMsgMoveVL( dest, src, errfmt, args );
va_end( args );
return;
}
/*
******** biffMsgErrNum
**
** returns number of errors in a message
*/
unsigned int
231 biffMsgErrNum( const biffMsg *msg ) {
if ( biffMsgNoop == msg ) {
return 0;
}
if ( !msg ) {
return 0;
}
return msg->errNum;
}
/*
******** biffMsgStrlen
**
** returns length of string ( not including null termination, as usual )
** of the error message that will be generated by biffMsgStrSet
*/
unsigned int
249 biffMsgStrlen( const biffMsg *msg ) {
static const char me[]="biffMsgStrlen";
unsigned int ii, len;
if ( biffMsgNoop == msg ) {
return 0;
}
if ( !( msg ) ) {
fprintf( stderr, "%s: PANIC got NULL msg %p\n", me, AIR_CVOIDP( msg ) );
return 0; /* exit( 1 ); */
}
len = 0;
for ( ii=0; ii<msg->errNum; ii++ ) {
len += AIR_UINT( strlen( msg->key )
+ strlen( msg->err[ii] ) + strlen( "[] \n" ) );
}
return len+1;
}
char *
270 biffMsgStrAlloc( const biffMsg *msg ) {
static const char me[]="biffMsgStrAlloc";
char *ret;
unsigned int len;
if ( biffMsgNoop == msg ) {
return NULL;
}
len = biffMsgStrlen( msg );
ret = AIR_CALLOC( len+1, char );
if ( !ret ) {
fprintf( stderr, "%s: PANIC couldn't alloc string", me );
return NULL; /* exit( 1 ); */
}
return ret;
}
/*
** ret is assumed to be allocated for biffMsgStrlen( )+1, or is the
** the return from biffMsgStrAlloc
*/
void
292 biffMsgStrSet( char *ret, const biffMsg *msg ) {
static const char me[]="biffMsgStrSet";
char *buff;
unsigned int ii;
if ( biffMsgNoop == msg ) {
return;
}
if ( !ret ) {
fprintf( stderr, "%s: PANIC got NULL ret", me );
return;
}
buff = AIR_CALLOC( biffMsgLineLenMax( msg )+1, char );
if ( !buff ) {
fprintf( stderr, "%s: PANIC couldn't alloc buffer", me );
return; /* exit( 1 ); */
}
strcpy( ret, "" );
for ( ii=msg->errNum; ii>0; ii-- ) {
sprintf( buff, "[%s] %s\n", msg->key, msg->err[ii-1] );
strcat( ret, buff );
}
free( buff );
}
char *
318 biffMsgStrGet( const biffMsg *msg ) {
char *ret;
if ( biffMsgNoop == msg ) {
return NULL;
}
ret = biffMsgStrAlloc( msg );
biffMsgStrSet( ret, msg );
return ret;
}
biffMsg
_biffMsgNoop = {
NULL,
NULL,
0,
NULL
};
/*
******** biffMsgNoop
**
** pass this instead of a real biffMsg ( allocated by biffMsgNew ) as a
** flag to say, "don't bother, really". This turns all the biffMsg
** functions into no-ops ( except that var-args are still consumed
** where they are used )
*/
biffMsg *
biffMsgNoop = &_biffMsgNoop;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../biff.h"
int
28 main( ) {
char *tmp, *s1, *s2;
biffMsg *msg1, *msg2;
/*
biffAdd( "axis", "the first error axis" );
biffAdd( "axis", "the second error axis" );
biffAdd( "axis", "the third error axis" );
biffAdd( "chard", "the first error chard" );
biffAdd( "chard", "the second error chard" );
biffAdd( "chard", "the third error chard" );
biffAdd( "bingo", "zero-eth bingo message" );
biffMove( "bingo", NULL, "chard" );
biffAdd( "bingo", "the first error bingo" );
biffAdd( "bingo", "the second bll boo boo boo error bingo" );
biffAdd( "bingo", "the third error bingo" );
printf( "%s\n", ( tmp = biffGet( "bingo" ) ) );
free( tmp );
biffDone( "bingo" );
printf( "%s\n", ( tmp = biffGet( "chard" ) ) );
free( tmp );
biffDone( "chard" );
printf( "%s\n", ( tmp = biffGet( "axis" ) ) );
free( tmp );
biffDone( "axis" );
biffAdd( "harold", "the first error harold" );
biffAdd( "harold", "the second error harold" );
biffAdd( "harold", "the third error harold" );
printf( "%s\n", ( tmp = biffGet( "harold" ) ) );
free( tmp );
*/
biffAdd( "axis", "the first error axis" );
biffAdd( "axis", "the second error axis" );
biffAdd( "axis", "the third error axis" );
biffAdd( "axis", "the fourth error axis" );
biffAdd( "axis", "the fifth error axis" );
printf( "%s", ( tmp = biffGet( "axis" ) ) );
free( tmp );
biffDone( "axis" );
biffAdd( "axo", "the first error axis" );
biffAdd( "axo", "the second error axis" );
biffAdd( "axo", "the third error axis" );
biffAdd( "axo", "the fourth error axis" );
biffAdd( "axo", "the fifth error axis" );
printf( "%s", ( tmp = biffGetDone( "axo" ) ) );
free( tmp );
printf( "=================================\n" );
msg1 = biffMsgNew( "roberts" );
biffMsgAdd( msg1, "biffMsgAdd hello, said roberts" );
biffMsgAddf( msg1, "biffMsgAddf: there's an int %d and a float %g",
42, AIR_PI );
s1 = biffMsgStrGet( msg1 );
printf( "from msg1:\n%s", s1 );
s1 = airFree( s1 );
msg2 = biffMsgNew( "sue" );
biffMsgAdd( msg2, "biffMsgAdd hi from sue" );
biffMsgAddf( msg2, "biffMsgAddf: another float %g", AIR_PI*AIR_PI );
s2 = biffMsgStrGet( msg2 );
printf( "from msg2:\n%s", s2 );
s2 = airFree( s2 );
biffMsgMovef( msg1, msg2, "biffMsgMovef: good int %d", 10 );
s1 = biffMsgStrGet( msg1 );
printf( "from msg1:\n%s", s1 );
s1 = airFree( s1 );
printf( "=================================\n" );
msg1 = biffMsgNix( msg1 );
msg2 = biffMsgNix( msg2 );
/*
biffAddf( "test", "%s: this is a test %d %f", "me", 1, 2.0 );
printf( "%s\n", ( tmp = biffGet( "test" ) ) );
free( tmp );
biffDone( "test" );
*/
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
int
27 main( int argc, char *argv[] ) {
int aret;
char *me;
AIR_UNUSED( argc );
me = argv[0];
aret = airSanity( );
if ( airInsane_not == aret ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: air sanity check passed.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, "airMyEndian( ) == %d\n", airMyEndian( ) );
fprintf( stderr, "AIR_QNANHIBIT == %d\n", AIR_QNANHIBIT );
fprintf( stderr, "AIR_DIO == %d\n", AIR_DIO );
fprintf( stderr, "sizeof( size_t ) = %s\n",
airSprintSize_t( stmp, sizeof( size_t ) ) );
fprintf( stderr, "sizeof( void* ) = %s\n",
airSprintSize_t( stmp, sizeof( void* ) ) );
return 0;
}
/* else */
fprintf( stderr, "%s: air sanity check FAILED:\n%s\n",
me, airInsaneErr( aret ) );
return 1;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/ell.h>
char *me;
void
30 usage( void ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <A> <B> <C>\n", me );
fprintf( stderr, "for cubic x^3 + Ax^2 + Bx + C == 0\n" );
exit( 1 );
}
int
38 main( int argc, char **argv ) {
char buf[512];
double ans0, ans1, ans2, A, B, C;
int ret;
double r[3];
me = argv[0];
if ( argc != 4 ) {
usage( );
}
sprintf( buf, "%s %s %s", argv[1], argv[2], argv[3] );
if ( 3 != sscanf( buf, "%lf %lf %lf", &A, &B, &C ) ) {
fprintf( stderr, "%s: couldn't parse 3 floats from command line\n", me );
exit( 1 );
}
ell_debug = AIR_TRUE;
ret = ell_cubic( r, A, B, C, AIR_TRUE );
ans0 = C + r[0]*( B + r[0]*( A + r[0] ) );
switch( ret ) {
case ell_cubic_root_single:
printf( "1 single root: %f -> %f\n", r[0], ans0 );
break;
case ell_cubic_root_triple:
printf( "1 triple root: %f -> %f\n", r[0], ans0 );
break;
case ell_cubic_root_single_double:
ans1 = C + r[1]*( B + r[1]*( A + r[1] ) );
printf( "1 single root %f -> %f, 1 double root %f -> %f\n",
r[0], ans0, r[1], ans1 );
break;
case ell_cubic_root_three:
ans1 = C + r[1]*( B + r[1]*( A + r[1] ) );
ans2 = C + r[2]*( B + r[2]*( A + r[2] ) );
printf( "3 distinct roots:\n %f -> %f\n %f -> %f\n %f -> %f\n",
r[0], ans0, r[1], ans1, r[2], ans2 );
break;
default:
printf( "%s: something fatally wacky happened\n", me );
exit( 1 );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/ten.h>
#include <teem/meet.h>
#define SPACING( spc ) ( AIR_EXISTS( spc ) ? spc: nrrdDefaultSpacing )
/* copied this from ten.h; I don't want gage to depend on ten */
#define PROBE_MAT2LIST( l, m ) ( \
( l )[1] = ( m )[0], \
( l )[2] = ( m )[3], \
( l )[3] = ( m )[6], \
( l )[4] = ( m )[4], \
42 ( l )[5] = ( m )[7], \
( l )[6] = ( m )[8] )
static const char *deconvInfo = ( "Does deconvolution. " );
int
main( int argc, const char *argv[] ) {
gageKind *kind;
const char *me;
char *outS, *err;
hestParm *hparm;
hestOpt *hopt = NULL;
NrrdKernelSpec *ksp;
int otype, separ, ret;
unsigned int maxIter;
double epsilon, lastDiff, step;
Nrrd *nin, *nout;
airArray *mop;
mop = airMopNew( );
me = argv[0];
hparm = hestParmNew( );
airMopAdd( mop, hparm, AIR_CAST( airMopper, hestParmFree ), airMopAlways );
hparm->elideSingleOtherType = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", "
"\"tensor\", or \"dwi\" )",
NULL, NULL, meetHestGageKind );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1, &ksp, NULL,
"convolution kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "mi", "max # iters", airTypeUInt, 1, 1, &maxIter, "100",
"maximum number of iterations with which to compute the "
"deconvolution" );
hestOptAdd( &hopt, "e", "epsilon", airTypeDouble, 1, 1, &epsilon,
"0.00000001", "convergence threshold" );
hestOptAdd( &hopt, "s", "step", airTypeDouble, 1, 1, &step, "1.0",
"scaling of value update" );
hestOptAdd( &hopt, "t", "type", airTypeOther, 1, 1, &otype, "default",
"type to save output as. By default ( not using this option ), "
"the output type is the same as the input type",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &hopt, "sep", "bool", airTypeBool, 1, 1, &separ, "false",
"use fast separable deconvolution instead of brain-dead "
"brute-force iterative method" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, deconvInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestOptFree ), airMopAlways );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestParseFree ), airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
if ( separ ) {
ret = gageDeconvolveSeparable( nout, nin, kind, ksp, otype );
} else {
ret = gageDeconvolve( nout, &lastDiff,
nin, kind,
ksp, otype,
maxIter, AIR_TRUE,
step, epsilon, 1 );
}
if ( ret ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/limn.h>
static const char *emapInfo =
( "Creates environment maps based on limn's \"checker\" "
"normal quantization methods. By taking into account "
"camera parameters, this allows for both lights in "
"both world and view space. Solely out of laziness, "
"the nrrd format is used for specifying the lights, but not "
"to worry: you can use a simple un-adorned text file, "
"defining one light per line, with 7 values per light: "
"0/1 ( world/view space ), R\tG\tB color, and "
"X\tY\tZ position." );
int
42 main( int argc, const char *argv[] ) {
hestOpt *hopt=NULL;
hestParm *hparm;
Nrrd *nlight, *nmap, *ndebug;
const char *me;
char *outS, *errS, *debugS;
airArray *mop;
float amb[3], *linfo, *debug, *map, vscl;
unsigned li, ui, vi;
int qn, bits, method, doerr;
limnLight *light;
limnCamera *cam;
double u, v, r, w, V2W[9], diff, WW[3], VV[3];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
hestOptAdd( &hopt, "i", "nlight", airTypeOther, 1, 1, &nlight, NULL,
"input nrrd containing light information",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b", "# bits", airTypeInt, 1, 1, &bits, "16",
"number of bits to use for normal quantization, "
"between 8 and 16 inclusive. " );
hestOptAdd( &hopt, "amb", "ambient RGB", airTypeFloat, 3, 3, amb, "0 0 0",
"ambient light color" );
hestOptAdd( &hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, "1 0 0",
"position of camera, used to determine view vector" );
hestOptAdd( &hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
"camera look-at point, used to determine view vector" );
hestOptAdd( &hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
"camera pseudo-up vector, used to determine view coordinates" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestOptAdd( &hopt, "vs", "view-dir scaling", airTypeFloat, 1, 1, &vscl, "1",
"scaling along view-direction of location of "
"view-space lights" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL,
"file to write output envmap to" );
hestOptAdd( &hopt, "d", "filename", airTypeString, 1, 1, &debugS, "",
"Use this option to save out ( to the given filename ) a rendering "
"of the front ( on the left ) and back ( on the right ) of a sphere "
"as shaded with the new environment map. U increases "
"right-ward, V increases downward. The back sphere half is "
"rendered as though the front half was removed" );
hestOptAdd( &hopt, "err", NULL, airTypeInt, 0, 0, &doerr, NULL,
"If using \"-d\", make the image represent the error between the "
"real and quantized vector" );
hestParseOrDie( hopt, argc-1, argv+1, hparm, me, emapInfo,
AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
switch( bits ) {
case 16: method = limnQN16octa; break;
case 15: method = limnQN15octa; break;
case 14: method = limnQN14octa; break;
case 13: method = limnQN13octa; break;
case 12: method = limnQN12octa; break;
case 11: method = limnQN11octa; break;
case 10: method = limnQN10octa; break;
case 9: method = limnQN9octa; break;
case 8: method = limnQN8octa; break;
default:
fprintf( stderr, "%s: requested #bits ( %d ) not in valid range [8, 16]\n",
me, bits );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeFloat == nlight->type &&
2 == nlight->dim &&
7 == nlight->axis[0].size &&
LIMN_LIGHT_NUM >= nlight->axis[1].size ) ) {
fprintf( stderr, "%s: nlight isn't valid format for light specification, "
"must be: float type, 2-dimensional, 7\tx\tN size, N <= %d\n",
me, LIMN_LIGHT_NUM );
airMopError( mop ); return 1;
}
cam->neer = -0.000000001;
cam->dist = 0;
cam->faar = 0.0000000001;
cam->atRelative = AIR_TRUE;
if ( limnCameraUpdate( cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: problem with camera:\n%s\n", me, errS );
airMopError( mop ); return 1;
}
light = limnLightNew( );
airMopAdd( mop, light, ( airMopper )limnLightNix, airMopAlways );
limnLightAmbientSet( light, amb[0], amb[1], amb[2] );
for ( li=0; li<nlight->axis[1].size; li++ ) {
int vsp;
float lxyz[3];
linfo = ( float * )( nlight->data ) + 7*li;
vsp = !!linfo[0];
ELL_3V_COPY( lxyz, linfo + 4 );
if ( vsp ) {
lxyz[2] *= vscl;
}
limnLightSet( light, li, vsp,
linfo[1], linfo[2], linfo[3], lxyz[0], lxyz[1], lxyz[2] );
}
if ( limnLightUpdate( light, cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: problem with lights:\n%s\n", me, errS );
airMopError( mop ); return 1;
}
nmap=nrrdNew( );
airMopAdd( mop, nmap, ( airMopper )nrrdNuke, airMopAlways );
if ( limnEnvMapFill( nmap, limnLightDiffuseCB, method, light ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: problem making environment map:\n%s\n", me, errS );
airMopError( mop ); return 1;
}
map = ( float * )nmap->data;
if ( nrrdSave( outS, nmap, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, errS = biffGetDone( NRRD ) );
free( errS ); return 1;
}
if ( airStrlen( debugS ) ) {
ELL_34M_EXTRACT( V2W, cam->V2W );
ndebug = nrrdNew( );
nrrdMaybeAlloc_va( ndebug, nrrdTypeFloat, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, 1024 ),
AIR_CAST( size_t, 512 ) );
airMopAdd( mop, ndebug, ( airMopper )nrrdNuke, airMopAlways );
debug = ( float * )ndebug->data;
for ( vi=0; vi<=511; vi++ ) {
v = AIR_AFFINE( 0, vi, 511, -0.999, 0.999 );
for ( ui=0; ui<=511; ui++ ) {
u = AIR_AFFINE( 0, ui, 511, -0.999, 0.999 );
r = sqrt( u*u + v*v );
if ( r > 1 ) {
continue;
}
w = sqrt( 1 - r*r );
/* first, the near side of the sphere */
ELL_3V_SET( VV, u, v, -w );
ELL_3MV_MUL( WW, V2W, VV );
qn = limnVtoQN_d[method]( WW );
if ( doerr ) {
limnQNtoV_d[method]( VV, qn );
ELL_3V_SUB( WW, WW, VV );
diff = ELL_3V_LEN( WW );
ELL_3V_SET_TT( debug + 3*( ui + 1024*vi ), float,
diff, diff, diff );
} else {
ELL_3V_COPY( debug + 3*( ui + 1024*vi ), map + 3*qn );
}
/* second, the far side of the sphere */
ELL_3V_SET( VV, u, v, w );
ELL_3MV_MUL( WW, V2W, VV );
qn = limnVtoQN_d[method]( WW );
if ( doerr ) {
limnQNtoV_d[method]( VV, qn );
ELL_3V_SUB( WW, WW, VV );
diff = ELL_3V_LEN( WW );
ELL_3V_SET_TT( debug + 3*( ui + 512 + 1024*vi ), float,
diff, diff, diff );
} else {
ELL_3V_COPY( debug + 3*( ui + 512 + 1024*vi ), map + 3*qn );
}
}
}
nrrdSave( debugS, ndebug, NULL );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/bane.h>
#define GKMS "gkms"
int
29 main( int argc, const char **argv ) {
int i, ret;
const char *me;
char *argv0 = NULL, *err;
hestParm *hparm;
airArray *mop;
me = argv[0];
/* no harm done in making sure we're sane */
if ( !nrrdSanity( ) ) {
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s: nrrd sanity check FAILED.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, " This means that either nrrd can't work on this "
"platform, or ( more likely )\n" );
fprintf( stderr, " there was an error in the compilation options "
"and variable definitions\n" );
fprintf( stderr, " for Teem.\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s\n", err = biffGetDone( NRRD ) );
fprintf( stderr, "\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
free( err );
return 1;
}
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_FALSE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->cleverPluralizeOtherY = AIR_TRUE;
hparm->columns = 78;
/* if there are no arguments, then we give general usage information */
if ( 1 >= argc ) {
baneGkmsUsage( GKMS, hparm );
airMopError( mop );
exit( 1 );
}
/* else, we should see if they're asking for a command we know about */
/* baneGkmsCmdList[] is NULL-terminated */
for ( i=0; baneGkmsCmdList[i]; i++ ) {
if ( !strcmp( argv[1], baneGkmsCmdList[i]->name ) )
break;
}
if ( baneGkmsCmdList[i] ) {
/* yes, we have that command */
/* initialize variables used by the various commands */
argv0 = AIR_CAST( char*, malloc( strlen( GKMS ) + strlen( argv[1] ) + 2 ) );
airMopMem( mop, &argv0, airMopAlways );
sprintf( argv0, "%s %s", GKMS, argv[1] );
/* run the individual unu program, saving its exit status */
ret = baneGkmsCmdList[i]->main( argc-2, argv+2, argv0, hparm );
if ( 1 == ret ) {
airMopAdd( mop, err=biffGetDone( BANE ), airFree, airMopAlways );
fprintf( stderr, "%s: error:\n%s", argv0, err );
} else if ( 2 == ret ) {
/* gkms command has already handled printing error messages */
ret = 1;
}
} else {
fprintf( stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for "
"complete list\n", me, argv[1], me );
ret = 1;
}
airMopDone( mop, ret );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/ten.h>
#include <teem/meet.h>
static void
35 printans( FILE *file, const double *ans, unsigned int len ) {
unsigned int ai;
AIR_UNUSED( file );
for ( ai=0; ai<len; ai++ ) {
if ( ai ) {
printf( ", " );
}
printf( "%g", ans[ai] );
}
}
static int
48 gridProbe( gageContext *ctx, gagePerVolume *pvl, int what,
Nrrd *nout, int typeOut, Nrrd *_ngrid,
int indexSpace, int verbose, int clamp, int scaleIsTau,
double eft, double eftVal ) {
char me[]="gridProbe";
Nrrd *ngrid;
airArray *mop;
double *grid, pos[4];
const double *answer;
unsigned int ansLen, dim, aidx, baseDim, gridDim;
size_t sizeOut[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX], II, NN;
double ( *ins )( void *v, size_t I, double d );
char stmp[2][AIR_STRLEN_SMALL];
if ( !( ctx && pvl && nout && _ngrid ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, typeOut ) ) {
biffAddf( GAGE, "%s: type %d not valid", me, typeOut );
return 1;
}
if ( !gagePerVolumeIsAttached( ctx, pvl ) ) {
biffAddf( GAGE, "%s: given pvl not attached to context", me );
return 1;
}
if ( !( 2 == _ngrid->dim ) ) {
biffAddf( GAGE, "%s: ngrid must be 2 ( not %u )", me, _ngrid->dim );
return 1;
}
if ( ( ctx->stackPos && _ngrid->axis[0].size != 5 )
|| ( !ctx->stackPos && _ngrid->axis[0].size != 4 ) ) {
biffAddf( GAGE, "%s: if %susing stack, need "
"ngrid->axis[0].size = %u = 1 + %u ( not %u )", me,
( ctx->stackPos ? "" : "not " ),
( ctx->stackPos ? 4 : 3 ) + 1,
( ctx->stackPos ? 4 : 3 ),
AIR_UINT( _ngrid->axis[0].size ) );
return 1;
}
mop = airMopNew( );
ngrid = nrrdNew( );
airMopAdd( mop, ngrid, ( airMopper )nrrdNuke, airMopAlways );
if ( ctx->stackPos ) {
if ( nrrdConvert( ngrid, _ngrid, nrrdTypeDouble ) ) {
biffMovef( GAGE, NRRD, "%s: trouble converting ngrid", me );
airMopError( mop ); return 1;
}
} else {
Nrrd *ntmp;
ptrdiff_t minIdx[2], maxIdx[2];
minIdx[0] = minIdx[1] = 0;
maxIdx[0] = 4; /* pad by one sample */
maxIdx[1] = AIR_CAST( ptrdiff_t, _ngrid->axis[1].size-1 ); /* no padding */
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ntmp, _ngrid, nrrdTypeDouble )
|| nrrdPad_nva( ngrid, ntmp, minIdx, maxIdx, nrrdBoundaryPad, 0.0 ) ) {
biffMovef( GAGE, NRRD, "%s: trouble converting/padding ngrid", me );
airMopError( mop ); return 1;
}
}
grid = AIR_CAST( double *, ngrid->data );
gridDim = AIR_ROUNDUP_UI( grid[0] );
if ( gridDim + 1 != ngrid->axis[1].size ) {
biffAddf( GAGE, "%s: ngrid->axis[1].size = %u but expected %u = 1 + %u",
me, AIR_UINT( ngrid->axis[1].size ),
1 + gridDim, gridDim );
airMopError( mop ); return 1;
}
answer = gageAnswerPointer( ctx, pvl, what );
ansLen = pvl->kind->table[what].answerLength;
baseDim = 1 == ansLen ? 0 : 1;
dim = baseDim + gridDim;
if ( dim > NRRD_DIM_MAX ) {
biffAddf( GAGE, "%s: output dimension %u unreasonable", me, dim );
airMopError( mop ); return 1;
}
if ( ansLen > 1 ) {
sizeOut[0] = ansLen;
coordOut[0] = 0;
}
NN = 1;
for ( aidx=0; aidx<gridDim; aidx++ ) {
sizeOut[aidx + baseDim] = AIR_ROUNDUP_UI( grid[0 + 5*( aidx+1 )] );
NN *= sizeOut[aidx + baseDim];
coordOut[aidx + baseDim] = 0;
}
if ( nrrdMaybeAlloc_nva( nout, typeOut, dim, sizeOut ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
ins = nrrdDInsert[nout->type];
for ( II=0; II<NN; II++ ) {
int E;
if ( verbose && 3 == gridDim
&& !coordOut[0+baseDim] && !coordOut[1+baseDim] ) {
if ( verbose > 1 ) {
fprintf( stderr, "z = " );
}
fprintf( stderr, " %s/%s",
airSprintSize_t( stmp[0], coordOut[2+baseDim] ),
airSprintSize_t( stmp[1], sizeOut[2+baseDim] ) );
fflush( stderr );
if ( verbose > 1 ) {
fprintf( stderr, "\n" );
}
}
ELL_4V_COPY( pos, grid + 1 + 5*0 );
for ( aidx=0; aidx<gridDim; aidx++ ) {
ELL_4V_SCALE_ADD2( pos, 1, pos,
AIR_CAST( double, coordOut[aidx + baseDim] ),
grid + 1 + 5*( 1+aidx ) );
}
if ( scaleIsTau && ctx->stackPos ) {
/* have to convert given tau values to sigma */
pos[3] = airSigmaOfTau( pos[3] );
}
/*
printf( "%s: %u -> ( %u %u ) -> %g %g %g %g ( %s )\n", me,
AIR_UINT( II ),
AIR_UINT( coordOut[0+baseDim] ),
AIR_UINT( coordOut[1+baseDim] ),
pos[0], pos[1], pos[2], pos[3],
indexSpace ? "index" : "world" );
*/
E = ( ctx->stackPos
? gageStackProbeSpace( ctx, pos[0], pos[1], pos[2], pos[3],
indexSpace, clamp )
: gageProbeSpace( ctx, pos[0], pos[1], pos[2],
indexSpace, clamp ) );
if ( E ) {
biffAddf( GAGE, "%s: trouble at II=%s =( %g, %g, %g, %g ):\n%s\n( %d )\n", me,
airSprintSize_t( stmp[0], II ),
pos[0], pos[1], pos[2], pos[3],
ctx->errStr, ctx->errNum );
airMopError( mop ); return 1;
}
if ( 1 == ansLen ) {
ins( nout->data, II, ( ctx->edgeFrac > eft
? eftVal
: *answer ) );
} else {
for ( aidx=0; aidx<ansLen; aidx++ ) {
ins( nout->data, aidx + ansLen*II, ( ctx->edgeFrac > eft
? eftVal
: answer[aidx] ) );
}
}
NRRD_COORD_INCR( coordOut, sizeOut, dim, baseDim );
}
if ( verbose && verbose <= 1 ) {
fprintf( stderr, "\n" );
}
if ( !indexSpace ) {
/* set the output space directions, but ( being conservative/cautious )
only do so when grid had specified world-space positions */
/* HEY: untested! whipped up out of frustration for GLK Bonn talk */
nout->spaceDim = 3;
if ( baseDim ) {
nrrdSpaceVecSetNaN( nout->axis[0].spaceDirection );
}
nrrdSpaceVecCopy( nout->spaceOrigin, grid + 1 + 5*0 );
for ( aidx=0; aidx<gridDim; aidx++ ) {
nrrdSpaceVecCopy( nout->axis[baseDim+aidx].spaceDirection,
grid + 1 + 5*( 1+aidx ) );
}
}
airMopOkay( mop );
return 0;
}
static const char *probeInfo =
( "Shows off the functionality of the gage library. "
"Uses gageProbe( ) to query various kinds of volumes "
"to learn various measured or derived quantities." );
int
228 main( int argc, const char *argv[] ) {
gageKind *kind;
const char *me;
char *whatS, *err, *outS, *stackFnameFormat;
hestParm *hparm;
hestOpt *hopt = NULL;
NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur;
int what, E=0, renorm, uniformSS, optimSS, verbose, zeroZ,
orientationFromSpacing, probeSpaceIndex, normdSS;
unsigned int iBaseDim, oBaseDim, axi, numSS, seed;
const double *answer;
Nrrd *nin, *_npos, *npos, *_ngrid, *ngrid, *nout, **ninSS=NULL;
Nrrd *ngrad=NULL, *nbmat=NULL;
size_t six, siy, siz, sox, soy, soz;
double bval=0, eps, gmc, rangeSS[2], *pntPos, scale[3], posSS, biasSS,
dsix, dsiy, dsiz, dsox, dsoy, dsoz, edgeFracInfo[2];
gageContext *ctx;
gagePerVolume *pvl=NULL;
double t0, t1, rscl[3], min[3], maxOut[3], maxIn[3];
airArray *mop;
#define NON_SBP_OPT_NUM 5
unsigned int ansLen, *skip, skipNum, pntPosNum,
nonSbpOpi[NON_SBP_OPT_NUM], nsi;
gageStackBlurParm *sbpIN, *sbpCL, *sbp;
int otype, clamp, scaleIsTau;
char stmp[4][AIR_STRLEN_SMALL];
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* no harm done in making sure we're sane */
if ( !nrrdSanity( ) ) {
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s: nrrd sanity check FAILED.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, " This means that either nrrd can't work on this "
"platform, or ( more likely )\n" );
fprintf( stderr, " there was an error in the compilation options "
"and variable definitions\n" );
fprintf( stderr, " for how Teem was built here.\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s\n", err = biffGetDone( NRRD ) );
fprintf( stderr, "\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
free( err );
return 1;
}
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, AIR_CAST( airMopper, hestParmFree ), airMopAlways );
hparm->elideSingleOtherType = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", "
"\"tensor\", or \"dwi\" )",
NULL, NULL, meetHestGageKind );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL,
"the quantity ( scalar, vector, or matrix ) to learn by probing" );
hestOptAdd( &hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc,
"0.0", "For curvature-based queries, use zero when gradient "
"magnitude is below this" );
hestOptAdd( &hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing,
NULL, "If only per-axis spacing is available, use that to "
"contrive full orientation info" );
hestOptAdd( &hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42",
"RNG seed; mostly for debugging" );
hestOptAdd( &hopt, "c", "bool", airTypeBool, 1, 1, &clamp, "false",
"clamp positions as part of probing" );
hestOptAdd( &hopt, "ev", "thresh val", airTypeDouble, 2, 2,
edgeFracInfo, "1 0",
"if using position clamping ( with \"-c true\" ), the fraction "
"of values invented for the kernel support, or \"edge frac\" "
"is saved per probe ( 0 means kernel support was entirely within "
"data; 1 means everything was invented ). "
"If this frac exceeds the first \"thresh\" "
"value given here, then the saved value for the probe will be "
"the second value \"val\" given here" );
hestOptAdd( &hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false",
"enable \"zeroZ\" behavior in gage that partially "
"implements working with 3D images as if they are 2D" );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"tent", "kernel for gageKernel00",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL,
"renormalize kernel weights at each new sample location. "
"\"Accurate\" kernels don't need this; doing it always "
"makes things go slower" );
nsi = 0;
nonSbpOpi[nsi++] =
hestOptAdd( &hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS,
"0", "how many scale-space samples to evaluate, or, "
"0 to turn-off all scale-space behavior" );
nonSbpOpi[nsi++] =
hestOptAdd( &hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS,
"nan nan", "range of scales in scale-space" );
nonSbpOpi[nsi++] =
hestOptAdd( &hopt, "ssu", NULL, airTypeInt, 0, 0, &uniformSS, NULL,
"do uniform samples along sigma, and not ( by default ) "
"samples according to the effective diffusion scale" );
nonSbpOpi[nsi++] =
hestOptAdd( &hopt, "sso", NULL, airTypeInt, 0, 0, &optimSS, NULL,
"if not using \"-ssu\", use pre-computed optimal "
"sigmas when possible" );
nonSbpOpi[nsi++] =
hestOptAdd( &hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur,
"dgauss:1, 5", "blurring kernel, to sample scale space",
NULL, NULL, nrrdHestKernelSpec );
if ( nsi != NON_SBP_OPT_NUM ) {
fprintf( stderr, "%s: PANIC nsi %u != %u", me, nsi, NON_SBP_OPT_NUM );
exit( 1 );
}
hestOptAdd( &hopt, "sbp", "blur spec", airTypeOther, 1, 1, &sbpCL, "",
"complete specification of stack blur parms; "
"over-rides all previous \"ss\" options",
NULL, NULL, gageHestStackBlurParm );
/* These two options are needed even if sbp is used, because they are *not*
part of the gageStackBlurParm. In meet, this info is handled by the
extraFlag/extraParm construct, which is not available here */
hestOptAdd( &hopt, "ssnd", NULL, airTypeInt, 0, 0, &normdSS, NULL,
"normalize derivatives by scale" );
hestOptAdd( &hopt, "ssnb", "bias", airTypeDouble, 1, 1, &biasSS, "0.0",
"bias on scale-based derivative normalization" );
hestOptAdd( &hopt, "ssf", "SS read/save format", airTypeString, 1, 1,
&stackFnameFormat, "",
"printf-style format ( including a \"%u\" ) for the "
"filenames from which to read "
"pre-blurred volumes computed for the stack, if they "
"exist and match the stack parameters, and where to save "
"them if they had to be re-computed. Leave this as empty "
"string to disable this." );
hestOptAdd( &hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS,
"hermite", "kernel for reconstructing from scale space samples",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "sit", "sit", airTypeBool, 1, 1, &scaleIsTau, "false",
"in some places, scale should be interpreted as tau, not "
"sigma. Currently limited to grid probing ( via \"-pg\" )" );
hestOptAdd( &hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale,
"1 1 1",
"scaling factor for resampling on each axis "
"( >1.0 : supersampling ); use \"-ssp\" ( and \"-psi\" )"
"to specify scale position of sampling" );
hestOptAdd( &hopt, "ssp", "pos", airTypeDouble, 1, 1, &posSS, "0",
"when using scale-space, scale-position at which to probe" );
hestOptAdd( &hopt, "pg", "nrrd", airTypeOther, 1, 1, &_ngrid, "",
"overrides \"-s\": "
"2-D nrrd which specifies origin and direction vectors "
"for sampling grid", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "pi", "nrrd", airTypeOther, 1, 1, &_npos, "",
"overrides \"-pv\": probes at this list of 3-vec or "
"4-vec positions", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "pp", "pos", airTypeDouble, 3, 4, &pntPos,
"nan nan nan",
"overrides \"-pi\": only sample at this specified point",
&pntPosNum );
hestOptAdd( &hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "0",
"if non-zero, and if query is a scalar, and if using \"pp\" "
"to query at a single point, then do epsilon offset probes "
"to calculate discrete differences, to find the numerical "
"gradient and hessian ( for debugging )" );
hestOptAdd( &hopt, "psi", "p", airTypeBool, 1, 1, &probeSpaceIndex, "false",
"whether the probe location specification ( by any of "
"the four previous flags ) are in index space" );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float",
"type of output volume", NULL, nrrdType );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestOptFree ), airMopAlways );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestParseFree ), airMopAlways );
what = airEnumVal( kind->enm, whatS );
if ( !what ) {
/* 0 indeed always means "unknown" for any gageKind */
fprintf( stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n",
me, whatS, kind->name );
hestUsage( stderr, hopt, me, hparm );
hestGlossary( stderr, hopt, hparm );
airMopError( mop ); return 1;
}
/* special set-up required for DWI kind */
if ( !strcmp( TEN_DWI_GAGE_KIND_NAME, kind->name ) ) {
if ( tenDWMRIKeyValueParse( &ngrad, &nbmat, &bval, &skip, &skipNum, nin ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( skipNum ) {
fprintf( stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me );
airMopError( mop ); return 1;
}
/* this could stand to use some more command-line arguments */
if ( tenDwiGageKindSet( kind, 50, 1, bval, 0.001, ngrad, nbmat,
tenEstimate1MethodLLS,
tenEstimate2MethodQSegLLS, seed ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
/* for setting up pre-blurred scale-space samples */
if ( numSS || sbpCL ) {
unsigned int vi;
int recompute, gotOld;
if ( sbpCL ) {
/* we got the whole stack blar parm here */
gotOld = AIR_FALSE;
for ( nsi=0; nsi<NON_SBP_OPT_NUM; nsi++ ) {
gotOld |= ( hestSourceUser == hopt[nonSbpOpi[nsi]].source );
}
if ( gotOld ) {
fprintf( stderr, "%s: with new -sbp option; can't also use older "
"scale-space options ( used", me );
for ( nsi=0; nsi<NON_SBP_OPT_NUM; nsi++ ) {
if ( hestSourceUser == hopt[nonSbpOpi[nsi]].source ) {
fprintf( stderr, " -%s", hopt[nonSbpOpi[nsi]].flag );
}
}
fprintf( stderr, " )\n" );
airMopError( mop ); return 1;
}
if ( gageStackBlurManage( &ninSS, &recompute, sbpCL,
stackFnameFormat, AIR_TRUE, NULL,
nin, kind ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble getting volume stack:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( sbpCL->verbose > 2 ) {
fprintf( stderr, "%s: sampling scale range %g--%g via %s:\n", me,
sbpCL->sigmaRange[0], sbpCL->sigmaRange[1],
airEnumStr( gageSigmaSampling, sbpCL->sigmaSampling ) );
for ( vi=0; vi<sbpCL->num; vi++ ) {
fprintf( stderr, " sigma[%u] = %g\n", vi, sbpCL->sigma[vi] );
}
}
sbp = sbpCL;
} else {
/* old way of doing things; depending on many separate options
to set numSS, rangeSS, uniformSS, optimSS, etc */
sbpIN = gageStackBlurParmNew( );
airMopAdd( mop, sbpIN, ( airMopper )gageStackBlurParmNix, airMopAlways );
if ( gageStackBlurParmVerboseSet( sbpIN, verbose )
|| gageStackBlurParmScaleSet( sbpIN, numSS, rangeSS[0], rangeSS[1],
uniformSS, optimSS )
|| gageStackBlurParmKernelSet( sbpIN, kSSblur )
|| gageStackBlurParmRenormalizeSet( sbpIN, AIR_TRUE )
|| gageStackBlurParmBoundarySet( sbpIN, nrrdBoundaryBleed, AIR_NAN )
|| gageStackBlurManage( &ninSS, &recompute, sbpIN,
stackFnameFormat, AIR_TRUE, NULL,
nin, kind ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble getting volume stack:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( verbose > 2 ) {
fprintf( stderr, "%s: sampling scale range %g--%g %suniformly:\n", me,
rangeSS[0], rangeSS[1], uniformSS ? "" : "non-" );
for ( vi=0; vi<numSS; vi++ ) {
fprintf( stderr, " scalePos[%u] = %g\n", vi, sbpIN->sigma[vi] );
}
}
sbp = sbpIN;
}
airMopAdd( mop, ninSS, airFree, airMopAlways );
} else {
ninSS = NULL;
sbpIN = NULL;
sbp = NULL;
}
/***
**** Except for the gageProbe( ) call in the inner loop below,
**** and the gageContextNix( ) call at the very end, all the gage
**** calls which set up ( and take down ) the context and state are here.
***/
ctx = gageContextNew( );
airMopAdd( mop, ctx, AIR_CAST( airMopper, gageContextNix ), airMopAlways );
gageParmSet( ctx, gageParmGradMagCurvMin, gmc );
gageParmSet( ctx, gageParmVerbose, verbose );
gageParmSet( ctx, gageParmTwoDimZeroZ, zeroZ );
gageParmSet( ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
gageParmSet( ctx, gageParmOrientationFromSpacing, orientationFromSpacing );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, kind ) );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( sbp ) {
gagePerVolume **pvlSS;
gageParmSet( ctx, gageParmStackUse, AIR_TRUE );
gageParmSet( ctx, gageParmStackNormalizeDeriv, normdSS );
gageParmSet( ctx, gageParmStackNormalizeDerivBias, biasSS );
if ( !E ) E |= !( pvlSS = AIR_CAST( gagePerVolume **,
calloc( sbp->num, sizeof( gagePerVolume * ) ) ) );
if ( !E ) airMopAdd( mop, pvlSS, ( airMopper )airFree, airMopAlways );
if ( !E ) E |= gageStackPerVolumeNew( ctx, pvlSS,
AIR_CAST( const Nrrd*const*, ninSS ),
sbp->num, kind );
if ( !E ) E |= gageStackPerVolumeAttach( ctx, pvl, pvlSS,
sbp->sigma, sbp->num );
if ( !E ) E |= gageKernelSet( ctx, gageKernelStack, kSS->kernel, kSS->parm );
} else {
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
}
if ( !E ) E |= gageQueryItemOn( ctx, pvl, what );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
answer = gageAnswerPointer( ctx, pvl, what );
ansLen = kind->table[what].answerLength;
/***
**** end gage setup.
***/
if ( verbose ) {
fprintf( stderr, "%s: kernel support = %d^3 samples\n", me,
2*ctx->radius );
}
if ( ELL_3V_EXISTS( pntPos ) ) {
/* only interested in a single point, make sure we have the right
info about the point WRT scale stuff */
if ( sbp ) {
if ( !( 4 == pntPosNum && ELL_4V_EXISTS( pntPos ) ) ) {
fprintf( stderr, "%s: need a 4-vec position with scale-space", me );
airMopError( mop ); return 1;
}
} else {
if ( !( 3 == pntPosNum ) ) {
fprintf( stderr, "%s: need a 3-vec position ( w/out scale-space )", me );
airMopError( mop ); return 1;
}
}
if ( sbp
? gageStackProbeSpace( ctx,
pntPos[0], pntPos[1], pntPos[2], pntPos[3],
probeSpaceIndex, clamp )
: gageProbeSpace( ctx,
pntPos[0], pntPos[1], pntPos[2],
probeSpaceIndex, clamp ) ) {
fprintf( stderr, "%s: trouble probing: ( errNum %d ) %s\n", me,
ctx->errNum, ctx->errStr );
airMopError( mop ); return 1;
}
if ( sbp ) {
printf( "%s: %s( %s:%g, %g, %g, %g ) = ", me, airEnumStr( kind->enm, what ),
probeSpaceIndex ? "index" : "world",
pntPos[0], pntPos[1], pntPos[2], pntPos[3] );
} else {
printf( "%s: %s( %s:%g, %g, %g ) = ", me, airEnumStr( kind->enm, what ),
probeSpaceIndex ? "index" : "world",
pntPos[0], pntPos[1], pntPos[2] );
}
printans( stdout, answer, ansLen );
printf( "\n" );
/* we're done, get out of here */
/* except if we're supposed to debug derivatives */
if ( eps && 1 == ansLen ) {
double v[3][3][3], fes, ee;
int xo, yo, zo;
if ( probeSpaceIndex ) {
fprintf( stderr, "\n%s: WARNING!!: not probing in world-space ( via "
"\"-wsp\" ) likely leads to errors in estimated "
"derivatives\n\n", me );
}
gageParmSet( ctx, gageParmVerbose, 0 );
#define PROBE( x, y, z ) \
( ( sbp \
? gageStackProbeSpace( ctx, x, y, z, posSS, \
probeSpaceIndex, clamp ) \
: gageProbeSpace( ctx, x, y, z, probeSpaceIndex, \
clamp ) ), answer[0] )
for ( xo=0; xo<=2; xo++ ) {
for ( yo=0; yo<=2; yo++ ) {
for ( zo=0; zo<=2; zo++ ) {
v[xo][yo][zo] = PROBE( pntPos[0] + ( xo-1 )*eps,
pntPos[1] + ( yo-1 )*eps,
pntPos[2] + ( zo-1 )*eps );
}
}
}
printf( "%s: approx gradient( %s ) at ( %g, %g, %g ) = %f %f %f\n", me,
airEnumStr( kind->enm, what ), pntPos[0], pntPos[1], pntPos[2],
( v[2][1][1] - v[0][1][1] )/( 2*eps ),
( v[1][2][1] - v[1][0][1] )/( 2*eps ),
( v[1][1][2] - v[1][1][0] )/( 2*eps ) );
fes = 4*eps*eps;
ee = eps*eps;
printf( "%s: approx hessian( %s ) at ( %g, %g, %g ) = \n"
"%f %f %f\n"
" %f %f\n"
" %f\n", me,
airEnumStr( kind->enm, what ), pntPos[0], pntPos[1], pntPos[2],
( v[0][1][1] - 2*v[1][1][1] + v[2][1][1] )/ee,
( v[2][2][1] - v[0][2][1] - v[2][0][1] + v[0][0][1] )/fes,
( v[2][1][2] - v[0][1][2] - v[2][1][0] + v[0][1][0] )/fes,
( v[1][2][1] - 2*v[1][1][1] + v[1][0][1] )/ee,
( v[1][2][2] - v[1][0][2] - v[1][2][0] + v[1][0][0] )/fes,
( v[1][1][2] - 2*v[1][1][1] + v[1][1][0] )/ee );
}
airMopOkay( mop ); return 0;
}
if ( _npos ) {
/* given a nrrd of probe locations */
double *pos, ( *ins )( void *v, size_t I, double d );
size_t II, NN;
unsigned int aidx;
if ( !( 2 == _npos->dim
&& ( 3 == _npos->axis[0].size || 4 == _npos->axis[0].size ) ) ) {
fprintf( stderr, "%s: need npos 2-D 3-by-N or 4-by-N "
"( not %u-D %u-by-N )\n", me, _npos->dim,
AIR_UINT( _npos->axis[0].size ) );
airMopError( mop ); return 1;
}
if ( ( sbp && 3 == _npos->axis[0].size )
|| ( !sbp && 4 == _npos->axis[0].size ) ) {
fprintf( stderr, "%s: have %u point coords but %s using scale-space\n",
me, AIR_UINT( _npos->axis[0].size ),
sbp ? "are" : "are not" );
airMopError( mop ); return 1;
}
NN = _npos->axis[1].size;
npos = nrrdNew( );
airMopAdd( mop, npos, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
if ( nrrdConvert( npos, _npos, nrrdTypeDouble )
|| nrrdMaybeAlloc_va( nout, otype, 2,
AIR_CAST( size_t, ansLen ),
AIR_CAST( size_t, NN ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with npos or nout:\n%s\n", me, err );
airMopError( mop ); return 1;
}
pos = AIR_CAST( double *, npos->data );
ins = nrrdDInsert[nout->type];
for ( II=0; II<NN; II++ ) {
if ( sbp ) {
gageStackProbeSpace( ctx, pos[0], pos[1], pos[2], pos[3],
probeSpaceIndex, clamp );
} else {
gageProbeSpace( ctx, pos[0], pos[1], pos[2],
probeSpaceIndex, clamp );
}
if ( 1 == ansLen ) {
ins( nout->data, II, ( ctx->edgeFrac > edgeFracInfo[0]
? edgeFracInfo[1]
: *answer ) );
} else {
for ( aidx=0; aidx<ansLen; aidx++ ) {
ins( nout->data, aidx + ansLen*II, ( ctx->edgeFrac > edgeFracInfo[0]
? edgeFracInfo[1]
: answer[aidx] ) );
}
}
/*
if ( sbp ) {
printf( "%s: %s( %s:%g, %g, %g, %g ) = ", me, airEnumStr( kind->enm, what ),
probeSpaceIndex ? "index" : "world",
pos[0], pos[1], pos[2], pos[3] );
} else {
printf( "%s: %s( %s:%g, %g, %g ) = ", me, airEnumStr( kind->enm, what ),
probeSpaceIndex ? "index" : "world",
pos[0], pos[1], pos[2] );
}
printans( stdout, answer, ansLen );
printf( "\n" );
*/
pos += _npos->axis[0].size;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
/* we're done, get out of here */
airMopOkay( mop );
exit( 0 );
}
/* else, we're sampling on some kind of grid */
ngrid = nrrdNew( );
airMopAdd( mop, ngrid, ( airMopper )nrrdNuke, airMopAlways );
iBaseDim = kind->baseDim;
oBaseDim = 1 == ansLen ? 0 : 1;
if ( !_ngrid ) {
/* did not get a grid, have to use "-s" args to define it */
double *grid;
size_t gridSize[NRRD_DIM_MAX];
six = nin->axis[0+iBaseDim].size;
siy = nin->axis[1+iBaseDim].size;
siz = nin->axis[2+iBaseDim].size;
dsix = AIR_CAST( double, six );
dsiy = AIR_CAST( double, siy );
dsiz = AIR_CAST( double, siz );
sox = AIR_CAST( size_t, scale[0]*dsix );
soy = AIR_CAST( size_t, scale[1]*dsiy );
soz = AIR_CAST( size_t, scale[2]*dsiz );
dsox = AIR_CAST( double, sox );
dsoy = AIR_CAST( double, soy );
dsoz = AIR_CAST( double, soz );
rscl[0] = dsix/dsox;
rscl[1] = dsiy/dsoy;
rscl[2] = dsiz/dsoz;
if ( verbose ) {
fprintf( stderr, "%s: creating %u x %s x %s x %s output\n", me,
ansLen,
airSprintSize_t( stmp[1], sox ),
airSprintSize_t( stmp[2], soy ),
airSprintSize_t( stmp[3], soz ) );
fprintf( stderr, "%s: effective scaling is %g %g %g\n", me,
rscl[0], rscl[1], rscl[2] );
}
gridSize[0] = sbp ? 5 : 4;
gridSize[1] = 4;
if ( nrrdMaybeAlloc_nva( ngrid, nrrdTypeDouble, 2, gridSize ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making ngrid:\n%s\n", me, err );
airMopError( mop ); return 1;
}
grid = AIR_CAST( double *, ngrid->data );
if ( nrrdCenterCell == ctx->shape->center ) {
ELL_3V_SET( min, -0.5, -0.5, -0.5 );
ELL_3V_SET( maxOut, dsox-0.5, dsoy-0.5, dsoz-0.5 );
ELL_3V_SET( maxIn, dsix-0.5, dsiy-0.5, dsiz-0.5 );
} else {
ELL_3V_SET( min, 0, 0, 0 );
ELL_3V_SET( maxOut, dsox-1, dsoy-1, dsoz-1 );
ELL_3V_SET( maxIn, dsix-1, dsiy-1, dsiz-1 );
}
ELL_4V_SET( grid + gridSize[0]*0, 3,
NRRD_POS( ctx->shape->center, min[0], maxIn[0], sox, 0 ),
NRRD_POS( ctx->shape->center, min[1], maxIn[1], soy, 0 ),
NRRD_POS( ctx->shape->center, min[2], maxIn[2], soz, 0 ) );
ELL_4V_SET( grid + gridSize[0]*1, dsox,
AIR_DELTA( min[0], 1, maxOut[0], min[0], maxIn[0] ),
0,
0 );
ELL_4V_SET( grid + gridSize[0]*2, dsoy,
0,
AIR_DELTA( min[1], 1, maxOut[1], min[1], maxIn[1] ),
0 );
ELL_4V_SET( grid + gridSize[0]*3, dsoz,
0,
0,
AIR_DELTA( min[2], 1, maxOut[2], min[2], maxIn[2] ) );
if ( sbp ) {
if ( !probeSpaceIndex ) {
double idxSS = AIR_NAN;
unsigned int vi;
/* there's actually work to do here, weirdly: gageProbe can
either work in index space, or in world space, but the
vprobe-style sampling is index-space-centric, so we have to
convert the world-space stack position to index space, to be
consistent with the way that the grid sampling will be
defined. So, we have to actually replicate work that is done
by _gageProbeSpace( ) in converting from world to index space */
/* HEY: the way that idxSS is set is very strange */
for ( vi=0; vi<sbp->num-1; vi++ ) {
if ( AIR_IN_CL( sbp->sigma[vi], posSS, sbp->sigma[vi+1] ) ) {
idxSS = vi + AIR_AFFINE( sbp->sigma[vi], posSS, sbp->sigma[vi+1],
0, 1 );
if ( verbose > 1 ) {
fprintf( stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me,
posSS, idxSS, vi,
AIR_AFFINE( sbp->sigma[vi], posSS, sbp->sigma[vi+1],
0, 1 ) );
}
break;
}
}
if ( vi == sbp->num-1 ) {
fprintf( stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me,
posSS, rangeSS[0], sbp->sigma[0],
rangeSS[1], sbp->sigma[sbp->num-1] );
airMopError( mop ); return 1;
}
grid[4 + 5*0] = idxSS;
} else {
grid[4 + 5*0] = posSS;
}
grid[4 + 5*1] = 0;
grid[4 + 5*2] = 0;
grid[4 + 5*3] = 0;
}
} else {
/* we did get a grid, here we only copy from _ngrid to ngrid,
and let further massaging be done in gridProbe below */
six = siy = siz = 0;
sox = soy = soz = 0;
if ( nrrdCopy( ngrid, _ngrid ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble copying ngrid:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
/* probe onto grid */
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
gageParmSet( ctx, gageParmVerbose, verbose );
t0 = airTime( );
if ( gridProbe( ctx, pvl, what, nout, otype, ngrid,
( _ngrid
? probeSpaceIndex /* user specifies grid space */
: AIR_TRUE ), /* copying vprobe index-space behavior */
verbose, clamp, scaleIsTau,
edgeFracInfo[0], edgeFracInfo[1] ) ) {
/* note hijacking of GAGE key */
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble probing on grid:\n%s\n", me, err );
airMopError( mop ); return 1;
}
t1 = airTime( );
if ( verbose ) {
fprintf( stderr, "probe rate = %g KHz\n",
AIR_CAST( double, nrrdElementNumber( nout )/ansLen )
/ ( 1000.0*( t1-t0 ) ) );
}
/* massage output some */
nrrdContentSet_va( nout, "gprobe", nin, "%s", airEnumStr( kind->enm, what ) );
if ( !_ngrid ) {
/* did not get a grid, have to emulate vprobe per-axis behavior */
for ( axi=0; axi<3; axi++ ) {
nout->axis[axi+oBaseDim].label =
airStrdup( nin->axis[axi+iBaseDim].label );
nout->axis[axi+oBaseDim].center = ctx->shape->center;
}
nrrdBasicInfoCopy( nout, nin, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_SPACEORIGIN_BIT
| NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) );
if ( ctx->shape->fromOrientation ) {
if ( nin->space ) {
nrrdSpaceSet( nout, nin->space );
} else {
nrrdSpaceDimensionSet( nout, nin->spaceDim );
}
nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
for ( axi=0; axi<3; axi++ ) {
double tmp;
nrrdSpaceVecScale( nout->axis[axi+oBaseDim].spaceDirection,
rscl[axi],
nin->axis[axi+iBaseDim].spaceDirection );
tmp = AIR_AFFINE( min[axi], 0, maxOut[axi], min[axi], maxIn[axi] );
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, nout->spaceOrigin,
tmp, nin->axis[axi+iBaseDim].spaceDirection );
}
} else {
for ( axi=0; axi<3; axi++ ) {
nout->axis[axi+oBaseDim].spacing =
rscl[axi]*nin->axis[axi+iBaseDim].spacing;
}
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/unrrdu.h>
#include <teem/moss.h>
static const char *ilkInfo =
( "( I )mage ( L )inear Trans( X-->K )forms. "
"Applies linear ( homogenous coordinate ) transforms "
"to a given image, using the given kernel for "
"resampling. " );
int
34 main( int argc, const char *argv[] ) {
const char *me;
char *errS, *outS;
hestOpt *hopt=NULL;
hestParm *hparm;
airArray *mop;
Nrrd *nin, *nout;
NrrdKernelSpec *ksp;
mossSampler *msp;
double mat[6], **matList, *origInfo, origMat[6], origInvMat[6], ox, oy,
min[2], max[2];
int d, bound, ax0, size[2]; /* HEY size[] should be size_t */
unsigned int matListLen, _bkgLen, i, avgNum;
float *bkg, *_bkg, scale[4];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_FALSE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->respFileEnable = AIR_TRUE;
hestOptAdd( &hopt, "i", "image", airTypeOther, 1, 1, &nin, "-",
"input image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "0", "origin", airTypeOther, 1, 1, &origInfo, "p:0, 0",
"where to location ( 0, 0 ) prior to applying transforms.\n "
"\b\bo \"u:<float>, <float>\" locate origin in a unit box "
"[0, 1]x[0, 1] which covers the original image\n "
"\b\bo \"p:<float>, <float>\" locate origin at a particular "
"pixel location, in the index space of the image",
NULL, NULL, mossHestOrigin );
hestOptAdd( &hopt, "t", "xform0", airTypeOther, 1, -1, &matList, NULL,
"transform( s ) to apply to image. Transforms "
"are applied in the order in which they appear.\n "
"\b\bo \"identity\": no geometric transform, just resampling\n "
"\b\bo \"translate:x, y\": shift image by vector ( x, y ), as "
"measured in pixels\n "
"\b\bo \"rotate:ang\": rotate CCW by ang degrees\n "
"\b\bo \"scale:xs, ys\": scale by xs in X, and ys in Y\n "
"\b\bo \"shear:fix, amnt\": shear by amnt, keeping fixed "
"the pixels along a direction <fix> degrees from the X axis\n "
"\b\bo \"flip:ang\": flip along axis an angle <ang> degrees from "
"the X axis\n "
"\b\bo \"a, b, tx, c, d, ty\": specify the transform explicitly "
"in row-major order ( opposite of PostScript ) ",
&matListLen, NULL, mossHestTransform );
hestOptAdd( &hopt, "k", "kernel", airTypeOther, 1, 1, &ksp,
"cubic:0, 0.5", "reconstruction kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "min", "xMin yMin", airTypeDouble, 2, 2, min, "nan nan",
"lower bounding corner of output image. Default ( by not "
"using this option ) is the lower corner of input image. " );
hestOptAdd( &hopt, "max", "xMax yMax", airTypeDouble, 2, 2, max, "nan nan",
"upper bounding corner of output image. Default ( by not "
"using this option ) is the upper corner of input image. " );
hestOptAdd( &hopt, "b", "boundary", airTypeEnum, 1, 1, &bound, "bleed",
"what to do when sampling outside original image.\n "
"\b\bo \"bleed\": copy values at image border outward\n "
"\b\bo \"wrap\": do wrap-around on image locations\n "
"\b\bo \"pad\": use a given background value ( via \"-bg\" )",
NULL, nrrdBoundary );
hestOptAdd( &hopt, "bg", "bg0 bg1", airTypeFloat, 1, -1, &_bkg, "nan",
"background color to use with boundary behavior \"pad\". "
"Defaults to all zeroes.",
&_bkgLen );
hestOptAdd( &hopt, "s", "xSize ySize", airTypeOther, 2, 2, scale, "x1 x1",
"For each axis, information about how many samples in output:\n "
"\b\bo \"x<float>\": number of output samples is some scaling of "
" the number input of samples; multiplied by <float>\n "
"\b\bo \"<int>\": specify exact number of samples",
NULL, NULL, &unrrduHestScaleCB );
hestOptAdd( &hopt, "a", "avg #", airTypeUInt, 1, 1, &avgNum, "0",
"number of averages ( if there there is only one "
"rotation )" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, ilkInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
/* HEY: this is commented out because there is a memory bug otherwise;
this needs to be debugged */
/* airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways ); */
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
msp = mossSamplerNew( );
airMopAdd( mop, msp, ( airMopper )mossSamplerNix, airMopAlways );
msp->boundary = bound;
if ( mossSamplerKernelSet( msp, ksp->kernel, ksp->parm ) ) {
fprintf( stderr, "%s: trouble with sampler:\n%s\n",
me, errS = biffGetDone( MOSS ) ); free( errS );
airMopError( mop ); return 1;
}
if ( nrrdBoundaryPad == bound ) {
if ( _bkgLen != MOSS_NCOL( nin ) ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: got %d background colors, image has %s colors\n",
me, _bkgLen, airSprintSize_t( stmp, MOSS_NCOL( nin ) ) );
airMopError( mop ); return 1;
} else {
bkg = _bkg;
}
} else {
/* maybe warn user if they gave a background that won't be used? */
/* No- because hest is stupid, and right now we always parse the
single ( default ) "nan" for this argument! */
bkg = NULL;
}
ax0 = MOSS_AXIS0( nin );
if ( !( AIR_EXISTS( nin->axis[ax0+0].min )
&& AIR_EXISTS( nin->axis[ax0+0].max ) ) ) {
nrrdAxisInfoMinMaxSet( nin, ax0+0, mossDefCenter );
}
if ( !( AIR_EXISTS( nin->axis[ax0+1].min )
&& AIR_EXISTS( nin->axis[ax0+1].max ) ) ) {
nrrdAxisInfoMinMaxSet( nin, ax0+1, mossDefCenter );
}
min[0] = AIR_EXISTS( min[0] ) ? min[0] : nin->axis[ax0+0].min;
max[0] = AIR_EXISTS( max[0] ) ? max[0] : nin->axis[ax0+0].max;
min[1] = AIR_EXISTS( min[1] ) ? min[1] : nin->axis[ax0+1].min;
max[1] = AIR_EXISTS( max[1] ) ? max[1] : nin->axis[ax0+1].max;
for ( d=0; d<2; d++ ) {
fprintf( stderr, "%s: scale[0 + 2*%d] = %d\n", me, d,
AIR_CAST( int, scale[0 + 2*d] ) );
switch( AIR_CAST( int, scale[0 + 2*d] ) ) {
case 0:
/* same number of samples as input */
size[d] = AIR_CAST( int, nin->axis[ax0+d].size );
break;
case 1:
/* scaling of input # samples */
size[d] = AIR_CAST( int, scale[1 + 2*d]*nin->axis[ax0+d].size );
break;
case 2:
/* explicit # of samples */
size[d] = AIR_CAST( int, scale[1 + 2*d] );
break;
default:
/* error */
fprintf( stderr, "%s: scale[0 + 2*%d] == %d unexpected\n",
me, AIR_CAST( int, scale[0 + 2*d] ), d );
airMopError( mop ); return 1;
}
}
/* find origin-based pre- and post- translate */
if ( 0 == origInfo[0] ) {
/* absolute pixel position */
mossMatTranslateSet( origMat, -origInfo[1], -origInfo[2] );
} else {
/* in unit box [0, 1]x[0, 1] */
ox = AIR_AFFINE( 0.0, origInfo[1], 1.0,
nin->axis[ax0+0].min, nin->axis[ax0+0].max );
oy = AIR_AFFINE( 0.0, origInfo[2], 1.0,
nin->axis[ax0+1].min, nin->axis[ax0+1].max );
mossMatTranslateSet( origMat, -ox, -oy );
}
mossMatInvert( origInvMat, origMat );
/* form complete transform */
mossMatIdentitySet( mat );
mossMatLeftMultiply( mat, origMat );
for ( i=0; i<matListLen; i++ ) {
mossMatLeftMultiply( mat, matList[i] );
}
mossMatLeftMultiply( mat, origInvMat );
if ( !AIR_EXISTS( nin->axis[ax0+0].min ) || !AIR_EXISTS( nin->axis[ax0+0].max ) ) {
nrrdAxisInfoMinMaxSet( nin, ax0+0, mossDefCenter );
}
if ( !AIR_EXISTS( nin->axis[ax0+1].min ) || !AIR_EXISTS( nin->axis[ax0+1].max ) ) {
nrrdAxisInfoMinMaxSet( nin, ax0+1, mossDefCenter );
}
if ( avgNum > 1 ) {
unsigned int ai;
double angleMax, angle, mrot[6];
Nrrd *ntmp, *nacc;
NrrdIter *itA, *itB;
int E;
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
nacc = nrrdNew( );
airMopAdd( mop, nacc, ( airMopper )nrrdNuke, airMopAlways );
itA = nrrdIterNew( );
airMopAdd( mop, itA, ( airMopper )nrrdIterNix, airMopAlways );
itB = nrrdIterNew( );
airMopAdd( mop, itB, ( airMopper )nrrdIterNix, airMopAlways );
E = 0;
angleMax = atan2( mat[3], mat[0] );
fprintf( stderr, "%s: %u angles ", me, avgNum );
for ( ai=0; ai<avgNum; ai++ ) {
fprintf( stderr, "." ); fflush( stderr );
angle = ( 180/AIR_PI )*AIR_AFFINE( 0, ai, avgNum-1, angleMax, -angleMax );
mossMatIdentitySet( mat );
mossMatLeftMultiply( mat, origMat );
mossMatRotateSet( mrot, angle );
mossMatLeftMultiply( mat, mrot );
mossMatLeftMultiply( mat, origInvMat );
if ( mossLinearTransform( ntmp, nin, bkg,
mat, msp,
min[0], max[0], min[1], max[1],
size[0], size[1] ) ) {
fprintf( stderr, "%s: problem doing transform:\n%s\n",
me, errS = biffGetDone( MOSS ) ); free( errS );
airMopError( mop ); return 1;
}
if ( !ai ) {
if ( !E ) E |= nrrdCopy( nacc, ntmp );
} else {
if ( !E ) E |= nrrdArithBinaryOp( nacc, nrrdBinaryOpAdd, nacc, ntmp );
}
if ( E ) {
break;
}
}
fprintf( stderr, "\n" );
nrrdIterSetNrrd( itA, nacc );
nrrdIterSetValue( itB, avgNum );
if ( !E ) E |= nrrdArithIterBinaryOp( nout, nrrdBinaryOpDivide,
itA, itB );
if ( E ) {
fprintf( stderr, "%s: problem making output:\n%s\n",
me, errS = biffGetDone( NRRD ) ); free( errS );
airMopError( mop ); return 1;
}
} else {
if ( mossLinearTransform( nout, nin, bkg,
mat, msp,
min[0], max[0], min[1], max[1],
size[0], size[1] ) ) {
fprintf( stderr, "%s: problem doing transform:\n%s\n",
me, errS = biffGetDone( MOSS ) ); free( errS );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
fprintf( stderr, "%s: problem saving output:\n%s\n",
me, errS = biffGetDone( NRRD ) ); free( errS );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/biff.h>
#include <teem/ell.h>
#include <teem/nrrd.h>
#include <teem/limn.h>
#include <teem/hoover.h>
#include <teem/mite.h>
static const char *miteInfo =
( "A simple but effective little volume renderer." );
int
36 main( int argc, const char *argv[] ) {
airArray *mop;
hestOpt *hopt=NULL;
hestParm *hparm=NULL;
miteUser *muu;
const char *me;
char *errS, *outS, *shadeStr, *normalStr, debugStr[AIR_STRLEN_MED];
int renorm, baseDim, verbPix[2], offfr;
int E, Ecode, Ethread;
float ads[3], isScale;
double turn, eye[3], eyedist, gmc;
double v[NRRD_SPACE_DIM_MAX];
Nrrd *nin;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
muu = miteUserNew( );
airMopAdd( mop, muu, ( airMopper )miteUserNix, airMopAlways );
hparm->respFileEnable = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hestOptAdd( &hopt, "i", "nsin", airTypeOther, 1, 1, &( muu->nsin ), "",
"input scalar volume to render", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "vi", "nvin", airTypeOther, 1, 1, &( muu->nvin ), "",
"input vector volume to render", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ti", "ntin", airTypeOther, 1, 1, &( muu->ntin ), "",
"input tensor volume to render", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "txf", "nin", airTypeOther, 1, -1, &( muu->ntxf ), NULL,
"one or more transfer functions",
&( muu->ntxfNum ), NULL, nrrdHestNrrd );
limnHestCameraOptAdd( &hopt, muu->hctx->cam,
NULL, "0 0 0", "0 0 1",
NULL, NULL, NULL,
"nan nan", "nan nan", "20" );
hestOptAdd( &hopt, "offfr", NULL, airTypeInt, 0, 0, &offfr, NULL,
"the given eye point ( \"-fr\" ) is to be interpreted "
"as an offset from the at point." );
hestOptAdd( &hopt, "ffr", "fake from", airTypeDouble, 3, 3,
&( muu->fakeFrom ), "nan nan nan",
"eye point to use for view-dependent transfer functions. "
"By default ( not using this option ), the point used is the "
"normally specified camera eye point." );
hestOptAdd( &hopt, "turn", "angle", airTypeDouble, 1, 1, &turn, "0.0",
"angle ( degrees ) by which to rotate the from point around "
"true up, for making stereo pairs. Positive means move "
"towards positive U ( the right )" );
hestOptAdd( &hopt, "am", "ambient", airTypeFloat, 3, 3, muu->lit->amb,
"1 1 1", "ambient light color" );
hestOptAdd( &hopt, "ld", "light pos", airTypeFloat, 3, 3, muu->lit->_dir[0],
"0 0 -1", "view space light position ( extended to infinity )" );
hestOptAdd( &hopt, "is", "image size", airTypeInt, 2, 2, muu->hctx->imgSize,
"256 256", "image dimensions" );
hestOptAdd( &hopt, "iss", "scale", airTypeFloat, 1, 1, &isScale, "1.0",
"scaling of image size ( from \"is\" )" );
hestOptAdd( &hopt, "ads", "ka kd ks", airTypeFloat, 3, 3, ads,
"0.1 0.6 0.3", "phong components" );
hestOptAdd( &hopt, "sp", "spec pow", mite_at, 1, 1,
&( muu->rangeInit[miteRangeSP] ), "30", "phong specular power" );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1,
&( muu->ksp[gageKernel00] ),
"tent", "value reconstruction kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kernel", airTypeOther, 1, 1,
&( muu->ksp[gageKernel11] ),
"cubicd:1, 0", "first derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kernel", airTypeOther, 1, 1,
&( muu->ksp[gageKernel22] ),
"cubicdd:1, 0", "second derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "ss", "shading spec", airTypeString, 1, 1, &shadeStr,
"phong:gage( scalar:n )", "how to do shading" );
hestOptAdd( &hopt, "ns", "normal spec", airTypeString, 1, 1, &normalStr,
"", "\"normal\" to use for those miteVal's that need one" );
hestOptAdd( &hopt, "side", "normal side", airTypeInt, 1, 1,
&( muu->normalSide ),
"1", "how to interpret gradients as normals:\n "
"\b\bo \"1\": normal points to lower values ( higher == "
"more \"inside\" )\n "
"\b\bo \"0\": \"two-sided\": dot-products are abs( )'d\n "
"\b\bo \"-1\": normal points to higher values ( lower == "
"more \"inside\" )" );
hestOptAdd( &hopt, "rn", NULL, airTypeBool, 0, 0, &renorm, NULL,
"renormalize kernel weights at each new sample location. "
"\"Accurate\" kernels don't need this; doing it always "
"makes things go slower" );
hestOptAdd( &hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0",
"For curvature-based transfer functions, set curvature to "
"zero when gradient magnitude is below this" );
hestOptAdd( &hopt, "step", "size", airTypeDouble, 1, 1, &( muu->rayStep ),
"0.01", "step size along ray in world space" );
hestOptAdd( &hopt, "ref", "size", airTypeDouble, 1, 1, &( muu->refStep ),
"0.01", "\"reference\" step size ( world space ) for doing "
"opacity correction in compositing" );
hestOptAdd( &hopt, "vp", "verbose pixel", airTypeInt, 2, 2, verbPix,
"-1 -1", "pixel for which to turn on verbose messages" );
hestOptAdd( &hopt, "n1", "near1", airTypeDouble, 1, 1, &( muu->opacNear1 ),
"0.99", "opacity close enough to 1.0 to terminate ray" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1,
&( muu->hctx->numThreads ), "1",
( airThreadCapable
? "number of threads hoover should use"
: "if pthreads where enabled in this Teem build, this is how "
"you would control the number of threads hoover should use" ) );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS,
NULL, "file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, miteInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( muu->nsin ) {
nin = muu->nsin;
baseDim = 0;
} else if ( muu->nvin ) {
nin = muu->nvin;
baseDim = 1;
} else if ( muu->ntin ) {
nin = muu->ntin;
baseDim = 1;
} else {
fprintf( stderr, "%s: didn't get any volumes to render!\n", me );
airMopError( mop );
return 1;
}
/* finish processing command-line args */
muu->rangeInit[miteRangeKa] = ads[0];
muu->rangeInit[miteRangeKd] = ads[1];
muu->rangeInit[miteRangeKs] = ads[2];
gageParmSet( muu->gctx0, gageParmGradMagCurvMin, gmc );
gageParmSet( muu->gctx0, gageParmRenormalize,
renorm ? AIR_TRUE : AIR_FALSE );
muu->verbUi = verbPix[0];
muu->verbVi = verbPix[1];
if ( offfr ) {
ELL_3V_INCR( muu->hctx->cam->from, muu->hctx->cam->at );
}
muu->hctx->imgSize[0] = AIR_CAST( int, isScale*muu->hctx->imgSize[0] );
muu->hctx->imgSize[1] = AIR_CAST( int, isScale*muu->hctx->imgSize[1] );
muu->nout = nrrdNew( );
airMopAdd( mop, muu->nout, ( airMopper )nrrdNuke, airMopAlways );
ELL_3V_SET( muu->lit->col[0], 1, 1, 1 );
muu->lit->on[0] = AIR_TRUE;
muu->lit->vsp[0] = AIR_TRUE;
if ( AIR_EXISTS( muu->hctx->cam->uRange[0] )
&& AIR_EXISTS( muu->hctx->cam->uRange[1] )
&& AIR_EXISTS( muu->hctx->cam->vRange[0] )
&& AIR_EXISTS( muu->hctx->cam->vRange[1] ) ) {
/* someone went to the trouble of setting the U, V minmax, which
means they probably don't want the "fov"-based view window,
whether or not the "fov" value came from the command-line or
from the ( unavoidable ) default */
muu->hctx->cam->fov = AIR_NAN;
}
if ( limnCameraAspectSet( muu->hctx->cam,
muu->hctx->imgSize[0], muu->hctx->imgSize[1],
nrrdCenterCell )
|| limnCameraUpdate( muu->hctx->cam )
|| limnLightUpdate( muu->lit, muu->hctx->cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting camera:\n%s\n", me, errS );
airMopError( mop );
return 1;
}
if ( turn ) {
turn *= AIR_PI/180;
ELL_3V_SUB( eye, muu->hctx->cam->from, muu->hctx->cam->at );
ELL_3V_NORM( eye, eye, eyedist );
ELL_3V_SCALE_ADD2( muu->hctx->cam->from,
cos( turn ), eye,
sin( turn ), muu->hctx->cam->U );
ELL_3V_SCALE( muu->hctx->cam->from, eyedist, muu->hctx->cam->from );
if ( limnCameraUpdate( muu->hctx->cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting camera ( again ):\n%s\n", me, errS );
airMopError( mop );
return 1;
}
}
/*
fprintf( stderr, "%s: camera info\n", me );
fprintf( stderr, " U = {%g, %g, %g}\n",
muu->hctx->cam->U[0], muu->hctx->cam->U[1], muu->hctx->cam->U[2] );
fprintf( stderr, " V = {%g, %g, %g}\n",
muu->hctx->cam->V[0], muu->hctx->cam->V[1], muu->hctx->cam->V[2] );
fprintf( stderr, " N = {%g, %g, %g}\n",
muu->hctx->cam->N[0], muu->hctx->cam->N[1], muu->hctx->cam->N[2] );
*/
airStrcpy( muu->shadeStr, AIR_STRLEN_MED, shadeStr );
airStrcpy( muu->normalStr, AIR_STRLEN_MED, normalStr );
if ( 0 ) {
muu->hctx->volSize[0] = nin->axis[baseDim+0].size;
muu->hctx->volSize[1] = nin->axis[baseDim+1].size;
muu->hctx->volSize[2] = nin->axis[baseDim+2].size;
/* Get the proper spacing from the NRRD volume */
nrrdSpacingCalculate ( nin, baseDim+0, &( muu->hctx->volSpacing[0] ), v );
nrrdSpacingCalculate ( nin, baseDim+1, &( muu->hctx->volSpacing[1] ), v );
nrrdSpacingCalculate ( nin, baseDim+2, &( muu->hctx->volSpacing[2] ), v );
} else {
if ( gageShapeSet( muu->shape, nin, baseDim ) ) {
fprintf( stderr, "%s: problem with shape:\n%s\n",
me, errS = biffGetDone( GAGE ) ); free( errS );
airMopError( mop );
return 1;
}
muu->hctx->shape = muu->shape;
}
muu->hctx->user = muu;
muu->hctx->renderBegin = ( hooverRenderBegin_t * )miteRenderBegin;
muu->hctx->threadBegin = ( hooverThreadBegin_t * )miteThreadBegin;
muu->hctx->rayBegin = ( hooverRayBegin_t * )miteRayBegin;
muu->hctx->sample = ( hooverSample_t * )miteSample;
muu->hctx->rayEnd = ( hooverRayEnd_t * )miteRayEnd;
muu->hctx->threadEnd = ( hooverThreadEnd_t * )miteThreadEnd;
muu->hctx->renderEnd = ( hooverRenderEnd_t * )miteRenderEnd;
if ( !airThreadCapable && 1 != muu->hctx->numThreads ) {
fprintf( stderr, "%s: This Teem not compiled with "
"multi-threading support.\n", me );
fprintf( stderr, "%s: ==> can't use %d threads; only using 1\n",
me, muu->hctx->numThreads );
muu->hctx->numThreads = 1;
}
fprintf( stderr, "%s: rendering ... ", me ); fflush( stderr );
E = hooverRender( muu->hctx, &Ecode, &Ethread );
if ( E ) {
if ( hooverErrInit == E ) {
errS = biffGetDone( HOOVER );
} else {
errS = biffGetDone( MITE );
}
airMopAdd( mop, errS, airFree, airMopAlways );
fprintf( stderr, "%s: %s error ( code %d, thread %d ):\n%s\n",
me, airEnumStr( hooverErr, E ), Ecode, Ethread, errS );
airMopError( mop );
return 1;
}
fprintf( stderr, "\n" );
fprintf( stderr, "%s: rendering time = %g secs\n", me, muu->rendTime );
fprintf( stderr, "%s: sampling rate = %g Khz\n", me, muu->sampRate );
if ( muu->ndebug ) {
/* if its been generated, we should save it */
sprintf( debugStr, "%04d-%04d-debug.nrrd", verbPix[0], verbPix[1] );
if ( nrrdSave( debugStr, muu->ndebug, NULL ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving ray debug:\n%s\n", me, errS );
airMopError( mop );
return 1;
}
}
if ( nrrdSave( outS, muu->nout, NULL ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving image:\n%s\n", me, errS );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/hest.h>
#include <teem/biff.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/limn.h>
#include <teem/hoover.h>
#include <teem/ten.h>
#include <teem/meet.h>
#define MREND "mrender"
static const char *info =
( "A demonstration of hoover, gage, and nrrd measures. "
"Uses hoover to cast rays through a volume ( scalar, vector, or "
"tensor ), gage to "
"measure one of various quantities along the rays, and a "
"specified nrrd measure to reduce all the values along a ray "
"down to one scalar, which is saved in the output ( double ) "
"image." );
/* -------------------------------------------------------------- */
/* Even though the gageContext is really thread-specific, and
therefore doesn't really belong in mrendUser, the first context
from which all others is copied is logically shared across threads,
as are the input parameter it contains. There is a per-thread
gageContext pointer in mrendThread */
typedef struct {
Nrrd *nin; /* input volume to render */
gageKind *kind; /* the kind of volume it is */
int verbPixel[2]; /* which pixel to do verbose stuff on */
double rayStep, /* distance between sampling planes */
fromNaN; /* what to convert non-existent value to */
int whatq, /* what to measure along the ray */
measr; /* how to reduce the ray samples to a scalar */
/* we have a separate copy of the kernel specs so that hest can
set these, and then we'll gageKernelSet( ) them in the context
in order to do the proper error checking- hest can't do the
error checking that we need. */
NrrdKernelSpec *ksp[GAGE_KERNEL_MAX+1];
gageShape *shape; /* used to document ItoW transform */
gageContext *gctx0; /* gage input and parent thread state */
hooverContext *hctx; /* hoover input and state */
char *outS; /* ( managed by hest ) output filename */
double imgOrig[3], /* output: set as extra info about camera */
imgU[3], imgV[3];
airArray *mrmop;
} mrendUser;
mrendUser *
77 mrendUserNew( void ) {
mrendUser *uu;
int i;
uu = AIR_CALLOC( 1, mrendUser );
uu->nin = NULL;
uu->kind = NULL;
uu->rayStep = 0.0;
uu->whatq = gageSclUnknown;
uu->measr = nrrdMeasureUnknown;
for ( i=gageKernelUnknown+1; i<gageKernelLast; i++ ) {
uu->ksp[i] = NULL;
}
uu->shape = gageShapeNew( );
uu->gctx0 = gageContextNew( );
uu->hctx = hooverContextNew( );
uu->outS = NULL;
uu->mrmop = airMopNew( );
airMopAdd( uu->mrmop, uu->shape, ( airMopper )gageShapeNix, airMopAlways );
airMopAdd( uu->mrmop, uu->gctx0, ( airMopper )gageContextNix, airMopAlways );
airMopAdd( uu->mrmop, uu->hctx, ( airMopper )hooverContextNix, airMopAlways );
return uu;
}
mrendUser *
102 mrendUserNix( mrendUser *uu ) {
if ( uu ) {
airMopOkay( uu->mrmop );
airFree( uu );
}
return NULL;
}
int
112 mrendUserCheck( mrendUser *uu ) {
static const char me[]="mrendUserCheck";
if ( 3 + uu->kind->baseDim != uu->nin->dim ) {
biffAddf( MREND, "%s: input nrrd needs %d dimensions, not %d",
me, + uu->kind->baseDim, uu->nin->dim );
return 1;
}
if ( !( uu->nin->axis[0].center == uu->nin->axis[1].center &&
uu->nin->axis[0].center == uu->nin->axis[2].center ) ) {
biffAddf( MREND, "%s: axes 0, 1, 2 centerings ( %s, %s, %s ) not equal", me,
airEnumStr( nrrdCenter, uu->nin->axis[0].center ),
airEnumStr( nrrdCenter, uu->nin->axis[1].center ),
airEnumStr( nrrdCenter, uu->nin->axis[2].center ) );
return 1;
}
if ( 1 != uu->kind->table[uu->whatq].answerLength ) {
biffAddf( MREND, "%s: quantity %s ( in %s volumes ) isn't a scalar; "
"can't render it",
me, airEnumStr( uu->kind->enm, uu->whatq ), uu->kind->name );
return 1;
}
return 0;
}
/* -------------------------------------------------------------- */
typedef struct mrendRender_t {
double time0, time1; /* render start and end times */
Nrrd *nout; /* output image: always 2D array of doubles */
double *imgData; /* output image data */
int sx, sy, /* image dimensions */
totalSamples; /* total number of samples used for all rays */
struct mrendThread_t *tinfo[HOOVER_THREAD_MAX];
} mrendRender;
typedef struct mrendThread_t {
double *val, /* array of ray samples */
rayLen, /* length of ray segment between near and far */
rayStep; /* ray step needed FOR THIS RAY, to acheive sampling on
planes ( same scaling of uu->rayStep ) */
int thrid, /* thread ID */
valLen, /* allocated size of val */
valNum, /* number of values set in val ( index of next value ) */
ui, vi, /* image coords */
numSamples, /* total number of samples this thread has done */
verbose; /* blah blah blah blah */
gageContext *gctx; /* thread-specific gage context ( or copy of uu->gctx0
for the first thread ) */
const double *answer; /* pointer to the SINGLE answer we care about */
} mrendThread;
int
166 mrendRenderBegin( mrendRender **rrP, mrendUser *uu ) {
static const char me[]="mrendRenderBegin";
gagePerVolume *pvl;
int E;
unsigned int thr;
/* this assumes that mrendUserCheck( uu ) has passed */
*rrP = AIR_CALLOC( 1, mrendRender );
airMopAdd( uu->mrmop, *rrP, airFree, airMopAlways );
/* pvl managed via parent context */
( *rrP )->time0 = airTime( );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( uu->gctx0, uu->nin, uu->kind ) );
if ( !E ) E |= gagePerVolumeAttach( uu->gctx0, pvl );
if ( !E ) E |= gageKernelSet( uu->gctx0, gageKernel00,
uu->ksp[gageKernel00]->kernel,
uu->ksp[gageKernel00]->parm );
if ( !E ) E |= gageKernelSet( uu->gctx0, gageKernel11,
uu->ksp[gageKernel11]->kernel,
uu->ksp[gageKernel11]->parm );
if ( !E ) E |= gageKernelSet( uu->gctx0, gageKernel22,
uu->ksp[gageKernel22]->kernel,
uu->ksp[gageKernel22]->parm );
if ( !E ) E |= gageQueryItemOn( uu->gctx0, pvl, uu->whatq );
if ( !E ) E |= gageUpdate( uu->gctx0 );
if ( E ) {
biffMovef( MREND, GAGE, "%s: gage trouble", me );
return 1;
}
fprintf( stderr, "%s: kernel support = %d^3 samples\n", me,
2*uu->gctx0->radius );
if ( nrrdMaybeAlloc_va( ( *rrP )->nout=nrrdNew( ), nrrdTypeDouble, 2,
AIR_CAST( size_t, uu->hctx->imgSize[0] ),
AIR_CAST( size_t, uu->hctx->imgSize[1] ) ) ) {
biffMovef( MREND, NRRD, "%s: nrrd trouble", me );
return 1;
}
( *rrP )->nout->axis[0].min = uu->hctx->cam->uRange[0];
( *rrP )->nout->axis[0].max = uu->hctx->cam->uRange[1];
( *rrP )->nout->axis[1].min = uu->hctx->cam->vRange[0];
( *rrP )->nout->axis[1].max = uu->hctx->cam->vRange[1];
airMopAdd( uu->mrmop, ( *rrP )->nout, ( airMopper )nrrdNuke, airMopAlways );
( *rrP )->imgData = AIR_CAST( double*, ( *rrP )->nout->data );
( *rrP )->sx = uu->hctx->imgSize[0];
( *rrP )->sy = uu->hctx->imgSize[1];
for ( thr=0; thr<uu->hctx->numThreads; thr++ ) {
( *rrP )->tinfo[thr] = AIR_CALLOC( 1, mrendThread );
airMopAdd( uu->mrmop, ( *rrP )->tinfo[thr], airFree, airMopAlways );
}
return 0;
}
int
225 mrendRenderEnd( mrendRender *rr, mrendUser *uu ) {
static const char me[]="mrendRenderEnd";
unsigned int thr;
/* add up # samples from all threads */
rr->totalSamples = 0;
for ( thr=0; thr<uu->hctx->numThreads; thr++ ) {
rr->totalSamples += rr->tinfo[thr]->numSamples;
}
rr->time1 = airTime( );
fprintf( stderr, "\n" );
fprintf( stderr, "%s: rendering time = %g secs\n", me,
rr->time1 - rr->time0 );
fprintf( stderr, "%s: sampling rate = %g KHz\n", me,
rr->totalSamples/( 1000.0*( rr->time1 - rr->time0 ) ) );
if ( nrrdSave( uu->outS, rr->nout, NULL ) ) {
biffMovef( MREND, NRRD, "%s: trouble saving image", me );
return 1;
}
return 0;
}
/* -------------------------------------------------------------- */
int
252 mrendThreadBegin( mrendThread **ttP,
mrendRender *rr, mrendUser *uu, int whichThread ) {
/* allocating the mrendThreads should be part of the thread body,
but as long as there isn't a mutex around registering them with
the airMop in the mrendRender, then all that needs to be done
as part of mrendRenderBegin ( see above ) */
( *ttP ) = rr->tinfo[whichThread];
if ( !whichThread ) {
/* this is the first thread- it just points to the parent gageContext */
( *ttP )->gctx = uu->gctx0;
} else {
/* we have to generate a new gageContext */
( *ttP )->gctx = gageContextCopy( uu->gctx0 );
}
( *ttP )->answer = gageAnswerPointer( ( *ttP )->gctx,
( *ttP )->gctx->pvl[0], uu->whatq );
( *ttP )->val = NULL;
( *ttP )->valLen = 0;
( *ttP )->valNum = 0;
( *ttP )->rayLen = 0;
( *ttP )->thrid = whichThread;
( *ttP )->numSamples = 0;
( *ttP )->verbose = 0;
return 0;
}
int
280 mrendThreadEnd( mrendThread *tt, mrendRender *rr, mrendUser *uu ) {
AIR_UNUSED( rr );
AIR_UNUSED( uu );
if ( tt->thrid ) {
tt->gctx = gageContextNix( tt->gctx );
}
tt->val = AIR_CAST( double*, airFree( tt->val ) );
return 0;
}
/* -------------------------------------------------------------- */
int
295 mrendRayBegin( mrendThread *tt, mrendRender *rr, mrendUser *uu,
int uIndex,
int vIndex,
double rayLen,
double rayStartWorld[3],
double rayStartIndex[3],
double rayDirWorld[3],
double rayDirIndex[3] ) {
static const char me[]="mrendRayBegin";
int newLen;
AIR_UNUSED( rr );
AIR_UNUSED( rayStartWorld );
AIR_UNUSED( rayStartIndex );
AIR_UNUSED( rayDirWorld );
AIR_UNUSED( rayDirIndex );
tt->ui = uIndex;
tt->vi = vIndex;
if ( !( -1 == uu->verbPixel[0] && -1 == uu->verbPixel[1] ) ) {
if ( uIndex == uu->verbPixel[0] && vIndex == uu->verbPixel[1] ) {
fprintf( stderr, "\n%s: verbose for pixel ( %d, %d )\n", me,
uu->verbPixel[0], uu->verbPixel[1] );
gageParmSet( tt->gctx, gageParmVerbose, 6 );
tt->verbose = 6;
} else {
gageParmSet( tt->gctx, gageParmVerbose, AIR_FALSE );
tt->verbose = 0;
}
}
tt->rayLen = rayLen;
tt->rayStep = ( uu->rayStep*tt->rayLen /
( uu->hctx->cam->vspFaar - uu->hctx->cam->vspNeer ) );
newLen = AIR_ROUNDUP( rayLen/tt->rayStep ) + 1;
if ( !tt->val || newLen > tt->valLen ) {
tt->val = AIR_CAST( double*, airFree( tt->val ) );
tt->valLen = newLen;
tt->val = AIR_CALLOC( newLen, double );
}
tt->valNum = 0;
if ( !uIndex ) {
fprintf( stderr, "%d/%d ", vIndex, uu->hctx->imgSize[1] );
fflush( stderr );
}
if ( 0 == uIndex && 0 == vIndex ) {
ELL_3V_COPY( uu->imgOrig, rayStartWorld );
} else if ( 1 == uIndex && 0 == vIndex ) {
ELL_3V_COPY( uu->imgU, rayStartWorld ); /* will fix later */
} else if ( 0 == uIndex && 1 == vIndex ) {
ELL_3V_COPY( uu->imgV, rayStartWorld ); /* will fix later */
}
fflush( stderr );
return 0;
}
int
352 mrendRayEnd( mrendThread *tt, mrendRender *rr, mrendUser *uu ) {
double answer;
if ( tt->valNum ) {
nrrdMeasureLine[uu->measr]( &answer,
nrrdTypeDouble,
tt->val, nrrdTypeDouble,
tt->valNum,
0, tt->rayLen );
answer = AIR_EXISTS( answer ) ? answer : uu->fromNaN;
rr->imgData[( tt->ui ) + ( rr->sx )*( tt->vi )] = answer;
} else {
rr->imgData[( tt->ui ) + ( rr->sx )*( tt->vi )] = 0.0;
}
return 0;
}
/* -------------------------------------------------------------- */
double
373 mrendSample( mrendThread *tt, mrendRender *rr, mrendUser *uu,
int num, double rayT,
int inside,
double samplePosWorld[3],
double samplePosIndex[3] ) {
static const char me[]="mrendSample";
AIR_UNUSED( rr );
AIR_UNUSED( uu );
AIR_UNUSED( num );
AIR_UNUSED( rayT );
AIR_UNUSED( samplePosWorld );
if ( tt->verbose ) {
fprintf( stderr, "%s: wrld( %g, %g, %g ) -> indx( %g, %g, %g ) -> %s\n", me,
samplePosWorld[0], samplePosWorld[1], samplePosWorld[2],
samplePosIndex[0], samplePosIndex[1], samplePosIndex[2],
inside ? "INSIDE" : "( outside )" );
}
if ( inside ) {
if ( gageProbe( tt->gctx,
samplePosIndex[0],
samplePosIndex[1],
samplePosIndex[2] ) ) {
biffAddf( MREND, "%s: gage trouble: %s ( %d )", me,
tt->gctx->errStr, tt->gctx->errNum );
return AIR_NAN;
}
if ( tt->verbose ) {
fprintf( stderr, "%s: val[%d] = %g\n", me,
tt->valNum, *( tt->answer ) );
}
tt->val[tt->valNum++] = *( tt->answer );
if ( tt->verbose ) {
fprintf( stderr, " ........ %g\n", tt->val[tt->valNum-1] );
}
tt->numSamples++;
}
return tt->rayStep;
}
/* -------------------------------------------------------------- */
#if 0
this was nixed once mrender learned to handle volume of general
kind, instead of being restricted to scalars
/*
** learned: if you're playing games with strings with two passes, where
** you first generate the set of strings in order to calculate their
** cumulative length, and then ( 2nd pass ) concatenate the strings
** together, be very sure that the generation of the strings on the
** two passes is identical. Had a very troublesome memory error because
** I was using short version of the description string to determine
** allocation, and then the long version in the second pass
*/
char *
mrendGage( char *prefix ) {
char *line, *ret;
int i, len;
/* 1st pass through- determine needed buffer size */
len = 0;
for ( i=airEnumUnknown( gageScl )+1; !airEnumValCheck( gageScl, i ); i++ ) {
if ( 1 == gageKindScl->table[i].answerLength ) {
line = airEnumFmtDesc( gageScl, i, AIR_FALSE, "\n \b\bo \"%s\": %s" );
len += strlen( line );
free( line );
}
}
ret = AIR_CALLOC( strlen( prefix ) + len + 1, char );
if ( ret ) {
strcpy( ret, prefix );
/* 2nd pass through: create output */
for ( i=airEnumUnknown( gageScl )+1; !airEnumValCheck( gageScl, i ); i++ ) {
if ( 1 == gageKindScl->table[i].answerLength ) {
line = airEnumFmtDesc( gageScl, i, AIR_FALSE, "\n \b\bo \"%s\": %s" );
strcat( ret, line );
free( line );
}
}
}
return ret;
}
#endif
int
main( int argc, const char *argv[] ) {
hestOpt *hopt=NULL;
hestParm *hparm;
int E, Ecode, Ethread, renorm, offfr;
const char *me;
char *errS, *whatS;
mrendUser *uu;
float isScale;
airArray *mop;
double gmc, turn, eye[3], eyedist;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hparm->respFileEnable = AIR_TRUE;
uu = mrendUserNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
airMopAdd( mop, uu, ( airMopper )mrendUserNix, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &( uu->nin ), NULL,
"input nrrd to render", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &( uu->kind ), NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", or \"tensor\" )",
NULL, NULL, meetHestGageKind );
limnHestCameraOptAdd( &hopt, uu->hctx->cam,
NULL, "0 0 0", "0 0 1",
NULL, NULL, NULL,
"nan nan", "nan nan", "20" );
hestOptAdd( &hopt, "offfr", NULL, airTypeInt, 0, 0, &offfr, NULL,
"the given eye point ( \"-fr\" ) is to be interpreted "
"as an offset from the at point." );
hestOptAdd( &hopt, "turn", "angle", airTypeDouble, 1, 1, &turn, "0.0",
"angle ( degrees ) by which to rotate the from point around "
"true up, for making stereo pairs. Positive means move "
"towards positive U ( the right )" );
hestOptAdd( &hopt, "is", "image size", airTypeInt, 2, 2, uu->hctx->imgSize,
"256 256", "image dimensions" );
hestOptAdd( &hopt, "iss", "scale", airTypeFloat, 1, 1, &isScale, "1.0",
"scaling of image size ( from \"is\" )" );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1,
&( uu->ksp[gageKernel00] ), "tent",
"value reconstruction kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kernel", airTypeOther, 1, 1,
&( uu->ksp[gageKernel11] ), "cubicd:1, 0",
"first derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kernel", airTypeOther, 1, 1,
&( uu->ksp[gageKernel22] ), "cubicdd:1, 0",
"second derivative kernel",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "rn", NULL, airTypeBool, 0, 0, &renorm, NULL,
"renormalize kernel weights at each new sample location. "
"\"Accurate\" kernels don't need this; doing it always "
"makes things go slower" );
hestOptAdd( &hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL,
"the quantity ( scalar, vector, or matrix ) to learn by probing" );
hestOptAdd( &hopt, "m", "measure", airTypeEnum, 1, 1, &( uu->measr ), NULL,
"how to collapse list of ray samples into one scalar. "
NRRD_MEASURE_DESC,
NULL, nrrdMeasure );
hestOptAdd( &hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc, "0.0",
"For curvature-related queries, set answer to zero when "
"gradient magnitude is below this" );
hestOptAdd( &hopt, "fn", "from nan", airTypeDouble, 1, 1, &( uu->fromNaN ),
"nan", "When histo-based measures generate NaN answers, the "
"value that should be substituted for NaN." );
hestOptAdd( &hopt, "step", "size", airTypeDouble, 1, 1, &( uu->rayStep ),
"0.01", "step size along ray in world space" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1,
&( uu->hctx->numThreads ),
"1", "number of threads hoover should use" );
hestOptAdd( &hopt, "vp", "img coords", airTypeInt, 2, 2, &( uu->verbPixel ),
"-1 -1", "pixel coordinates for which to turn on all verbose "
"debugging messages, or \"-1 -1\" to disable this." );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &( uu->outS ), "-",
"file to write output nrrd to. Defaults to stdout ( \"-\" )." );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
uu->hctx->imgSize[0] = AIR_CAST( int, isScale*uu->hctx->imgSize[0] );
uu->hctx->imgSize[1] = AIR_CAST( int, isScale*uu->hctx->imgSize[1] );
uu->whatq = airEnumVal( uu->kind->enm, whatS );
if ( -1 == uu->whatq ) {
/* -1 indeed always means "unknown" for any gageKind */
fprintf( stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n",
me, whatS, uu->kind->name );
hestUsage( stderr, hopt, me, hparm );
hestGlossary( stderr, hopt, hparm );
airMopError( mop );
return 1;
}
if ( mrendUserCheck( uu ) ) {
fprintf( stderr, "%s: problem with input parameters:\n%s\n",
me, errS = biffGetDone( MREND ) ); free( errS );
airMopError( mop );
return 1;
}
if ( gageShapeSet( uu->shape, uu->nin, uu->kind->baseDim ) ) {
fprintf( stderr, "%s: problem with shape:\n%s\n",
me, errS = biffGetDone( GAGE ) ); free( errS );
airMopError( mop );
return 1;
}
gageParmSet( uu->gctx0, gageParmGradMagCurvMin, gmc );
/* gageParmSet( uu->gctx0, gageParmRequireAllSpacings, AIR_FALSE ); */
gageParmSet( uu->gctx0, gageParmRenormalize, renorm );
fprintf( stderr, "%s: will render %s of %s in %s volume\n", me,
airEnumStr( nrrdMeasure, uu->measr ),
airEnumStr( uu->kind->enm, uu->whatq ), uu->kind->name );
if ( offfr ) {
ELL_3V_INCR( uu->hctx->cam->from, uu->hctx->cam->at );
}
if ( limnCameraAspectSet( uu->hctx->cam,
uu->hctx->imgSize[0], uu->hctx->imgSize[1],
nrrdCenterCell )
|| limnCameraUpdate( uu->hctx->cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting camera:\n%s\n", me, errS );
airMopError( mop );
return 1;
}
if ( turn ) {
turn *= AIR_PI/180;
ELL_3V_SUB( eye, uu->hctx->cam->from, uu->hctx->cam->at );
ELL_3V_NORM( eye, eye, eyedist );
ELL_3V_SCALE_ADD2( uu->hctx->cam->from,
cos( turn ), eye,
sin( turn ), uu->hctx->cam->U );
ELL_3V_SCALE( uu->hctx->cam->from, eyedist, uu->hctx->cam->from );
if ( limnCameraUpdate( uu->hctx->cam ) ) {
airMopAdd( mop, errS = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting camera ( again ):\n%s\n", me, errS );
airMopError( mop );
return 1;
}
}
/*
fprintf( stderr, "%s: camera info\n", me );
fprintf( stderr, " U = {%g, %g, %g}\n",
uu->hctx->cam->U[0], uu->hctx->cam->U[1], uu->hctx->cam->U[2] );
fprintf( stderr, " V = {%g, %g, %g}\n",
uu->hctx->cam->V[0], uu->hctx->cam->V[1], uu->hctx->cam->V[2] );
fprintf( stderr, " N = {%g, %g, %g}\n",
uu->hctx->cam->N[0], uu->hctx->cam->N[1], uu->hctx->cam->N[2] );
*/
/* set remaining fields of hoover context */
if ( 0 ) {
/* this is the old code that assumed everything about orientation
was determined by per-axis spacing ( pre-gageShape ) */
unsigned int base;
base = uu->kind->baseDim;
uu->hctx->volSize[0] = uu->nin->axis[base+0].size;
uu->hctx->volSize[1] = uu->nin->axis[base+1].size;
uu->hctx->volSize[2] = uu->nin->axis[base+2].size;
uu->hctx->volSpacing[0] = uu->nin->axis[base+0].spacing;
uu->hctx->volSpacing[1] = uu->nin->axis[base+1].spacing;
uu->hctx->volSpacing[2] = uu->nin->axis[base+2].spacing;
if ( nrrdCenterUnknown != uu->nin->axis[base].center ) {
uu->hctx->volCentering = uu->nin->axis[base].center;
fprintf( stderr, "%s: setting volCentering to %s\n", me,
airEnumStr( nrrdCenter, uu->nin->axis[base].center ) );
}
fprintf( stderr, "!%s: uu->hctx->volCentering = %d\n",
me, uu->hctx->volCentering );
} else {
uu->hctx->shape = uu->shape;
}
/* this is reasonable for now */
uu->hctx->imgCentering = nrrdCenterCell;
uu->hctx->user = uu;
uu->hctx->renderBegin = ( hooverRenderBegin_t * )mrendRenderBegin;
uu->hctx->threadBegin = ( hooverThreadBegin_t * )mrendThreadBegin;
uu->hctx->rayBegin = ( hooverRayBegin_t * )mrendRayBegin;
uu->hctx->sample = ( hooverSample_t * )mrendSample;
uu->hctx->rayEnd = ( hooverRayEnd_t * )mrendRayEnd;
uu->hctx->threadEnd = ( hooverThreadEnd_t * )mrendThreadEnd;
uu->hctx->renderEnd = ( hooverRenderEnd_t * )mrendRenderEnd;
if ( !airThreadCapable && 1 != uu->hctx->numThreads ) {
fprintf( stderr, "%s: This Teem not compiled with "
"multi-threading support.\n", me );
fprintf( stderr, "%s: --> can't use %d threads; only using 1\n",
me, uu->hctx->numThreads );
uu->hctx->numThreads = 1;
}
E = hooverRender( uu->hctx, &Ecode, &Ethread );
if ( E ) {
if ( hooverErrInit == E ) {
fprintf( stderr, "%s: ERROR ( code %d, thread %d ):\n%s\n",
me, Ecode, Ethread, errS = biffGetDone( HOOVER ) );
free( errS );
} else {
fprintf( stderr, "%s: ERROR ( code %d, thread %d ):\n%s\n",
me, Ecode, Ethread, errS = biffGetDone( MREND ) );
free( errS );
}
airMopError( mop );
return 1;
}
if ( 1 ) {
ELL_3V_SUB( uu->imgU, uu->imgU, uu->imgOrig );
ELL_3V_SUB( uu->imgV, uu->imgV, uu->imgOrig );
fprintf( stderr, "%s: loc( 0, 0 ) = ( %g, %g, %g )\n", me,
uu->imgOrig[0], uu->imgOrig[1], uu->imgOrig[2] );
fprintf( stderr, "%s: loc( 1, 0 ) - loc( 0, 0 ) = ( %g, %g, %g )\n", me,
uu->imgU[0], uu->imgU[1], uu->imgU[2] );
fprintf( stderr, "%s: loc( 0, 1 ) - loc( 0, 0 ) = ( %g, %g, %g )\n", me,
uu->imgV[0], uu->imgV[1], uu->imgV[2] );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/hest.h>
#include <teem/biff.h>
#include <teem/nrrd.h>
#include <teem/ell.h> /* for macros only */
#define NINSPECT "ninspect"
int
34 fixproj( Nrrd *nproj[3], const Nrrd *nvol ) {
static const char me[]="fixproj";
airArray *mop;
Nrrd *ntmp[3], *nt;
int sz[3], ii, map[3], h[3], E, mi;
size_t rsz[3];
double vec[3][3], dot[3], sp[3], parm[NRRD_KERNEL_PARMS_NUM];
mop = airMopNew( );
if ( !( ELL_3V_EXISTS( nvol->axis[0].spaceDirection )
&& ELL_3V_EXISTS( nvol->axis[1].spaceDirection )
&& ELL_3V_EXISTS( nvol->axis[2].spaceDirection ) ) ) {
biffAddf( NINSPECT, "%s: space directions don't exist for all 3 axes", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nt = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp[0] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp[1] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp[2] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* RL AP SI */
ELL_3V_SET( vec[0], 1, 0, 0 );
ELL_3V_SET( vec[1], 0, 1, 0 );
ELL_3V_SET( vec[2], 0, 0, 1 );
for ( ii=0; ii<3; ii++ ) {
dot[0] = ELL_3V_DOT( vec[ii], nvol->axis[0].spaceDirection );
dot[1] = ELL_3V_DOT( vec[ii], nvol->axis[1].spaceDirection );
dot[2] = ELL_3V_DOT( vec[ii], nvol->axis[2].spaceDirection );
dot[0] = AIR_ABS( dot[0] );
dot[1] = AIR_ABS( dot[1] );
dot[2] = AIR_ABS( dot[2] );
map[ii] = ELL_MAX3_IDX( dot[0], dot[1], dot[2] );
}
ELL_3V_SET( h, 1, 0, 0 );
E = 0;
for ( ii=0; ii<3; ii++ ) {
if ( h[map[ii]] != map[h[ii]] ) {
if ( !E ) E |= nrrdAxesSwap( ntmp[ii], nproj[map[ii]], 1, 2 );
} else {
if ( !E ) E |= nrrdCopy( ntmp[ii], nproj[map[ii]] );
}
}
if ( E ) {
biffMovef( NINSPECT, NRRD, "%s: trouble with nrrd operations", me );
airMopError( mop ); return 1;
}
E = 0;
if ( nvol->axis[map[0]].spaceDirection[0] > 0 ) {
if ( !E ) E |= nrrdFlip( nt, ntmp[1], 0+1 );
if ( !E ) E |= nrrdCopy( ntmp[1], nt );
if ( !E ) E |= nrrdFlip( nt, ntmp[2], 0+1 );
if ( !E ) E |= nrrdCopy( ntmp[2], nt );
}
if ( nvol->axis[map[1]].spaceDirection[1] > 0 ) {
if ( !E ) E |= nrrdFlip( nt, ntmp[0], 0+1 );
if ( !E ) E |= nrrdCopy( ntmp[0], nt );
if ( !E ) E |= nrrdFlip( nt, ntmp[2], 1+1 );
if ( !E ) E |= nrrdCopy( ntmp[2], nt );
}
if ( nvol->axis[map[2]].spaceDirection[2] > 0 ) {
if ( !E ) E |= nrrdFlip( nt, ntmp[0], 1+1 );
if ( !E ) E |= nrrdCopy( ntmp[0], nt );
if ( !E ) E |= nrrdFlip( nt, ntmp[1], 1+1 );
if ( !E ) E |= nrrdCopy( ntmp[1], nt );
}
if ( E ) {
biffMovef( NINSPECT, NRRD, "%s: trouble with nrrd operations", me );
airMopError( mop ); return 1;
}
for ( ii=0; ii<3; ii++ ) {
sz[ii] = nvol->axis[map[ii]].size;
sp[ii] = ELL_3V_LEN( nvol->axis[map[ii]].spaceDirection );
}
mi = ELL_MIN3_IDX( sp[0], sp[1], sp[2] );
sz[0] = ( int )( sz[0]*sp[0]/sp[mi] );
sz[1] = ( int )( sz[1]*sp[1]/sp[mi] );
sz[2] = ( int )( sz[2]*sp[2]/sp[mi] );
parm[0] = 1;
ELL_3V_SET( rsz, 3, sz[1], sz[2] );
nrrdSimpleResample( nproj[0], ntmp[0], nrrdKernelBox, parm, rsz, NULL );
ELL_3V_SET( rsz, 3, sz[0], sz[2] );
nrrdSimpleResample( nproj[1], ntmp[1], nrrdKernelBox, parm, rsz, NULL );
ELL_3V_SET( rsz, 3, sz[0], sz[1] );
nrrdSimpleResample( nproj[2], ntmp[2], nrrdKernelBox, parm, rsz, NULL );
airMopOkay( mop );
return 0;
}
int
128 ninspect_proj( Nrrd *nout, const Nrrd *nin, int axis, int smart, float amount ) {
static const char me[]="ninspect_proj";
airArray *mop;
Nrrd *ntmpA, *ntmpB, **nrgb;
int bins;
if ( !( nout && nin ) ) {
biffAddf( NINSPECT, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_CL( 0, axis, 2 ) ) ) {
biffAddf( NINSPECT, "%s: given axis %d outside valid range [0, 1, 2]",
me, axis );
return 1;
}
/* allocate a bunch of nrrds to use as basically temp variables */
mop = airMopNew( );
airMopAdd( mop, ntmpA = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmpB = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* HEY: this used to be nrgb[3], but its passing to nrrdJoin caused
"dereferencing type-punned pointer might break strict-aliasing rules"
warning; GLK not sure how else to fix it */
nrgb = AIR_CALLOC( 3, Nrrd* );
airMopAdd( mop, nrgb, airFree, airMopAlways );
airMopAdd( mop, nrgb[0] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nrgb[1] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nrgb[2] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* these arguments to nrrdHistoEq will control its behavior */
bins = 3000; /* equalization will use a histogram with this many bins */
/* the following idiom is one way of handling the fact that any
non-trivial nrrd call can fail, and if it does, then any subsequent
nrrd calls should be avoided ( to be perfectly safe ), so that you can
get the error message from biff. Because of the left-to-right ordering
ensured for logical expressions, this will all be called in sequence
until one of them has a non-zero return. If he had exception handling,
we'd put all the nrrd calls in one "try" block. */
if ( nrrdProject( ntmpA, nin, axis, nrrdMeasureSum, nrrdTypeDefault )
|| nrrdHistoEq( ntmpB, ntmpA, NULL, bins, smart, amount )
|| nrrdQuantize( nrgb[0], ntmpB, NULL, 8 )
|| nrrdProject( ntmpA, nin, axis, nrrdMeasureVariance, nrrdTypeDefault )
|| nrrdHistoEq( ntmpB, ntmpA, NULL, bins, smart, amount )
|| nrrdQuantize( nrgb[1], ntmpB, NULL, 8 )
|| nrrdProject( ntmpA, nin, axis, nrrdMeasureMax, nrrdTypeDefault )
|| nrrdQuantize( nrgb[2], ntmpA, NULL, 8 )
|| nrrdJoin( nout, ( const Nrrd*const* )nrgb, 3, 0, AIR_TRUE ) ) {
biffMovef( NINSPECT, NRRD, "%s: trouble with nrrd operations", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
185 doit( Nrrd *nout, const Nrrd *nin, int smart, float amount, unsigned int margin,
const unsigned char *back ) {
static const char me[]="doit";
Nrrd *nproj[3];
airArray *mop;
int axis, srl, sap, ssi, E, which;
size_t min[3], ii, nn;
unsigned char *out;
if ( !( nout && nin && back ) ) {
biffAddf( NINSPECT, "%s: got NULL pointer", me );
return 1;
}
if ( !( 3 == nin->dim ) ) {
biffAddf( NINSPECT, "%s: given nrrd has dimension %d, not 3\n",
me, nin->dim );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nproj[0]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nproj[1]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nproj[2]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* do projections for each axis, with some progress indication to sterr */
for ( axis=0; axis<=2; axis++ ) {
fprintf( stderr, "%s: doing axis %d projections ... ", me, axis );
fflush( stderr );
if ( ninspect_proj( nproj[axis], nin, axis, smart, amount ) ) {
fprintf( stderr, "ERROR\n" );
biffAddf( NINSPECT, "%s: trouble doing projections for axis %d",
me, axis );
airMopError( mop ); return 1;
}
fprintf( stderr, "done\n" );
}
if ( nrrdSpaceRightAnteriorSuperior == nin->space ) {
if ( fixproj( nproj, nin ) ) {
fprintf( stderr, "ERROR\n" );
biffAddf( NINSPECT, "%s: trouble orienting projections", me );
airMopError( mop ); return 1;
}
}
srl = nproj[1]->axis[0+1].size;
sap = nproj[0]->axis[0+1].size;
ssi = nproj[1]->axis[1+1].size;
/* allocate output as 8-bit color image. We know output type is
nrrdTypeUChar because ninspect_proj finishes each projection
with nrrdQuantize to 8-bits */
if ( nrrdMaybeAlloc_va( nout, nrrdTypeUChar, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, srl + 3*margin + sap ),
AIR_CAST( size_t, ssi + 3*margin + sap ) ) ) {
biffMovef( NINSPECT, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
nn = nout->axis[1].size * nout->axis[2].size;
out = AIR_CAST( unsigned char *, nout->data );
for ( ii=0; ii<nn; ii++ ) {
ELL_3V_COPY( out + 3*ii, back );
}
min[0] = 0;
E = 0;
which = 0;
if ( !E ) { min[1] = margin; min[2] = margin; which = 1; }
if ( !E ) E |= nrrdInset( nout, nout, nproj[1], min );
if ( !E ) { min[1] = margin; min[2] = 2*margin + ssi; which = 2; }
if ( !E ) E |= nrrdInset( nout, nout, nproj[2], min );
if ( !E ) { min[1] = 2*margin + srl; min[2] = margin; which = 3; }
if ( !E ) E |= nrrdInset( nout, nout, nproj[0], min );
if ( E ) {
biffAddf( NINSPECT, NRRD, "%s: couldn't composite output ( which = %d )",
me, which );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
static const char *info =
( "Quick way of seeing what's inside a 3D volume. A color image "
"of three axis-aligned projections is composed of histogram-"
"equalized and quantized images of the summation ( red ), "
"variance ( green ), and maximum ( blue ) intensity projections. "
"If volume is orientation in RAS space, then a standard "
"orientation is used for projections and projections are "
"upsampled ( with box kernel ) to have isotropic pixels." );
int
279 main( int argc, const char *argv[] ) {
hestOpt *hopt=NULL;
airArray *mop;
const char *me;
char *outS, *err;
Nrrd *nin, *nout;
NrrdIoState *nio;
unsigned int margin;
float heqamount;
unsigned int back[3];
unsigned char backUC[3];
me = argv[0];
mop = airMopNew( );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd to project. Must be three dimensional.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "amt", "heq", airTypeFloat, 1, 1, &heqamount, "0.5",
"how much to apply histogram equalization to projection images" );
hestOptAdd( &hopt, "m", "margin", airTypeUInt, 1, 1, &margin, "6",
"pixel size of margin on boundary, and space between the projections" );
hestOptAdd( &hopt, "b", "background", airTypeUInt, 3, 3, back, "0 0 0",
"background color ( 8-bit RGB )" );
hestOptAdd( &hopt, "o", "img out", airTypeString, 1, 1, &outS,
NULL, "output image to save to. Will try to use whatever "
"format is implied by extension, but will fall back to PPM." );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nrrdStateDisableContent = AIR_TRUE;
ELL_3V_COPY_TT( backUC, unsigned char, back );
if ( doit( nout, nin, 1, heqamount, margin, backUC ) ) {
err=biffGetDone( NINSPECT );
airMopAdd( mop, err, airFree, airMopAlways );
fprintf( stderr, "%s: trouble creating output:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nrrdFormatPNG->nameLooksLike( outS )
&& !nrrdFormatPNG->available( ) ) {
fprintf( stderr, "( %s: using PPM format for output )\n", me );
nio->format = nrrdFormatPNM;
}
if ( nrrdSave( outS, nout, nio ) ) {
err=biffGetDone( NRRD );
airMopAdd( mop, err, airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output image \"%s\":\n%s",
me, outS, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/biff.h>
#include <teem/nrrd.h>
int
29 main( int argc, char *argv[] ) {
char *me, *err;
int enc, form;
me = argv[0];
if ( 2 == argc ) {
if ( !strcmp( argv[1], "--version" ) ) {
char vbuff[AIR_STRLEN_LARGE];
airTeemVersionSprint( vbuff );
printf( "%s\n", vbuff );
exit( 0 );
} else if ( !strcmp( argv[1], "--help" ) ) {
char par1[] = "\n Usage: nrrdSanity\n ";
char par2[] = "\t\t\t\t"
"nrrdSanity calls the nrrdSanity( ) check to verify the correctness "
"of all the information ( set at compile-time ) about the architecture, "
"such as endianness, 32/64-bit, and the size of various types, as "
"well as running sanity checks on the global default ( nrrdDefault* ) "
"and state ( nrrdState* ) variables. ";
char par3[] = "\t\t\t\t"
"As a convenience, nrrdSanity also list the availability of the "
"different formats and data encodings ( for Nrrd files ) supported "
"by this build.\n ";
_hestPrintStr( stdout, 1, 0, 78, par1, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par2, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par3, AIR_FALSE );
exit( 0 );
} else {
fprintf( stderr, "%s: unexpected arguments; "
"\"%s --help\" for more information\n", me, me );
exit( 1 );
}
}
/* else it was run with no arguments */
if ( !nrrdSanity( ) ) {
printf( "%s: nrrd sanity check FAILED:\n%s\n", me, err = biffGet( NRRD ) );
free( err );
return 1;
}
else {
printf( "%s: nrrd sanity check passed.\n", me );
printf( "\n" );
printf( "%s: encodings supported in this build:\n", me );
for ( enc=nrrdEncodingTypeUnknown+1; enc<nrrdEncodingTypeLast; enc++ ) {
printf( "%s: %s\n", airEnumStr( nrrdEncodingType, enc ),
nrrdEncodingArray[enc]->available( ) ? "yes" : "not available" );
}
printf( "%s: formats supported in this build:\n", me );
for ( form=nrrdFormatTypeUnknown+1; form<nrrdFormatTypeLast; form++ ) {
printf( "%s: %s\n", airEnumStr( nrrdFormatType, form ),
nrrdFormatArray[form]->available( ) ? "yes" : "not available" );
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
static const char *overInfo = (
"Composites an RGBA nrrd over "
"a background color ( or image ), after doing gamma correction, "
"then quantizes to an 8-bit image. Actually, the "
"input nrrd can have more than 4 values per pixel, "
"but only the first four are used. If the RGBA nrrd "
"is floating point, the values are taken at face value; "
"if it is fixed point, the values interpreted as having "
"been quantized ( so that 8-bit RGBA images will act as "
"you expect ). When compositing with a background image, the given "
"background image does not have to be the same size as the input "
"image; it will be resampled ( with linear interpolation ) to fit. " );
double
43 docontrast( double val, double cfp, double cpow ) {
double v;
if ( val < cfp ) {
v = AIR_AFFINE( 0.0, val, cfp, 0.0, 1.0 );
v = pow( v, cpow );
val = AIR_AFFINE( 0.0, v, 1.0, 0.0, cfp );
} else {
v = AIR_AFFINE( cfp, val, 1.0, 1.0, 0.0 );
v = pow( v, cpow );
val = AIR_AFFINE( 1.0, v, 0.0, cfp, 1.0 );
}
return val;
}
int
59 main( int argc, const char *argv[] ) {
hestOpt *hopt=NULL;
Nrrd *nin, *nout, /* initial input and final output */
*ninD, /* input converted to double */
*_nbg, /* given background image ( optional ) */
*nbg, /* resampled background image */
*nrgbaD; /* rgba input as double */
const char *me;
char *outS, *errS;
double _gamma, contr, cfp, cpow, back[3], *rgbaD, r, g, b, a;
airArray *mop;
int E;
size_t min[3], max[3], sx, sy, pi;
unsigned char *outUC, *bgUC;
NrrdResampleInfo *rinfo;
me = argv[0];
mop = airMopNew( );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd to composite", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "c", "contrast", airTypeDouble, 1, 1, &contr, "0.0",
"contrast to apply to RGB values, before gamma. \"0.0\" "
"means no change, \"1.0\" means thresholding, \"-1.0\" "
"means a complete washout." );
hestOptAdd( &hopt, "cfp", "fixed point", airTypeDouble, 1, 1, &cfp, "0.5",
"component level that doesn't change with contrast" );
hestOptAdd( &hopt, "g", "gamma", airTypeDouble, 1, 1, &_gamma, "1.0",
"gamma to apply to image data, after contrast" );
hestOptAdd( &hopt, "b", "background", airTypeDouble, 3, 3, back, "0 0 0",
"background color to composite against; white is "
"1 1 1, not 255 255 255." );
hestOptAdd( &hopt, "bi", "nbg", airTypeOther, 1, 1, &_nbg, "",
"8-bit RGB background image to composite against",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS,
NULL, "file to write output PPM image to" );
hestParseOrDie( hopt, argc-1, argv+1, NULL, me, overInfo,
AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 3 == nin->dim && 4 <= nin->axis[0].size ) ) {
fprintf( stderr, "%s: doesn't look like an RGBA nrrd\n", me );
airMopError( mop ); return 1;
}
if ( nrrdTypeBlock == nin->type ) {
fprintf( stderr, "%s: can't use a %s nrrd\n", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
airMopError( mop ); return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
if ( _nbg ) {
if ( !( 3 == _nbg->dim
&& 3 == _nbg->axis[0].size
&& 2 <= _nbg->axis[1].size
&& 2 <= _nbg->axis[2].size
&& nrrdTypeUChar == _nbg->type ) ) {
fprintf( stderr, "%s: background not an 8-bit RGB image\n", me );
airMopError( mop ); return 1;
}
nbg = nrrdNew( );
airMopAdd( mop, nbg, ( airMopper )nrrdNuke, airMopAlways );
if ( sx == _nbg->axis[1].size && sy == _nbg->axis[2].size ) {
/* no resampling needed, just copy */
E = nrrdCopy( nbg, _nbg );
} else {
/* have to resample background image to fit. */
/* because we're using the old resampler, we have to kill off any
space direction information, which is incompatible with
setting per-axis min and max, as is required by the old resampler */
nrrdOrientationReduce( _nbg, _nbg, AIR_FALSE );
rinfo = nrrdResampleInfoNew( );
airMopAdd( mop, rinfo, ( airMopper )nrrdResampleInfoNix, airMopAlways );
rinfo->kernel[0] = NULL;
nrrdKernelParse( &( rinfo->kernel[1] ), rinfo->parm[1], "tent" );
rinfo->min[1] = _nbg->axis[1].min = 0;
rinfo->max[1] = _nbg->axis[1].max = _nbg->axis[1].size-1;
rinfo->samples[1] = sx;
nrrdKernelParse( &( rinfo->kernel[2] ), rinfo->parm[2], "tent" );
rinfo->min[2] = _nbg->axis[2].min = 0;
rinfo->max[2] = _nbg->axis[2].max = _nbg->axis[2].size-1;
rinfo->samples[2] = sy;
rinfo->renormalize = AIR_TRUE;
rinfo->round = AIR_TRUE;
E = nrrdSpatialResample( nbg, _nbg, rinfo );
}
if ( E ) {
fprintf( stderr, "%s: trouble:\n%s", me, errS = biffGetDone( NRRD ) );
free( errS ); return 1;
}
} else {
nbg = NULL;
}
ninD = nrrdNew( );
airMopAdd( mop, ninD, ( airMopper )nrrdNuke, airMopAlways );
nrgbaD = nrrdNew( );
airMopAdd( mop, nrgbaD, ( airMopper )nrrdNuke, airMopAlways );
nout=nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( nrrdTypeIsIntegral[nin->type] ) {
if ( !E ) E |= nrrdUnquantize( ninD, nin, nrrdTypeDouble );
} else if ( nrrdTypeFloat == nin->type ) {
if ( !E ) E |= nrrdConvert( ninD, nin, nrrdTypeDouble );
} else {
if ( !E ) E |= nrrdCopy( ninD, nin );
}
min[0] = min[1] = min[2] = 0;
max[0] = 3;
max[1] = sx-1;
max[2] = sy-1;
if ( !E ) E |= nrrdCrop( nrgbaD, ninD, min, max );
if ( !E ) E |= nrrdPPM( nout, sx, sy );
if ( E ) {
fprintf( stderr, "%s: trouble:\n%s", me, errS = biffGetDone( NRRD ) );
free( errS ); return 1;
}
contr = AIR_CLAMP( -1, contr, 1 );
cpow = tan( AIR_AFFINE( -1.000001, contr, 1.000001, 0, AIR_PI/2 ) );
outUC = ( unsigned char* )nout->data;
bgUC = nbg ? ( unsigned char * )nbg->data : NULL;
rgbaD = ( double * )nrgbaD->data;
for ( pi=0; pi<sx*sy; pi++ ) {
r = AIR_CLAMP( 0, rgbaD[0], 1 );
g = AIR_CLAMP( 0, rgbaD[1], 1 );
b = AIR_CLAMP( 0, rgbaD[2], 1 );
a = AIR_CLAMP( 0, rgbaD[3], 1 );
if ( 1 != cpow ) {
r = docontrast( r, cfp, cpow );
g = docontrast( g, cfp, cpow );
b = docontrast( b, cfp, cpow );
}
r = pow( r, 1.0/_gamma );
g = pow( g, 1.0/_gamma );
b = pow( b, 1.0/_gamma );
if ( bgUC ) {
r = a*r + ( 1-a )*bgUC[0 + 3*pi]/255;
g = a*g + ( 1-a )*bgUC[1 + 3*pi]/255;
b = a*b + ( 1-a )*bgUC[2 + 3*pi]/255;
} else {
r = a*r + ( 1-a )*back[0];
g = a*g + ( 1-a )*back[1];
b = a*b + ( 1-a )*back[2];
}
outUC[0] = airIndex( 0.0, r, 1.0, 256 );
outUC[1] = airIndex( 0.0, g, 1.0, 256 );
outUC[2] = airIndex( 0.0, b, 1.0, 256 );
outUC += 3;
rgbaD += 4;
}
if ( nrrdSave( outS, nout, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, errS = biffGetDone( NRRD ) );
free( errS ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/limn.h>
#include <teem/ten.h>
#include <teem/meet.h>
#define SPACING( spc ) ( AIR_EXISTS( spc ) ? spc: nrrdDefaultSpacing )
/* copied this from ten.h; I don't want gage to depend on ten */
#define PROBE_MAT2LIST( l, m ) ( \
( l )[1] = ( m )[0], \
( l )[2] = ( m )[3], \
41 ( l )[3] = ( m )[6], \
( l )[4] = ( m )[4], \
( l )[5] = ( m )[7], \
( l )[6] = ( m )[8] )
void
printans( FILE *file, const double *ans, int len ) {
int a;
AIR_UNUSED( file );
for ( a=0; a<=len-1; a++ ) {
if ( a ) {
printf( ", " );
}
printf( "%g", ans[a] );
}
}
58
static const char *probeInfo =
( "Uses gageProbe( ) to query scalar or vector volumes "
"at a single probe location." );
int
main( int argc, const char *argv[] ) {
gageKind *kind;
const char *me;
char *whatS, *err, *outS, *stackSavePath;
hestParm *hparm;
hestOpt *hopt = NULL;
NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur;
float pos[3], lineInfo[4];
double gmc, rangeSS[2], posSS;
unsigned int ansLen, numSS, ninSSIdx, lineStepNum;
int what, E=0, renorm, SSnormd, SSuniform, verbose, orientationFromSpacing;
const double *answer;
Nrrd *nin, **ninSS=NULL, *nout=NULL;
gageContext *ctx;
gagePerVolume *pvl;
limnPolyData *lpld=NULL;
airArray *mop;
int worldSpace;
Nrrd *ngrad=NULL, *nbmat=NULL;
double bval, eps;
unsigned int *skip, skipNum;
gageStackBlurParm *sbp;
mop = airMopNew( );
me = argv[0];
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleOtherType = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", or \"tensor\" )",
NULL, NULL, meetHestGageKind );
hestOptAdd( &hopt, "p", "x y z", airTypeFloat, 3, 3, pos, NULL,
"the position in space at which to probe" );
hestOptAdd( &hopt, "wsp", NULL, airTypeInt, 0, 0, &worldSpace, NULL,
"if using this option, position ( \"-p\" ) will be in world "
"space, instead of index space ( the default )" );
hestOptAdd( &hopt, "pi", "lpld in", airTypeOther, 1, 1, &lpld, "",
"input polydata ( overrides \"-p\" )",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, "pl", "x y z s", airTypeFloat, 4, 4, lineInfo, "0 0 0 0",
"probe along line, instead of at point. "
"The \"-p\" three coords are the line start point. "
"If \"s\" is zero, ( x, y, z ) is the line end point. "
"If \"s\" is non-zero, ( x, y, z ) is the line direction, "
"which is scaled to have length \"s\", "
"and then used as the step between line samples. " );
hestOptAdd( &hopt, "pln", "num", airTypeUInt, 1, 1, &lineStepNum, "0",
"if non-zero, number of steps of probing to do along line, "
"which overrides \"-p\" and \"-pi\"" );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL,
"the quantity ( scalar, vector, or matrix ) to learn by probing" );
hestOptAdd( &hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "0",
"if non-zero, and if query is a scalar, epsilon around probe "
"location where we will do discrete differences to find the "
"gradient and hessian ( for debugging )" );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"tent", "kernel for gageKernel00",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS,
"0", "how many scale-space samples to evaluate, or, "
"0 to turn-off all scale-space behavior" );
hestOptAdd( &hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS,
"nan nan", "range of scales in scale-space" );
hestOptAdd( &hopt, "sss", "scale save path", airTypeString, 1, 1,
&stackSavePath, "",
"give a non-empty path string ( like \"./\" ) to save out "
"the pre-blurred volumes computed for the stack" );
hestOptAdd( &hopt, "ssp", "SS pos", airTypeDouble, 1, 1, &posSS, "0",
"position at which to sample in scale-space" );
hestOptAdd( &hopt, "kssblur", "kernel", airTypeOther, 1, 1, &kSSblur,
"dgauss:1, 5", "blurring kernel, to sample scale space",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "kss", "kernel", airTypeOther, 1, 1, &kSS,
"tent", "kernel for reconstructing from scale space samples",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "ssnd", "ssnd", airTypeInt, 1, 1, &SSnormd, "0",
"enable derivative normalization based on scale space" );
hestOptAdd( &hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL,
"do uniform samples along sigma, and not ( by default ) "
"samples according to the logarithm of diffusion time" );
hestOptAdd( &hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL,
"renormalize kernel weights at each new sample location. "
"\"Accurate\" kernels don't need this; doing it always "
"makes things go slower" );
hestOptAdd( &hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc,
"0.0", "For curvature-based queries, use zero when gradient "
"magnitude is below this" );
hestOptAdd( &hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing,
NULL, "If only per-axis spacing is available, use that to "
"guess orientation info" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output array, when probing on polydata vertices" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
what = airEnumVal( kind->enm, whatS );
if ( !what ) {
/* 0 indeed always means "unknown" for any gageKind */
fprintf( stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n",
me, whatS, kind->name );
hestUsage( stderr, hopt, me, hparm );
hestGlossary( stderr, hopt, hparm );
airMopError( mop );
return 1;
}
if ( ELL_4V_LEN( lineInfo ) && !lineStepNum ) {
fprintf( stderr, "%s: gave line info ( \"-pl\" ) but not "
"# samples ( \"-pln\" )", me );
hestUsage( stderr, hopt, me, hparm );
hestGlossary( stderr, hopt, hparm );
airMopError( mop );
return 1;
}
/* special set-up required for DWI kind */
if ( !strcmp( TEN_DWI_GAGE_KIND_NAME, kind->name ) ) {
if ( tenDWMRIKeyValueParse( &ngrad, &nbmat, &bval, &skip, &skipNum, nin ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( skipNum ) {
fprintf( stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me );
airMopError( mop ); return 1;
}
/* this could stand to use some more command-line arguments */
if ( tenDwiGageKindSet( kind, 50, 1, bval, 0.001, ngrad, nbmat,
tenEstimate1MethodLLS,
tenEstimate2MethodQSegLLS,
/* randSeed */ 7919 ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
ansLen = kind->table[what].answerLength;
/* for setting up pre-blurred scale-space samples */
if ( numSS ) {
sbp = gageStackBlurParmNew( );
airMopAdd( mop, sbp, ( airMopper )gageStackBlurParmNix, airMopAlways );
ninSS = AIR_CAST( Nrrd **, calloc( numSS, sizeof( Nrrd * ) ) );
if ( !ninSS ) {
fprintf( stderr, "%s: couldn't allocate ninSS", me );
airMopError( mop ); return 1;
}
for ( ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++ ) {
ninSS[ninSSIdx] = nrrdNew( );
airMopAdd( mop, ninSS[ninSSIdx], ( airMopper )nrrdNuke, airMopAlways );
}
if ( gageStackBlurParmScaleSet( sbp, numSS, rangeSS[0], rangeSS[1],
SSuniform, AIR_FALSE )
|| gageStackBlurParmKernelSet( sbp, kSSblur )
|| gageStackBlurParmRenormalizeSet( sbp, AIR_TRUE )
|| gageStackBlurParmBoundarySet( sbp, nrrdBoundaryBleed, AIR_NAN )
|| gageStackBlurParmVerboseSet( sbp, verbose )
|| gageStackBlur( ninSS, sbp, nin, kind ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble pre-computing blurrings:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( airStrlen( stackSavePath ) ) {
char fnform[AIR_STRLEN_LARGE];
sprintf( fnform, "%s/blur-%%02u.nrrd", stackSavePath );
fprintf( stderr, "%s: |%s|\n", me, fnform );
if ( nrrdSaveMulti( fnform, AIR_CAST( const Nrrd *const *, ninSS ),
numSS, 0, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving blurrings:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
} else {
sbp = NULL;
ninSS = NULL;
}
ctx = gageContextNew( );
airMopAdd( mop, ctx, AIR_CAST( airMopper, gageContextNix ), airMopAlways );
gageParmSet( ctx, gageParmGradMagCurvMin, gmc );
gageParmSet( ctx, gageParmVerbose, verbose );
gageParmSet( ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
gageParmSet( ctx, gageParmOrientationFromSpacing, orientationFromSpacing );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, kind ) );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( numSS ) {
gagePerVolume **pvlSS;
gageParmSet( ctx, gageParmStackUse, AIR_TRUE );
gageParmSet( ctx, gageParmStackNormalizeDeriv,
SSnormd ? AIR_TRUE : AIR_FALSE );
if ( !E ) E |= !( pvlSS = AIR_CAST( gagePerVolume **,
calloc( numSS, sizeof( gagePerVolume * ) ) ) );
if ( !E ) airMopAdd( mop, pvlSS, ( airMopper )airFree, airMopAlways );
if ( !E ) E |= gageStackPerVolumeNew( ctx, pvlSS,
AIR_CAST( const Nrrd*const*, ninSS ),
numSS, kind );
if ( !E ) E |= gageStackPerVolumeAttach( ctx, pvl, pvlSS, sbp->sigma, numSS );
if ( !E ) E |= gageKernelSet( ctx, gageKernelStack, kSS->kernel, kSS->parm );
} else {
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
}
if ( !E ) E |= gageQueryItemOn( ctx, pvl, what );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
/* test with original context */
answer = gageAnswerPointer( ctx, pvl, what );
if ( lpld ) {
/* probing on locations of polydata */
double *dout, xyzw[4];
unsigned int vidx, ai;
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( 1 == ansLen ) {
E = nrrdAlloc_va( nout, nrrdTypeDouble, 1,
AIR_CAST( size_t, lpld->xyzwNum ) );
} else {
E = nrrdAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, ansLen ),
AIR_CAST( size_t, lpld->xyzwNum ) );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
dout = AIR_CAST( double *, nout->data );
for ( vidx=0; vidx<lpld->xyzwNum; vidx++ ) {
ELL_4V_COPY( xyzw, lpld->xyzw + 4*vidx );
ELL_4V_HOMOG( xyzw, xyzw );
if ( gageProbeSpace( ctx, xyzw[0], xyzw[1], xyzw[2],
!worldSpace, AIR_TRUE ) ) {
fprintf( stderr, "%s: trouble:\n%s\n( %d )\n",
me, ctx->errStr, ctx->errNum );
airMopError( mop );
return 1;
}
for ( ai=0; ai<ansLen; ai++ ) {
dout[ai + ansLen*vidx] = answer[ai];
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
} else if ( lineStepNum ) {
/* we're probing along a line */
double *dout;
double start[3], dir[3], end[3], lpos[3];
unsigned int vidx, ai;
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( 1 == ansLen ) {
E = nrrdAlloc_va( nout, nrrdTypeDouble, 1,
AIR_CAST( size_t, lineStepNum ) );
} else {
E = nrrdAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, ansLen ),
AIR_CAST( size_t, lineStepNum ) );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
dout = AIR_CAST( double *, nout->data );
ELL_3V_COPY( start, pos );
if ( lineInfo[3] ) {
double tmp;
/* stepping along vector */
ELL_3V_COPY( dir, 0 + lineInfo );
ELL_3V_SET( end, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_NORM( dir, dir, tmp );
if ( !tmp ) {
fprintf( stderr, "%s: requested vector stepping, but vlen = 0", me );
airMopError( mop );
return 1;
}
ELL_3V_SCALE( dir, lineInfo[3], dir );
} else {
/* stepping between points */
ELL_3V_SET( dir, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_COPY( end, 0 + lineInfo );
}
for ( vidx=0; vidx<lineStepNum; vidx++ ) {
if ( lineInfo[3] ) {
ELL_3V_SCALE_ADD2( lpos, 1, start, vidx, dir );
} else {
ELL_3V_AFFINE( lpos, 0, vidx, lineStepNum-1, start, end );
}
if ( gageProbeSpace( ctx, lpos[0], lpos[1], lpos[2],
!worldSpace, AIR_FALSE ) ) {
fprintf( stderr, "%s: trouble:\n%s\n( %d )\n",
me, ctx->errStr, ctx->errNum );
airMopError( mop );
return 1;
}
for ( ai=0; ai<ansLen; ai++ ) {
dout[ai + ansLen*vidx] = answer[ai];
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
} else {
/* simple probing at a point */
E = ( numSS
? gageStackProbeSpace( ctx, pos[0], pos[1], pos[2], posSS,
!worldSpace, AIR_FALSE )
: gageProbeSpace( ctx, pos[0], pos[1], pos[2],
!worldSpace, AIR_FALSE ) );
if ( E ) {
fprintf( stderr, "%s: trouble:\n%s\n( %d )\n",
me, ctx->errStr, ctx->errNum );
airMopError( mop );
return 1;
}
printf( "%s: %s( %g, %g, %g ) = ", me,
airEnumStr( kind->enm, what ), pos[0], pos[1], pos[2] );
printans( stdout, answer, ansLen );
printf( "\n" );
if ( eps && 1 == ansLen ) {
double v[3][3][3], fes, ee;
int xo, yo, zo;
if ( !worldSpace ) {
fprintf( stderr, "\n%s: WARNING!!: not probing in world-space ( via "
"\"-wsp\" ) likely leads to errors in estimated "
"derivatives\n\n", me );
}
gageParmSet( ctx, gageParmVerbose, 0 );
#define PROBE( x, y, z ) \
( ( numSS \
? gageStackProbeSpace( ctx, x, y, z, posSS, !worldSpace, AIR_FALSE ) \
: gageProbeSpace( ctx, x, y, z, !worldSpace, AIR_FALSE ) ), answer[0] )
for ( xo=0; xo<=2; xo++ ) {
for ( yo=0; yo<=2; yo++ ) {
for ( zo=0; zo<=2; zo++ ) {
v[xo][yo][zo] = PROBE( pos[0] + ( xo-1 )*eps,
pos[1] + ( yo-1 )*eps,
pos[2] + ( zo-1 )*eps );
}
}
}
printf( "%s: approx gradient( %s ) at ( %g, %g, %g ) = %f %f %f\n", me,
airEnumStr( kind->enm, what ), pos[0], pos[1], pos[2],
( v[2][1][1] - v[0][1][1] )/( 2*eps ),
( v[1][2][1] - v[1][0][1] )/( 2*eps ),
( v[1][1][2] - v[1][1][0] )/( 2*eps ) );
fes = 4*eps*eps;
ee = eps*eps;
printf( "%s: approx hessian( %s ) at ( %g, %g, %g ) = \n"
"%f %f %f\n"
" %f %f\n"
" %f\n", me,
airEnumStr( kind->enm, what ), pos[0], pos[1], pos[2],
( v[0][1][1] - 2*v[1][1][1] + v[2][1][1] )/ee,
( v[2][2][1] - v[0][2][1] - v[2][0][1] + v[0][0][1] )/fes,
( v[2][1][2] - v[0][1][2] - v[2][1][0] + v[0][1][0] )/fes,
( v[1][2][1] - 2*v[1][1][1] + v[1][0][1] )/ee,
( v[1][2][2] - v[1][0][2] - v[1][2][0] + v[1][0][0] )/fes,
( v[1][1][2] - 2*v[1][1][1] + v[1][1][0] )/ee );
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
** NOTE: all the "#ifdef DEFT" directives refer to an FLTK2-based GUI
** for some parts of Teem called "Deft". Unfortunately FLTK2 has been
** abandoned, and Deft is not released or supported in any way. The
** Deft-related code is preserved for legacy purposes.
*/
#ifdef DEFT
#include "Deft.h"
#include "Contour.h"
#include "Viewer.h"
#include "ViewerUI.h"
#include "TensorGlyph.h"
#include "TensorGlyphUI.h"
#include "Slider.h"
#include "TriPlane.h"
#include "TriPlaneUI.h"
#endif
#include <teem/pull.h>
#include <teem/meet.h>
static const char *info =
( "Command-line interface to the \"pull\" library. "
"Published research using this tool or the \"pull\" library "
"should cite the paper: \n "
"\t\tGordon L. Kindlmann, Ra{\\'u}l San Jos{\\'e} Est{\\'e}par, Stephen M. Smith, \n "
"\t\tCarl-Fredrik Westin. Sampling and Visualizing Creases with Scale-Space\n "
"\t\tParticles. IEEE Trans. on Visualization and Computer Graphics, \n "
"\t\t15( 6 ):1415-1424 ( 2009 )." );
#ifdef DEFT
typedef struct {
fltk::FloatInput *scaleVecInput[3];
fltk::ValueInput *glyphScaleRadInput;
Deft::Slider *isoval;
Deft::Slider *strength;
Deft::Slider *quality;
Deft::Slider *alpha, *beta, *cwell, *gamma;
/* Deft::Slider *height; */
Deft::Slider *ccSelect, *rho, *sclMean, *sclWind;
fltk::CheckButton *ccSingle;
Deft::Contour *contour;
Deft::Scene *scene;
Deft::Viewer *viewer;
Deft::TensorGlyph *glyph, *hedge;
fltk::IntInput *iters;
fltk::FloatInput *radius;
fltk::ValueInput *verbose;
pullContext *pctx;
Nrrd *nPosOut, *nTenOut, *nFrcOut, *nten, *ntmp, *nenr, *nscl,
*nidcc, *nstrn, *nqual, *ncovar, *ntcovar, *nstab, *nintern,
*nstuck, *nfrcOld, *nfrcNew, *nposOld, *nposNew, *nrgb, *nccrgb,
*ncval, *ncmap, *ncmapOut, *nblur;
NrrdResampleContext *rsmc;
const Nrrd *norig;
NrrdRange *cvalRange;
limnPolyData *phistLine, *phistTube;
Deft::PolyData *phistSurf;
double icvalr[2], sclMin, sclMax, strnMin, qualMin,
scaleVec[3], glyphScaleRad, energyIncreasePermitFrac;
} pullBag;
void
89 verbose_cb( fltk::Widget *widget, pullBag *bag ) {
fltk::ValueInput *val;
val = ( fltk::ValueInput * )widget;
pullVerboseSet( bag->pctx, ( int )val->value( ) );
}
/* ... DEFT ... */
void
97 isovalue_cb( fltk::Widget *widget, pullBag *bag ) {
Deft::Slider *slider;
slider = ( Deft::Slider * )widget;
if ( bag->contour ) {
bag->contour->extract( slider->value( ) );
}
bag->viewer->redraw( );
}
void
108 outputGet( pullBag *bag ) {
char me[]="outputGet", *err;
size_t cropMin[2], cropMax[2];
if ( pullOutputGet( bag->nPosOut, bag->nTenOut,
bag->nstrn, /* may be NULL */
bag->scaleVec, bag->glyphScaleRad,
bag->pctx )
|| pullPropGet( bag->nscl, pullPropScale, bag->pctx )
|| ( bag->pctx->ispec[pullInfoQuality]
? pullInfoGet( bag->nqual, pullInfoQuality, bag->pctx )
: 0 )
|| pullPropGet( bag->nenr, pullPropEnergy, bag->pctx )
|| pullPropGet( bag->nidcc, pullPropIdCC, bag->pctx )
|| pullPropGet( bag->nstuck, pullPropStuck, bag->pctx )
|| pullPropGet( bag->ncovar, pullPropNeighCovar7Ten, bag->pctx )
#if PULL_TANCOVAR
|| pullPropGet( bag->ntcovar, pullPropNeighTanCovar, bag->pctx )
#endif
|| pullPropGet( bag->nstab, pullPropStability, bag->pctx )
|| pullPropGet( bag->nintern, pullPropNeighInterNum, bag->pctx )
|| pullPropGet( bag->nFrcOut, pullPropForce, bag->pctx )
|| ( pullPhistEnabled
? pullPositionHistoryGet( bag->phistLine, bag->pctx )
: 0 ) ) {
err = biffGetDone( PULL );
fprintf( stderr, "%s: error getting pull output:\n%s\n", me, err );
free( err );
exit( 1 );
}
/* ... DEFT ... */
cropMin[0] = 0;
cropMin[1] = 0;
cropMax[0] = 2;
cropMax[1] = bag->nPosOut->axis[1].size-1;
if ( ( !bag->pctx->iter
? 0
: ( nrrdCopy( bag->nfrcOld, bag->nfrcNew )
|| nrrdCopy( bag->nposOld, bag->nposNew ) ) )
|| nrrdConvert( bag->nten, bag->nTenOut, nrrdTypeFloat )
/* hacks to visualize the ( tan ) covariance tensors
|| nrrdCopy( bag->nten, bag->ncovar )
|| nrrdCopy( bag->nten, bag->ntcovar )
*/
|| nrrdCrop( bag->ntmp, bag->nPosOut, cropMin, cropMax )
|| nrrdConvert( bag->nposNew, bag->ntmp, nrrdTypeFloat )
|| nrrdCrop( bag->ntmp, bag->nFrcOut, cropMin, cropMax )
|| nrrdConvert( bag->nfrcNew, bag->ntmp, nrrdTypeFloat )
|| ( !bag->pctx->iter
? ( nrrdCopy( bag->nfrcOld, bag->nfrcNew )
|| nrrdCopy( bag->nposOld, bag->nposNew ) )
: 0 ) ) {
err = biffGetDone( NRRD );
fprintf( stderr, "%s: another error 0\n%s\n", me, err );
free( err );
exit( 1 );
}
}
/* ... DEFT ... */
void
168 outputShow( pullBag *bag ) {
char me[]="outputShow", *err;
float *rgb;
unsigned int ii, nn, *idcc;
unsigned char *stuck;
int first;
double *cval, emean, estdv, *pos;
/*
if ( limnPolyDataSpiralTubeWrap( bag->phistTube, bag->phistLine,
( 1 << limnPolyDataInfoRGBA )
| ( 1 << limnPolyDataInfoNorm ),
NULL,
8, 8, bag->glyph->glyphScale( )/5 ) ) {
err = biffGetDone( LIMN );
fprintf( stderr, "%s: another error 1\n%s\n", me, err );
free( err );
exit( 1 );
}
*/
if ( pullPhistEnabled ) {
bag->phistSurf->changed( );
}
bag->ncval = bag->nenr;
/* bag->ncval = bag->nstrn; */
/* bag->ncval = bag->nstuck; */
/* bag->ncval = bag->nscl; */
/* ... DEFT ... */
if ( bag->ncval ) {
nrrdRangeSet( bag->cvalRange, bag->ncval, AIR_FALSE );
} else {
bag->cvalRange->min = AIR_NAN;
bag->cvalRange->max = AIR_NAN;
}
if ( bag->ncval ) {
cval = AIR_CAST( double *, bag->ncval->data );
} else {
cval = NULL;
}
if ( cval ) {
nn = bag->ncval->axis[0].size;
emean = 0;
for ( ii=0; ii<nn; ii++ ) {
emean += cval[ii];
}
emean /= nn;
estdv = 0;
for ( ii=0; ii<nn; ii++ ) {
estdv += ( emean - cval[ii] )*( emean - cval[ii] );
}
estdv = sqrt( estdv/nn );
if ( bag->cvalRange->hasNonExist ) {
fprintf( stderr, "!%s: cval range %g -- %g ( %s ), mean %g, stdv %g\n", me,
bag->cvalRange->min, bag->cvalRange->max,
bag->cvalRange->hasNonExist ? "HAS non-exist" : "no non-exist",
emean, estdv );
}
bag->cvalRange->min = AIR_LERP( 0.7, bag->cvalRange->min, emean - 2*estdv );
bag->cvalRange->max = AIR_LERP( 0.7, bag->cvalRange->max, emean + 2*estdv );
}
/* ... DEFT ... */
float *cmapOut;
if ( bag->ncmap
&& bag->ncval
&& AIR_EXISTS( bag->cvalRange->min )
&& AIR_EXISTS( bag->cvalRange->max ) ) {
/* double mmin, mmax; */
fprintf( stderr, "!%s: cval cmap range %g %g ----------- \n", me,
bag->cvalRange->min, bag->cvalRange->max );
/*
mmin = -0.0342937;
mmax = -0.0105725;
*/
/* */
/*
bag->cvalRange->min = AIR_LERP( 0.05, mmin, mmax );
bag->cvalRange->max = AIR_LERP( 0.3, mmin, mmax );
*/
/* */
if ( nrrdApply1DRegMap( bag->ncmapOut, bag->ncval, bag->cvalRange,
bag->ncmap, nrrdTypeFloat, AIR_TRUE ) ) {
err = biffGetDone( NRRD );
fprintf( stderr, "%s: cmap error\n%s\n", me, err );
free( err ); exit( 1 );
}
cmapOut = AIR_CAST( float *, bag->ncmapOut->data );
} else {
cmapOut = NULL;
}
/* ... DEFT ... */
idcc = AIR_CAST( unsigned int *, bag->nidcc->data );
stuck = AIR_CAST( unsigned char *, bag->nstuck->data );
nn = bag->nPosOut->axis[1].size;
pos = AIR_CAST( double *, bag->nPosOut->data );
first = bag->nrgb->axis[1].size != bag->nPosOut->axis[1].size;
/*
fprintf( stderr, "!%s: %u %u -> %d\n", me,
AIR_UINT( bag->nrgb->axis[1].size ),
AIR_UINT( bag->nPosOut->axis[1].size ), first );
*/
if ( first ) {
if ( nrrdMaybeAlloc_va( bag->nrgb, nrrdTypeFloat, 2,
AIR_CAST( size_t, 3 ),
bag->nPosOut->axis[1].size ) ) {
err = biffGetDone( NRRD );
fprintf( stderr, "%s: error creating RGB:\n%s\n", me, err );
free( err );
exit( 1 );
}
}
bag->icvalr[0] = bag->cvalRange->min;
bag->icvalr[1] = bag->cvalRange->max;
/* ... DEFT ... */
double *strnOut;
strnOut = ( bag->nstrn
? AIR_CAST( double *, bag->nstrn->data )
: NULL );
double *qualOut;
qualOut = ( bag->nqual
? AIR_CAST( double *, bag->nqual->data )
: NULL );
rgb = ( float* )bag->nrgb->data;
for ( ii=0; ii<nn; ii++ ) {
float ee, *ccrgb;
ccrgb = ( float* )bag->nccrgb->data;
/* ee = bag->cvalRange->min - ( bag->icvalr[1] - bag->icvalr[0] )/50;*/
ee = bag->cvalRange->min;
if ( bag->pctx->CCNum && ccrgb ) {
rgb[0 + 3*ii] = ccrgb[0 + 3*idcc[ii]];
rgb[1 + 3*ii] = ccrgb[1 + 3*idcc[ii]];
rgb[2 + 3*ii] = ccrgb[2 + 3*idcc[ii]];
} else if ( cmapOut ) {
ELL_3V_COPY( rgb + 3*ii, cmapOut + 3*ii );
} else {
ELL_3V_SET( rgb + 3*ii, 0.95, 0.95, 0.95 );
/*
if ( AIR_EXISTS( cval[ii] ) ) {
rgb[1 + 3*ii] = AIR_AFFINE( ee, cval[ii], bag->cvalRange->max, 0, 1 );
rgb[1 + 3*ii] = sqrt( rgb[1 + 3*ii] );
rgb[0 + 3*ii] = rgb[1 + 3*ii];
} else {
rgb[1 + 3*ii] = 0;
rgb[0 + 3*ii] = 1;
}
rgb[2 + 3*ii] = stuck[ii];
*/
}
}
/* ... DEFT ... */
if ( 1 ) {
float *ten, *pos;
double *posOut;
ten = AIR_CAST( float *, bag->nten->data );
posOut = AIR_CAST( double *, bag->nPosOut->data );
pos = AIR_CAST( float *, bag->nposNew->data );
for ( ii=0; ii<nn; ii++ ) {
if ( !( AIR_IN_CL( bag->sclMin-0.00001, posOut[3],
bag->sclMax+0.00001 ) ) ) {
ten[0] = 0;
} else if ( strnOut && strnOut[ii] < bag->strnMin ) {
ten[0] = 0;
} else if ( qualOut && qualOut[ii] < bag->qualMin-0.000001 ) {
ten[0] = 0;
} else if ( bag->pctx->CCNum
&& ( bag->ccSingle->value( )
? idcc[ii] != bag->ccSelect->value( )
: idcc[ii] > bag->ccSelect->value( ) ) ) {
ten[0] = 0;
} else {
ten[0] = 1;
}
/*
if ( 4589 == ii ) {
fprintf( stderr, "!%s: point %u/%u at ( %g, %g, %g )=( %g, %g, %g, %g ) got ten[0] %g\n",
me, ii, nn,
pos[0], pos[1], pos[1],
posOut[0], posOut[1], posOut[2], posOut[3],
ten[0] );
}
*/
pos += 3;
posOut += 4;
ten += 7;
}
}
/* ... DEFT ... */
if ( bag->nPosOut->axis[1].size ) {
bag->glyph->dataSet( bag->nPosOut->axis[1].size,
( float* )bag->nten->data, 7,
( float* )bag->nposNew->data, 3, rgb, 3, NULL );
bag->glyph->update( );
/*
bag->hedge->dataSet( bag->nPosOut->axis[1].size,
( float* )bag->nfrcNew->data, 3,
( float* )bag->nposOld->data, 3, rgb, 3, NULL );
bag->hedge->update( );
*/
bag->viewer->redraw( );
fltk::flush( );
} else {
fprintf( stderr, "!%s: got zero tensors out!\n", me );
}
return;
}
void
386 iter_cb( void *_bag ) {
pullBag *bag;
bag = AIR_CAST( pullBag *, _bag );
outputGet( bag );
outputShow( bag );
}
/* ... DEFT ... */
void
396 step_cb( fltk::Widget *, pullBag *bag ) {
/* static double lastthresh = -42; */
char me[]="step_cb", *err;
static unsigned int itersTotal=0;
unsigned int iters = bag->iters->ivalue( );
bag->pctx->iterParm.max += iters;
if ( pullRun( bag->pctx ) ) {
err = biffGetDone( PULL );
fprintf( stderr, "%s: error running pull:\n%s\n", me, err );
free( err );
exit( 1 );
}
itersTotal += iters;
fprintf( stderr, "!%s: enr = %g; time = %g sec; %u iters ( %g iters/sec )\n",
me, bag->pctx->energy, bag->pctx->timeRun, itersTotal,
itersTotal/bag->pctx->timeRun );
outputGet( bag );
outputShow( bag );
for ( unsigned int ci=pullCountUnknown+1; ci<pullCountLast; ci++ ) {
if ( bag->pctx->count[ci] ) {
fprintf( stderr, " %u: %s\n", bag->pctx->count[ci],
airEnumStr( pullCount, ci ) );
}
}
}
/* ... DEFT ... */
void
426 gammaSet_cb( fltk::Widget *, pullBag *bag ) {
char me[]="gammaSet_cb";
if ( pullGammaLearn( bag->pctx ) ) {
char *err = biffGetDone( PULL );
fprintf( stderr, "%s: problem learning gamma:\n%s", me, err );
free( err );
}
if ( bag->pctx->sysParm.gamma > bag->gamma->maximum( ) ) {
bag->gamma->maximum( 2*bag->pctx->sysParm.gamma );
}
bag->gamma->value( bag->pctx->sysParm.gamma );
}
/* ... DEFT ... */
void
443 cc_cb( fltk::Widget *, pullBag *bag ) {
char me[]="cc_cb";
unsigned int cc;
float *rgb;
if ( pullCCFind( bag->pctx )
|| pullCCSort( bag->pctx,
( bag->pctx->ispec[pullInfoQuality]
? pullInfoQuality
: 0 ), bag->rho->value( ) ) ) {
char *err = biffGetDone( PULL );
fprintf( stderr, "%s: problem finding/sorting CCs:\n%s", me, err );
free( err );
}
printf( "%s: found %u CCs\n", me, bag->pctx->CCNum );
bag->ccSelect->range( 0, bag->pctx->CCNum-1 );
if ( bag->nccrgb->axis[1].size != bag->pctx->CCNum ) {
airSrandMT( AIR_UINT( airTime( ) ) );
if ( nrrdMaybeAlloc_va( bag->nccrgb, nrrdTypeFloat, 2,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, bag->pctx->CCNum ) ) ) {
char *err = biffGetDone( NRRD );
fprintf( stderr, "%s: problem alloc'ing cc rgb:\n%s", me, err );
free( err );
}
rgb = ( float* )bag->nccrgb->data;
ELL_3V_SET( rgb + 0*3, 0.95, 0.95, 0.95 );
for ( cc=0; cc<bag->pctx->CCNum; cc++ ) {
rgb[0 + 3*cc] = AIR_AFFINE( 0, airDrandMT( ), 1, 0.3, 1.0 );
rgb[1 + 3*cc] = AIR_AFFINE( 0, airDrandMT( ), 1, 0.3, 1.0 );
rgb[2 + 3*cc] = AIR_AFFINE( 0, airDrandMT( ), 1, 0.3, 1.0 );
}
}
outputGet( bag );
outputShow( bag );
}
/* ... DEFT ... */
void
483 ccSelect_cb( fltk::Widget *, pullBag *bag ) {
outputShow( bag );
}
void
489 scaleGlyph_cb( fltk::Widget *, pullBag *bag ) {
bag->scaleVec[0] = bag->scaleVecInput[0]->fvalue( );
bag->scaleVec[1] = bag->scaleVecInput[1]->fvalue( );
bag->scaleVec[2] = bag->scaleVecInput[2]->fvalue( );
bag->glyphScaleRad = bag->glyphScaleRadInput->value( );
outputGet( bag );
outputShow( bag );
}
/* ... DEFT ... */
void
502 reblur_cb( fltk::Widget *, pullBag *bag ) {
static const char me[]="reblur_cb";
double kparm[NRRD_KERNEL_PARMS_NUM], scl;
int E;
if ( !bag->pctx->haveScale ) {
return;
}
scl = bag->sclMean->value( );
if ( bag->pctx->flag.scaleIsTau ) {
kparm[0] = gageSigOfTau( scl );
printf( "!%s: tau = %g ---> sigma = %g\n", me, scl, kparm[0] );
} else {
kparm[0] = scl;
printf( "!%s: sigma = %g\n", me, kparm[0] );
}
kparm[1] = 3;
E = 0;
for ( unsigned int axi=0; axi<3; axi++ ) {
if ( !E ) E |= nrrdResampleKernelSet( bag->rsmc, axi,
nrrdKernelDiscreteGaussian,
kparm );
}
if ( !E ) E |= nrrdResampleExecute( bag->rsmc, bag->nblur );
if ( E ) {
char *err = biffGetDone( NRRD );
fprintf( stderr, "%s: problem resampling to scale %g:\n%s",
me, bag->sclMean->value( ), err );
free( err );
}
outputShow( bag );
return;
}
/* ... DEFT ... */
void
539 scale_cb( fltk::Widget *, pullBag *bag ) {
double sclMean, sclWind;
if ( bag->pctx->haveScale ) {
sclMean = bag->sclMean->value( );
sclWind = bag->sclWind->value( );
bag->sclMin = sclMean - sclWind/2;
bag->sclMax = sclMean + sclWind/2;
} else {
bag->sclMin = 0;
bag->sclMax = 0;
}
outputShow( bag );
return;
}
void
556 alpha_cb( fltk::Widget *, pullBag *bag ) {
pullSysParmSet( bag->pctx, pullSysParmAlpha, bag->alpha->value( ) );
}
void
562 beta_cb( fltk::Widget *, pullBag *bag ) {
pullSysParmSet( bag->pctx, pullSysParmBeta, bag->beta->value( ) );
}
/* ... DEFT ... */
void
570 cwell_cb( fltk::Widget *, pullBag *bag ) {
double *parm;
parm = bag->pctx->energySpecR->parm;
parm[1] = bag->cwell->value( );
pullSysParmSet( bag->pctx, pullSysParmEnergyIncreasePermit,
bag->energyIncreasePermitFrac*bag->cwell->value( ) );
{
unsigned int ii, nn;
double xx, yy, de;
FILE *file;
if ( ( file = fopen( "eplot.txt", "w" ) ) ) {
nn = 800;
for ( ii=0; ii<nn; ii++ ) {
xx = AIR_AFFINE( 0, ii, nn-1, 0.0, 1.0 );
yy = bag->pctx->energySpecR->energy->eval( &de, xx, parm );
fprintf( file, "%f %f\n", xx, yy );
}
fclose( file );
}
}
}
void
595 gamma_cb( fltk::Widget *, pullBag *bag ) {
pullSysParmSet( bag->pctx, pullSysParmGamma, bag->gamma->value( ) );
}
/* ... DEFT ... */
void
603 strength_cb( fltk::Widget *, pullBag *bag ) {
bag->strnMin = bag->strength->value( );
outputShow( bag );
}
void
610 quality_cb( fltk::Widget *, pullBag *bag ) {
bag->qualMin = bag->quality->value( );
outputShow( bag );
}
void
617 save_cb( fltk::Widget *, pullBag *bag ) {
static const char me[]="save_cb";
unsigned int ii, nn, count;
float *ten;
Nrrd *nPosSel, *nStrnSel;
double *posSel, *posAll, *strnSel, *strnAll;
if ( !( 0.0 == ELL_3V_LEN( bag->scaleVec ) ) ) {
fprintf( stderr, "%s: refusing to save with non-zero scaleVec %g %g %g\n",
me, bag->scaleVec[0], bag->scaleVec[1], bag->scaleVec[2] );
return;
}
/* ... DEFT ... */
nPosSel = nrrdNew( );
if ( bag->nstrn ) {
nStrnSel = nrrdNew( );
} else {
nStrnSel = NULL;
}
outputGet( bag );
outputShow( bag ); /* will exploit its masking of ten[0] */
count = 0;
nn = bag->nPosOut->axis[1].size;
ten = AIR_CAST( float *, bag->nten->data );
for ( ii=0; ii<nn; ii++ ) {
count += !!ten[0];
ten += 7;
}
nrrdMaybeAlloc_va( nPosSel, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, count ) );
posAll = AIR_CAST( double *, bag->nPosOut->data );
posSel = AIR_CAST( double *, nPosSel->data );
if ( bag->nstrn ) {
nrrdMaybeAlloc_va( nStrnSel, nrrdTypeDouble, 1,
AIR_CAST( size_t, count ) );
strnAll = AIR_CAST( double *, bag->nstrn->data );
strnSel = AIR_CAST( double *, nStrnSel->data );
} else {
strnSel = NULL;
strnAll = NULL;
}
ten = AIR_CAST( float *, bag->nten->data );
count = 0;
for ( ii=0; ii<nn; ii++ ) {
if ( ten[0] ) {
ELL_4V_COPY( posSel, posAll );
if ( strnSel && strnAll ) {
strnSel[count] = strnAll[ii];
}
posSel += 4;
count++;
}
posAll += 4;
ten += 7;
}
/* ... DEFT ... */
nrrdSave( "pos-all.nrrd", bag->nPosOut, NULL );
if ( 0 ) {
nrrdSave( "pos-sel.nrrd", nPosSel, NULL );
} else {
char fname[512];
FILE *ff;
unsigned int ii=0;
for ( ii=0, ff=NULL; AIR_TRUE; ii++ ) {
ff = airFclose( ff );
sprintf( fname, "pos-sel-%03u.nrrd", ii );
if ( !( ff = fopen( fname, "rb" ) ) ) {
break;
}
}
ff = airFclose( ff );
nrrdSave( fname, nPosSel, NULL );
}
nrrdSave( "covar-all.nrrd", bag->ncovar, NULL );
#if PULL_TANCOVAR
nrrdSave( "tcovar-all.nrrd", bag->ntcovar, NULL );
#endif
nrrdSave( "stab-all.nrrd", bag->nstab, NULL );
nrrdSave( "intern-all.nrrd", bag->nintern, NULL );
if ( bag->nstrn ) {
nrrdSave( "strn-all.nrrd", bag->nstrn, NULL );
nrrdSave( "strn-sel.nrrd", nStrnSel, NULL );
}
nrrdNuke( nPosSel );
if ( bag->nstrn ) {
nrrdNuke( nStrnSel );
}
return;
}
#endif /* DEFT */
int
716 main( int argc, const char **argv ) {
hestOpt *hopt=NULL;
hestParm *hparm;
airArray *mop;
const char *me;
#ifdef DEFT
float fr[3], at[3], up[3], fovy, neer, faar, dist, bg[3];
int imgSize[2], ortho, rght, atrel, camkeep;
float anisoThresh, anisoThreshMin, glyphScale, haloTraceBound,
glyphHaloWidth, glyphNormPow, glyphEvalPow, sqdSharp;
int glyphType, glyphFacetRes, aniso;
double ssrange[2];
pullBag bag;
#endif
char *err, *outS, *extraOutBaseS, *addLogS, *cachePathSS;
FILE *addLog;
meetPullVol **vspec;
meetPullInfo **idef;
Nrrd *nPosIn=NULL, *nPosOut;
pullEnergySpec *enspR, *enspS, *enspWin;
NrrdKernelSpec *k00, *k11, *k22, *kSSrecon, *kSSblur;
NrrdBoundarySpec *bspec;
pullContext *pctx;
int E=0, ret=0;
unsigned int vspecNum, idefNum;
double scaleVec[3], glyphScaleRad;
/* things that used to be set directly inside pullContext */
int energyFromStrength, nixAtVolumeEdgeSpace, constraintBeforeSeedThresh,
binSingle, liveThresholdOnInit, permuteOnRebin, noPopCntlWithZeroAlpha,
useBetaForGammaLearn, restrictiveAddToBins, noAdd, unequalShapesAllow,
popCntlEnoughTest, convergenceIgnoresPopCntl, zeroZ;
int verbose;
int interType, allowCodimension3Constraints, scaleIsTau, useHalton,
pointPerVoxel;
unsigned int samplesAlongScaleNum, pointNumInitial,
ppvZRange[2], snap, iterMax, stuckIterMax, constraintIterMax,
popCntlPeriod, addDescent, iterCallback, rngSeed, progressBinMod,
threadNum, eipHalfLife, kssOpi, kssFinished, bspOpi, bspFinished;
double jitter, stepInitial, constraintStepMin, radiusSpace, binWidthSpace,
radiusScale, alpha, beta, _gamma, wall, energyIncreasePermit,
backStepScale, opporStepScale, energyDecreaseMin, energyDecreasePopCntlMin,
neighborTrueProb, probeProb, fracNeighNixedMax;
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
nPosOut = nrrdNew( );
airMopAdd( mop, nPosOut, ( airMopper )nrrdNuke, airMopAlways );
hparm->respFileEnable = AIR_TRUE;
me = argv[0];
#ifdef DEFT
unsigned int baryRes;
int saveAndQuit, fog;
hestOptAdd( &hopt, "csqvmm", "min max", airTypeDouble, 2, 2,
Deft::colorSclQuantityValueMinMax, "nan nan",
"min/max values for cutting planes of scalar values" );
hestOptAdd( &hopt, "saq", "save & quit", airTypeInt, 0, 0, &saveAndQuit,
NULL, "save image and quit, for batch processing" );
hestOptAdd( &hopt, "cmap", "nin", airTypeOther, 1, 1, &( bag.ncmap ), "",
"colormap for particles", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "fr", "from point", airTypeFloat, 3, 3, fr, "3 4 5",
"position of camera, used to determine view vector" );
hestOptAdd( &hopt, "at", "at point", airTypeFloat, 3, 3, at, "0 0 0",
"camera look-at point, used to determine view vector" );
hestOptAdd( &hopt, "up", "up vector", airTypeFloat, 3, 3, up, "0 0 1",
"camera pseudo-up vector, used to determine view coordinates" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &rght, NULL,
"normally, use a right-handed UVN frame ( V points down ), "
"but in Deft this is always true" );
hestOptAdd( &hopt, "fv", "fov", airTypeFloat, 1, 1, &fovy, "20",
"vertical field-of-view, in degrees" );
hestOptAdd( &hopt, "or", NULL, airTypeInt, 0, 0, &ortho, NULL,
"use orthogonal projection instead of perspective" );
hestOptAdd( &hopt, "dn", "near clip", airTypeFloat, 1, 1, &neer, "-2",
"position of near clipping plane, relative to look-at point" );
hestOptAdd( &hopt, "di", "image", airTypeFloat, 1, 1, &dist, "0.0",
"normally, distance to image plane, "
"but in Deft this is always 0.0" );
hestOptAdd( &hopt, "df", "far clip", airTypeFloat, 1, 1, &faar, "2",
"position of far clipping plane, relative to look-at point" );
hestOptAdd( &hopt, "ar", NULL, airTypeInt, 0, 0, &atrel, NULL,
"normally: near, image, and far plane distances are relative to "
"the *at* point, instead of the eye point, "
"but for Deft, this is always true" );
hestOptAdd( &hopt, "usecam", NULL, airTypeInt, 0, 0, &camkeep, NULL,
"hack: by default, a camera reset is done to put the volume "
"in view. Use this to say that the camera specified by the "
"flags above should be preserved and used" );
hestOptAdd( &hopt, "bg", "R G B", airTypeFloat, 3, 3, bg, "0.2 0.3 0.4",
"background color" );
hestOptAdd( &hopt, "fog", NULL, airTypeInt, 0, 0, &fog, NULL,
"hack: turn on fog" );
hestOptAdd( &hopt, "is", "su sv", airTypeInt, 2, 2, imgSize, "640 480",
"initial window size" );
/* ... DEFT ... */
/* this tensor stuff is here because we're hijacking the tensor glyph
object for doing the particle display ... */
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL,
"anisotropy metric to make volume of",
NULL, tenAniso );
hestOptAdd( &hopt, "atr", "aniso thresh", airTypeFloat, 1, 1,
&anisoThresh, "0.85",
"Glyphs will be drawn only for tensors with anisotropy "
"greater than this threshold" );
hestOptAdd( &hopt, "atrm", "aniso thresh min", airTypeFloat, 1, 1,
&anisoThreshMin, "0.4",
"lower bound on aniso thresh" );
hestOptAdd( &hopt, "g", "glyph shape", airTypeEnum, 1, 1, &glyphType, "sqd",
"shape of glyph to use for display. Possibilities "
"include \"box\", \"sphere\"=\"sph\", \"cylinder\"=\"cyl\", and "
"\"superquad\"=\"sqd\"", NULL, tenGlyphType );
hestOptAdd( &hopt, "gsc", "scale", airTypeFloat, 1, 1, &glyphScale,
"0.25", "over-all glyph size" );
hestOptAdd( &hopt, "htb", "trace", airTypeFloat, 1, 1, &haloTraceBound,
"1.0", "halo trace bound" );
hestOptAdd( &hopt, "ghw", "hwidth", airTypeFloat, 1, 1, &glyphHaloWidth,
"0.0", "glyph halo width" );
hestOptAdd( &hopt, "gnp", "npow", airTypeFloat, 1, 1, &glyphNormPow,
"1.0", "pow( ) exponent for compressing range of norms" );
hestOptAdd( &hopt, "gep", "epow", airTypeFloat, 1, 1, &glyphEvalPow,
"1.0", "pow( ) exponent for compressing single eigenvalues" );
hestOptAdd( &hopt, "br", "barycentric res", airTypeInt, 1, 1, &baryRes, "50",
"resolution of sampling of tensor shape palette" );
hestOptAdd( &hopt, "gr", "glyph res", airTypeInt, 1, 1, &glyphFacetRes, "7",
"resolution of polygonalization of glyphs ( other than box )" );
hestOptAdd( &hopt, "sh", "sharpness", airTypeFloat, 1, 1, &sqdSharp, "2.5",
"for superquadric glyphs, how much to sharp edges form as a "
"function of differences between eigenvalues. Higher values "
"mean that edges form more easily" );
#endif /* DEFT */
hestOptAdd( &hopt, "int", "int", airTypeEnum, 1, 1, &interType,
"justr", "inter-particle energy type", NULL, pullInterType );
hestOptAdd( &hopt, "enr", "spec", airTypeOther, 1, 1, &enspR, "cotan",
"inter-particle energy, radial component",
NULL, NULL, pullHestEnergySpec );
hestOptAdd( &hopt, "ens", "spec", airTypeOther, 1, 1, &enspS, "zero",
"inter-particle energy, scale component",
NULL, NULL, pullHestEnergySpec );
hestOptAdd( &hopt, "enw", "spec", airTypeOther, 1, 1, &enspWin,
"butter:16, 0.8", "windowing to create locality with additive "
"scale-space interaction ( \"-int add\" )",
NULL, NULL, pullHestEnergySpec );
hestOptAdd( &hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false",
"always constrain Z=0, to process 2D images" );
hestOptAdd( &hopt, "efs", "bool", airTypeBool, 1, 1,
&energyFromStrength, "false",
"whether or not strength contributes to particle-image energy" );
hestOptAdd( &hopt, "nave", "bool", airTypeBool, 1, 1,
&nixAtVolumeEdgeSpace, "false",
"whether or not to nix points at edge of volume, where gage had "
"to invent values for kernel support" );
hestOptAdd( &hopt, "cbst", "bool", airTypeBool, 1, 1,
&constraintBeforeSeedThresh, "false",
"during initialization, try constraint satisfaction before "
"testing seedThresh" );
hestOptAdd( &hopt, "noadd", NULL, airTypeBool, 0, 0,
&noAdd, NULL, "turn off adding during population control" );
hestOptAdd( &hopt, "usa", "bool", airTypeBool, 1, 1,
&unequalShapesAllow, "false",
"allow volumes to have different shapes ( false is safe as "
"different volume sizes are often accidental )" );
hestOptAdd( &hopt, "pcet", "bool", airTypeBool, 1, 1, &popCntlEnoughTest,
"true", "use neighbor-counting \"enough\" heuristic to "
"bail out of pop cntl" );
hestOptAdd( &hopt, "cipc", "bool", airTypeBool, 1, 1, &convergenceIgnoresPopCntl,
"false", "convergence test doesn't care if there has been "
"recent changes due to population control" );
hestOptAdd( &hopt, "nobin", NULL, airTypeBool, 0, 0,
&binSingle, NULL,
"turn off spatial binning ( which prevents multi-threading "
"from being useful ), for debugging or speed-up measurement" );
hestOptAdd( &hopt, "lti", "bool", airTypeBool, 1, 1,
&liveThresholdOnInit, "true",
"impose liveThresh on initialization" );
hestOptAdd( &hopt, "por", "bool", airTypeBool, 1, 1,
&permuteOnRebin, "true",
"permute points during rebinning" );
hestOptAdd( &hopt, "npcwza", "bool", airTypeBool, 1, 1,
&noPopCntlWithZeroAlpha, "false",
"no pop cntl with zero alpha" );
hestOptAdd( &hopt, "ubfgl", "bool", airTypeBool, 1, 1,
&useBetaForGammaLearn, "false",
"use beta for gamma learning" );
hestOptAdd( &hopt, "ratb", "bool", airTypeBool, 1, 1,
&restrictiveAddToBins, "true",
"be choosy when adding points to bins to avoid overlap" );
hestOptAdd( &hopt, "svec", "vec", airTypeDouble, 3, 3, scaleVec, "0 0 0",
"if non-zero ( length ), vector to use for displaying scale "
"in 3-space" );
hestOptAdd( &hopt, "gssr", "rad", airTypeDouble, 1, 1, &glyphScaleRad, "0.0",
"if non-zero ( length ), scaling of scale to cylindrical tensors" );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "vol", "vol0 vol1", airTypeOther, 1, -1, &vspec, NULL,
"input volumes, in format <filename>:<kind>:<volname>",
&vspecNum, NULL, meetHestPullVol );
hestOptAdd( &hopt, "info", "info0 info1", airTypeOther, 1, -1, &idef, NULL,
"info definitions, in format "
"<info>[-c]:<volname>:<item>[:<zero>:<scale>]",
&idefNum, NULL, meetHestPullInfo );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"cubic:1, 0", "kernel for gageKernel00",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "sscp", "path", airTypeString, 1, 1, &cachePathSS, "./",
"path ( without trailing / ) for where to read/write "
"pre-blurred volumes for scale-space" );
kssOpi =
hestOptAdd( &hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur,
"dgauss:1, 5", "default blurring kernel, to sample scale space",
NULL, NULL, nrrdHestKernelSpec );
bspOpi =
hestOptAdd( &hopt, "bsp", "boundary", airTypeOther, 1, 1, &bspec,
"wrap", "default boundary behavior of scale-space blurring",
NULL, NULL, nrrdHestBoundarySpec );
hestOptAdd( &hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSSrecon,
"hermite", "kernel for reconstructing from scale space samples",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "nss", "# scl smpls", airTypeUInt, 1, 1,
&samplesAlongScaleNum, "1",
"if using \"-ppv\", number of samples along scale axis "
"for each spatial position" );
hestOptAdd( &hopt, "np", "# points", airTypeUInt, 1, 1,
&pointNumInitial, "1000",
"number of points to start in system" );
hestOptAdd( &hopt, "halton", NULL, airTypeInt, 0, 0,
&useHalton, NULL,
"use Halton sequence initialization instead of "
"uniform random" );
hestOptAdd( &hopt, "ppv", "# pnts/vox", airTypeInt, 1, 1,
&pointPerVoxel, "0",
"number of points per voxel to start in simulation "
"( need to have a seed thresh vol, overrides \"-np\" )" );
hestOptAdd( &hopt, "ppvzr", "z range", airTypeUInt, 2, 2,
ppvZRange, "1 0",
"range of Z slices ( 1st num < 2nd num ) to do ppv in, or, "
"\"1 0\" for whole volume" );
hestOptAdd( &hopt, "jit", "jitter", airTypeDouble, 1, 1,
&jitter, "0",
"amount of jittering to do with ppv" );
hestOptAdd( &hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "",
"4-by-N array of positions to start at ( overrides \"-np\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1,
&stepInitial, "1",
"initial step size for gradient descent" );
hestOptAdd( &hopt, "csm", "step", airTypeDouble, 1, 1,
&constraintStepMin, "0.0001",
"convergence criterion for constraint satisfaction" );
hestOptAdd( &hopt, "snap", "# iters", airTypeUInt, 1, 1,
&snap, "0",
"if non-zero, # iters between saved snapshots" );
hestOptAdd( &hopt, "maxi", "# iters", airTypeUInt, 1, 1,
&iterMax, "0",
"if non-zero, max # iterations to run whole system" );
hestOptAdd( &hopt, "stim", "# iters", airTypeUInt, 1, 1,
&stuckIterMax, "5",
"if non-zero, max # iterations to allow a particle "
" to be stuck before nixing" );
hestOptAdd( &hopt, "maxci", "# iters", airTypeUInt, 1, 1,
&constraintIterMax, "15",
"if non-zero, max # iterations for contraint enforcement" );
hestOptAdd( &hopt, "irad", "scale", airTypeDouble, 1, 1,
&radiusSpace, "1",
"particle radius in spatial domain" );
hestOptAdd( &hopt, "srad", "scale", airTypeDouble, 1, 1,
&radiusScale, "1",
"particle radius in scale domain" );
hestOptAdd( &hopt, "bws", "bin width", airTypeDouble, 1, 1,
&binWidthSpace, "1.001",
"spatial bin width as multiple of spatial radius" );
hestOptAdd( &hopt, "alpha", "alpha", airTypeDouble, 1, 1,
&alpha, "0.5",
"blend between particle-image ( alpha=0 ) and "
"inter-particle ( alpha=1 ) energies" );
hestOptAdd( &hopt, "beta", "beta", airTypeDouble, 1, 1,
&beta, "1.0",
"when using Phi2 energy, blend between pure "
"space repulsion ( beta=0 ) and "
"scale attraction ( beta=1 )" );
hestOptAdd( &hopt, "gamma", "gamma", airTypeDouble, 1, 1,
&_gamma, "1.0",
"scaling factor on energy from strength" );
hestOptAdd( &hopt, "wall", "k", airTypeDouble, 1, 1,
&wall, "0.0",
"spring constant on walls" );
hestOptAdd( &hopt, "eip", "k", airTypeDouble, 1, 1,
&energyIncreasePermit, "0.0",
"amount by which its okay for *per-particle* energy to increase "
"during gradient descent process" );
hestOptAdd( &hopt, "ess", "scl", airTypeDouble, 1, 1,
&backStepScale, "0.5",
"when energy goes up instead of down, scale step "
"size by this" );
hestOptAdd( &hopt, "oss", "scl", airTypeDouble, 1, 1,
&opporStepScale, "1.0",
"opportunistic scaling ( hopefully up, >1 ) of step size "
"on every iteration" );
hestOptAdd( &hopt, "edmin", "frac", airTypeDouble, 1, 1,
&energyDecreaseMin, "0.0001",
"convergence threshold: stop when fractional improvement "
"( decrease ) in energy dips below this" );
hestOptAdd( &hopt, "edpcmin", "frac", airTypeDouble, 1, 1,
&energyDecreasePopCntlMin, "0.01",
"population control is triggered when energy improvement "
"goes below this threshold" );
hestOptAdd( &hopt, "fnnm", "frac", airTypeDouble, 1, 1,
&fracNeighNixedMax, "0.25",
"don't nix if this fraction ( or more ) of neighbors "
"have been nixed" );
hestOptAdd( &hopt, "pcp", "period", airTypeUInt, 1, 1,
&popCntlPeriod, "20",
"# iters to wait between attempts at population control" );
hestOptAdd( &hopt, "iad", "# iters", airTypeUInt, 1, 1,
&addDescent, "10",
"# iters to run descent on tentative new points during PC" );
hestOptAdd( &hopt, "icb", "# iters", airTypeUInt, 1, 1,
&iterCallback, "1",
"periodicity of calling rendering callback" );
hestOptAdd( &hopt, "ac3c", "ac3c", airTypeBool, 1, 1,
&allowCodimension3Constraints, "false",
"allow codimensions 3 constraints" );
hestOptAdd( &hopt, "sit", "sit", airTypeBool, 1, 1, &scaleIsTau, "false",
"scale is tau" );
hestOptAdd( &hopt, "rng", "seed", airTypeUInt, 1, 1,
&rngSeed, "42",
"base seed value for RNGs" );
hestOptAdd( &hopt, "pbm", "mod", airTypeUInt, 1, 1,
&progressBinMod, "50",
"progress bin mod" );
hestOptAdd( &hopt, "eiphl", "hl", airTypeUInt, 1, 1, &eipHalfLife, "0",
"half-life of energyIncreasePermute ( \"-eip\" )" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1,
&threadNum, "1",
( airThreadCapable
? "number of threads hoover should use"
: "if threads where enabled in this Teem build, this is how "
"you would control the number of threads to use" ) );
hestOptAdd( &hopt, "nprob", "prob", airTypeDouble, 1, 1,
&neighborTrueProb, "1.0",
"do full neighbor discovery with this probability" );
hestOptAdd( &hopt, "pprob", "prob", airTypeDouble, 1, 1,
&probeProb, "1.0",
"probe local image values with this probability" );
hestOptAdd( &hopt, "addlog", "fname", airTypeString, 1, 1, &addLogS, "",
"name of file in which to log all particle additions" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"filename for saving computed positions" );
hestOptAdd( &hopt, "eob", "base", airTypeString, 1, 1, &extraOutBaseS, "",
"save extra info ( besides position ), and use this string as "
"the base of the filenames. Not using this means the extra "
"info is not saved." );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
#ifdef DEFT
if ( const char *envS = getenv( "DEFT_HOME" ) ) {
strcpy( Deft::homeDir, envS );
strcat( Deft::homeDir, "/" );
} else {
fprintf( stderr, "%s: WARNING: \"DEFT_HOME\" environment variable "
"not set; assuming \".\"\n", me );
strcpy( Deft::homeDir, "./" );
}
#endif
/*
airEnumPrint( stderr, gageScl );
exit( 0 );
*/
if ( airStrlen( addLogS ) ) {
if ( !( addLog = airFopen( addLogS, stdout, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing", me, addLogS );
airMopError( mop ); return 1;
}
airMopAdd( mop, addLog, ( airMopper )airFclose, airMopAlways );
} else {
addLog = NULL;
}
pctx = pullContextNew( );
airMopAdd( mop, pctx, ( airMopper )pullContextNix, airMopAlways );
if ( pullVerboseSet( pctx, verbose )
|| pullFlagSet( pctx, pullFlagZeroZ, zeroZ )
|| pullFlagSet( pctx, pullFlagEnergyFromStrength, energyFromStrength )
|| pullFlagSet( pctx, pullFlagNixAtVolumeEdgeSpace, nixAtVolumeEdgeSpace )
|| pullFlagSet( pctx, pullFlagConstraintBeforeSeedThresh,
constraintBeforeSeedThresh )
|| pullFlagSet( pctx, pullFlagPopCntlEnoughTest, popCntlEnoughTest )
|| pullFlagSet( pctx, pullFlagConvergenceIgnoresPopCntl,
convergenceIgnoresPopCntl )
|| pullFlagSet( pctx, pullFlagBinSingle, binSingle )
|| pullFlagSet( pctx, pullFlagNoAdd, noAdd )
|| pullFlagSet( pctx, pullFlagPermuteOnRebin, permuteOnRebin )
|| pullFlagSet( pctx, pullFlagNoPopCntlWithZeroAlpha,
noPopCntlWithZeroAlpha )
|| pullFlagSet( pctx, pullFlagUseBetaForGammaLearn,
useBetaForGammaLearn )
|| pullFlagSet( pctx, pullFlagRestrictiveAddToBins,
restrictiveAddToBins )
|| pullFlagSet( pctx, pullFlagAllowCodimension3Constraints,
allowCodimension3Constraints )
|| pullFlagSet( pctx, pullFlagScaleIsTau, scaleIsTau )
|| pullInitUnequalShapesAllowSet( pctx, unequalShapesAllow )
|| pullIterParmSet( pctx, pullIterParmSnap, snap )
|| pullIterParmSet( pctx, pullIterParmMax, iterMax )
|| pullIterParmSet( pctx, pullIterParmStuckMax, stuckIterMax )
|| pullIterParmSet( pctx, pullIterParmConstraintMax, constraintIterMax )
|| pullIterParmSet( pctx, pullIterParmPopCntlPeriod, popCntlPeriod )
|| pullIterParmSet( pctx, pullIterParmAddDescent, addDescent )
|| pullIterParmSet( pctx, pullIterParmCallback, iterCallback )
|| pullIterParmSet( pctx, pullIterParmEnergyIncreasePermitHalfLife,
eipHalfLife )
|| pullSysParmSet( pctx, pullSysParmStepInitial, stepInitial )
|| pullSysParmSet( pctx, pullSysParmConstraintStepMin, constraintStepMin )
|| pullSysParmSet( pctx, pullSysParmRadiusSpace, radiusSpace )
|| pullSysParmSet( pctx, pullSysParmRadiusScale, radiusScale )
|| pullSysParmSet( pctx, pullSysParmBinWidthSpace, binWidthSpace )
|| pullSysParmSet( pctx, pullSysParmAlpha, alpha )
|| pullSysParmSet( pctx, pullSysParmBeta, beta )
|| pullSysParmSet( pctx, pullSysParmGamma, _gamma )
|| pullSysParmSet( pctx, pullSysParmWall, wall )
|| pullSysParmSet( pctx, pullSysParmEnergyIncreasePermit,
energyIncreasePermit )
|| pullSysParmSet( pctx, pullSysParmEnergyDecreaseMin,
energyDecreaseMin )
|| pullSysParmSet( pctx, pullSysParmFracNeighNixedMax,
fracNeighNixedMax )
|| pullSysParmSet( pctx, pullSysParmEnergyDecreasePopCntlMin,
energyDecreasePopCntlMin )
|| pullSysParmSet( pctx, pullSysParmBackStepScale, backStepScale )
|| pullSysParmSet( pctx, pullSysParmOpporStepScale, opporStepScale )
|| pullSysParmSet( pctx, pullSysParmNeighborTrueProb,
neighborTrueProb )
|| pullSysParmSet( pctx, pullSysParmProbeProb, probeProb )
|| pullRngSeedSet( pctx, rngSeed )
|| pullProgressBinModSet( pctx, progressBinMod )
|| pullThreadNumSet( pctx, threadNum )
|| pullInterEnergySet( pctx, interType, enspR, enspS, enspWin )
|| pullInitLiveThreshUseSet( pctx, liveThresholdOnInit )
|| pullLogAddSet( pctx, addLog ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with flags:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nPosIn ) {
E = pullInitGivenPosSet( pctx, nPosIn );
} else if ( pointPerVoxel ) {
E = pullInitPointPerVoxelSet( pctx, pointPerVoxel,
ppvZRange[0], ppvZRange[1],
samplesAlongScaleNum, jitter );
} else if ( useHalton ) {
E = pullInitHaltonSet( pctx, pointNumInitial, 0 );
} else {
E = pullInitRandomSet( pctx, pointNumInitial );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with flags:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( meetPullVolStackBlurParmFinishMulti( vspec, vspecNum,
&kssFinished, &bspFinished,
kSSblur, bspec )
|| meetPullVolLoadMulti( vspec, vspecNum, cachePathSS, verbose )
|| meetPullVolAddMulti( pctx, vspec, vspecNum,
k00, k11, k22, kSSrecon )
|| meetPullInfoAddMulti( pctx, idef, idefNum ) ) {
airMopAdd( mop, err = biffGetDone( MEET ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with volumes or infos:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( !kssFinished && hestSourceUser == hopt[kssOpi].source ) {
fprintf( stderr, "\n\n%s: WARNING! Used the -%s flag, but the "
"meetPullVol specified blurring kernels\n\n\n", me,
hopt[kssOpi].flag );
}
if ( !bspFinished && hestSourceUser == hopt[bspOpi].source ) {
fprintf( stderr, "\n\n%s: WARNING! Used the -%s flag, but the "
"meetPullVol specified boundary specs\n\n\n", me,
hopt[bspOpi].flag );
}
if ( pullStart( pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble starting system:\n%s", me, err );
airMopError( mop ); return 1;
}
/* -------------------------------------------------- */
/* not sure when this table was created, don't have heart to nix it
*
* hght scl tang1 tang2 mode scl strength
* ridge surface: -1 evec2 - - -eval2
* ridge line: -1 evec2 evec1 - -eval1
* all ridges: -1 evec2 evec1 +1 ??
* valley surface: +1 evec0 - - eval0
* valley line: +1 evec0 evec1 - eval1
* all lines: +1 evec0 evec1 -1
*/
#ifdef DEFT
ssrange[0] = FLT_MAX;
ssrange[1] = -FLT_MAX;
for ( vsi=0; vsi<vspecNum; vsi++ ) {
meetPullVol *vol;
vol = vspec[vsi];
if ( vol->numSS ) {
ssrange[0] = AIR_MIN( ssrange[0], vol->rangeSS[0] );
ssrange[1] = AIR_MAX( ssrange[1], vol->rangeSS[1] );
}
}
if ( pctx->flag.scaleIsTau ) {
ssrange[0] = gageTauOfSig( ssrange[0] );
ssrange[1] = gageTauOfSig( ssrange[1] );
}
/* -------------------------------------------------- */
/* initialize bag and its UI */
bag.pctx = pctx;
bag.nPosOut = nrrdNew( );
bag.nTenOut = nrrdNew( );
bag.nFrcOut = nrrdNew( );
bag.nposOld = nrrdNew( );
bag.nposNew = nrrdNew( );
bag.nten = nrrdNew( );
bag.ntmp = nrrdNew( );
bag.nenr = nrrdNew( );
bag.nscl = nrrdNew( );
bag.nidcc = nrrdNew( );
bag.ncovar = nrrdNew( );
#if PULL_TANCOVAR
bag.ntcovar = nrrdNew( );
#endif
bag.nstab = nrrdNew( );
bag.nintern = nrrdNew( );
if ( pctx->ispec[pullInfoStrength] ) {
printf( "!%s: trouble creating strength nrrd\n", me );
bag.nstrn = nrrdNew( );
} else {
bag.nstrn = NULL;
}
if ( pctx->ispec[pullInfoQuality] ) {
printf( "!%s: trouble creating quality nrrd\n", me );
bag.nqual = nrrdNew( );
} else {
bag.nqual = NULL;
}
bag.nstuck = nrrdNew( );
bag.nfrcOld = nrrdNew( );
bag.nfrcNew = nrrdNew( );
bag.nrgb = nrrdNew( );
bag.nccrgb = nrrdNew( );
bag.ncmapOut = nrrdNew( );
bag.nblur = nrrdNew( );
bag.norig = vspec[0]->nin;
nrrdCopy( bag.nblur, bag.norig );
bag.rsmc = nrrdResampleContextNew( );
E = 0;
if ( !E ) E |= nrrdResampleDefaultCenterSet( bag.rsmc, nrrdDefaultCenter );
if ( !E ) E |= nrrdResampleInputSet( bag.rsmc, bag.norig );
for ( unsigned int axi=0; axi<3; axi++ ) {
if ( !E ) E |= nrrdResampleSamplesSet( bag.rsmc, axi,
bag.norig->axis[axi].size );
if ( !E ) E |= nrrdResampleRangeFullSet( bag.rsmc, axi );
}
if ( !E ) E |= nrrdResampleBoundarySet( bag.rsmc, nrrdBoundaryBleed );
if ( !E ) E |= nrrdResampleTypeOutSet( bag.rsmc, nrrdTypeDefault );
if ( !E ) E |= nrrdResampleRenormalizeSet( bag.rsmc, AIR_TRUE );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up resampler:\n%s", me, err );
airMopError( mop ); return 1;
}
/* bag.ncval is just a pointer to other nrrds */
bag.cvalRange = nrrdRangeNew( AIR_NAN, AIR_NAN );
ELL_3V_COPY( bag.scaleVec, scaleVec );
bag.glyphScaleRad = glyphScaleRad;
bag.scene = new Deft::Scene( );
bag.scene->bgColor( bg[0], bg[1], bg[2] );
int winy = 10;
int incy;
fltk::Window *win = new fltk::Window( 400, 600, "pull UI" );
win->begin( );
fltk::Button *stepButton = new fltk::Button( 10, winy, 50, incy=20, "step" );
stepButton->callback( ( fltk::Callback* )step_cb, &bag );
bag.verbose = new fltk::ValueInput( 100, winy, 30, 20, "verb" );
bag.verbose->value( pctx->verbose );
bag.verbose->callback( ( fltk::Callback* )verbose_cb, &bag );
bag.iters = new fltk::IntInput( 200, winy, 50, 20, "# iters" );
bag.iters->value( 1 );
if ( ssrange[1] > ssrange[0] ) {
fltk::Button *gamButton = new fltk::Button( 260, winy,
50, 20, "gamma" );
gamButton->callback( ( fltk::Callback* )gammaSet_cb, &bag );
}
fltk::Button *ccButton = new fltk::Button( 360, winy,
30, 20, "CC" );
ccButton->callback( ( fltk::Callback* )cc_cb, &bag );
winy += incy + 5;
fltk::Button *saveButton = new fltk::Button( 10, winy, 50, 20, "save" );
saveButton->callback( ( fltk::Callback* )save_cb, &bag );
bag.scaleVecInput[0] = new fltk::FloatInput( 120, winy, 35, 20, "scaleVec" );
bag.scaleVecInput[1] = new fltk::FloatInput( 160, winy, 35, 20, "" );
bag.scaleVecInput[2] = new fltk::FloatInput( 200, winy, 35, 20, "" );
bag.scaleVecInput[0]->value( scaleVec[0] );
bag.scaleVecInput[1]->value( scaleVec[1] );
bag.scaleVecInput[2]->value( scaleVec[2] );
bag.scaleVecInput[0]->callback( ( fltk::Callback* )scaleGlyph_cb, &bag );
bag.scaleVecInput[1]->callback( ( fltk::Callback* )scaleGlyph_cb, &bag );
bag.scaleVecInput[2]->callback( ( fltk::Callback* )scaleGlyph_cb, &bag );
bag.glyphScaleRadInput = new fltk::ValueInput( 300, winy, 45, 20, "gssr" );
bag.glyphScaleRadInput->range( 0.0, 100.0 );
bag.glyphScaleRadInput->step( 0.1 );
bag.glyphScaleRadInput->linesize( 0.1 );
bag.glyphScaleRadInput->value( glyphScaleRad );
bag.glyphScaleRadInput->callback( ( fltk::Callback* )scaleGlyph_cb, &bag );
winy += incy;
bag.alpha = new Deft::Slider( 0, winy, win->w( ), incy=55, "alpha" );
bag.alpha->align( fltk::ALIGN_LEFT );
bag.alpha->range( 0, 1 );
bag.alpha->value( pctx->sysParm.alpha );
bag.alpha->fastUpdate( 1 );
bag.alpha->callback( ( fltk::Callback* )alpha_cb, &bag );
if ( pullInterTypeAdditive == pctx->interType ) {
winy += incy;
bag.beta = new Deft::Slider( 0, winy, win->w( ), incy=55, "beta" );
bag.beta->align( fltk::ALIGN_LEFT );
bag.beta->range( 0, 1 );
bag.beta->value( pctx->sysParm.beta );
bag.beta->fastUpdate( 1 );
bag.beta->callback( ( fltk::Callback* )beta_cb, &bag );
}
if ( pullEnergyCubicWell == pctx->energySpecR->energy
|| pullEnergyBetterCubicWell == pctx->energySpecR->energy ) {
winy += incy;
bag.cwell = new Deft::Slider( 0, winy, win->w( ), incy=55, "well depth" );
bag.cwell->align( fltk::ALIGN_LEFT );
bag.cwell->range( -0.04, 0 );
bag.cwell->value( bag.pctx->energySpecR->parm[1] );
bag.cwell->fastUpdate( 1 );
bag.cwell->callback( ( fltk::Callback* )cwell_cb, &bag );
/* remember eip as fraction of well depth */
bag.energyIncreasePermitFrac =
energyIncreasePermit/bag.pctx->energySpecR->parm[1];
} else {
bag.energyIncreasePermitFrac = AIR_NAN;
}
winy += incy;
bag.gamma = new Deft::Slider( 0, winy, win->w( ), incy=55, "gamma" );
bag.gamma->align( fltk::ALIGN_LEFT );
bag.gamma->range( 0, 2*pctx->sysParm.gamma );
bag.gamma->value( pctx->sysParm.gamma );
bag.gamma->fastUpdate( 1 );
bag.gamma->callback( ( fltk::Callback* )gamma_cb, &bag );
winy += incy;
bag.ccSelect = new Deft::Slider( 0, winy, win->w( ), incy=55, "CC Select" );
bag.ccSelect->align( fltk::ALIGN_LEFT );
bag.ccSelect->range( 0, 0 );
bag.ccSelect->step( 1 );
bag.ccSelect->value( 0 );
bag.ccSelect->fastUpdate( 1 );
bag.ccSelect->callback( ( fltk::Callback* )ccSelect_cb, &bag );
bag.ccSingle = new fltk::CheckButton( 130, winy+4, 50, 20, "Single" );
bag.ccSingle->value( 0 );
bag.ccSingle->callback( ( fltk::Callback* )ccSelect_cb, &bag );
winy += incy;
bag.rho = new Deft::Slider( 0, winy, win->w( ), incy=55, "rho" );
bag.rho->align( fltk::ALIGN_LEFT );
bag.rho->range( 0, 1.0 );
bag.rho->value( 0.5 );
bag.rho->fastUpdate( 1 );
bag.rho->callback( ( fltk::Callback* )cc_cb, &bag );
if ( ssrange[1] > ssrange[0] ) {
winy += incy;
bag.sclMean = new Deft::Slider( 0, winy, win->w( ), incy=55, "scale mean" );
bag.sclMean->align( fltk::ALIGN_LEFT );
bag.sclMean->range( ssrange[0], ssrange[1] );
bag.sclMean->value( ( ssrange[0] + ssrange[1] )/2 );
bag.sclMean->fastUpdate( 1 );
bag.sclMean->callback( ( fltk::Callback* )scale_cb, &bag );
fltk::Button *reblurButton = new fltk::Button( 130, winy+4, 50, 20, "reblur" );
reblurButton->callback( ( fltk::Callback* )reblur_cb, &bag );
winy += incy;
bag.sclWind = new Deft::Slider( 0, winy, win->w( ), incy=55, "scale window" );
bag.sclWind->align( fltk::ALIGN_LEFT );
bag.sclWind->range( 0, ssrange[1] - ssrange[0] );
bag.sclWind->value( ssrange[1] - ssrange[0] );
bag.sclWind->fastUpdate( 1 );
bag.sclWind->callback( ( fltk::Callback* )scale_cb, &bag );
scale_cb( NULL, &bag );
} else {
bag.sclMin = bag.sclMax = 0;
}
if ( pctx->ispec[pullInfoStrength] ) {
winy += incy;
bag.strength = new Deft::Slider( 0, winy, win->w( ), incy=55, "strength" );
bag.strength->align( fltk::ALIGN_LEFT );
bag.strength->range( 0, 1 );
bag.strength->callback( ( fltk::Callback* )strength_cb, &bag );
bag.strength->fastUpdate( 1 );
bag.strength->value( 0 );
} else {
bag.strnMin = 0;
}
if ( pctx->ispec[pullInfoQuality] ) {
winy += incy;
bag.quality = new Deft::Slider( 0, winy, win->w( ), incy=55, "quality" );
bag.quality->align( fltk::ALIGN_LEFT );
bag.quality->range( -0.1, 1 );
bag.quality->callback( ( fltk::Callback* )quality_cb, &bag );
bag.quality->fastUpdate( 1 );
bag.quality->value( -0.1 );
} else {
bag.qualMin = 0;
}
/*
winy += incy;
bag.height = new Deft::Slider( 0, winy, win->w( ), incy=55, "height" );
bag.height->align( fltk::ALIGN_LEFT );
bag.height->range( 0, 10 );
bag.height->value( 10 );
bag.height->fastUpdate( 1 );
bag.height->callback( ( fltk::Callback* )strength_cb, &bag );
*/
win->end( );
win->show( );
/* -------------------------------------------------- */
bag.viewer = new Deft::Viewer( bag.scene, imgSize[0], imgSize[1] );
if ( camkeep ) {
bag.viewer->camera( fr[0], fr[1], fr[2],
at[0], at[1], at[2],
up[0], up[1], up[2],
fovy, neer, faar );
}
bag.viewer->resizable( bag.viewer );
bag.viewer->end( );
const char *fakeArgv[2] = {"Deft_pull", NULL};
bag.viewer->show( 1, ( char** )fakeArgv );
if ( ortho ) {
/* total hack */
bag.viewer->keyboard( 'p', 0, 0 );
}
/* bag.viewer->helpPrint( ); */
Deft::ViewerUI *viewerUI = new Deft::ViewerUI( bag.viewer );
viewerUI->show( );
fltk::flush( );
/* -------------------------------------------------- */
if ( gageKindScl == vspec[0]->kind ) {
bag.contour = new Deft::Contour( );
bag.contour->volumeSet( bag.nblur );
bag.contour->twoSided( true );
bag.scene->objectAdd( bag.contour );
} else {
bag.contour = NULL;
}
/* -------------------------------------------------- */
Deft::TensorGlyph *glyph = new Deft::TensorGlyph( );
glyph->dynamic( true );
glyph->twoSided( true );
glyph->anisoType( aniso );
glyph->anisoThreshMin( anisoThreshMin );
glyph->anisoThresh( anisoThresh );
glyph->glyphType( glyphType );
glyph->superquadSharpness( sqdSharp );
glyph->glyphResolution( glyphFacetRes );
if ( tenGlyphTypeBetterquad ) {
glyph->barycentricResolution( baryRes );
} else {
glyph->barycentricResolution( 20 );
}
glyph->glyphScale( glyphScale );
glyph->glyphHaloWidth( glyphHaloWidth );
glyph->haloTraceBound( haloTraceBound );
glyph->glyphNormPow( glyphNormPow );
glyph->glyphEvalPow( glyphEvalPow );
glyph->rgbEvecParmSet( tenAniso_Cl2, 0, 0.7, 1.0, 2.3, 1.0 );
glyph->rgbEvecParmSet( tenAniso_Cl2, 0, 0, 1.0, 1, 0.0 );
glyph->maskThresh( 0.5 );
/*
void rgbParmSet( int aniso, unsigned int evec,
double maxSat, double isoGray,
double gamma, double modulate );
*/
bag.scene->objectAdd( glyph );
bag.glyph = glyph;
Deft::TensorGlyphUI *glyphUI = new Deft::TensorGlyphUI( bag.glyph,
bag.viewer );
glyphUI->show( );
/* -------------------------------------------------- */
/*
Deft::TensorGlyph *hedge = new Deft::TensorGlyph( );
hedge->dynamic( true );
hedge->anisoType( aniso );
hedge->anisoThreshMin( anisoThreshMin );
hedge->anisoThresh( anisoThresh );
hedge->glyphType( glyphType );
hedge->superquadSharpness( sqdSharp );
hedge->glyphResolution( glyphFacetRes );
hedge->glyphScale( glyphScale/4 );
hedge->rgbParmSet( tenAniso_Cl2, 0, 0.7, 1.0, 2.3, 1.0 );
hedge->rgbParmSet( tenAniso_Cl2, 0, 0, 1.0, 1, 0.0 );
hedge->maskThresh( 0.0 );
bag.hedge = hedge;
bag.scene->objectAdd( hedge );
Deft::TensorGlyphUI *hedgeUI = new Deft::TensorGlyphUI( bag.hedge,
bag.viewer );
hedgeUI->show( );
*/
/* -------------------------------------------------- */
Deft::Volume *vol = new Deft::Volume( vspec[0]->kind, bag.nblur );
fprintf( stderr, "!%s: vol = %p *********************\n", me, vol );
Deft::TriPlane *triplane = new Deft::TriPlane( vol );
/*
** HEY: you can eventually segfault if this isn't set here
** shouldn't doing so be optional?
*/
if ( gageKindScl == vspec[0]->kind ) {
triplane->alphaMaskQuantity( Deft::alphaMaskSclQuantityValue );
triplane->alphaMaskThreshold( 0 );
triplane->colorQuantity( Deft::colorSclQuantityValue );
} else if ( tenGageKind == vspec[0]->kind ) {
triplane->alphaMaskQuantity( Deft::alphaMaskTenQuantityConfidence );
triplane->alphaMaskThreshold( 0 );
triplane->colorQuantity( Deft::colorTenQuantityRgbLinear );
}
NrrdKernelSpec *ksp = nrrdKernelSpecNew( );
double kparm[10];
kparm[0] = 1.0;
nrrdKernelSpecSet( ksp, nrrdKernelTent, kparm );
triplane->kernel( gageKernel00, ksp );
ELL_3V_SET( kparm, 1, 1, 0 );
nrrdKernelSpecSet( ksp, nrrdKernelBCCubicD, kparm );
triplane->kernel( gageKernel11, ksp );
nrrdKernelSpecSet( ksp, nrrdKernelBCCubicDD, kparm );
triplane->kernel( gageKernel22, ksp );
triplane->visible( false );
bag.scene->groupAdd( triplane );
Deft::TriPlaneUI *planeUI = new Deft::TriPlaneUI( triplane, bag.viewer );
planeUI->show( );
/* -------------------------------------------------- */
if ( gageKindScl == vspec[0]->kind ) {
fltk::Window *window = new fltk::Window( 400, 80, "isovalue" );
window->begin( );
window->resizable( window );
winy = 0;
bag.isoval = new Deft::Slider( 0, winy, window->w( ), incy=55, "isovalue" );
winy += incy;
bag.isoval->align( fltk::ALIGN_LEFT );
bag.isoval->range( bag.contour->minimum( ), bag.contour->maximum( ) );
bag.isoval->value( bag.contour->maximum( ) );
bag.contour->wireframe( false );
bag.isoval->fastUpdate( 0 );
bag.isoval->callback( ( fltk::Callback* )isovalue_cb, &bag );
/* bag.isoval->value( 1.0 ); */
window->end( );
window->show( argc, ( char** )argv );
}
if ( pullPhistEnabled ) {
bag.phistLine = limnPolyDataNew( );
/* bag.phistTube = limnPolyDataNew( ); */
bag.phistSurf = new Deft::PolyData( bag.phistLine, false );
bag.phistSurf->wireframe( false );
bag.phistSurf->normalsUse( true );
bag.phistSurf->colorUse( true );
bag.phistSurf->visible( true );
bag.phistSurf->changed( );
bag.scene->objectAdd( bag.phistSurf );
}
fltk::flush( );
fltk::redraw( );
/* this is to get "output" prior to any iterations,
but after seeding and initialization */
outputGet( &bag );
outputShow( &bag );
if ( !camkeep ) {
bag.viewer->cameraReset( );
}
if ( 0 ) {
Nrrd *nplot;
nplot = nrrdNew( );
if ( pullEnergyPlot( pctx, nplot, 1, 0, 0, 601 ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble plotting:\n%s", me, err );
airMopError( mop ); return 1;
}
nrrdSave( "eplot.nrrd", nplot, NULL );
nplot = nrrdNuke( nplot );
#if PULL_HINTER
pctx->nhinter = nrrdNew( );
#endif
}
if ( fog ) {
bag.viewer->fog( true );
bag.viewer->redraw( );
ret = fltk::wait( );
}
if ( saveAndQuit ) {
bag.scene->draw( );
bag.viewer->screenDump( );
} else {
/* set up callbacks in pull */
pullCallbackSet( pctx, iter_cb, &bag );
/* this returns when the user quits */
ret = fltk::run( );
}
#else
/* else not running as Deft, but as command-line */
if ( !E ) E |= pullRun( pctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble 3:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( pullOutputGet( nPosOut, NULL, NULL, NULL, 0.0, pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble 3.1:\n%s", me, err );
airMopError( mop ); return 1;
}
nrrdSave( outS, nPosOut, NULL );
if ( airStrlen( extraOutBaseS ) ) {
Nrrd *nstrn, *nstab, *nintern;
char fname[3][AIR_STRLEN_MED];
nstrn = nrrdNew( );
nstab = nrrdNew( );
nintern = nrrdNew( );
airMopAdd( mop, nstrn, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nstab, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nintern, ( airMopper )nrrdNuke, airMopAlways );
if ( pullInfoGet( nstrn, pullInfoStrength, pctx )
|| pullPropGet( nstab, pullPropStability, pctx )
|| pullPropGet( nintern, pullPropNeighInterNum, pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble 3.2:\n%s", me, err );
airMopError( mop ); return 1;
}
sprintf( fname[0], "%s-strn.nrrd", extraOutBaseS );
sprintf( fname[1], "%s-stab.nrrd", extraOutBaseS );
sprintf( fname[2], "%s-intern.nrrd", extraOutBaseS );
if ( nrrdSave( fname[0], nstrn, NULL )
|| nrrdSave( fname[1], nstab, NULL )
|| nrrdSave( fname[2], nintern, NULL ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble 3.3:\n%s", me, err );
airMopError( mop ); return 1;
}
}
#endif
pullFinish( pctx );
airMopOkay( mop );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/bane.h>
#define QBERT "qbert"
#define QBERT_HIST_BINS 1024 /* histogram size for v, g, and h */
int qbertSaveAll = AIR_FALSE; /* can be used to save output of every stage */
/*
** This padding/resampling is to get axis[i]'s size >= sz[i], which
** is only needed if the input volume is smaller along any of the axes
** than the desired output volume.
*/
int
42 qbertSizeUp( Nrrd *nout, Nrrd *nin, unsigned int *sz,
NrrdKernelSpec *uk ) {
char me[]="qbertSizeUp";
int i, anyneed, need;
ptrdiff_t padMin[3], padMax[3];
NrrdResampleInfo *rsi;
airArray *mop;
mop = airMopNew( );
rsi=nrrdResampleInfoNew( );
airMopAdd( mop, rsi, ( airMopper )nrrdResampleInfoNix, airMopAlways );
anyneed = 0;
if ( uk ) {
for ( i=0; i<=2; i++ ) {
anyneed |= need = sz[i] - nin->axis[i].size;
fprintf( stderr, "%s: sz[%d] = %u -> need = %d --> ",
me, i, AIR_UINT( nin->axis[i].size ), need );
need = AIR_MAX( 0, need );
fprintf( stderr, "%d --> %s resample\n", need, need ? "WILL" : "won't" );
if ( need ) {
rsi->kernel[i] = uk->kernel;
memcpy( rsi->parm[i], uk->parm, uk->kernel->numParm*sizeof( double ) );
if ( !AIR_EXISTS( nin->axis[i].min ) ) {
nin->axis[i].min = 0.0;
}
if ( !AIR_EXISTS( nin->axis[i].max ) ) {
nin->axis[i].max = nin->axis[i].size-1;
}
rsi->min[i] = nin->axis[i].min;
rsi->max[i] = nin->axis[i].max;
rsi->samples[i] = sz[i];
nin->axis[i].center = nrrdCenterNode;
} else {
rsi->kernel[i] = NULL;
}
}
if ( anyneed ) {
rsi->boundary = nrrdBoundaryBleed;
rsi->type = nrrdTypeFloat;
rsi->renormalize = AIR_TRUE;
rsi->clamp = AIR_TRUE;
fprintf( stderr, "%s: resampling ... ", me ); fflush( stderr );
if ( nrrdSpatialResample( nout, nin, rsi ) ) {
biffMovef( QBERT, NRRD, "%s: trouble upsampling", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "done\n" );
}
} else {
for ( i=0; i<=2; i++ ) {
char stmp[AIR_STRLEN_SMALL];
anyneed |= need = sz[i] - nin->axis[i].size;
fprintf( stderr, "%s: sz[%d] = %s -> need = %d --> ", me, i,
airSprintSize_t( stmp, nin->axis[i].size ), need );
need = AIR_MAX( 0, need );
fprintf( stderr, "%d --> ", need );
padMin[i] = 0 - ( int )floor( need/2.0 );
padMax[i] = nin->axis[i].size - 1 + ( int )ceil( need/2.0 );
fprintf( stderr, "pad indices: [%d..%d]\n",
AIR_CAST( int, padMin[i] ),
AIR_CAST( int, padMax[i] ) );
}
if ( anyneed ) {
fprintf( stderr, "%s: padding ... ", me ); fflush( stderr );
if ( nrrdPad_va( nout, nin, padMin, padMax, nrrdBoundaryPad, 0.0 ) ) {
biffMovef( QBERT, NRRD, "%s: trouble padding", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "done\n" );
}
}
if ( !anyneed ) {
if ( nrrdCopy( nout, nin ) ) {
biffMovef( QBERT, NRRD, "%s: trouble copying", me );
airMopError( mop ); return 1;
}
}
if ( qbertSaveAll ) {
fprintf( stderr, "%s: saving up.nrrd\n", me );
nrrdSave( "up.nrrd", nout, NULL );
}
airMopOkay( mop );
return 0;
}
/*
** resampling to get axis[i]'s size down to exactly sz[i]
*/
int
131 qbertSizeDown( Nrrd *nout, Nrrd *nin, unsigned int *sz,
NrrdKernelSpec *dk ) {
char me[]="qbertSizeDown";
NrrdResampleInfo *rsi;
int need;
unsigned int i;
airArray *mop;
mop = airMopNew( );
rsi = nrrdResampleInfoNew( );
airMopAdd( mop, rsi, ( airMopper )nrrdResampleInfoNix, airMopAlways );
rsi->boundary = nrrdBoundaryBleed;
rsi->type = nrrdTypeFloat;
rsi->renormalize = AIR_TRUE;
need = 0;
for ( i=0; i<=2; i++ ) {
if ( nin->axis[i].size > sz[i] ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
need = 1;
rsi->kernel[i] = dk->kernel;
memcpy( rsi->parm[i], dk->parm, dk->kernel->numParm*sizeof( double ) );
rsi->samples[i] = sz[i];
if ( !AIR_EXISTS( nin->axis[i].min ) ) {
nin->axis[i].min = 0.0;
}
if ( !AIR_EXISTS( nin->axis[i].max ) ) {
nin->axis[i].max = nin->axis[i].size-1;
}
rsi->min[i] = nin->axis[i].min;
rsi->max[i] = nin->axis[i].max;
nin->axis[i].center = nrrdCenterNode;
fprintf( stderr, "%s: downsampling axis %d from %s to %s samples\n",
me, i, airSprintSize_t( stmp1, nin->axis[i].size ),
airSprintSize_t( stmp2, rsi->samples[i] ) );
}
else {
rsi->kernel[i] = NULL;
}
}
if ( need ) {
fprintf( stderr, "%s: resampling ... ", me ); fflush( stderr );
if ( nrrdSpatialResample( nout, nin, rsi ) ) {
biffMovef( QBERT, NRRD, "%s: trouble resampling", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "done\n" );
}
else {
if ( nrrdCopy( nout, nin ) ) {
biffMovef( QBERT, NRRD, "%s: trouble copying", me );
airMopError( mop ); return 1;
}
}
if ( qbertSaveAll ) {
fprintf( stderr, "%s: saving down.nrrd\n", me );
nrrdSave( "down.nrrd", nout, NULL );
}
airMopOkay( mop );
return 0;
}
/*
** probing to getting floating point V, G, and maybe H values
*/
int
197 qbertProbe( Nrrd *nout, Nrrd *nin,
NrrdKernelSpec *k00, NrrdKernelSpec *k11, NrrdKernelSpec *k22,
int doH, unsigned int *sz ) {
char me[]="qbertProbe", prog[AIR_STRLEN_SMALL];
gageContext *ctx;
gagePerVolume *pvl;
const double *val, *gmag, *scnd;
float *vghF;
int E;
unsigned int i, j, k;
airArray *mop;
doH = !!doH;
mop = airMopNew( );
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
nin->axis[0].center = nrrdCenterNode;
nin->axis[1].center = nrrdCenterNode;
nin->axis[2].center = nrrdCenterNode;
if ( !( pvl = gagePerVolumeNew( ctx, nin, gageKindScl ) ) ) {
biffMovef( QBERT, GAGE, "%s: gage trouble", me );
airMopError( mop ); return 1;
}
gageParmSet( ctx, gageParmVerbose, 0 );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
E = 0;
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
/* about kernel setting for probing: currently, the way that probing is
done is ONLY on grid locations, and never in between voxels. That
means that the kernels set below are really only used for creating
discrete convolution masks at unit locations */
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclValue );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclGradMag );
if ( doH ) {
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageScl2ndDD );
}
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
biffMovef( QBERT, GAGE, "%s: gage trouble", me );
airMopError( mop ); return 1;
}
gageParmSet( ctx, gageParmVerbose, 0 );
val = gageAnswerPointer( ctx, pvl, gageSclValue );
gmag = gageAnswerPointer( ctx, pvl, gageSclGradMag );
scnd = gageAnswerPointer( ctx, pvl, gageScl2ndDD );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 2+doH ),
AIR_CAST( size_t, sz[0] ),
AIR_CAST( size_t, sz[1] ),
AIR_CAST( size_t, sz[2] ) ) ) {
biffMovef( QBERT, NRRD,
"%s: couldn't allocate floating point VG%s volume",
me, doH ? "H" : "" );
airMopError( mop ); return 1;
}
vghF = ( float * )nout->data;
fprintf( stderr, "%s: probing ... ", me ); fflush( stderr );
for ( k=0; k<sz[2]; k++ ) {
for ( j=0; j<sz[1]; j++ ) {
if ( !( ( j + sz[1]*k )%100 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, j + sz[1]*k, sz[1]*sz[2], prog ) );
fflush( stderr );
}
for ( i=0; i<sz[0]; i++ ) {
gageProbe( ctx, i, j, k );
vghF[0] = AIR_CAST( float, *val );
vghF[1] = AIR_CAST( float, *gmag );
if ( doH ) {
vghF[2] = AIR_CAST( float, *scnd );
}
vghF += 2+doH;
}
}
}
fprintf( stderr, "%s\n", airDoneStr( 0, 2, 1, prog ) );
if ( qbertSaveAll ) {
fprintf( stderr, "%s: saving vghF.nrrd\n", me );
nrrdSave( "vghF.nrrd", nout, NULL );
}
airMopOkay( mop );
return 0;
}
/*
** make histograms of v, g, h values as first step in determining the
** inclusions for the later quantization
*/
int
291 qbertMakeVghHists( Nrrd *nvhist, Nrrd *nghist, Nrrd *nhhist,
unsigned int *sz, int bins,
Nrrd *nvghF, Nrrd *nin ) {
char me[]="qbertMakeVghHists";
double minv, maxv, ming, maxg, minh=0, maxh=0;
float *vghF;
unsigned int i;
int nval, doH, E, *vhist, *ghist, *hhist=NULL, vi, gi, hi;
nval = nvghF->axis[0].size;
doH = !!( nval == 3 );
vghF = ( float * )nvghF->data;
minv = maxv = vghF[0 + nval*0];
ming = maxg = vghF[1 + nval*0];
if ( doH ) {
minh = maxh = vghF[2 + nval*0];
}
for ( i=0; i<sz[0]*sz[1]*sz[2]; i++ ) {
minv = AIR_MIN( minv, vghF[0 + nval*i] );
maxv = AIR_MAX( maxv, vghF[0 + nval*i] );
ming = AIR_MIN( ming, vghF[1 + nval*i] );
maxg = AIR_MAX( maxg, vghF[1 + nval*i] );
if ( doH ) {
minh = AIR_MIN( minh, vghF[2 + nval*i] );
maxh = AIR_MAX( maxh, vghF[2 + nval*i] );
}
}
fprintf( stderr, "%s: values: [%g .. %g] -> ", me, minv, maxv );
/* just because we're bastards, we're going to enforce minv >= 0 for
types that started as unsigned integral types. Downsampling with a
ringing kernel can have produced negative values, so this change to
minv can actually restrict the range, in contrast to to the changes
to ming, minh, and maxh below */
if ( nrrdTypeIsUnsigned[nin->type] ) {
minv = AIR_MAX( minv, 0.0 );
}
fprintf( stderr, "[%g .. %g]\n", minv, maxv );
fprintf( stderr, "%s: grads: [%g .. %g] -> ", me, ming, maxg );
ming = 0;
fprintf( stderr, "[%g .. %g]\n", ming, maxg );
if ( doH ) {
fprintf( stderr, "%s: 2ndDDs: [%g .. %g] -> ", me, minh, maxh );
if ( maxh > -minh )
minh = -maxh;
else
maxh = -minh;
fprintf( stderr, "[%g .. %g]\n", minh, maxh );
}
fprintf( stderr, "%s: using %d-bin histograms\n", me, bins );
E = 0;
if ( !E ) E |= nrrdMaybeAlloc_va( nvhist, nrrdTypeInt, 1,
AIR_CAST( size_t, bins ) );
if ( !E ) E |= nrrdMaybeAlloc_va( nghist, nrrdTypeInt, 1,
AIR_CAST( size_t, bins ) );
if ( doH ) {
if ( !E ) E |= nrrdMaybeAlloc_va( nhhist, nrrdTypeInt, 1,
AIR_CAST( size_t, bins ) );
}
if ( E ) {
biffMovef( QBERT, NRRD, "%s: couldn't allocate %d %d-bin histograms",
me, nval, bins );
return 1;
}
nvhist->axis[0].min = minv; nvhist->axis[0].max = maxv;
nghist->axis[0].min = ming; nghist->axis[0].max = maxg;
vhist = ( int * )nvhist->data;
ghist = ( int * )nghist->data;
memset( vhist, 0, bins*sizeof( int ) );
memset( ghist, 0, bins*sizeof( int ) );
if ( doH ) {
nhhist->axis[0].min = minh; nhhist->axis[0].max = maxh;
hhist = ( int * )nhhist->data;
memset( hhist, 0, bins*sizeof( int ) );
}
vghF = ( float * )nvghF->data;
for ( i=0; i<sz[0]*sz[1]*sz[2]; i++ ) {
vi = airIndexClamp( minv, vghF[0], maxv, bins );
gi = airIndex( ming, vghF[1], maxg, bins );
vhist[vi]++;
ghist[gi]++;
if ( doH ) {
hi = airIndex( minh, vghF[2], maxh, bins );
hhist[hi]++;
}
vghF += nval;
}
if ( qbertSaveAll ) {
fprintf( stderr, "%s: saving {v, g, h}hist.nrrd\n", me );
nrrdSave( "vhist.nrrd", nvhist, NULL );
nrrdSave( "ghist.nrrd", nghist, NULL );
if ( doH ) {
nrrdSave( "hhist.nrrd", nhhist, NULL );
}
}
return 0;
}
/*
** determine inclusion from histograms and create 8-bit VGH volume
*/
int
393 qbertMakeVgh( Nrrd *nvgh, Nrrd *nvhist, Nrrd *nghist, Nrrd *nhhist,
unsigned int *sz, float *perc,
Nrrd *nvghF ) {
char me[]="qbertMakeVgh", cmt[AIR_STRLEN_SMALL];
double minv, maxv, ming, maxg, minh=0, maxh=0;
int lose, *vhist, *ghist, *hhist=NULL, bins, vi, gi, hi, nval, doH;
unsigned int i;
unsigned char *vgh;
float *vghF;
nval = nvghF->axis[0].size;
doH = !!( nval == 3 );
minv = nvhist->axis[0].min; maxv = nvhist->axis[0].max;
ming = nghist->axis[0].min; maxg = nghist->axis[0].max;
vhist = ( int * )nvhist->data;
ghist = ( int * )nghist->data;
if ( doH ) {
minh = nhhist->axis[0].min; maxh = nhhist->axis[0].max;
hhist = ( int * )nhhist->data;
}
lose = ( int )( perc[0]*sz[0]*sz[1]*sz[2]/100 );
bins = nvhist->axis[0].size;
i = bins-1;
while ( lose > 0 ) {
/* HEY: we're nibbling only from top, even though for signed
value types, there could be a tail at low negative values ( had
this problem with some CT data ) */
lose -= vhist[i--];
}
maxv = AIR_AFFINE( 0, i, bins-1, minv, maxv );
lose = ( int )( perc[1]*sz[0]*sz[1]*sz[2]/100 );
bins = nghist->axis[0].size;
i = bins-1;
while ( lose > 0 ) {
/* nibble from top */
lose -= ghist[i--];
}
maxg = AIR_AFFINE( 0, i, bins-1, ming, maxg );
if ( doH ) {
lose = ( int )( perc[2]*sz[0]*sz[1]*sz[2]/100 );
bins = nhhist->axis[0].size;
i = 0;
while ( lose > 0 ) {
/* nibble from top and bottom at equal rates */
lose -= hhist[i] + hhist[bins-1-i];
i++;
}
minh = AIR_AFFINE( 0, i, bins-1, minh, maxh );
maxh = -minh;
}
fprintf( stderr, "%s: new values ( ignored %5d ): [%g .. %g]\n",
me, ( int )( perc[0]*sz[0]*sz[1]*sz[2]/100 ), minv, maxv );
fprintf( stderr, "%s: new grads ( ignored %5d ): [%g .. %g]\n",
me, ( int )( perc[1]*sz[0]*sz[1]*sz[2]/100 ), ming, maxg );
if ( doH ) {
fprintf( stderr, "%s: new 2ndDDs ( ignored %5d ): [%g .. %g]\n",
me, ( int )( perc[2]*sz[0]*sz[1]*sz[2]/100 ), minh, maxh );
fprintf( stderr, "%s: putting 2ndDD in range 1 to 169 ( 0.0 -> 85 )\n", me );
}
if ( nrrdMaybeAlloc_va( nvgh, nrrdTypeUChar, 4,
AIR_CAST( size_t, nval ),
AIR_CAST( size_t, sz[0] ),
AIR_CAST( size_t, sz[1] ),
AIR_CAST( size_t, sz[2] ) ) ) {
biffMovef( QBERT, NRRD, "%s: couldn't allocate 8-bit VG%s volume",
me, doH ? "H" : "" );
return 1;
}
vgh = ( unsigned char* )nvgh->data;
vghF = ( float* )nvghF->data;
for ( i=0; i<sz[0]*sz[1]*sz[2]; i++ ) {
vi = airIndex( minv, vghF[0], maxv, 254 );
vgh[0] = AIR_CLAMP( 1, vi+1, 254 );
gi = airIndex( ming, vghF[1], maxg, 254 );
vgh[1] = AIR_CLAMP( 1, gi+1, 254 );
if ( doH ) {
hi = airIndex( minh, vghF[2], maxh, 168 );
vgh[2] = AIR_CLAMP( 1, hi+1, 169 );
}
vgh += nval;
vghF += nval;
}
if ( doH ) {
sprintf( cmt, "exclusions ( v g h ): %g %g %g", perc[0], perc[1], perc[2] );
} else {
sprintf( cmt, "exclusions ( v g ): %g %g", perc[0], perc[1] );
}
nrrdCommentAdd( nvgh, cmt );
sprintf( cmt, "minv: %g", minv ); nrrdCommentAdd( nvgh, cmt );
sprintf( cmt, "maxv: %g", maxv ); nrrdCommentAdd( nvgh, cmt );
sprintf( cmt, "ming: %g", ming ); nrrdCommentAdd( nvgh, cmt );
sprintf( cmt, "maxg: %g", maxg ); nrrdCommentAdd( nvgh, cmt );
if ( doH ) {
sprintf( cmt, "minh: %g", minh ); nrrdCommentAdd( nvgh, cmt );
sprintf( cmt, "maxh: %g", maxh ); nrrdCommentAdd( nvgh, cmt );
}
nrrdAxisInfoSet_va( nvgh, nrrdAxisInfoCenter, nrrdCenterUnknown,
nrrdCenterNode, nrrdCenterNode, nrrdCenterNode );
return 0;
}
int
501 qbertScat( Nrrd *nvgh, int pos, int size, const char *name ) {
static const char me[]="qbertScat";
Nrrd **nin, *nv, *nx, *nscA, *nscB;
airArray *mop;
size_t bins[2];
int E, clamp[2];
NrrdRange *range;
bins[0] = bins[1] = size;
clamp[0] = clamp[1] = AIR_FALSE;
mop = airMopNew( );
airMopAdd( mop, nv=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nx=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nscA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nscB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* HEY: this used to be nin[2], but its passing to nrrdJoin caused
"dereferencing type-punned pointer might break strict-aliasing rules"
warning; GLK not sure how else to fix it */
nin = AIR_CALLOC( 2, Nrrd* );
airMopAdd( mop, nin, airFree, airMopAlways );
nin[0] = nv;
nin[1] = nx;
E = 0;
if ( !E ) E |= nrrdSlice( nv, nvgh, 0, 0 );
if ( !E ) E |= nrrdSlice( nx, nvgh, 0, pos );
if ( !E ) E |= nrrdHistoJoint( nscA, ( const Nrrd*const* )nin, NULL, 2,
NULL, bins, nrrdTypeFloat, clamp );
if ( !E ) E |= nrrdArithUnaryOp( nscB, nrrdUnaryOpLog1p, nscA );
if ( !E ) E |= nrrdHistoEq( nscA, nscB, NULL, 2048, 2, 0.45f );
if ( !E ) {
range = nrrdRangeNewSet( nscA, nrrdBlind8BitRangeTrue );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
range->max *= 0.8;
}
if ( !E ) E |= nrrdQuantize( nscB, nscA, range, 8 );
if ( !E ) E |= nrrdFlip( nscA, nscB, 1 );
if ( !E ) E |= nrrdSave( name, nscA, NULL );
if ( E ) {
biffMovef( QBERT, NRRD, "%s: trouble generating/saving scatterplot", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
static const char qbertInfo[]=
"Generates volume datasets friendly to hardware-based "
"volume renderers. "
"The main value of this is a means of combining the functions of "
"resampling a dataset to a particular size, measuring first ( and "
"optionally second ) derivatives, and doing some semi-intelligent "
"quantization of the derivative values down to 8-bits ( if quantization "
"is desired ). The various up and down sampling, as well as the "
"the VGH measurements, can be done with various nrrd kernels. Also, "
"histogram-equalized "
"VG and VH scatterplots can be generated at a specified resolution.";
int
560 main( int argc, const char *argv[] ) {
const char *me;
char *outS, *errS;
Nrrd *nin, *npad, *nrsmp, *nvghF, *nvhist, *nghist, *nhhist, *nvgh;
int E, i, ups, notdoH, useFloat, scat;
unsigned int sz[3];
NrrdKernelSpec *k00, *k11, *k22;
double amin[4], amax[4], spacing[4];
float vperc, gperc, hperc, perc[3];
NrrdKernelSpec *dk, *uk;
hestParm *hparm;
hestOpt *hopt = NULL;
airArray *mop;
mop = airMopNew( );
me = argv[0];
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume, in nrrd format",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "vg", NULL, airTypeInt, 0, 0, ¬doH, NULL,
"Make a 2-channel VG volume, instead of the usual ( default ) "
"3-channel VGH volume." );
hestOptAdd( &hopt, "f", NULL, airTypeInt, 0, 0, &useFloat, NULL,
"Keep the output volume in floating point, instead of "
"( by default ) quantizing down to 8-bits. The "
"\"-vp\", \"-gp\", and \"-hp\" options become moot." );
hestOptAdd( &hopt, "d", "dimX dimY dimZ", airTypeUInt, 3, 3, sz, NULL,
"dimensions of output volume" );
hestOptAdd( &hopt, "up", NULL, airTypeInt, 0, 0, &ups, NULL,
"Instead of just padding axes up to dimensions given "
"with \"-d\" when original dimensions are smaller, do filtered "
"upsampling." );
hestOptAdd( &hopt, "uk", "upsample k", airTypeOther, 1, 1, &uk,
"cubic:0, 0.5",
"kernel to use when doing the upsampling enabled by \"-up\"",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "dk", "downsample k", airTypeOther, 1, 1, &dk, "tent",
"kernel to use when DOWNsampling volume to fit with specified "
"dimensions. NOTE: ringing can be problematic here.",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"tent", "kernel for gageKernel00, used to probe values ( \"V\" ) "
"in the volume that has been padded/resampled to fit in the "
"dimensions given by \"-d\"",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11, used with k00 to probe "
"gradient magnitudes ( \"G\" )",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22, used with k00, k11 to "
"probe Hessian-based 2nd derivatives ( \"H\" )",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "vp", "V excl perc", airTypeFloat, 1, 1, &vperc,
"0.000",
"Percent of voxels to through away in quantization ( if doing "
"quantization ) based their data value being too high or "
"too low. " );
hestOptAdd( &hopt, "gp", "G perc", airTypeFloat, 1, 1, &gperc, "0.002",
"Like \"-vp\", but for gradient magnitudes. " );
hestOptAdd( &hopt, "hp", "H perc", airTypeFloat, 1, 1, &hperc, "0.004",
"Like \"-vp\", but for Hessian-based 2nd derivatives. " );
hestOptAdd( &hopt, "scat", "scat size", airTypeInt, 1, 1, &scat, "0",
"generate VG ( and VH ) scatterplots with this resolution. "
"Size 0 means \"no scatterplots\". The scatterplots are "
"histogram equalized, quantized, and saved out as PGM images "
"named \"vg.pgm\" ( and \"vh.pgm\" )." );
hestOptAdd( &hopt, "o", "output", airTypeString, 1, 1, &outS, NULL,
"output volume in nrrd format" );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, qbertInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( 3 != nin->dim ) {
fprintf( stderr, "%s: input nrrd is %-dimensional, not 3\n", me, nin->dim );
airMopError( mop ); exit( 1 );
}
if ( !AIR_EXISTS( nin->axis[0].spacing ) ) {
nrrdAxisInfoSpacingSet( nin, 0 );
}
if ( !AIR_EXISTS( nin->axis[1].spacing ) ) {
nrrdAxisInfoSpacingSet( nin, 1 );
}
if ( !AIR_EXISTS( nin->axis[2].spacing ) ) {
nrrdAxisInfoSpacingSet( nin, 2 );
}
npad = nrrdNew( );
airMopAdd( mop, npad, ( airMopper )nrrdNuke, airMopAlways );
if ( qbertSizeUp( npad, nin, sz, ups ? uk : NULL ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
nrsmp = nrrdNew( );
airMopAdd( mop, nrsmp, ( airMopper )nrrdNuke, airMopAlways );
if ( qbertSizeDown( nrsmp, npad, sz, dk ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
airMopSub( mop, npad, ( airMopper )nrrdNuke );
npad = nrrdNuke( npad );
/* this axis info is being saved so that it can be re-enstated at the end */
spacing[0] = amin[0] = amax[0] = AIR_NAN;
nrrdAxisInfoGet_nva( nrsmp, nrrdAxisInfoSpacing, spacing+1 );
nrrdAxisInfoGet_nva( nrsmp, nrrdAxisInfoMin, amin+1 );
nrrdAxisInfoGet_nva( nrsmp, nrrdAxisInfoMax, amax+1 );
/* if we had to downsample, we may have enstated axis mins and maxs where
they didn't exist before, and those shouldn't be saved in output. But
we can't just copy axis mins and maxs from the original input because
padding could have changed them. If no axis mins and maxs existed on
the input nrrd, these will all be nan, so they won't be saved out.
NOTE: we're only nixing axis min/max information, not spacing. */
for ( i=0; i<=2; i++ ) {
if ( !AIR_EXISTS( nin->axis[i].min ) )
amin[1+i] = AIR_NAN;
if ( !AIR_EXISTS( nin->axis[i].max ) )
amax[1+i] = AIR_NAN;
}
nvghF = nrrdNew( );
airMopAdd( mop, nvghF, ( airMopper )nrrdNuke, airMopAlways );
if ( qbertProbe( nvghF, nrsmp, k00, k11, k22, !notdoH, sz ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
airMopSub( mop, nrsmp, ( airMopper )nrrdNuke );
nrsmp = nrrdNuke( nrsmp );
if ( useFloat ) {
/* we're done! */
if ( scat && ( qbertScat( nvghF, 1, scat, "vg.pgm" )
|| ( !notdoH && qbertScat( nvghF, 2, scat, "vh.pgm" ) ) ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
E = nrrdSave( outS, nvghF, NULL );
} else {
nvhist = nrrdNew( );
nghist = nrrdNew( );
nhhist = nrrdNew( );
airMopAdd( mop, nvhist, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nghist, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nhhist, ( airMopper )nrrdNuke, airMopAlways );
if ( qbertMakeVghHists( nvhist, nghist, nhhist,
sz, QBERT_HIST_BINS,
nvghF, nin ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, errS = biffGetDone( QBERT ) );
free( errS ); exit( 1 );
}
nvgh = nrrdNew( );
airMopAdd( mop, nvgh, ( airMopper )nrrdNuke, airMopAlways );
ELL_3V_SET( perc, vperc, gperc, hperc );
if ( qbertMakeVgh( nvgh, nvhist, nghist, nhhist, sz, perc, nvghF ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
airMopSub( mop, nvghF, ( airMopper )nrrdNuke );
nvghF = nrrdNuke( nvghF );
if ( scat && ( qbertScat( nvgh, 1, scat, "vg.pgm" )
|| ( !notdoH && qbertScat( nvgh, 2, scat, "vh.pgm" ) ) ) ) {
airMopAdd( mop, errS=biffGetDone( QBERT ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
/* do final decoration of axes */
nrrdAxisInfoSet_va( nvgh, nrrdAxisInfoLabel,
!notdoH ? "vgh" : "vg",
"x", "y", "z" );
nrrdAxisInfoSet_nva( nvgh, nrrdAxisInfoMin, amin );
nrrdAxisInfoSet_nva( nvgh, nrrdAxisInfoMax, amax );
nrrdAxisInfoSet_nva( nvgh, nrrdAxisInfoSpacing, spacing );
nrrdContentSet_va( nvgh, "qbert", nin, "" );
E = nrrdSave( outS, nvgh, NULL );
}
if ( E ) {
airMopAdd( mop, errS=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, errS );
airMopError( mop ); exit( 1 );
}
/* HEY: why am I getting memory-in-use with purify? */
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/alan.h>
#include <teem/ten.h>
static const char *spotsInfo =
( "Generate reaction-diffusion textures based on "
"Turing's second example formulation ( page 65 ) of "
"his 1954 paper \"The Chemical Basis of Morphogenesis.\" " );
int
38 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt = NULL;
airArray *mop;
char *outS;
alanContext *actx;
int *size, sizeLen, fi, si, wrap, nt, cfn, ha, maxi;
unsigned int srnd;
double deltaT, mch, xch, alphabeta[2], time0, time1, deltaX, react, rrange;
Nrrd *ninit=NULL, *nten=NULL, *nparm=NULL;
me = argv[0];
hestOptAdd( &hopt, "s", "sx sy", airTypeInt, 2, 3, &size, "128 128",
"size of texture, and also determines its dimension",
&sizeLen );
hestOptAdd( &hopt, "srand", "N", airTypeUInt, 1, 1, &srnd, "42",
"number to seed random number generator with. This uses "
"airDrandMT( ), so it should be portable." );
hestOptAdd( &hopt, "i", "tensors", airTypeOther, 1, 1, &nten, "",
"diffusion tensors to use for guiding the texture generation. "
"If used, over-rides the \"-s\" option, both for setting "
"texture dimension and size. If you want upsampling, you "
"do it yourself before sending it here.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ha", NULL, airTypeInt, 0, 0, &ha, NULL,
"use the homogenous anisotropy assumption- that the spatial "
"derivative of the diffusion tensor is negligible when "
"computing the diffusive term " );
hestOptAdd( &hopt, "wrap", NULL, airTypeInt, 0, 0, &wrap, NULL,
"wrap edges of texture around a topological torus ( which "
"makes a texture suitable for tiling )" );
hestOptAdd( &hopt, "ab", "alpha beta", airTypeDouble, 2, 2, alphabeta,
"16.0 12.0",
"the growth and decay parameters appearing in the reaction "
"terms of the reaction-diffusion equations. The default "
"values were the ones published by Turing." );
hestOptAdd( &hopt, "sr", "react", airTypeDouble, 1, 1, &react, "1.0",
"scaling of reaction term" );
hestOptAdd( &hopt, "rr", "range range", airTypeDouble, 1, 1, &rrange, "4.0",
"amount of random noise to add to inital textures" );
hestOptAdd( &hopt, "dt", "time", airTypeDouble, 1, 1, &deltaT, "1.0",
"time-step size in Euler integration. Can be larger, at "
"risk of hitting divergent instability." );
hestOptAdd( &hopt, "dx", "size", airTypeDouble, 1, 1, &deltaX, "1.3",
"nominal size of simulation grid element." );
hestOptAdd( &hopt, "mch", "change", airTypeDouble, 1, 1, &mch, "0.00001",
"the minimum significant change ( averaged over the whole "
"texture ) in the first morphogen: to signify convergence" );
hestOptAdd( &hopt, "xch", "change", airTypeDouble, 1, 1, &xch, "6",
"the maximum allowable change ( averaged over the whole "
"texture ) in the first morphogen: to signify divergence" );
hestOptAdd( &hopt, "maxi", "# iter", airTypeInt, 1, 1, &maxi, "0",
"maximum number of iterations to run for, or \"0\" to have "
"no limit based on iteration count" );
hestOptAdd( &hopt, "fi", "frame inter", airTypeInt, 1, 1, &fi, "0",
"the number of iterations between which to save out an 8-bit "
"image of the texture, or \"0\" to disable such action" );
hestOptAdd( &hopt, "si", "snap inter", airTypeInt, 1, 1, &si, "0",
"the number of iterations between which to save out a complete "
"floating-point snapshot of the morphogen state, suitable for "
"later re-initialization, or \"0\" to disable such action" );
hestOptAdd( &hopt, "cfn", NULL, airTypeInt, 0, 0, &cfn, NULL,
"when saving out frames or snapshots, use a constant filename, "
"instead of incrementing it each save" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1, &nt, "1",
( airThreadCapable
? "number of threads to use in computation"
: "number of \"threads\" to use in computation, which is "
"moot here because this Teem build doesn't support "
"multi-threading. " ) );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"filename for output of final converged ( two-channel ) texture" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, spotsInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
actx = alanContextNew( );
airMopAdd( mop, actx, ( airMopper )alanContextNix, airMopAlways );
if ( nten ) {
if ( alanDimensionSet( actx, nten->dim - 1 )
|| alanTensorSet( actx, nten, 1 ) ) {
airMopAdd( mop, err = biffGetDone( ALAN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting parameters:\n%s\n", me, err );
airMopError( mop );
return 1;
}
} else {
alanDimensionSet( actx, sizeLen );
if ( 2 == sizeLen ) {
alan2DSizeSet( actx, size[0], size[1] );
} else {
alan3DSizeSet( actx, size[0], size[1], size[2] );
}
}
airSrandMT( srnd );
if ( alanParmSet( actx, alanParmVerbose, 1 )
|| alanParmSet( actx, alanParmTextureType, alanTextureTypeTuring )
|| alanParmSet( actx, alanParmK, 0.0125 )
|| alanParmSet( actx, alanParmAlpha, alphabeta[0] )
|| alanParmSet( actx, alanParmBeta, alphabeta[1] )
|| alanParmSet( actx, alanParmDeltaX, deltaX )
|| alanParmSet( actx, alanParmDeltaT, deltaT )
|| alanParmSet( actx, alanParmReact, react )
|| alanParmSet( actx, alanParmMinAverageChange, mch )
|| alanParmSet( actx, alanParmMaxPixelChange, xch )
|| alanParmSet( actx, alanParmMaxIteration, maxi )
|| alanParmSet( actx, alanParmRandRange, rrange )
|| alanParmSet( actx, alanParmSaveInterval, si )
|| alanParmSet( actx, alanParmFrameInterval, fi )
|| alanParmSet( actx, alanParmConstantFilename, cfn )
|| alanParmSet( actx, alanParmWrapAround, wrap )
|| alanParmSet( actx, alanParmHomogAniso, ha )
|| alanParmSet( actx, alanParmNumThreads, nt ) ) {
airMopAdd( mop, err = biffGetDone( ALAN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting parameters:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( alanUpdate( actx ) || alanInit( actx, ninit, nparm ) ) {
airMopAdd( mop, err = biffGetDone( ALAN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble initializing texture: %s\n", me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "%s: going to run ( %d threads ) ...\n", me, actx->numThreads );
time0 = airTime( );
if ( alanRun( actx ) ) {
airMopAdd( mop, err = biffGetDone( ALAN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble generating texture: %s\n", me, err );
airMopError( mop );
return 1;
}
time1 = airTime( );
fprintf( stderr, "%s: stopped after %d iterations ( %g seconds ): %s\n",
me, actx->iter, time1 - time0,
airEnumDesc( alanStop, actx->stop ) );
if ( nrrdSave( outS, actx->nlev, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <teem/air.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#define TKWB "talkweb"
#define TKWB_TAG_TOC 0
#define TKWB_TAG_TITLE 1
#define TKWB_TAG_IMAGE 2
#define TKWB_TAG_FIRST 3
#define TKWB_TAG_PREV 4
#define TKWB_TAG_NEXT 5
#define TKWB_TAG_LAST 6
#define TKWB_TAG_TEXT 7
#define TKWB_TAG_MAX 7
static const char *tkwbInfo =
( "Generates HTML pages from slide images and text. "
"This program takes multiple inputs: a template for the table of contents "
"that will become \"index.html\" ( \"-i\" ), a template for the pages "
"generated for each slide e.g. \"slide000.html\" ( \"-t\" ), and a script "
"text file that contains all the information that will go into the slide "
"pages. The format of this file is:\n "
"\t\t- Separator line indicating slide transitions, e.g. \"-------------\"\n "
"\t\t- Title of first slide ( one line )\n "
"\t\t- Filename for image to put on first slide ( one line )\n "
"\t\t- Body of HTML text to put with the slide image ( multiple lines )\n "
"\t\t- Separator\n "
"followed by information for the second slide, and so forth. Textual "
"subtitutions are performed in the template files, according to the "
"replacement tags ( \"-r\", see below ). Within the slide pages, navigation "
"arrows are based on remaining command-line options."
);
int tkwbArrayIncr = 16;
typedef struct {
char *title, *image, *text;
} tkwbSlide;
tkwbSlide *
69 tkwbSlideNew( char *title, char *image, char *text ) {
tkwbSlide *ret;
ret = ( tkwbSlide* )calloc( 1, sizeof( tkwbSlide ) );
if ( ret ) {
ret->title = airStrdup( title );
ret->image = airStrdup( image );
ret->text = airStrdup( text );
}
return ret;
}
tkwbSlide *
82 tkwbSlideNix( tkwbSlide *slide ) {
slide->title = ( char * )airFree( slide->title );
slide->image = ( char * )airFree( slide->image );
slide->text = ( char * )airFree( slide->text );
slide = ( tkwbSlide * )airFree( slide );
return NULL;
}
typedef union {
tkwbSlide ***ps;
char ***pc;
void **v;
} _tkwbU;
int
98 tkwbReadFileToString( char **strP, int *hitEOF, FILE *file, char *stop ) {
char **all, line[AIR_STRLEN_HUGE];
airArray *allArr;
unsigned int allLen;
unsigned int lineLen, lineIdx, totalLen;
_tkwbU uu;
uu.pc = &all;
allArr = airArrayNew( uu.v, &allLen, sizeof( char* ), tkwbArrayIncr );
airArrayPointerCB( allArr, airNull, airFree );
lineLen = airOneLine( file, line, AIR_STRLEN_HUGE );
totalLen = 0;
while ( lineLen && ( !( airStrlen( stop ) && !strcmp( line, stop ) ) ) ) {
lineIdx = airArrayLenIncr( allArr, 1 ); /* HEY error checking */
all[lineIdx] = ( char * )calloc( strlen( line ) + strlen( "\n" ) + 1,
sizeof( char ) );
sprintf( all[lineIdx], "%s\n", line );
totalLen += strlen( line ) + 1;
lineLen = airOneLine( file, line, AIR_STRLEN_HUGE );
}
if ( hitEOF ) {
*hitEOF = !lineLen;
}
*strP = ( char* )calloc( totalLen+1, sizeof( char ) );
strcpy( *strP, "" );
for ( lineIdx=0; lineIdx<allLen; lineIdx++ ) {
strcat( *strP, all[lineIdx] );
}
airArrayNuke( allArr );
return 0;
}
int
133 tkwbReadTemplate( char **tmplSP, char *filename ) {
char me[]="tkwbReadTemplate";
FILE *file;
airArray *mop;
mop = airMopNew( );
if ( !( file = airFopen( filename, stdin, "rb" ) ) ) {
biffAddf( TKWB, "%s: couldn't open %s: %s", me, filename, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( tkwbReadFileToString( tmplSP, NULL, file, NULL ) ) {
biffAddf( TKWB, "%s: couldn't read in template file %s", me, filename );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
155 tkwbReadSlides( tkwbSlide ***slideP, char *filename, airArray *pmop ) {
static const char me[]="tkwbReadSlides";
FILE *file;
airArray *mop, *slideArr;
tkwbSlide **slide = NULL;
char *title, *image, *text, stop[AIR_STRLEN_HUGE], line[AIR_STRLEN_HUGE];
int slideIdx=0, hitEOF, notReally;
unsigned int len;
_tkwbU uu;
mop = airMopNew( );
if ( !( file = airFopen( filename, stdin, "rb" ) ) ) {
biffAddf( TKWB, "%s: couldn't open %s: %s", me, filename, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
len = airOneLine( file, stop, AIR_STRLEN_HUGE );
if ( !( len > 1 ) ) {
biffAddf( TKWB, "%s: didn't get a stop delimiter from %s", me, filename );
airMopError( mop ); return 1;
}
uu.ps = &slide;
slideArr = airArrayNew( uu.v, NULL,
sizeof( tkwbSlide* ), tkwbArrayIncr );
airMopAdd( mop, slideArr, ( airMopper )airArrayNix, airMopAlways );
hitEOF = notReally = AIR_FALSE;
while ( !hitEOF ) {
slideIdx = airArrayLenIncr( slideArr, 1 ); /* HEY error checking */
len = airOneLine( file, line, AIR_STRLEN_HUGE );
if ( !len ) {
/* got EOF after a division marker, that's okay */
notReally = AIR_TRUE;
break;
}
title = airStrdup( line );
len = airOneLine( file, line, AIR_STRLEN_HUGE );
if ( !len ) {
break;
}
image = airStrdup( line );
if ( tkwbReadFileToString( &text, &hitEOF, file, stop ) ) {
biffAddf( TKWB, "%s: couldn't read in slide %d", me, slideIdx );
airMopError( mop ); return 1;
}
slide[slideIdx] = tkwbSlideNew( title, image, text );
airMopAdd( pmop, slide[slideIdx], ( airMopper )tkwbSlideNix, airMopAlways );
}
if ( !hitEOF && !notReally ) {
biffAddf( TKWB, "%s: got incomplete slide info for slide %d\n",
me, slideIdx );
airMopError( mop ); return 1;
}
if ( !notReally ) {
slideIdx = airArrayLenIncr( slideArr, 1 ); /* HEY error checking */
}
slide[slideIdx] = NULL;
*slideP = slide;
airMopOkay( mop );
return 0;
}
int
220 tkwbExpandImageInfo( tkwbSlide **slide ) {
static const char me[]="tkwbExpandImageInfo";
char *image;
Nrrd *nimg;
int si, sx, sy, len;
airArray *mop;
mop = airMopNew( );
nimg = nrrdNew( );
airMopAdd( mop, nimg, ( airMopper )nrrdNuke, airMopAlways );
for ( si=0; slide[si]; si++ ) {
if ( nrrdLoad( nimg, slide[si]->image, NULL ) ) {
biffMovef( TKWB, NRRD, "%s: trouble reading slide image \"%s\"",
me, slide[si]->image );
airMopError( mop ); return 1;
}
if ( !nrrdFormatPNG->fitsInto( nimg, nrrdEncodingGzip, AIR_TRUE ) ) {
biffMovef( TKWB, NRRD,
"%s: slide image \"%s\" doesn't seem to be an image",
me, slide[si]->image );
airMopError( mop ); return 1;
}
sx = nimg->axis[nimg->dim-2].size;
sy = nimg->axis[nimg->dim-1].size;
len = ( strlen( "<img width=xxxx height=xxxx src=\"\">" )
+ strlen( slide[si]->image ) + 1 );
image = ( char * )calloc( len, sizeof( char ) );
sprintf( image, "<img width=%d height=%d src=\"%s\">",
sx, sy, slide[si]->image );
free( slide[si]->image );
slide[si]->image = image;
}
airMopOkay( mop );
return 0;
}
int
258 tkwbWriteStringToFile( const char *filename, const char *content ) {
static const char me[]="tkwbWriteStringToFile";
FILE *file;
if ( !( file = fopen( filename, "wb" ) ) ) {
biffAddf( TKWB, "%s: trouble opening file \"%s\": %s",
me, filename, strerror( errno ) );
return 1;
}
fprintf( file, "%s", content );
fclose( file );
return 0;
}
int
274 _tkwbStringSubst( char **sP, /* string to search in */
char *f, /* find */
char *r ) { /* replace */
char *p, /* place where find was found */
*n; /* new string */
p = strstr( *sP, f );
if ( !p ) {
/* nothing to do */
return 0;
}
n = ( char* )calloc( strlen( *sP ) - strlen( f ) + strlen( r ) + 1, sizeof( char ) );
strncpy( n, *sP, p - *sP );
strncpy( n + ( p - *sP ), r, strlen( r ) );
strcpy( n + ( p - *sP ) + strlen( r ), p + strlen( f ) );
free( *sP );
*sP = n;
return _tkwbStringSubst( sP, f, r );
}
/*
** NOTE: this will re-allocate *stringP if a substitution is done
*/
void
298 tkwbStringSubst( char **sP, /* string to search in */
char *f, /* find */
char *r ) { /* replace */
_tkwbStringSubst( sP, f, r );
return;
}
int
307 tkwbWriteIndex( char *_indx, tkwbSlide **slide, char *tag[TKWB_TAG_MAX+1] ) {
static const char me[]="tkwbWriteIndex";
char *repl, *indx, tmp[AIR_STRLEN_MED];
int replLen, si;
airArray *mop;
mop = airMopNew( );
replLen = 0;
replLen += strlen( "<ol>\n" );
for ( si=0; slide[si]; si++ ) {
replLen += ( strlen( "<li> <a href=\"slideXXX.html\"></a>\n" )
+ strlen( slide[si]->title ) );
}
replLen += strlen( "</ol>\n" );
if ( !( repl = ( char* )calloc( replLen+1, sizeof( char ) ) ) ) {
biffAddf( TKWB, "%s: couldn't allocate link buffer!", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, repl, airFree, airMopAlways );
strcpy( repl, "<ol>\n" );
for ( si=0; slide[si]; si++ ) {
sprintf( tmp, "<li> <a href=\"slide%03d.html\">%s</a>\n",
si+1, slide[si]->title );
strcat( repl, tmp );
}
strcat( repl, "</ol>" );
indx = airStrdup( _indx );
tkwbStringSubst( &indx, tag[TKWB_TAG_TOC], repl );
airMopAdd( mop, indx, airFree, airMopAlways );
if ( tkwbWriteStringToFile( "index.html", indx ) ) {
biffAddf( TKWB, "%s: couldn't write \"index.html\"", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
349 tkwbWriteSlides( tkwbSlide **slide, int numSlides, char *tmpl,
char *tag[TKWB_TAG_MAX+1], char *link[4] ) {
static const char me[]="tkwbWriteSlides";
char *text, name[AIR_STRLEN_MED], frst[AIR_STRLEN_MED], prev[AIR_STRLEN_MED],
next[AIR_STRLEN_MED], last[AIR_STRLEN_MED];
int si;
airArray *mop;
mop = airMopNew( );
sprintf( frst, "<a href=\"slide001.html\">%s</a>", link[0] );
sprintf( last, "<a href=\"slide%03d.html\">%s</a>", numSlides, link[3] );
for ( si=0; si<numSlides; si++ ) {
text = airStrdup( tmpl );
tkwbStringSubst( &text, tag[TKWB_TAG_TITLE], slide[si]->title );
tkwbStringSubst( &text, tag[TKWB_TAG_IMAGE], slide[si]->image );
tkwbStringSubst( &text, tag[TKWB_TAG_TEXT], slide[si]->text );
if ( si ) {
tkwbStringSubst( &text, tag[TKWB_TAG_FIRST], frst );
sprintf( prev, "<a href=\"slide%03d.html\">%s</a>", si, link[1] );
tkwbStringSubst( &text, tag[TKWB_TAG_PREV], prev );
}
if ( si < numSlides-1 ) {
tkwbStringSubst( &text, tag[TKWB_TAG_LAST], last );
sprintf( next, "<a href=\"slide%03d.html\">%s</a>", si+2, link[2] );
tkwbStringSubst( &text, tag[TKWB_TAG_NEXT], next );
}
airMopAdd( mop, text, airFree, airMopAlways );
sprintf( name, "slide%03d.html", si+1 );
if ( tkwbWriteStringToFile( name, text ) ) {
biffAddf( TKWB, "%s: couldn't write \"%s\"", me, name );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
int
388 tkwbDoit( char *indxS, char *tmplS, char *scriptS,
char *tag[TKWB_TAG_MAX+1], char *link[4] ) {
static const char me[]="tkwbDoit";
char *indx, *tmpl;
tkwbSlide **slide;
airArray *mop;
int numSlides;
mop = airMopNew( );
if ( tkwbReadTemplate( &indx, indxS ) ) {
biffAddf( TKWB, "%s: trouble reading in index template file", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, indx, airFree, airMopAlways );
if ( tkwbReadTemplate( &tmpl, tmplS ) ) {
biffAddf( TKWB, "%s: trouble reading in slide template file", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, tmpl, airFree, airMopAlways );
if ( tkwbReadSlides( &slide, scriptS, mop ) ) {
biffAddf( TKWB, "%s: trouble reading in slide script", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, slide, airFree, airMopAlways );
if ( tkwbExpandImageInfo( slide ) ) {
biffAddf( TKWB, "%s: trouble learning details of images", me );
airMopError( mop ); return 1;
}
if ( tkwbWriteIndex( indx, slide, tag ) ) {
biffAddf( TKWB, "%s: trouble writing index.html", me );
airMopError( mop ); return 1;
}
for ( numSlides=0; slide[numSlides]; numSlides++ )
;
if ( tkwbWriteSlides( slide, numSlides, tmpl, tag, link ) ) {
biffAddf( TKWB, "%s: trouble writing slide pages", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
437 main( int argc, const char *argv[] ) {
const char *me;
char *err, *indxS, *tmplS, *scriptS, *pretag[TKWB_TAG_MAX+1],
*tag[AIR_STRLEN_MED],
*frstLink, *prevLink, *nextLink, *lastLink, *link[4];
hestOpt *hopt = NULL;
airArray *mop;
int ti;
me = argv[0];
hestOptAdd( &hopt, "i", "index", airTypeString, 1, 1, &indxS, NULL,
"*index* template HTML filename. This will be turned into "
"the \"index.html\" index file, after the links to all the "
"slides have been substituted in." );
hestOptAdd( &hopt, "t", "slide", airTypeString, 1, 1, &tmplS, NULL,
"*slide* template HTML filename. "
"The text of this includes the tags "
"that are replaced with their per-slide values, to produce the "
"HTML file for each slide's page. " );
hestOptAdd( &hopt, "s", "script", airTypeString, 1, 1, &scriptS, NULL,
"script filename. This file contains information about each "
"slide: the slide title, the slide image filename, and the "
"HTML text to accompany the slide image." );
hestOptAdd( &hopt, "r", "tags", airTypeString,
TKWB_TAG_MAX+1, TKWB_TAG_MAX+1, pretag,
"TOC TITLE IMAGE FIRST PREV NEXT LAST TEXT",
"replacement tags that will be converted into links. "
"The actual replcement tag is the string given here embedded "
"in an HTML comment ( no space ). So saying \"TOC\" means the "
"actual replacement tag will be \"<!--TOC-->\". The first tag "
"is replaced in the index template; all others are in the "
"slide template. "
"In order, the tags are for:\n "
"\b\bo In the index template, the list of links to slide pages\n "
"\b\bo The slide title\n "
"\b\bo The slide image\n "
"\b\bo The link to the first slide\n "
"\b\bo The link to the previous slide\n "
"\b\bo The link to the next slide\n "
"\b\bo The link to the last slide\n "
"\b\bo The text accompanying each slide" );
hestOptAdd( &hopt, "first", "text", airTypeString, 1, 1, &frstLink,
"<b>|<<</b>", "Snippet of HTML text to be converted into "
"link to first slide. Some image could be used here. "
"Following three arguments are similar. " );
hestOptAdd( &hopt, "prev", "text", airTypeString, 1, 1, &prevLink,
"<b><--</b>", "HTML for link to previous slide" );
hestOptAdd( &hopt, "next", "text", airTypeString, 1, 1, &nextLink,
"<b>--></b>", "HTML for link to next slide" );
hestOptAdd( &hopt, "last", "text", airTypeString, 1, 1, &lastLink,
"<b>>>|</b>", "HTML for link to last slide" );
hestParseOrDie( hopt, argc-1, argv+1, NULL, me, tkwbInfo,
AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
link[0] = frstLink;
link[1] = prevLink;
link[2] = nextLink;
link[3] = lastLink;
for ( ti=0; ti<=TKWB_TAG_MAX; ti++ ) {
tag[ti] = ( char * )calloc( strlen( pretag[ti] ) + strlen( "<!---->" ) + 1,
sizeof( char ) );
airMopAdd( mop, tag[ti], airFree, airMopAlways );
sprintf( tag[ti], "<!--%s-->", pretag[ti] );
}
if ( tkwbDoit( indxS, tmplS, scriptS, tag, link ) ) {
airMopAdd( mop, err = biffGetDone( TKWB ), airFree, airMopAlways );
fprintf( stderr, "%s: error:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/ten.h>
#define TEND "tend"
int
29 main( int argc, const char **argv ) {
int i, ret;
const char *me;
char *argv0 = NULL;
hestParm *hparm;
airArray *mop;
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* no harm done in making sure we're sane */
nrrdSanityOrDie( me );
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_FALSE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->cleverPluralizeOtherY = AIR_TRUE;
hparm->columns = 78;
/* if there are no arguments, then we give general usage information */
if ( 1 >= argc ) {
unrrduUsage( TEND, hparm, tendTitle, tendCmdList );
airMopError( mop );
exit( 1 );
}
/* else, we see if its --version */
if ( !strcmp( "--version", argv[1] ) ) {
char vbuff[AIR_STRLEN_LARGE];
airTeemVersionSprint( vbuff );
printf( "%s\n", vbuff );
exit( 0 );
}
/* else, we should see if they're asking for a command we know about */
for ( i=0; tendCmdList[i]; i++ ) {
if ( !strcmp( argv[1], tendCmdList[i]->name ) )
break;
if ( !strcmp( "--help", argv[1] )
&& !strcmp( "about", tendCmdList[i]->name ) ) {
break;
}
}
if ( tendCmdList[i] ) {
/* yes, we have that command */
/* initialize variables used by the various commands */
argv0 = ( char * )calloc( strlen( TEND ) + strlen( argv[1] ) + 2, sizeof( char ) );
airMopMem( mop, &argv0, airMopAlways );
sprintf( argv0, "%s %s", TEND, argv[1] );
/* run the individual tend program, saving its exit status */
ret = tendCmdList[i]->main( argc-2, argv+2, argv0, hparm );
} else {
fprintf( stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for "
"complete list\n", me, argv[1], me );
ret = 1;
}
airMopDone( mop, ret );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/hest.h>
#include <teem/biff.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
char info[]="Gantry tilt be gone! This program is actually of limited "
"utility: it can only change the tilt by shearing with the "
"X and Z axis fixed, by some angle \"around\" the X axis, assuming "
"that ( X, Y, Z ) is a right-handed frame. ";
int
36 main( int argc, const char *argv[] ) {
hestParm *hparm;
hestOpt *hopt = NULL;
gageContext *ctx;
gagePerVolume *pvl;
Nrrd *nin, *nout;
const char *me;
char *outS;
float angle;
double xs, ys, zs, y, z, padval;
const double *val;
int sx, sy, sz, E, xi, yi, zi, clamp;
NrrdKernelSpec *gantric;
void *out;
double ( *insert )( void *v, size_t I, double d );
me = argv[0];
hparm = hestParmNew( );
hparm->elideSingleOtherType = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume, in nrrd format",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "a", "angle", airTypeFloat, 1, 1, &angle, NULL,
"angle, in degrees, of the gantry tilt around the X axis. "
"This is opposite of the amount of tweak we apply." );
hestOptAdd( &hopt, "k", "kern", airTypeOther, 1, 1, &gantric,
"tent",
"The kernel to use for resampling. Chances are, there "
"is no justification for anything more than \"tent\". "
"Possibilities include:\n "
"\b\bo \"box\": nearest neighbor interpolation\n "
"\b\bo \"tent\": linear interpolation\n "
"\b\bo \"cubic:B, C\": Mitchell/Netravali BC-family of "
"cubics:\n "
"\t\t\"cubic:1, 0\": B-spline; maximal blurring\n "
"\t\t\"cubic:0, 0.5\": Catmull-Rom; good interpolating kernel\n "
"\b\bo \"quartic:A\": 1-parameter family of "
"interpolating quartics ( \"quartic:0.0834\" is most accurate )\n "
"\b\bo \"gauss:S, C\": Gaussian blurring, with standard deviation "
"S and cut-off at C standard deviations",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "clamp", NULL, airTypeInt, 0, 0, &clamp, NULL,
"clamp sampling positions to inside original volume, "
"effectively does a bleed of the boundary values" );
hestOptAdd( &hopt, "p", "pad value", airTypeDouble, 1, 1, &padval, "0.0",
"when NOT doing clampging ( no \"-clamp\" ), what value to the "
"boundary of the volume with" );
hestOptAdd( &hopt, "o", "output", airTypeString, 1, 1, &outS, NULL,
"output volume in nrrd format" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
xs = nin->axis[0].spacing;
ys = nin->axis[1].spacing;
zs = nin->axis[2].spacing;
if ( !( AIR_EXISTS( xs ) && AIR_EXISTS( ys ) && AIR_EXISTS( zs ) ) ) {
fprintf( stderr, "%s: all axis spacings must exist in input nrrd\n", me );
exit( 1 );
}
fprintf( stderr, "%s: input and output have dimensions %d %d %d\n",
me, sx, sy, sz );
/* start by just copying the nrrd; then we'll meddle with the values */
if ( nrrdCopy( nout = nrrdNew( ), nin ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, biffGet( NRRD ) );
exit( 1 );
}
out = nout->data;
insert = nrrdDInsert[nout->type];
ctx = gageContextNew( );
gageParmSet( ctx, gageParmVerbose, 1 );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00,
gantric->kernel, gantric->parm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclValue );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, biffGet( GAGE ) );
exit( 1 );
}
gageParmSet( ctx, gageParmVerbose, 0 );
val = gageAnswerPointer( ctx, pvl, gageSclValue );
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
/* convert to world space, use angle to determine new
world space position, convert back to index space,
clamp z to find inside old volume */
y = ( yi - sy/2.0 )*ys;
z = ( zi*zs + y*sin( -angle*3.141592653/180.0 ) )/zs;
if ( clamp || AIR_IN_OP( 0, z, sz-1 ) ) {
z = AIR_CLAMP( 0, z, sz-1 );
gageProbe( ctx, xi, yi, zi );
insert( out, xi + sx*( yi + sy*zi ), *val );
} else {
insert( out, xi + sx*( yi + sy*zi ), padval );
}
}
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, biffGet( NRRD ) );
exit( 1 );
}
ctx = gageContextNix( ctx );
hparm = hestParmFree( hparm );
hopt = hestOptFree( hopt );
nrrdNuke( nout );
nrrdNuke( nin );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/unrrdu.h>
/* learning columns
#include <sys/types.h>
#include <sys/ioctl.h>
*/
#define UNU "unu"
int
34 main( int argc, const char **argv ) {
int i, ret;
const char *me;
char *argv0 = NULL;
hestParm *hparm;
airArray *mop;
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* if user hasn't tried to set nrrdStateKindNoop by an environment
variable, we set it to false, since its probably what people expect */
if ( !getenv( nrrdEnvVarStateKindNoop ) ) {
nrrdStateKindNoop = AIR_FALSE;
}
/* if user hasn't tried to set nrrdStateKeyValuePairsPropagate by an envvar,
we set it to true, since that's probably what unu users expect */
if ( !getenv( nrrdEnvVarStateKeyValuePairsPropagate ) ) {
nrrdStateKeyValuePairsPropagate = AIR_TRUE;
}
/* no harm done in making sure we're sane */
nrrdSanityOrDie( me );
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_TRUE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->columns = unrrduDefNumColumns;
/* learning columns
if ( 1 ) {
struct winsize ws;
ioctl( 1, TIOCGWINSZ, &ws );
hparm->columns = ws.ws_col - 1;
}
*/
hparm->greedySingleString = AIR_TRUE;
/* if there are no arguments, then we give general usage information */
if ( 1 >= argc ) {
unrrduUsageUnu( "unu", hparm );
airMopError( mop );
exit( 1 );
}
/* else, we see if its --version */
if ( !strcmp( "--version", argv[1] ) ) {
char vbuff[AIR_STRLEN_LARGE];
airTeemVersionSprint( vbuff );
printf( "%s\n", vbuff );
exit( 0 );
}
/* else, we should see if they're asking for a command we know about */
for ( i=0; unrrduCmdList[i]; i++ ) {
if ( !strcmp( argv[1], unrrduCmdList[i]->name ) ) {
break;
}
if ( !strcmp( "--help", argv[1] )
&& !strcmp( "about", unrrduCmdList[i]->name ) ) {
break;
}
}
/* unrrduCmdList[] is NULL-terminated */
if ( unrrduCmdList[i] ) {
/* yes, we have that command */
/* initialize variables used by the various commands */
argv0 = AIR_CALLOC( strlen( UNU ) + strlen( argv[1] ) + 2, char );
airMopMem( mop, &argv0, airMopAlways );
sprintf( argv0, "%s %s", UNU, argv[1] );
/* run the individual unu program, saving its exit status */
ret = unrrduCmdList[i]->main( argc-2, argv+2, argv0, hparm );
} else {
fprintf( stderr, "%s: unrecognized command \"%s\"; type \"%s\" for "
"complete list\n", me, argv[1], me );
ret = 1;
}
airMopDone( mop, ret );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <teem/biff.h>
#include <teem/hest.h>
#include <teem/nrrd.h>
#include <teem/gage.h>
#include <teem/ten.h>
#include <teem/meet.h>
static const char *probeInfo =
( "Shows off the functionality of the gage library. "
"Uses gageProbe( ) to query various kinds of volumes "
"to learn various measured or derived quantities. "
"Can set environment variable TEEM_VPROBE_HACK_ZI "
"to limit probing to a single z slice." );
int
42 main( int argc, const char *argv[] ) {
gageKind *kind;
const char *me;
char *whatS, *err, *outS, *stackReadFormat, *stackSaveFormat;
hestParm *hparm;
hestOpt *hopt = NULL;
NrrdKernelSpec *k00, *k11, *k22, *kSS, *kSSblur;
int what, E=0, renorm, SSuniform, SSoptim, verbose, zeroZ,
orientationFromSpacing, SSnormd;
unsigned int iBaseDim, oBaseDim, axi, numSS, ninSSIdx, seed;
const double *answer;
Nrrd *nin, *nout, **ninSS=NULL;
Nrrd *ngrad=NULL, *nbmat=NULL;
size_t ai, ansLen, idx, xi, yi, zi, six, siy, siz, sox, soy, soz;
double bval=0, gmc, rangeSS[2], wrlSS, idxSS=AIR_NAN,
dsix, dsiy, dsiz, dsox, dsoy, dsoz;
gageContext *ctx;
gagePerVolume *pvl=NULL;
double t0, t1, x, y, z, scale[3], rscl[3], min[3], maxOut[3], maxIn[3];
airArray *mop;
unsigned int hackZi, *skip, skipNum;
double ( *ins )( void *v, size_t I, double d );
gageStackBlurParm *sbp;
char hackKeyStr[]="TEEM_VPROBE_HACK_ZI", *hackValStr;
int otype, hackSet;
char stmp[4][AIR_STRLEN_SMALL];
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* no harm done in making sure we're sane */
if ( !nrrdSanity( ) ) {
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s: nrrd sanity check FAILED.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, " This means that either nrrd can't work on this "
"platform, or ( more likely )\n" );
fprintf( stderr, " there was an error in the compilation options "
"and variable definitions\n" );
fprintf( stderr, " for how Teem was built here.\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s\n", err = biffGetDone( NRRD ) );
fprintf( stderr, "\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
free( err );
return 1;
}
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, AIR_CAST( airMopper, hestParmFree ), airMopAlways );
hparm->elideSingleOtherType = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", "
"\"tensor\", or \"dwi\" )",
NULL, NULL, meetHestGageKind );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "q", "query", airTypeString, 1, 1, &whatS, NULL,
"the quantity ( scalar, vector, or matrix ) to learn by probing" );
hestOptAdd( &hopt, "s", "sclX sclY sxlZ", airTypeDouble, 3, 3, scale,
"1.0 1.0 1.0",
"scaling factor for resampling on each axis "
"( >1.0 : supersampling )" );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"tent", "kernel for gageKernel00",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "seed", "N", airTypeUInt, 1, 1, &seed, "42",
"RNG seed; mostly for debugging" );
hestOptAdd( &hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false",
"enable \"zeroZ\" behavior in gage that partially "
"implements working with 3D images as if they are 2D" );
hestOptAdd( &hopt, "ssn", "SS #", airTypeUInt, 1, 1, &numSS,
"0", "how many scale-space samples to evaluate, or, "
"0 to turn-off all scale-space behavior" );
hestOptAdd( &hopt, "ssr", "scale range", airTypeDouble, 2, 2, rangeSS,
"nan nan", "range of scales in scale-space" );
hestOptAdd( &hopt, "ssrf", "SS read format", airTypeString, 1, 1,
&stackReadFormat, "",
"printf-style format ( including a \"%u\" ) for the "
"filenames from which to *read* "
"pre-blurred volumes computed for the stack" );
hestOptAdd( &hopt, "sssf", "SS save format", airTypeString, 1, 1,
&stackSaveFormat, "",
"printf-style format ( including a \"%u\" ) for the "
"filenames in which to *save* "
"pre-blurred volumes computed for the stack" );
hestOptAdd( &hopt, "ssw", "SS pos", airTypeDouble, 1, 1, &wrlSS, "0",
"\"world\"-space position ( true sigma ) "
"at which to sample in scale-space" );
hestOptAdd( &hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur,
"dgauss:1, 5", "blurring kernel, to sample scale space",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSS,
"hermite", "kernel for reconstructing from scale space samples",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "ssu", NULL, airTypeInt, 0, 0, &SSuniform, NULL,
"do uniform samples along sigma, and not ( by default ) "
"samples according to the effective diffusion scale" );
hestOptAdd( &hopt, "sso", NULL, airTypeInt, 0, 0, &SSoptim, NULL,
"if not using \"-ssu\", use pre-computed optimal "
"sigmas when possible" );
hestOptAdd( &hopt, "ssnd", NULL, airTypeInt, 0, 0, &SSnormd, NULL,
"normalize derivatives by scale" );
hestOptAdd( &hopt, "rn", NULL, airTypeInt, 0, 0, &renorm, NULL,
"renormalize kernel weights at each new sample location. "
"\"Accurate\" kernels don't need this; doing it always "
"makes things go slower" );
hestOptAdd( &hopt, "gmc", "min gradmag", airTypeDouble, 1, 1, &gmc,
"0.0", "For curvature-based queries, use zero when gradient "
"magnitude is below this" );
hestOptAdd( &hopt, "ofs", "ofs", airTypeInt, 0, 0, &orientationFromSpacing,
NULL, "If only per-axis spacing is available, use that to "
"contrive full orientation info" );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &otype, "float",
"type of output volume", NULL, nrrdType );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, probeInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestOptFree ), airMopAlways );
airMopAdd( mop, hopt, AIR_CAST( airMopper, hestParseFree ), airMopAlways );
what = airEnumVal( kind->enm, whatS );
if ( !what ) {
/* 0 indeed always means "unknown" for any gageKind */
fprintf( stderr, "%s: couldn't parse \"%s\" as measure of \"%s\" volume\n",
me, whatS, kind->name );
hestUsage( stderr, hopt, me, hparm );
hestGlossary( stderr, hopt, hparm );
airMopError( mop );
return 1;
}
/* special set-up required for DWI kind */
if ( !strcmp( TEN_DWI_GAGE_KIND_NAME, kind->name ) ) {
if ( tenDWMRIKeyValueParse( &ngrad, &nbmat, &bval, &skip, &skipNum, nin ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( skipNum ) {
fprintf( stderr, "%s: sorry, can't do DWI skipping in tenDwiGage", me );
airMopError( mop ); return 1;
}
/* this could stand to use some more command-line arguments */
if ( tenDwiGageKindSet( kind, 50, 1, bval, 0.001, ngrad, nbmat,
tenEstimate1MethodLLS,
tenEstimate2MethodQSegLLS, seed ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
/* for setting up pre-blurred scale-space samples */
if ( numSS ) {
unsigned int vi;
sbp = gageStackBlurParmNew( );
airMopAdd( mop, sbp, ( airMopper )gageStackBlurParmNix, airMopAlways );
ninSS = AIR_CAST( Nrrd **, calloc( numSS, sizeof( Nrrd * ) ) );
if ( !ninSS ) {
fprintf( stderr, "%s: couldn't allocate ninSS", me );
airMopError( mop ); return 1;
}
for ( ninSSIdx=0; ninSSIdx<numSS; ninSSIdx++ ) {
ninSS[ninSSIdx] = nrrdNew( );
airMopAdd( mop, ninSS[ninSSIdx], ( airMopper )nrrdNuke, airMopAlways );
}
if ( gageStackBlurParmScaleSet( sbp, numSS, rangeSS[0], rangeSS[1],
SSuniform, SSoptim )
|| gageStackBlurParmKernelSet( sbp, kSSblur )
|| gageStackBlurParmRenormalizeSet( sbp, AIR_TRUE )
|| gageStackBlurParmBoundarySet( sbp, nrrdBoundaryBleed, AIR_NAN )
|| gageStackBlurParmVerboseSet( sbp, verbose ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with stack blur info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( airStrlen( stackReadFormat ) ) {
if ( nrrdLoadMulti( ninSS, numSS,
stackReadFormat, 0, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble loading blurrings:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( gageStackBlurCheck( AIR_CAST( const Nrrd *const*, ninSS ),
sbp, nin, kind ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
} else {
if ( gageStackBlur( ninSS, sbp, nin, kind ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble pre-computing blurrings:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( airStrlen( stackSaveFormat ) && !airStrlen( stackReadFormat ) ) {
if ( nrrdSaveMulti( stackSaveFormat,
AIR_CAST( const Nrrd *const *, ninSS ),
numSS, 0, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving blurrings:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
}
/* there's actually work to do here, weirdly: gageProbe can either
work in index space, or in world space, but vprobe has
basically always been index-space-centric. For doing any kind
scale/stack space hacking for which vprobe is suited, its nicer
to have the user-specified stack position be in the stack's
"world" space, not the ( potentially non-uniform ) index space.
So here, we have to actually replicate work that is done by
_gageProbeSpace( ) in converting from world to index space */
for ( vi=0; vi<numSS-1; vi++ ) {
if ( AIR_IN_CL( sbp->sigma[vi], wrlSS, sbp->sigma[vi+1] ) ) {
idxSS = vi + AIR_AFFINE( sbp->sigma[vi], wrlSS, sbp->sigma[vi+1], 0, 1 );
if ( verbose > 1 ) {
fprintf( stderr, "%s: scale pos %g -> idx %g = %u + %g\n", me,
wrlSS, idxSS, vi,
AIR_AFFINE( sbp->sigma[vi], wrlSS, sbp->sigma[vi+1], 0, 1 ) );
}
break;
}
}
if ( vi == numSS-1 ) {
fprintf( stderr, "%s: scale pos %g outside range %g=%g, %g=%g\n", me,
wrlSS, rangeSS[0], sbp->sigma[0],
rangeSS[1], sbp->sigma[numSS-1] );
airMopError( mop ); return 1;
}
} else {
ninSS = NULL;
sbp = NULL;
}
/***
**** Except for the gageProbe( ) call in the inner loop below,
**** and the gageContextNix( ) call at the very end, all the gage
**** calls which set up ( and take down ) the context and state are here.
***/
ctx = gageContextNew( );
airMopAdd( mop, ctx, AIR_CAST( airMopper, gageContextNix ), airMopAlways );
gageParmSet( ctx, gageParmGradMagCurvMin, gmc );
gageParmSet( ctx, gageParmVerbose, verbose );
gageParmSet( ctx, gageParmTwoDimZeroZ, zeroZ );
gageParmSet( ctx, gageParmRenormalize, renorm ? AIR_TRUE : AIR_FALSE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
gageParmSet( ctx, gageParmOrientationFromSpacing, orientationFromSpacing );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, kind ) );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( numSS ) {
gagePerVolume **pvlSS;
gageParmSet( ctx, gageParmStackUse, AIR_TRUE );
gageParmSet( ctx, gageParmStackNormalizeDeriv, SSnormd );
if ( !E ) E |= !( pvlSS = AIR_CAST( gagePerVolume **,
calloc( numSS, sizeof( gagePerVolume * ) ) ) );
if ( !E ) airMopAdd( mop, pvlSS, ( airMopper )airFree, airMopAlways );
if ( !E ) E |= gageStackPerVolumeNew( ctx, pvlSS,
AIR_CAST( const Nrrd*const*, ninSS ),
numSS, kind );
if ( !E ) E |= gageStackPerVolumeAttach( ctx, pvl, pvlSS, sbp->sigma, numSS );
if ( !E ) E |= gageKernelSet( ctx, gageKernelStack, kSS->kernel, kSS->parm );
} else {
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
}
if ( !E ) E |= gageQueryItemOn( ctx, pvl, what );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
answer = gageAnswerPointer( ctx, pvl, what );
/***
**** end gage setup.
***/
ansLen = kind->table[what].answerLength;
iBaseDim = kind->baseDim;
oBaseDim = 1 == ansLen ? 0 : 1;
six = nin->axis[0+iBaseDim].size;
siy = nin->axis[1+iBaseDim].size;
siz = nin->axis[2+iBaseDim].size;
dsix = AIR_CAST( double, six );
dsiy = AIR_CAST( double, siy );
dsiz = AIR_CAST( double, siz );
sox = AIR_CAST( size_t, scale[0]*dsix );
soy = AIR_CAST( size_t, scale[1]*dsiy );
soz = AIR_CAST( size_t, scale[2]*dsiz );
dsox = AIR_CAST( double, sox );
dsoy = AIR_CAST( double, soy );
dsoz = AIR_CAST( double, soz );
if ( verbose ) {
fprintf( stderr, "%s: six, y, z = %u %u %u\n", me,
AIR_UINT( six ),
AIR_UINT( siy ),
AIR_UINT( siz ) );
fprintf( stderr, "%s: sox, y, z = %u %u %u\n", me,
AIR_UINT( sox ),
AIR_UINT( soy ),
AIR_UINT( soz ) );
}
rscl[0] = dsix/dsox;
rscl[1] = dsiy/dsoy;
rscl[2] = dsiz/dsoz;
if ( verbose ) {
fprintf( stderr, "%s: kernel support = %d^3 samples\n", me,
2*ctx->radius );
fprintf( stderr, "%s: effective scaling is %g %g %g\n", me,
rscl[0], rscl[1], rscl[2] );
}
/* else, normal volume probing */
if ( ansLen > 1 ) {
if ( verbose ) {
fprintf( stderr, "%s: creating %s x %s x %s x %s output\n", me,
airSprintSize_t( stmp[0], ansLen ),
airSprintSize_t( stmp[1], sox ),
airSprintSize_t( stmp[2], soy ),
airSprintSize_t( stmp[3], soz ) );
}
if ( !E ) E |= nrrdMaybeAlloc_va( nout=nrrdNew( ), otype, 4,
ansLen, sox, soy, soz );
} else {
if ( verbose ) {
fprintf( stderr, "%s: creating %s x %s x %s output\n", me,
airSprintSize_t( stmp[0], sox ),
airSprintSize_t( stmp[1], soy ),
airSprintSize_t( stmp[2], soz ) );
}
if ( !E ) E |= nrrdMaybeAlloc_va( nout=nrrdNew( ), otype, 3,
sox, soy, soz );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopAdd( mop, nout, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
hackSet = nrrdGetenvUInt( &hackZi, &hackValStr, hackKeyStr );
if ( AIR_FALSE == hackSet ) {
fprintf( stderr, "%s: couldn't parse value of \"%s\" ( \"%s\" ) as uint\n",
me, hackKeyStr, hackValStr );
airMopError( mop );
return 1;
}
if ( AIR_TRUE == hackSet ) {
fprintf( stderr, "%s: %s hack on: will only measure Zi=%u\n",
me, hackKeyStr, hackZi );
}
if ( nrrdCenterCell == ctx->shape->center ) {
ELL_3V_SET( min, -0.5, -0.5, -0.5 );
ELL_3V_SET( maxOut, dsox-0.5, dsoy-0.5, dsoz-0.5 );
ELL_3V_SET( maxIn, dsix-0.5, dsiy-0.5, dsiz-0.5 );
} else {
ELL_3V_SET( min, 0, 0, 0 );
ELL_3V_SET( maxOut, dsox-1, dsoy-1, dsoz-1 );
ELL_3V_SET( maxIn, dsix-1, dsiy-1, dsiz-1 );
}
t0 = airTime( );
ins = nrrdDInsert[nout->type];
gageParmSet( ctx, gageParmVerbose, verbose/10 );
for ( zi=0; zi<soz; zi++ ) {
if ( verbose ) {
if ( verbose > 1 ) {
fprintf( stderr, "z = " );
}
fprintf( stderr, " %s/%s",
airSprintSize_t( stmp[0], zi ),
airSprintSize_t( stmp[1], soz-1 ) );
fflush( stderr );
if ( verbose > 1 ) {
fprintf( stderr, "\n" );
}
}
if ( AIR_TRUE == hackSet ) {
if ( hackZi != zi ) {
continue;
}
}
z = AIR_AFFINE( min[2], zi, maxOut[2], min[2], maxIn[2] );
for ( yi=0; yi<soy; yi++ ) {
y = AIR_AFFINE( min[1], yi, maxOut[1], min[1], maxIn[1] );
if ( 2 == verbose ) {
fprintf( stderr, " %u/%u", AIR_UINT( yi ),
AIR_UINT( soy ) );
fflush( stderr );
}
for ( xi=0; xi<sox; xi++ ) {
if ( verbose > 2 ) {
fprintf( stderr, " ( %u, %u )/( %u, %u )",
AIR_UINT( xi ), AIR_UINT( yi ),
AIR_UINT( sox ), AIR_UINT( soy ) );
fflush( stderr );
}
x = AIR_AFFINE( min[0], xi, maxOut[0], min[0], maxIn[0] );
idx = xi + sox*( yi + soy*zi );
E = ( numSS
? gageStackProbe( ctx, x, y, z, idxSS )
: gageProbe( ctx, x, y, z ) );
if ( E ) {
fprintf( stderr,
"%s: trouble at i=( %s, %s, %s ) -> f=( %g, %g, %g ):\n%s\n( %d )\n",
me, airSprintSize_t( stmp[0], xi ),
airSprintSize_t( stmp[1], yi ),
airSprintSize_t( stmp[2], zi ), x, y, z,
ctx->errStr, ctx->errNum );
airMopError( mop );
return 1;
}
if ( 1 == ansLen ) {
ins( nout->data, idx, *answer );
} else {
for ( ai=0; ai<=ansLen-1; ai++ ) {
ins( nout->data, ai + ansLen*idx, answer[ai] );
}
}
}
}
}
/* HEY: this isn't actually correct in general, but is true
for gageKindScl and gageKindVec */
nrrdContentSet_va( nout, "probe", nin, "%s", airEnumStr( kind->enm, what ) );
for ( axi=0; axi<3; axi++ ) {
/* HEY: why not using nrrdAxisInfoCopy? */
nout->axis[axi+oBaseDim].label = airStrdup( nin->axis[axi+iBaseDim].label );
nout->axis[axi+oBaseDim].center = ctx->shape->center;
nout->axis[axi+oBaseDim].kind = nin->axis[axi+iBaseDim].kind;
}
nrrdBasicInfoCopy( nout, nin, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_SPACEORIGIN_BIT
| NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) );
if ( ctx->shape->fromOrientation ) {
if ( nin->space ) {
nrrdSpaceSet( nout, nin->space );
} else {
nrrdSpaceDimensionSet( nout, nin->spaceDim );
}
nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
for ( axi=0; axi<3; axi++ ) {
nrrdSpaceVecScale( nout->axis[axi+oBaseDim].spaceDirection,
rscl[axi],
nin->axis[axi+iBaseDim].spaceDirection );
z = AIR_AFFINE( min[axi], 0, maxOut[axi], min[axi], maxIn[axi] );
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, nout->spaceOrigin,
z, nin->axis[axi+iBaseDim].spaceDirection );
}
} else {
for ( axi=0; axi<3; axi++ ) {
nout->axis[axi+oBaseDim].spacing =
rscl[axi]*nin->axis[axi+iBaseDim].spacing;
}
}
fprintf( stderr, "\n" );
t1 = airTime( );
fprintf( stderr, "probe rate = %g KHz\n", dsox*dsoy*dsoz/( 1000.0*( t1-t0 ) ) );
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
#define _COIL_IV3_FILL( radius, diam, valLen ) \
27 if ( 0 && xx0 ) { \
28 /* cycle through slices */ \
tmp = iv3[0]; \
for ( xni=0; xni<diam-1; xni++ ) { \
iv3[xni] = iv3[xni+1]; \
} \
iv3[diam-1] = tmp; \
/* refill only newest one */ \
xni = diam-1; \
xvi = AIR_CLAMP( 0, xni-( int )radius+xx0, sizeX-1 ) - xx0; \
for ( zni=0; zni<diam; zni++ ) { \
zvi = AIR_CLAMP( 0, zni-( int )radius+zz0, sizeZ-1 ) - zz0; \
for ( yni=0; yni<diam; yni++ ) { \
yvi = AIR_CLAMP( 0, yni-( int )radius+yy0, sizeY-1 ) - yy0; \
for ( vi=0; vi<valLen; vi++ ) { \
iv3[xni][vi + valLen*( yni + diam*zni )] = \
here[vi + valLen*( 0 + 2*( xvi + sizeX*( yvi + sizeY*zvi ) ) )]; \
} \
} \
} \
} else { \
/* have to re-fill entire thing */ \
for ( zni=0; zni<diam; zni++ ) { \
zvi = AIR_CLAMP( 0, zni-( int )radius+zz0, sizeZ-1 ) - zz0; \
for ( yni=0; yni<diam; yni++ ) { \
yvi = AIR_CLAMP( 0, yni-( int )radius+yy0, sizeY-1 ) - yy0; \
for ( xni=0; xni<diam; xni++ ) { \
xvi = AIR_CLAMP( 0, xni-( int )radius+xx0, sizeX-1 ) - xx0; \
for ( vi=0; vi<valLen; vi++ ) { \
iv3[xni][vi + valLen*( yni + diam*zni )] = \
here[vi + valLen*( 0 + 2*( xvi + sizeX*( yvi + sizeY*zvi ) ) )]; \
} \
} \
} \
} \
}
/*
**
** iv3 is: diam x diam x diam x valLen
**
** this should be parameterized on both radius and valLen
*/
void
_coilIv3Fill_R_L( coil_t **iv3, coil_t *here, unsigned int radius, int valLen,
int xx0, int yy0, int zz0, int sizeX, int sizeY, int sizeZ ) {
int diam, vi, /* value index */
xni, yni, zni, /* neighborhood ( iv3 ) indices */
xvi, yvi, zvi; /* volume indices */
coil_t *tmp;
diam = 1 + 2*radius;
_COIL_IV3_FILL( radius, diam, valLen );
return;
}
void
_coilIv3Fill_1_1( coil_t **iv3, coil_t *here, unsigned int radius, int valLen,
int xx0, int yy0, int zz0, int sizeX, int sizeY, int sizeZ ) {
int vi, /* value index */
xni, yni, zni, /* neighborhood ( iv3 ) indices */
xvi, yvi, zvi; /* volume indices */
coil_t *tmp;
AIR_UNUSED( radius );
AIR_UNUSED( valLen );
_COIL_IV3_FILL( 1, 3, 1 );
return;
}
void
_coilIv3Fill_1_7( coil_t **iv3, coil_t *here, unsigned int radius, int valLen,
int xx0, int yy0, int zz0, int sizeX, int sizeY, int sizeZ ) {
int vi, /* value index */
xni, yni, zni, /* neighborhood ( iv3 ) indices */
xvi, yvi, zvi; /* volume indices */
coil_t *tmp;
AIR_UNUSED( radius );
AIR_UNUSED( valLen );
_COIL_IV3_FILL( 1, 3, 7 );
return;
}
int
_coilThisZGet( coilTask *task, int doFilter ) {
int thisZ, *thisFlag, *thatFlag;
if ( doFilter ) {
thisFlag = &( task->cctx->todoFilter );
thatFlag = &( task->cctx->todoUpdate );
} else {
thisFlag = &( task->cctx->todoUpdate );
thatFlag = &( task->cctx->todoFilter );
}
if ( task->cctx->numThreads > 1 ) {
airThreadMutexLock( task->cctx->nextSliceMutex );
}
if ( task->cctx->nextSlice == task->cctx->size[2]
&& *thisFlag ) {
/* we're the first thread to start this phase */
task->cctx->nextSlice = 0;
*thisFlag = AIR_FALSE;
}
thisZ = task->cctx->nextSlice;
if ( task->cctx->nextSlice < task->cctx->size[2] ) {
task->cctx->nextSlice++;
if ( task->cctx->nextSlice == task->cctx->size[2] ) {
/* we just grabbed the last slice of this phase */
*thatFlag = AIR_TRUE;
}
}
if ( task->cctx->numThreads > 1 ) {
airThreadMutexUnlock( task->cctx->nextSliceMutex );
}
return thisZ;
}
void
_coilProcess( coilTask *task, int doFilter ) {
static const char me[]="_coilProcess";
int xi, yi, sizeX, sizeY, thisZ, sizeZ, valLen, radius;
coil_t *here;
void ( *filter )( coil_t *delta, int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] );
sizeX = task->cctx->size[0];
sizeY = task->cctx->size[1];
sizeZ = task->cctx->size[2];
valLen = task->cctx->kind->valLen;
radius = task->cctx->radius;
filter = task->cctx->kind->filter[task->cctx->method->type];
if ( doFilter ) {
while ( 1 ) {
thisZ = _coilThisZGet( task, doFilter );
if ( thisZ == sizeZ ) {
break;
}
if ( task->cctx->verbose > 2 ) {
fprintf( stderr, "%s( %u ), f: iter=%u, z=%d\n",
me, task->threadIdx, task->cctx->iter, thisZ );
}
here = ( coil_t* )( task->cctx->nvol->data ) + 2*valLen*sizeX*sizeY*thisZ;
for ( yi=0; yi<sizeY; yi++ ) {
for ( xi=0; xi<sizeX; xi++ ) {
task->iv3Fill( task->iv3, here + 0*valLen, radius, valLen,
xi, yi, thisZ, sizeX, sizeY, sizeZ );
filter( here + 1*valLen, xi, yi, thisZ, task->iv3,
task->cctx->spacing, task->cctx->parm );
here += 2*valLen;
}
}
}
} else {
while ( 1 ) {
thisZ = _coilThisZGet( task, doFilter );
if ( thisZ == sizeZ ) {
break;
}
if ( task->cctx->verbose > 3 ) {
fprintf( stderr, "%s( %u ), u: iter=%u, z=%d\n",
me, task->threadIdx, task->cctx->iter, thisZ );
}
here = ( coil_t* )( task->cctx->nvol->data ) + 2*valLen*sizeX*sizeY*thisZ;
for ( yi=0; yi<sizeY; yi++ ) {
for ( xi=0; xi<sizeX; xi++ ) {
task->cctx->kind->update( here + 0*valLen, here + 1*valLen );
here += 2*valLen;
}
}
}
}
return;
}
coilTask *
_coilTaskNew( coilContext *cctx, int threadIdx ) {
coilTask *task;
int len, diam, xi;
len = cctx->kind->valLen;
diam = 1 + 2*cctx->radius;
task = ( coilTask * )calloc( 1, sizeof( coilTask ) );
if ( task ) {
task->cctx = cctx;
task->thread = airThreadNew( );
task->threadIdx = threadIdx;
task->_iv3 = ( coil_t* )calloc( len*diam*diam*diam, sizeof( coil_t ) );
task->iv3 = ( coil_t** )calloc( diam, sizeof( coil_t* ) );
for ( xi=0; xi<diam; xi++ ) {
task->iv3[xi] = task->_iv3 + xi*len*diam*diam;
}
if ( 1 == cctx->radius && 1 == cctx->kind->valLen ) {
task->iv3Fill = _coilIv3Fill_1_1;
} else if ( 1 == cctx->radius && 7 == cctx->kind->valLen ) {
task->iv3Fill = _coilIv3Fill_1_7;
} else {
task->iv3Fill = _coilIv3Fill_R_L;
}
task->returnPtr = NULL;
}
return task;
}
coilTask *
_coilTaskNix( coilTask *task ) {
if ( task ) {
task->thread = airThreadNix( task->thread );
task->_iv3 = ( coil_t * )airFree( task->_iv3 );
task->iv3 = ( coil_t ** )airFree( task->iv3 );
free( task );
}
return NULL;
}
void *
_coilWorker( void *_task ) {
static const char me[]="_coilWorker";
coilTask *task;
task = ( coilTask * )_task;
while ( 1 ) {
/* wait until parent has set cctx->finished */
if ( task->cctx->verbose > 1 ) {
fprintf( stderr, "%s( %d ): waiting to check finished\n",
me, task->threadIdx );
}
if ( task->cctx->numThreads > 1 ) {
airThreadBarrierWait( task->cctx->filterBarrier );
}
if ( task->cctx->finished ) {
if ( task->cctx->verbose > 1 ) {
fprintf( stderr, "%s( %d ): done!\n", me, task->threadIdx );
}
break;
}
/* else there's work to do ... */
/* first: filter */
if ( task->cctx->verbose > 1 ) {
fprintf( stderr, "%s( %d ): filtering ... \n",
me, task->threadIdx );
}
_coilProcess( task, AIR_TRUE );
/* second: update */
if ( task->cctx->numThreads > 1 ) {
airThreadBarrierWait( task->cctx->updateBarrier );
}
if ( task->cctx->verbose > 1 ) {
fprintf( stderr, "%s( %d ): updating ... \n",
me, task->threadIdx );
}
_coilProcess( task, AIR_FALSE );
}
return _task;
}
int
coilStart( coilContext *cctx ) {
static const char me[]="coilStart";
int valIdx, valLen;
coil_t ( *lup )( const void*, size_t ), *val;
unsigned tidx, elIdx;
if ( !cctx ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
cctx->task = ( coilTask ** )calloc( cctx->numThreads, sizeof( coilTask * ) );
if ( !( cctx->task ) ) {
biffAddf( COIL, "%s: couldn't allocate array of tasks", me );
return 1;
}
/* we create tasks for ALL threads, including me, thread 0 */
cctx->task[0] = NULL;
for ( tidx=0; tidx<cctx->numThreads; tidx++ ) {
cctx->task[tidx] = _coilTaskNew( cctx, tidx );
if ( !( cctx->task[tidx] ) ) {
biffAddf( COIL, "%s: couldn't allocate task %d", me, tidx );
return 1;
}
}
cctx->finished = AIR_FALSE;
if ( cctx->numThreads > 1 ) {
cctx->nextSliceMutex = airThreadMutexNew( );
cctx->filterBarrier = airThreadBarrierNew( cctx->numThreads );
cctx->updateBarrier = airThreadBarrierNew( cctx->numThreads );
}
/* initialize the values in cctx->nvol */
val = ( coil_t* )( cctx->nvol->data );
valLen = cctx->kind->valLen;
#if COIL_TYPE_FLOAT
lup = nrrdFLookup[cctx->nin->type];
#else
lup = nrrdDLookup[cctx->nin->type];
#endif
for ( elIdx=0; elIdx<cctx->size[0]*cctx->size[1]*cctx->size[2]; elIdx++ ) {
for ( valIdx=0; valIdx<valLen; valIdx++ ) {
val[valIdx + 0*valLen] = lup( cctx->nin->data, valIdx + valLen*elIdx );
val[valIdx + 1*valLen] = 0;
}
val += 2*valLen;
}
/* start threads 1 and up running; they'll all hit filterBarrier */
if ( cctx->numThreads > 1 ) {
for ( tidx=1; tidx<cctx->numThreads; tidx++ ) {
if ( cctx->verbose > 1 ) {
fprintf( stderr, "%s: spawning thread %d\n", me, tidx );
}
airThreadStart( cctx->task[tidx]->thread, _coilWorker,
( void * )( cctx->task[tidx] ) );
}
}
/* set things as though we've just finished an update phase */
cctx->nextSlice = cctx->size[2];
cctx->todoFilter = AIR_TRUE;
cctx->todoUpdate = AIR_FALSE;
return 0;
}
/*
******** coilIterate
**
** ( documentation )
**
** NB: this implements the body of thread 0
*/
int
coilIterate( coilContext *cctx, int numIterations ) {
static const char me[]="coilIterate";
int iter;
double time0, time1;
if ( !cctx ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
time0 = airTime( );
for ( iter=0; iter<numIterations; iter++ ) {
cctx->iter = iter;
if ( cctx->verbose ) {
fprintf( stderr, "%s: starting iter %d ( of %d )\n", me, iter,
numIterations );
}
cctx->finished = AIR_FALSE;
if ( cctx->numThreads > 1 ) {
airThreadBarrierWait( cctx->filterBarrier );
}
/* first: filter */
if ( cctx->verbose > 1 ) {
fprintf( stderr, "%s: filtering ... \n", me );
}
_coilProcess( cctx->task[0], AIR_TRUE );
/* second: update */
if ( cctx->verbose > 1 ) {
fprintf( stderr, "%s: updating ... \n", me );
}
if ( cctx->numThreads > 1 ) {
airThreadBarrierWait( cctx->updateBarrier );
}
_coilProcess( cctx->task[0], AIR_FALSE );
}
time1 = airTime( );
if ( cctx->verbose ) {
fprintf( stderr, "%s: elapsed time = %g ( %g/iter )\n", me,
time1 - time0, ( time1 - time0 )/numIterations );
}
return 0;
}
int
coilFinish( coilContext *cctx ) {
static const char me[]="coilFinish";
unsigned int tidx;
if ( !cctx ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
if ( cctx->verbose > 1 ) {
fprintf( stderr, "%s: finishing workers\n", me );
}
cctx->finished = AIR_TRUE;
if ( cctx->numThreads > 1 ) {
airThreadBarrierWait( cctx->filterBarrier );
for ( tidx=1; tidx<cctx->numThreads; tidx++ ) {
airThreadJoin( cctx->task[tidx]->thread, &( cctx->task[tidx]->returnPtr ) );
cctx->task[tidx]->thread = airThreadNix( cctx->task[tidx]->thread );
cctx->task[tidx] = _coilTaskNix( cctx->task[tidx] );
}
}
cctx->task[0]->thread = airThreadNix( cctx->task[0]->thread );
cctx->task[0] = _coilTaskNix( cctx->task[0] );
cctx->task = ( coilTask ** )airFree( cctx->task );
if ( cctx->numThreads > 1 ) {
cctx->nextSliceMutex = airThreadMutexNix( cctx->nextSliceMutex );
cctx->filterBarrier = airThreadBarrierNix( cctx->filterBarrier );
cctx->updateBarrier = airThreadBarrierNix( cctx->updateBarrier );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
const int
coilPresent = 42;
const char *
coilBiffKey = "coil";
int
coilDefaultRadius = 1;
int
coilVerbose = 0;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
const char *
_coilMethodTypeStr[COIL_METHOD_TYPE_MAX+1] = {
"( unknown_method )",
"testing",
"homogeneous",
"perona-malik",
"modified curvature",
"modified curvature rings",
"curvature flow",
"self",
"finish"
};
const char *
_coilMethodTypeDesc[COIL_METHOD_TYPE_MAX+1] = {
"unknown_method",
"nothing, actually, just here for testing",
"homogenous isotropic diffusion ( Gaussian blurring )",
"Perona-Malik",
"modified curvature diffusion",
"modified curvature diffusion rings",
"curvature flow",
"self-diffusion of diffusion tensors",
"finish a phd already"
};
const char *
_coilMethodTypeStrEqv[] = {
"test", "testing",
"iso", "homog", "homogeneous",
"pm", "perona-malik",
"mcde", "modified curvature",
"mcder", "modified curvature rings",
"flow", "curvature flow",
"self",
"finish",
""
};
const int
_coilMethodTypeValEqv[] = {
coilMethodTypeTesting, coilMethodTypeTesting,
coilMethodTypeHomogeneous, coilMethodTypeHomogeneous, coilMethodTypeHomogeneous,
coilMethodTypePeronaMalik, coilMethodTypePeronaMalik,
coilMethodTypeModifiedCurvature, coilMethodTypeModifiedCurvature,
coilMethodTypeModifiedCurvatureRings, coilMethodTypeModifiedCurvatureRings,
coilMethodTypeCurvatureFlow, coilMethodTypeCurvatureFlow,
coilMethodTypeSelf,
coilMethodTypeFinish,
};
const airEnum
_coilMethodType = {
"method",
COIL_METHOD_TYPE_MAX,
_coilMethodTypeStr, NULL,
_coilMethodTypeDesc,
_coilMethodTypeStrEqv, _coilMethodTypeValEqv,
AIR_FALSE
};
86 const airEnum *const
coilMethodType = &_coilMethodType;
/* -------------------------------------------------- */
const char *
_coilKindTypeStr[COIL_KIND_TYPE_MAX+1] = {
"( unknown_kind )",
"scalar",
"3color",
"7tensor"
};
const char *
_coilKindTypeDesc[COIL_KIND_TYPE_MAX+1] = {
"unknown_kind",
"plain old scalar quantities",
"3-component color",
"ten-style 7-valued tensor"
};
const char *
_coilKindTypeStrEqv[] = {
"scalar",
"3color",
"7tensor", "tensor",
""
};
const int
_coilKindTypeValEqv[] = {
coilKindTypeScalar,
coilKindType3Color,
coilKindType7Tensor, coilKindType7Tensor
};
const airEnum
_coilKindType = {
"kind",
COIL_KIND_TYPE_MAX,
_coilKindTypeStr, NULL,
_coilKindTypeDesc,
_coilKindTypeStrEqv, _coilKindTypeValEqv,
AIR_FALSE
};
131 const airEnum *const
coilKindType = &_coilKindType;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
int
27 coilVolumeCheck( const Nrrd *nin, const coilKind *kind ) {
static const char me[]="coilVolumeCheck";
unsigned int baseDim;
if ( !( nin && kind ) ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( COIL, "%s: can only operate on scalar types, not %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
baseDim = ( 1 == kind->valLen ? 0 : 1 );
if ( 3 + baseDim != nin->dim ) {
biffAddf( COIL, "%s: dim of input must be 3+%d ( 3 + baseDim ), not %d",
me, baseDim, nin->dim );
return 1;
}
return 0;
}
coilContext *
51 coilContextNew( ) {
coilContext *cctx;
cctx = ( coilContext * )calloc( 1, sizeof( coilContext ) );
if ( cctx ) {
cctx->nin = NULL;
cctx->radius = coilDefaultRadius;
cctx->numThreads = 1;
ELL_3V_SET( cctx->spacing, AIR_NAN, AIR_NAN, AIR_NAN );
cctx->nvol = NULL;
cctx->finished = AIR_FALSE;
cctx->task = NULL;
cctx->nextSliceMutex = NULL;
cctx->filterBarrier = NULL;
cctx->updateBarrier = NULL;
}
return cctx;
}
int
71 coilContextAllSet( coilContext *cctx, const Nrrd *nin,
const coilKind *kind, const coilMethod *method,
unsigned int radius, unsigned int numThreads, int verbose,
double parm[COIL_PARMS_NUM] ) {
static const char me[]="coilContextAllSet";
int someExist, allExist, baseDim, pi;
size_t size[NRRD_DIM_MAX], sx, sy, sz;
double xsp, ysp, zsp;
airArray *mop;
cctx->verbose = verbose;
if ( !( cctx && nin && kind && method ) ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
if ( coilVolumeCheck( nin, kind ) ) {
biffAddf( COIL, "%s: input volume not usable as %s", me, kind->name );
return 1;
}
if ( !( radius >= 1 && numThreads >= 1 ) ) {
biffAddf( COIL, "%s: radius ( %d ) not >= 1 or numThreads ( %d ) not >= 1", me,
radius, numThreads );
return 1;
}
if ( !( AIR_IN_OP( coilMethodTypeUnknown, method->type,
coilMethodTypeLast ) ) ) {
biffAddf( COIL, "%s: method->type %d not valid", me, method->type );
return 1;
}
if ( !kind->filter[method->type] ) {
biffAddf( COIL, "%s: sorry, %s filtering not available on %s kind",
me, method->name, kind->name );
return 1;
}
/* warn if we can't do the multiple threads user wants */
if ( numThreads > 1 && !airThreadCapable && airThreadNoopWarning ) {
fprintf( stderr, "%s: WARNING: this Teem not thread capable: using 1 "
"thread, not %d\n", me, numThreads );
numThreads = 1;
}
mop = airMopNew( );
/* set parms */
for ( pi=0; pi<method->numParm; pi++ ) {
if ( !AIR_EXISTS( parm[pi] ) ) {
biffAddf( COIL, "%s: parm[%d] ( need %d ) doesn't exist",
me, pi, method->numParm );
airMopError( mop ); return 1;
}
cctx->parm[pi] = parm[pi];
}
/* set sizes and spacings */
baseDim = ( 1 == kind->valLen ? 0 : 1 );
sx = nin->axis[0 + baseDim].size;
sy = nin->axis[1 + baseDim].size;
sz = nin->axis[2 + baseDim].size;
if ( sz < numThreads ) {
char stmp[AIR_STRLEN_SMALL];
airSprintSize_t( stmp, sz );
fprintf( stderr, "%s: wanted %d threads but volume only has %s slices, "
"using %s threads instead\n", me, numThreads, stmp, stmp );
numThreads = AIR_UINT( sz );
}
ELL_3V_SET( cctx->size, sx, sy, sz );
xsp = nin->axis[0 + baseDim].spacing;
ysp = nin->axis[1 + baseDim].spacing;
zsp = nin->axis[2 + baseDim].spacing;
someExist = AIR_EXISTS( xsp ) || AIR_EXISTS( ysp ) || AIR_EXISTS( zsp );
allExist = AIR_EXISTS( xsp ) && AIR_EXISTS( ysp ) && AIR_EXISTS( zsp );
if ( !( someExist ) ) {
fprintf( stderr, "%s: WARNING: assuming unit spacing for all axes\n", me );
xsp = 1;
ysp = 1;
zsp = 1;
} else {
if ( !allExist ) {
biffAddf( COIL, "%s: spacings ( %g, %g, %g ) not uniformly existent",
me, xsp, ysp, zsp );
airMopError( mop ); return 1;
}
}
ELL_3V_SET( cctx->spacing, xsp, ysp, zsp );
if ( cctx->verbose ) {
fprintf( stderr, "%s: spacings: %g %g %g\n", me,
cctx->spacing[0], cctx->spacing[1], cctx->spacing[2] );
}
/* allocate nvol */
if ( 0 == baseDim ) {
ELL_4V_SET( size, 2, sx, sy, sz );
} else {
ELL_5V_SET( size, kind->valLen, 2, sx, sy, sz );
}
cctx->nvol = nrrdNew( );
if ( nrrdMaybeAlloc_nva( cctx->nvol, coil_nrrdType, 4 + baseDim, size ) ) {
biffMovef( COIL, NRRD,
"%s: couldn't allocate internal processing volume", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, cctx->nvol, ( airMopper )nrrdNuke, airMopOnError );
cctx->nin = nin;
cctx->kind = kind;
cctx->method = method;
cctx->radius = radius;
cctx->numThreads = numThreads;
airMopOkay( mop );
return 0;
}
/*
******** coilOutputGet
**
** slice the present intermediate volume to get an output.
**
** No, this does not do quantization or rounding to match the input
** type ( of cctx->nin ). The reason is that after filtering, it is often
** the case that subtle differences in values emerge, and it may be
** reckless to dump them back into the limited type or value range
** that they started with. That sort of operation should be under
** explicit user control.
*/
int
199 coilOutputGet( Nrrd *nout, coilContext *cctx ) {
static const char me[]="coilOutputGet";
int baseDim;
if ( !( nout && cctx ) ) {
biffAddf( COIL, "%s: got NULL pointer", me );
return 1;
}
baseDim = ( 1 == cctx->kind->valLen ? 0 : 1 );
if ( nrrdSlice( nout, cctx->nvol, baseDim, 0 )
|| nrrdAxisInfoCopy( nout, cctx->nin, NULL, NRRD_AXIS_INFO_NONE )
|| nrrdBasicInfoCopy( nout, cctx->nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( COIL, NRRD, "%s: trouble getting output", me );
return 1;
}
return 0;
}
coilContext *
227 coilContextNix( coilContext *cctx ) {
if ( cctx ) {
/* thread machinery destroyed with coilFinish( ) */
cctx->nvol = nrrdNuke( cctx->nvol );
airFree( cctx );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
/* ------------------------------------------ */
const coilMethod
_coilMethodTesting = {
"testing",
coilMethodTypeTesting,
0
};
const coilMethod*
coilMethodTesting = &_coilMethodTesting;
/* ------------------------------------------ */
const coilMethod
_coilMethodHomogeneous = {
"homogeneous",
coilMethodTypeHomogeneous,
1
};
const coilMethod*
coilMethodHomogeneous = &_coilMethodHomogeneous;
/* ------------------------------------------ */
const coilMethod
_coilMethodPeronaMalik = {
"perona-malik",
coilMethodTypePeronaMalik,
2
};
const coilMethod*
coilMethodPeronaMalik = &_coilMethodPeronaMalik;
/* ------------------------------------------ */
const coilMethod
_coilMethodModifiedCurvature = {
"modified-curvature",
coilMethodTypeModifiedCurvature,
3
};
const coilMethod*
coilMethodModifiedCurvature = &_coilMethodModifiedCurvature;
/* ------------------------------------------ */
const coilMethod
_coilMethodModifiedCurvatureRings = {
"modified-curvature-rings",
coilMethodTypeModifiedCurvatureRings,
6
};
const coilMethod*
coilMethodModifiedCurvatureRings = &_coilMethodModifiedCurvatureRings;
/* ------------------------------------------ */
const coilMethod
_coilMethodSelf = {
"self",
coilMethodTypeSelf,
1
};
const coilMethod*
coilMethodSelf = &_coilMethodSelf;
/* ------------------------------------------ */
const coilMethod
_coilMethodFinish = {
"finish",
coilMethodTypeFinish,
4
};
const coilMethod*
coilMethodFinish = &_coilMethodFinish;
/* ------------------------------------------ */
const coilMethod*
coilMethodArray[COIL_METHOD_TYPE_MAX+1] = {
NULL,
&_coilMethodTesting,
&_coilMethodHomogeneous,
&_coilMethodPeronaMalik,
&_coilMethodModifiedCurvature,
&_coilMethodModifiedCurvatureRings,
NULL,
&_coilMethodSelf,
&_coilMethodFinish
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
/*
** x ----> X
** \ [0][0] [1][0] [2][0]
** | \ [0][1] [1][1] [2][1]
** | Y [0][2] [1][2] [2][2]
** |
** | [0][3] [1][3] [2][3]
** Z [0][4] [1][4] [2][4]
** [0][5] [1][5] [2][5]
**
** [0][6] [1][6] [2][6]
** [0][7] [1][7] [2][7]
** [0][8] [1][8] [2][8]
*/
coil_t
42 _coilLaplacian3( coil_t **iv3, double spacing[3] ) {
double ret;
ret = ( ( iv3[0][4] - 2*iv3[1][4] + iv3[2][4] )/( spacing[0]*spacing[0] )
+ ( iv3[1][3] - 2*iv3[1][4] + iv3[1][5] )/( spacing[1]*spacing[1] )
+ ( iv3[1][1] - 2*iv3[1][4] + iv3[1][7] )/( spacing[2]*spacing[2] ) );
return AIR_CAST( coil_t, ret );
}
void
52 _coilKindScalarFilterTesting( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
AIR_UNUSED( iv3 );
AIR_UNUSED( spacing );
AIR_UNUSED( parm );
delta[0] = 0;
}
void
66 _coilKindScalarFilterHomogeneous( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
delta[0] = AIR_CAST( coil_t, parm[0] )*_coilLaplacian3( iv3, spacing );
}
void
78 _coilKindScalar3x3x3Gradients( coil_t *forwX, coil_t *backX,
coil_t *forwY, coil_t *backY,
coil_t *forwZ, coil_t *backZ,
coil_t **i,
coil_t rspX, coil_t rspY, coil_t rspZ ) {
/* gradients at forward and backward X */
forwX[0] = rspX*( i[2][4] - i[1][4] );
forwX[1] = rspY*( i[1][5] + i[2][5] - i[1][3] - i[2][3] )/2;
forwX[2] = rspZ*( i[1][7] + i[2][7] - i[1][1] - i[2][1] )/2;
backX[0] = rspX*( i[1][4] - i[0][4] );
backX[1] = rspY*( i[0][5] + i[1][5] - i[0][3] - i[1][3] )/2;
backX[2] = rspZ*( i[0][7] + i[1][7] - i[0][1] - i[1][1] )/2;
/* gradients at forward and backward Y */
forwY[0] = rspX*( i[2][4] + i[2][5] - i[0][4] - i[0][5] )/2;
forwY[1] = rspY*( i[1][5] - i[1][4] );
forwY[2] = rspZ*( i[1][7] + i[1][8] - i[1][1] - i[1][2] )/2;
backY[0] = rspX*( i[2][3] + i[2][4] - i[0][3] - i[0][4] )/2;
backY[1] = rspY*( i[1][4] - i[1][3] );
backY[2] = rspZ*( i[1][6] + i[1][7] - i[1][0] - i[1][1] )/2;
/* gradients at forward and backward Z */
forwZ[0] = rspX*( i[2][4] + i[2][7] - i[0][4] - i[0][7] )/2;
forwZ[1] = rspY*( i[1][5] + i[1][8] - i[1][3] - i[1][6] )/2;
forwZ[2] = rspZ*( i[1][7] - i[1][4] );
backZ[0] = rspX*( i[2][1] + i[2][4] - i[0][1] - i[0][4] )/2;
backZ[1] = rspY*( i[1][2] + i[1][5] - i[1][0] - i[1][3] )/2;
backZ[2] = rspZ*( i[1][4] - i[1][1] );
return;
}
#define _COIL_CONDUCT( LL, KK ) \
AIR_CAST( coil_t, exp( -0.5*( LL )/( KK ) ) )
/*
#define _COIL_CONDUCT( vec, KK ) \
AIR_CAST( coil_t, 1.0/( 1.0 + ( LL )/( KK ) ) )
*/
119 void
_coilKindScalarFilterPeronaMalik( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3],
KK, rspX, rspY, rspZ;
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
/* reciprocals of spacings in X, Y, and Z */
rspX = AIR_CAST( coil_t, 1.0/spacing[0] );
rspY = AIR_CAST( coil_t, 1.0/spacing[1] );
rspZ = AIR_CAST( coil_t, 1.0/spacing[2] );
_coilKindScalar3x3x3Gradients( forwX, backX,
forwY, backY,
forwZ, backZ,
iv3,
rspX, rspY, rspZ );
/* compute fluxes */
KK = AIR_CAST( coil_t, parm[1]*parm[1] );
forwX[0] *= _COIL_CONDUCT( ELL_3V_DOT( forwX, forwX ), KK );
forwY[1] *= _COIL_CONDUCT( ELL_3V_DOT( forwY, forwY ), KK );
forwZ[2] *= _COIL_CONDUCT( ELL_3V_DOT( forwZ, forwZ ), KK );
backX[0] *= _COIL_CONDUCT( ELL_3V_DOT( backX, backX ), KK );
backY[1] *= _COIL_CONDUCT( ELL_3V_DOT( backY, backY ), KK );
backZ[2] *= _COIL_CONDUCT( ELL_3V_DOT( backZ, backZ ), KK );
delta[0] = AIR_CAST( coil_t, parm[0] )*( rspX*( forwX[0] - backX[0] )
+ rspY*( forwY[1] - backY[1] )
+ rspZ*( forwZ[2] - backZ[2] ) );
}
/*
** ( mcde )
** parm vector:
** 0 1 2 ( 3 )
** step K lerp ( lerp=1: all laplacian )
*/
162 void
_coilKindScalarFilterModifiedCurvature( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
/* char me[]="_coilKindScalarFilterModifiedCurvature"; */
coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3],
grad[3], gm, eps, KK, LL, denom, rspX, rspY, rspZ, lerp;
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
/*
if ( coilVerbose ) {
fprintf( stderr, "!%s: --------- hello --------\n", me );
}
*/
/* reciprocals of spacings in X, Y, and Z */
rspX = AIR_CAST( coil_t, 1.0/spacing[0] );
rspY = AIR_CAST( coil_t, 1.0/spacing[1] );
rspZ = AIR_CAST( coil_t, 1.0/spacing[2] );
_coilKindScalar3x3x3Gradients( forwX, backX,
forwY, backY,
forwZ, backZ,
iv3,
rspX, rspY, rspZ );
grad[0] = rspX*( iv3[2][4] - iv3[0][4] );
grad[1] = rspY*( iv3[1][5] - iv3[1][3] );
grad[2] = rspZ*( iv3[1][7] - iv3[1][1] );
gm = AIR_CAST( coil_t, ELL_3V_LEN( grad ) );
/*
if ( coilVerbose ) {
fprintf( stderr, "forwX = %g %g %g backX = %g %g %g\n",
forwX[0], forwX[1], forwX[2],
backX[0], backX[1], backX[2] );
fprintf( stderr, "forwY = %g %g %g backY = %g %g %g\n",
forwY[0], forwY[1], forwY[2],
backY[0], backY[1], backY[2] );
fprintf( stderr, "forwZ = %g %g %g backZ = %g %g %g\n",
forwZ[0], forwZ[1], forwZ[2],
backZ[0], backZ[1], backZ[2] );
fprintf( stderr, "grad = %g %g %g --> gm = %g\n",
grad[0], grad[1], grad[2], gm );
}
*/
/* compute fluxes */
eps = 0.0000000001f;
KK = AIR_CAST( coil_t, parm[1]*parm[1] );
LL = ELL_3V_DOT( forwX, forwX );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwX[0] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( forwY, forwY );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwY[1] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( forwZ, forwZ );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwZ[2] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backX, backX );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backX[0] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backY, backY );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backY[1] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backZ, backZ );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backZ[2] *= _COIL_CONDUCT( LL, KK )*denom;
lerp = AIR_CAST( coil_t, parm[2] );
delta[0] = ( lerp*_coilLaplacian3( iv3, spacing )
+ ( 1-lerp )*gm*( rspX*( forwX[0] - backX[0] )
+ rspY*( forwY[1] - backY[1] )
+ rspZ*( forwZ[2] - backZ[2] ) ) );
delta[0] *= AIR_CAST( coil_t, parm[0] );
/*
if ( coilVerbose ) {
fprintf( stderr, "!%s: delta = %g\n", me, delta[0] );
}
*/
}
/*
** parm vector:
** 0 1 2 3 4 5 ( 6 )
** step K_perp K_tan lerp X_ring Y_ring
*/
249 void
_coilKindScalarFilterModifiedCurvatureRings( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
coil_t forwX[3], backX[3], forwY[3], backY[3], forwZ[3], backZ[3],
grad[3], gm, eps, KK, LL, denom, rspX, rspY, rspZ, lerp;
double bas0[3], bas1[3], bas2[3], len, norm[3], sk;
AIR_UNUSED( zi );
ELL_3V_SET( bas0, 0, 0, 1 );
ELL_3V_SET( bas1, xi - parm[4], yi - parm[5], 0 );
ELL_3V_NORM( bas1, bas1, len );
ELL_3V_CROSS( bas2, bas0, bas1 );
rspX = AIR_CAST( coil_t, 1.0/spacing[0] );
rspY = AIR_CAST( coil_t, 1.0/spacing[1] );
rspZ = AIR_CAST( coil_t, 1.0/spacing[2] );
_coilKindScalar3x3x3Gradients( forwX, backX,
forwY, backY,
forwZ, backZ,
iv3,
rspX, rspY, rspZ );
grad[0] = rspX*( iv3[2][4] - iv3[0][4] );
grad[1] = rspY*( iv3[1][5] - iv3[1][3] );
grad[2] = rspZ*( iv3[1][7] - iv3[1][1] );
gm = AIR_CAST( coil_t, ELL_3V_LEN( grad ) );
if ( gm ) {
double tc, rcsq;
ELL_3V_SCALE( norm, 1.0/gm, grad );
tc = ELL_3V_DOT( norm, bas2 );
rcsq = 1 - tc*tc;
sk = AIR_LERP( rcsq, parm[1], parm[2] );
} else {
sk = parm[1];
}
/* compute fluxes */
eps = 0.0000000001f;
KK = AIR_CAST( coil_t, sk*sk );
LL = ELL_3V_DOT( forwX, forwX );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwX[0] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( forwY, forwY );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwY[1] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( forwZ, forwZ );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
forwZ[2] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backX, backX );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backX[0] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backY, backY );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backY[1] *= _COIL_CONDUCT( LL, KK )*denom;
LL = ELL_3V_DOT( backZ, backZ );
denom = AIR_CAST( coil_t, 1.0/( eps + sqrt( LL ) ) );
backZ[2] *= _COIL_CONDUCT( LL, KK )*denom;
lerp = AIR_CAST( coil_t, parm[2] );
delta[0] = ( lerp*_coilLaplacian3( iv3, spacing )
+ ( 1-lerp )*gm*( rspX*( forwX[0] - backX[0] )
+ rspY*( forwY[1] - backY[1] )
+ rspZ*( forwZ[2] - backZ[2] ) ) );
delta[0] *= AIR_CAST( coil_t, parm[0] );
}
319 void
_coilKindScalarUpdate( coil_t *val, coil_t *delta ) {
val[0] += delta[0];
}
const coilKind
_coilKindScalar = {
"scalar",
1,
{NULL,
_coilKindScalarFilterTesting,
_coilKindScalarFilterHomogeneous,
_coilKindScalarFilterPeronaMalik,
_coilKindScalarFilterModifiedCurvature,
_coilKindScalarFilterModifiedCurvatureRings,
NULL,
NULL,
NULL},
_coilKindScalarUpdate
};
const coilKind *
coilKindScalar = &_coilKindScalar;
/* ------------------------------------------ */
#ifdef __cplusplus
extern "C" {
#endif
extern const coilKind _coilKind7Tensor;
#ifdef __cplusplus
}
#endif
const coilKind*
coilKindArray[COIL_KIND_TYPE_MAX+1] = {
NULL,
&_coilKindScalar,
NULL,
&_coilKind7Tensor
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "coil.h"
void
27 _coilKind7TensorTangents( coil_t traceGrad[6],
coil_t varianceGrad[6],
coil_t skewGrad[6],
coil_t rot0Grad[6],
coil_t rot1Grad[6],
coil_t rot2Grad[6],
coil_t tensor[7] ) {
AIR_UNUSED( traceGrad );
AIR_UNUSED( varianceGrad );
AIR_UNUSED( skewGrad );
AIR_UNUSED( rot0Grad );
AIR_UNUSED( rot1Grad );
AIR_UNUSED( rot2Grad );
AIR_UNUSED( tensor );
/*
coil_t a, b, c, d, e, f;
a = tensor[1];
b = tensor[2];
c = tensor[3];
d = tensor[4];
e = tensor[5];
f = tensor[6];
ELL_6V_SET( traceGrad, 1, 0, 0, 1, 0, 1 );
*/
}
void
56 _coilKind7TensorFilterTesting( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
AIR_UNUSED( iv3 );
AIR_UNUSED( spacing );
AIR_UNUSED( parm );
delta[0] = 0;
delta[1] = 0;
delta[2] = 0;
delta[3] = 0;
delta[4] = 0;
delta[5] = 0;
delta[6] = 0;
}
/*
** o ----> X
** \ [0][ 0]-[06] [1][ 0] [2][ 0]
** | \ [0][ 7]-[13] [1][ 7] [2][ 7]
** | Y [0][14]-[20] [1][14] [2][14]
** |
** | [0][21]-[27] [1][21] [2][21]
** Z [0][28]-[34] [1][28] [2][28]
** [0][35]-[41] [1][35] [2][35]
**
** [0][42]-[48] [1][42] [2][42]
** [0][49]-[55] [1][49] [2][49]
** [0][56]-[62] [1][56] [2][56]
*/
#define IND( iv3, vi, X, Y, Z ) ( ( iv3 )[( X )][( vi ) + 7*( ( Y ) + 3*( Z ) )] )
#define TENS( ten, iv3 ) \
TEN_T_SET( ten, \
IND( iv3, 0, 1, 1, 1 ), \
IND( iv3, 1, 1, 1, 1 ), IND( iv3, 2, 1, 1, 1 ), IND( iv3, 3, 1, 1, 1 ), \
IND( iv3, 4, 1, 1, 1 ), IND( iv3, 5, 1, 1, 1 ), \
IND( iv3, 6, 1, 1, 1 ) )
100 #define TENGRAD( tengrad, iv3, rspX, rspY, rspZ ) ( \
TEN_T_SET( tengrad + 0*7, \
1.0, \
rspX*( IND( iv3, 1, 2, 1, 1 ) - IND( iv3, 1, 0, 1, 1 ) )/2, \
rspX*( IND( iv3, 2, 2, 1, 1 ) - IND( iv3, 2, 0, 1, 1 ) )/2, \
rspX*( IND( iv3, 3, 2, 1, 1 ) - IND( iv3, 3, 0, 1, 1 ) )/2, \
rspX*( IND( iv3, 4, 2, 1, 1 ) - IND( iv3, 4, 0, 1, 1 ) )/2, \
rspX*( IND( iv3, 5, 2, 1, 1 ) - IND( iv3, 5, 0, 1, 1 ) )/2, \
rspX*( IND( iv3, 6, 2, 1, 1 ) - IND( iv3, 6, 0, 1, 1 ) )/2 ), \
TEN_T_SET( tengrad + 1*7, \
1.0, \
rspY*( IND( iv3, 1, 1, 2, 1 ) - IND( iv3, 1, 1, 0, 1 ) )/2, \
rspY*( IND( iv3, 2, 1, 2, 1 ) - IND( iv3, 2, 1, 0, 1 ) )/2, \
rspY*( IND( iv3, 3, 1, 2, 1 ) - IND( iv3, 3, 1, 0, 1 ) )/2, \
rspY*( IND( iv3, 4, 1, 2, 1 ) - IND( iv3, 4, 1, 0, 1 ) )/2, \
rspY*( IND( iv3, 5, 1, 2, 1 ) - IND( iv3, 5, 1, 0, 1 ) )/2, \
rspY*( IND( iv3, 6, 1, 2, 1 ) - IND( iv3, 6, 1, 0, 1 ) )/2 ), \
TEN_T_SET( tengrad + 2*7, \
1.0, \
rspZ*( IND( iv3, 1, 1, 1, 2 ) - IND( iv3, 1, 1, 1, 0 ) )/2, \
rspZ*( IND( iv3, 2, 1, 1, 2 ) - IND( iv3, 2, 1, 1, 0 ) )/2, \
rspZ*( IND( iv3, 3, 1, 1, 2 ) - IND( iv3, 3, 1, 1, 0 ) )/2, \
rspZ*( IND( iv3, 4, 1, 1, 2 ) - IND( iv3, 4, 1, 1, 0 ) )/2, \
rspZ*( IND( iv3, 5, 1, 1, 2 ) - IND( iv3, 5, 1, 1, 0 ) )/2, \
rspZ*( IND( iv3, 6, 1, 1, 2 ) - IND( iv3, 6, 1, 1, 0 ) )/2 ) )
#define LAPL( iv3, vi, rspsqX, rspsqY, rspsqZ ) \
( rspsqX*( IND( iv3, vi, 0, 1, 1 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 2, 1, 1 ) ) \
+ rspsqY*( IND( iv3, vi, 1, 0, 1 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 1, 2, 1 ) ) \
+ rspsqZ*( IND( iv3, vi, 1, 1, 0 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 1, 1, 2 ) ) )
void
_coilKind7TensorFilterHomogeneous( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
135 double parm[COIL_PARMS_NUM] ) {
coil_t rspsqX, rspsqY, rspsqZ, parm0;
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
rspsqX = AIR_CAST( coil_t, 1.0/( spacing[0]*spacing[0] ) );
rspsqY = AIR_CAST( coil_t, 1.0/( spacing[1]*spacing[1] ) );
rspsqZ = AIR_CAST( coil_t, 1.0/( spacing[2]*spacing[2] ) );
parm0 = AIR_CAST( coil_t, parm[0] );
delta[0] = 0;
delta[1] = parm0*LAPL( iv3, 1, rspsqX, rspsqY, rspsqZ );
delta[2] = parm0*LAPL( iv3, 2, rspsqX, rspsqY, rspsqZ );
delta[3] = parm0*LAPL( iv3, 3, rspsqX, rspsqY, rspsqZ );
delta[4] = parm0*LAPL( iv3, 4, rspsqX, rspsqY, rspsqZ );
delta[5] = parm0*LAPL( iv3, 5, rspsqX, rspsqY, rspsqZ );
delta[6] = parm0*LAPL( iv3, 6, rspsqX, rspsqY, rspsqZ );
}
#define HESS( hess, iv3, vi, rspX, rspY, rspZ ) \
( hess )[1] = rspX*rspX*( IND( iv3, vi, 0, 1, 1 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 2, 1, 1 ) ); \
( hess )[4] = rspY*rspY*( IND( iv3, vi, 1, 0, 1 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 1, 2, 1 ) ); \
( hess )[6] = rspZ*rspZ*( IND( iv3, vi, 1, 1, 0 ) - 2*IND( iv3, vi, 1, 1, 1 ) + IND( iv3, vi, 1, 1, 2 ) ); \
( hess )[2] = rspX*rspY*( IND( iv3, vi, 2, 2, 1 ) - IND( iv3, vi, 0, 2, 1 ) - IND( iv3, vi, 2, 0, 1 ) + IND( iv3, vi, 0, 0, 1 ) )/4.0f; \
( hess )[3] = rspX*rspZ*( IND( iv3, vi, 2, 1, 2 ) - IND( iv3, vi, 0, 1, 2 ) - IND( iv3, vi, 2, 1, 0 ) + IND( iv3, vi, 0, 1, 0 ) )/4.0f; \
( hess )[5] = rspY*rspZ*( IND( iv3, vi, 1, 2, 2 ) - IND( iv3, vi, 1, 0, 2 ) - IND( iv3, vi, 1, 2, 0 ) + IND( iv3, vi, 1, 0, 0 ) )/4.0f
#define _COIL_CONDUCT( LL, KK ) \
163 ( exp( -0.5*( LL )/( KK ) ) )
/*
#define _COIL_CONDUCT( vec, KK ) \
( 1.0/( 1.0 + ( LL )/( KK ) ) )
*/
/*
** watch out for false advertising!
*/
void
_coilKind7TensorFilterSelf( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
coil_t hess[7], rspX, rspY, rspZ, parm0;
float eval[3], evec[9], tens[7], lin;
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
rspX = AIR_CAST( coil_t, 1.0/spacing[0] );
rspY = AIR_CAST( coil_t, 1.0/spacing[1] );
rspZ = AIR_CAST( coil_t, 1.0/spacing[2] );
TENS( tens, iv3 );
tenEigensolve_f( eval, evec, tens );
lin = ( eval[0] - eval[1] )/( eval[0] - eval[2] + 0.000001f );
TEN_T3V_OUTER( tens, evec + 3*0 );
delta[0] = 0;
parm0 = AIR_CAST( coil_t, parm[0] );
HESS( hess, iv3, 1, rspX, rspY, rspZ ); delta[1] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
HESS( hess, iv3, 2, rspX, rspY, rspZ ); delta[2] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
HESS( hess, iv3, 3, rspX, rspY, rspZ ); delta[3] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
HESS( hess, iv3, 4, rspX, rspY, rspZ ); delta[4] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
HESS( hess, iv3, 5, rspX, rspY, rspZ ); delta[5] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
HESS( hess, iv3, 6, rspX, rspY, rspZ ); delta[6] = lin*parm0*tens[0]*TEN_T_DOT( hess, tens );
}
void
_coilKind7TensorFilterFinish( coil_t *delta,
int xi, int yi, int zi,
coil_t **iv3, double spacing[3],
double parm[COIL_PARMS_NUM] ) {
coil_t rspX, rspY, rspZ,
rspsqX, rspsqY, rspsqZ;
double eval[3], evec[9], tens[7], tengrad[21], grad[3], LL, KK,
cnd,
dmu1[7], dmu2[7], dskw[7], phi3[7];
AIR_UNUSED( xi );
AIR_UNUSED( yi );
AIR_UNUSED( zi );
rspX = AIR_CAST( coil_t, 1.0/spacing[0] ); rspsqX = rspX*rspX;
rspY = AIR_CAST( coil_t, 1.0/spacing[1] ); rspsqY = rspY*rspY;
rspZ = AIR_CAST( coil_t, 1.0/spacing[2] ); rspsqZ = rspZ*rspZ;
TENS( tens, iv3 );
TENGRAD( tengrad, iv3, rspX, rspY, rspZ );
tenEigensolve_d( eval, evec, tens );
tenInvariantGradientsK_d( dmu1, dmu2, dskw, tens, 0.000001 );
tenRotationTangents_d( NULL, NULL, phi3, evec );
223 /* \midhat{\nabla} \mu_1 ----------------- */
ELL_3V_SET( grad,
TEN_T_DOT( dmu1, tengrad + 0*7 ),
TEN_T_DOT( dmu1, tengrad + 1*7 ),
TEN_T_DOT( dmu1, tengrad + 2*7 ) );
LL = ELL_3V_DOT( grad, grad );
KK = parm[1]*parm[1];
cnd = _COIL_CONDUCT( LL, KK );
/* \midhat{\nabla} \mu_2 ----------------- */
ELL_3V_SET( grad,
TEN_T_DOT( dmu2, tengrad + 0*7 ),
TEN_T_DOT( dmu2, tengrad + 1*7 ),
TEN_T_DOT( dmu2, tengrad + 2*7 ) );
LL = ELL_3V_DOT( grad, grad );
KK = parm[2]*parm[2];
cnd *= _COIL_CONDUCT( LL, KK );
/* \midhat{\nabla} \skw and twist! ----------------- */
ELL_3V_SET( grad,
TEN_T_DOT( dskw, tengrad + 0*7 ),
TEN_T_DOT( dskw, tengrad + 1*7 ),
TEN_T_DOT( dskw, tengrad + 2*7 ) );
LL = ELL_3V_DOT( grad, grad );
ELL_3V_SET( grad,
TEN_T_DOT( phi3, tengrad + 0*7 ),
TEN_T_DOT( phi3, tengrad + 1*7 ),
TEN_T_DOT( phi3, tengrad + 2*7 ) );
LL += ELL_3V_DOT( grad, grad );
KK = AIR_CAST( coil_t, parm[3]*parm[3] );
cnd *= _COIL_CONDUCT( LL, KK );
delta[0]= 0.0f;
delta[1]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 1, rspsqX, rspsqY, rspsqZ ) );
delta[2]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 2, rspsqX, rspsqY, rspsqZ ) );
delta[3]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 3, rspsqX, rspsqY, rspsqZ ) );
delta[4]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 4, rspsqX, rspsqY, rspsqZ ) );
delta[5]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 5, rspsqX, rspsqY, rspsqZ ) );
delta[6]= AIR_CAST( coil_t, parm[0]*cnd*LAPL( iv3, 6, rspsqX, rspsqY, rspsqZ ) );
}
void
_coilKind7TensorUpdate( coil_t *val, coil_t *delta ) {
val[0] += delta[0]; /* WARNING: this could change confidence! */
val[1] += delta[1];
val[2] += delta[2];
val[3] += delta[3];
val[4] += delta[4];
val[5] += delta[5];
val[6] += delta[6];
}
const coilKind
_coilKind7Tensor = {
"tensor",
7,
{NULL,
_coilKind7TensorFilterTesting,
_coilKind7TensorFilterHomogeneous,
NULL,
NULL,
NULL,
NULL,
_coilKind7TensorFilterSelf,
_coilKind7TensorFilterFinish},
_coilKind7TensorUpdate
};
const coilKind *
coilKind7Tensor = &_coilKind7Tensor;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../coil.h"
char *info = ( "Test program for coil library." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int numIters, numThreads, methodType, kindType, _parmLen, pi, radius,
verbose;
Nrrd *nin, *nout;
coilContext *cctx;
double *_parm, parm[COIL_PARMS_NUM];
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "iter", "# iters", airTypeInt, 1, 1, &numIters, "5",
"number of iterations to do processing for" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1, &numThreads, "5",
"number of threads to run" );
hestOptAdd( &hopt, "k", "kind", airTypeEnum, 1, 1, &kindType, NULL,
"what kind of volume is input", NULL, coilKindType );
hestOptAdd( &hopt, "m", "method", airTypeEnum, 1, 1, &methodType, "test",
"what kind of filtering to perform", NULL, coilMethodType );
hestOptAdd( &hopt, "p", "parms", airTypeDouble, 1, -1, &_parm, NULL,
"all the parameters required for filtering method", &_parmLen );
hestOptAdd( &hopt, "r", "radius", airTypeInt, 1, 1, &radius, "1",
"radius of filtering neighborhood" );
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &( nin ), "",
"input volume to filter", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file to save filtering result into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
cctx = coilContextNew( );
airMopAdd( mop, cctx, ( airMopper )coilContextNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( _parmLen != coilMethodArray[methodType]->numParm ) {
fprintf( stderr, "%s: %s method wants %d parms, but got %d\n", me,
coilMethodArray[methodType]->name,
coilMethodArray[methodType]->numParm, _parmLen );
airMopError( mop );
return 1;
}
for ( pi=0; pi<_parmLen; pi++ ) {
parm[pi] = _parm[pi];
}
if ( coilContextAllSet( cctx, nin,
coilKindArray[kindType], coilMethodArray[methodType],
radius, numThreads, verbose,
parm )
|| coilStart( cctx )
|| coilIterate( cctx, numIters )
|| coilFinish( cctx )
|| coilOutputGet( nout, cctx ) ) {
airMopAdd( mop, err = biffGetDone( COIL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with coil:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dye.h"
/*
** values in these matrices were copied from:
** from http://www.cs.rit.edu/~ncs/color/t_convert.html
**
** from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
** these look to be specific to sRGB with D65 white
**
** NB: for a long time this matrices were wrong, because they
** never got transposed at the same time that matrices in Teem
** were switched from column-major to row-major ordering
*/
float dyeRGBtoXYZMatx[9] = {
0.412453f, 0.357580f, 0.180423f,
0.212671f, 0.715160f, 0.072169f,
0.019334f, 0.119193f, 0.950227f};
float dyeXYZtoRGBMatx[9] = {
3.240479f, -1.537150f, -0.498535f,
-0.969256f, 1.875992f, 0.041556f,
0.055648f, -0.204043f, 1.057311f};
/* summing the rows of the RGBtoXYZ matrix to get X_n, Y_n, Z_n */
float dyeWhiteXYZ_n[3] = {0.950456f, 1.0f, 1.088754f};
/* so x = X/( X+Y+Z ) = 0.312731268 and
and y = Y/( X+Y+Z ) = 0..32903287;
then http://en.wikipedia.org/wiki/Illuminant_D65
confirms that this is D65 white */
/* the u'_n and v'_n which appear in the XYZ -> LUV conversion;
u'_n = 4X_n / ( X_n + 15Y_n + 3Z_n )
v'_n = 9Y_n / ( X_n + 15Y_n + 3Z_n )
*/
float dyeWhiteuvp_n[2] = {0.197839f, 0.468342f};
void
61 dyeRGBtoHSV( float *H, float *S, float *V,
float R, float G, float B ) {
float max, min, delta;
max = AIR_MAX( R, G );
max = AIR_MAX( B, max );
min = AIR_MIN( R, G );
min = AIR_MIN( B, min );
*V = max;
if ( max != 0 )
*S = ( max - min )/max;
else
*S = 0;
if ( 0 == *S ) {
*H = 0;
return;
}
/* else there is hue */
delta = max - min;
if ( R == max )
*H = ( G - B )/delta;
else if ( G == max )
*H = 2 + ( B - R )/delta;
else
*H = 4 + ( R - G )/delta;
*H /= 6;
if ( *H < 0 )
*H += 1;
return;
}
/*
******** dyeHSVtoRGB( )
**
** conversion from HSV single hexcone to RGB
**
** input and ouput are all floats in interval [0, 1]
** DOES NO RANGE CHECKING WHATSOEVER
**
** From Foley + vanDam, 2nd Ed., p. 593
*/
void
104 dyeHSVtoRGB( float *R, float *G, float *B,
float H, float S, float V ) {
float min, fract, vsf, mid1, mid2;
int sextant;
if ( 0 == S ) {
*R = *G = *B = V;
return;
}
/* else there is hue */
if ( 1 == H )
H = 0;
H *= 6;
sextant = ( int ) floor( H );
fract = H - sextant;
vsf = V*S*fract;
min = V*( 1 - S );
mid1 = min + vsf;
mid2 = V - vsf;
switch ( sextant ) {
case 0: { *R = V; *G = mid1; *B = min; break; }
case 1: { *R = mid2; *G = V; *B = min; break; }
case 2: { *R = min; *G = V; *B = mid1; break; }
case 3: { *R = min; *G = mid2; *B = V; break; }
case 4: { *R = mid1; *G = min; *B = V; break; }
case 5: { *R = V; *G = min; *B = mid2; break; }
}
}
/*
******** dyeRGBtoHSL( )
**
** converts from RGB to HSL double hexcone
** L: "lightness" = ( max( R, G, B )+min( R, G, B ) )/2
** note that saturation ( S ) is different than the saturation in HSV
** hue ( H ) is the same in HSL and HSV
**
** r, g, b input and *h, *s, *l output are all floats in [0, 1]
** DOES NO RANGE CHECKING WHATSOEVER
**
** From Foley + vanDam, 2nd Ed., p. 595
*/
void
147 dyeRGBtoHSL( float *H, float *S, float *L,
float R, float G, float B ) {
float min, max, lev, delta;
max = AIR_MAX( R, G );
max = AIR_MAX( max, B );
min = AIR_MIN( R, G );
min = AIR_MIN( min, B );
*L = lev = ( max + min )/2.0f;
if ( max == min ) {
*S = 0;
*H = 0; /* actually, undefined */
return;
}
/* else there is hue */
delta = max - min;
if ( lev <= 0.5 )
*S = delta/( max + min );
else
*S = delta/( 2-( max + min ) );
if ( R == max )
*H = ( G - B )/delta;
else if ( G == max )
*H = 2 + ( B - R )/delta;
else
*H = 4 + ( R - G )/delta;
*H /= 6;
if ( *H < 0 )
*H += 1;
return;
}
/*
******** dyeHSLtoRGB( )
**
** converts from HSL double hexcone back to RGB
**
** input and ouput are all floats in interval [0, 1]
** DOES NO RANGE CHECKING WHATSOEVER
**
** From Foley + vanDam, 2nd Ed., p. 596
*/
void
191 dyeHSLtoRGB( float *R, float *G, float *B,
float H, float S, float L ) {
float m1, m2, fract, mid1, mid2;
int sextant;
if ( S == 0 ) {
*R = *G = *B = L;
return;
}
/* else there is hue */
if ( L <= 0.5 )
m2 = L*( 1+S ); /* the book says L*( L+S ) which is ?? wrong ?? */
else
m2 = L + S - L*S;
m1 = 2*L - m2;
if ( 1 == H )
H = 0;
H *= 6;
sextant = ( int ) floor( H );
fract = H - sextant;
mid1 = m1 + fract*( m2 - m1 );
mid2 = m2 + fract*( m1 - m2 );
/* compared to HSVtoRGB: V -> m2, min -> m1 */
switch ( sextant ) {
case 0: { *R = m2; *G = mid1; *B = m1; break; }
case 1: { *R = mid2; *G = m2; *B = m1; break; }
case 2: { *R = m1; *G = m2; *B = mid1; break; }
case 3: { *R = m1; *G = mid2; *B = m2; break; }
case 4: { *R = mid1; *G = m1; *B = m2; break; }
case 5: { *R = m2; *G = m1; *B = mid2; break; }
}
}
void
225 dyeRGBtoXYZ( float *X, float *Y, float *Z,
float R, float G, float B ) {
float in[3], out[3];
ELL_3V_SET( in, R, G, B );
ELL_3MV_MUL( out, dyeRGBtoXYZMatx, in );
ELL_3V_GET( *X, *Y, *Z, out );
return;
}
void
236 dyeXYZtoRGB( float *R, float *G, float *B,
float X, float Y, float Z ) {
float in[3], out[3];
ELL_3V_SET( in, X, Y, Z );
ELL_3MV_MUL( out, dyeXYZtoRGBMatx, in );
ELL_3V_GET( *R, *G, *B, out );
return;
}
float
247 dyeLcbrt( float t ) {
return AIR_CAST( float, ( t > 0.008856
? airCbrt( t )
: 7.787*t + 16.0/116.0 ) );
}
float
254 dyeLcubed( float t ) {
return( t > 0.206893 ? t*t*t : ( t - 16.0f/116.0f )/7.787f );
}
void
259 dyeXYZtoLAB( float *L, float *A, float *B,
float X, float Y, float Z ) {
float Xnorm, Ynorm, Znorm;
Xnorm = X/dyeWhiteXYZ_n[0];
Ynorm = Y/dyeWhiteXYZ_n[1];
Znorm = Z/dyeWhiteXYZ_n[2];
*L = 116.0f*dyeLcbrt( Ynorm ) - 16.0f;
*A = 500.0f*( dyeLcbrt( Xnorm ) - dyeLcbrt( Ynorm ) );
*B = 200.0f*( dyeLcbrt( Ynorm ) - dyeLcbrt( Znorm ) );
}
void
272 dyeXYZtoLUV( float *L, float *U, float *V,
float X, float Y, float Z ) {
float Ynorm, up, vp;
Ynorm = Y/dyeWhiteXYZ_n[1];
*L = 116.0f*dyeLcbrt( Ynorm ) - 16.0f;
up = 4.0f*X/( X + 15.0f*Y + 3.0f*Z );
vp = 9.0f*Y/( X + 15.0f*Y + 3.0f*Z );
*U = 13.0f*( *L )*( up - dyeWhiteuvp_n[0] );
*V = 13.0f*( *L )*( vp - dyeWhiteuvp_n[1] );
}
void
285 dyeLABtoXYZ( float *X, float *Y, float *Z,
float L, float A, float B ) {
float YnormCbrt;
YnormCbrt = ( 16 + L )/116;
*X = dyeWhiteXYZ_n[0]*dyeLcubed( A/500 + YnormCbrt );
*Y = dyeWhiteXYZ_n[1]*dyeLcubed( YnormCbrt );
*Z = dyeWhiteXYZ_n[2]*dyeLcubed( YnormCbrt - B/200 );
return;
}
void
297 dyeLUVtoXYZ( float *X, float *Y, float *Z,
float L, float U, float V ) {
float up, vp, YnormCbrt;
YnormCbrt = ( 16 + L )/116;
up = U/( 13*L ) + dyeWhiteuvp_n[0];
vp = V/( 13*L ) + dyeWhiteuvp_n[1];
*Y = dyeWhiteXYZ_n[1]*dyeLcubed( YnormCbrt );
*X = -9*( *Y )*up/( ( up - 4 )*vp - up*vp );
*Z = ( 9*( *Y ) - 15*vp*( *Y ) - vp*( *X ) )/( 3*vp );
return;
}
void
311 dyeLABtoLCH( float *Lp, float *C, float *H,
float L, float A, float B ) {
*Lp = L;
*C = sqrt( A*A + B*B );
*H = atan2( B, A )/( 2*AIR_PI ) + 0.5;
}
void
320 dyeLCHtoLAB( float *Lp, float *A, float *B,
float L, float C, float H ) {
float phi = ( H*2 - 1 )*AIR_PI;
*Lp = L;
*A = C*cos( phi );
*B = C*sin( phi );
}
void
329 dyeXYZtoLCH( float *_L, float *C, float *H,
float X, float Y, float Z ) {
float L, A, B;
dyeXYZtoLAB( &L, &A, &B, X, Y, Z );
dyeLABtoLCH( _L, C, H, L, A, B );
}
void
336 dyeLCHtoXYZ( float *X, float *Y, float *Z,
float _L, float C, float H ) {
float L, A, B;
dyeLCHtoLAB( &L, &A, &B, _L, C, H );
dyeLABtoXYZ( X, Y, Z, L, A, B );
}
void
344 dyeIdentity( float *A, float *B, float *C,
float a, float b, float c ) {
*A = a;
*B = b;
*C = c;
return;
}
dyeConverter dyeSimpleConvert[DYE_MAX_SPACE+1][DYE_MAX_SPACE+1] =
{
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{NULL, dyeIdentity, NULL, dyeHSVtoRGB, NULL, NULL, NULL, NULL},
{NULL, NULL, dyeIdentity, dyeHSLtoRGB, NULL, NULL, NULL, NULL},
{NULL, dyeRGBtoHSV, dyeRGBtoHSL, dyeIdentity, dyeRGBtoXYZ, NULL, NULL, NULL},
{NULL, NULL, NULL, dyeXYZtoRGB, dyeIdentity, dyeXYZtoLAB, dyeXYZtoLUV, dyeXYZtoLCH},
{NULL, NULL, NULL, NULL, dyeLABtoXYZ, dyeIdentity, NULL, dyeLABtoLCH},
{NULL, NULL, NULL, NULL, dyeLUVtoXYZ, NULL, dyeIdentity, NULL},
{NULL, NULL, NULL, NULL, dyeLCHtoXYZ, dyeLCHtoLAB, NULL, dyeIdentity},
};
/*
******** dyeConvert( )
**
** master color conversion function. Can convert between any two
** types by recursive calls and calls to the simple converters above.
*/
int
371 dyeConvert( dyeColor *col, int outSpace ) {
static const char me[] = "dyeConvert";
float i0, i1, i2, o0, o1, o2;
dyeConverter simple;
int inSpace, E;
E = 0;
if ( !col ) {
biffAddf( DYE, "%s: got NULL pointer", me );
return 1;
}
inSpace = dyeColorGet( &i0, &i1, &i2, col );
if ( !DYE_VALID_SPACE( inSpace ) ) {
biffAddf( DYE, "%s: invalid input space #%d\n", me, inSpace );
return 1;
}
if ( !DYE_VALID_SPACE( outSpace ) ) {
biffAddf( DYE, "%s: invalid output space #%d\n", me, outSpace );
return 1;
}
if ( ( simple = dyeSimpleConvert[inSpace][outSpace] ) ) {
( *simple )( &o0, &o1, &o2, i0, i1, i2 );
dyeColorSet( col, outSpace, o0, o1, o2 );
}
else {
/* we have some work to do . . . */
if ( inSpace < dyeSpaceRGB && outSpace < dyeSpaceRGB ) {
/* its an easy HSV <-- RGB --> HSL conversion */
if ( !E ) E |= dyeConvert( col, dyeSpaceRGB );
if ( !E ) E |= dyeConvert( col, outSpace );
}
else if ( inSpace > dyeSpaceXYZ && outSpace > dyeSpaceXYZ ) {
/* its an easy conversion among XYZ, LAB, LUV, LCH */
if ( !E ) E |= dyeConvert( col, dyeSpaceXYZ );
if ( !E ) E |= dyeConvert( col, outSpace );
}
else {
/* the start and end spaces are at different stages */
if ( inSpace < outSpace ) {
/* we are going towards higher stages */
if ( inSpace < dyeSpaceRGB ) {
if ( !E ) E |= dyeConvert( col, dyeSpaceRGB );
if ( !E ) E |= dyeConvert( col, outSpace );
}
else if ( inSpace == dyeSpaceRGB ) {
if ( !E ) E |= dyeConvert( col, dyeSpaceXYZ );
if ( !E ) E |= dyeConvert( col, outSpace );
}
else {
biffAddf( DYE, "%s: CONFUSED! can't go %s -> %s\n",
me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace] );
E = 1;
}
}
else {
/* we are going towards lower stages */
if ( outSpace < dyeSpaceRGB ) {
if ( !E ) E |= dyeConvert( col, dyeSpaceRGB );
if ( !E ) E |= dyeConvert( col, outSpace );
}
else if ( outSpace == dyeSpaceRGB ) {
if ( !E ) E |= dyeConvert( col, dyeSpaceXYZ );
if ( !E ) E |= dyeConvert( col, dyeSpaceRGB );
}
else {
biffAddf( DYE, "%s: CONFUSED! can't go %s -> %s\n",
me, dyeSpaceToStr[inSpace], dyeSpaceToStr[outSpace] );
E = 1;
}
}
}
}
return E;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "dye.h"
const int
dyePresent = 42;
const char *
dyeBiffKey = "dye";
const char *
dyeSpaceToStr[DYE_MAX_SPACE+1] = {
"( unknown )",
"HSV",
"HSL",
"RGB",
"XYZ",
"LAB",
"LUV",
"LCH"
};
static const char *
_dyeSpaceDesc[DYE_MAX_SPACE+1] = {
"unknown colorspace",
"single hexcone",
"double hexcone",
"traditional device primaries",
"CIE 1931 XYZ space",
"CIE L*a*b*",
"CIE 1976 L*u*v*",
"polar coord( L*a*b* )"
};
static const airEnum
_dyeSpace = {
"colorspace",
DYE_MAX_SPACE,
dyeSpaceToStr, NULL,
_dyeSpaceDesc,
NULL, NULL,
AIR_FALSE
};
66 const airEnum *const
dyeSpace = &_dyeSpace;
/*
** this function predates the dyeSpace airEnum, so we'll keep it.
*/
int
73 dyeStrToSpace( char *_str ) {
int spc;
char *str;
spc = dyeSpaceUnknown;
if ( ( str = airStrdup( _str ) ) ) {
airToUpper( str );
for ( spc=0; spc<dyeSpaceLast; spc++ ) {
if ( !strcmp( str, dyeSpaceToStr[spc] ) ) {
break;
}
}
if ( dyeSpaceLast == spc ) {
spc = dyeSpaceUnknown;
}
str = ( char * )airFree( str );
}
return spc;
}
dyeColor *
94 dyeColorInit( dyeColor *col ) {
if ( col ) {
ELL_3V_SET( col->val[0], AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( col->val[1], AIR_NAN, AIR_NAN, AIR_NAN );
col->xWhite = col->yWhite = AIR_NAN;
col->spc[0] = dyeSpaceUnknown;
col->spc[1] = dyeSpaceUnknown;
col->ii = 0;
}
return col;
}
dyeColor *
108 dyeColorSet( dyeColor *col, int space, float v0, float v1, float v2 ) {
if ( col && DYE_VALID_SPACE( space ) ) {
col->ii = AIR_CLAMP( 0, col->ii, 1 );
/* We switch to the other one if the current one seems to be used,
but we don't switch if new and current colorspaces are the same.
If the other one is being used too, oh well. */
if ( dyeSpaceUnknown != col->spc[col->ii] &&
AIR_EXISTS( col->val[col->ii][0] ) &&
col->spc[col->ii] != space ) {
col->ii = 1 - col->ii;
}
ELL_3V_SET( col->val[col->ii], v0, v1, v2 );
col->spc[col->ii] = space;
}
return col;
}
int
129 dyeColorGet( float *v0P, float *v1P, float *v2P, dyeColor *col ) {
int spc;
spc = dyeSpaceUnknown;
if ( v0P && v1P && v2P && col ) {
col->ii = AIR_CLAMP( 0, col->ii, 1 );
spc = col->spc[col->ii];
ELL_3V_GET( *v0P, *v1P, *v2P, col->val[col->ii] );
}
return spc;
}
int
142 dyeColorGetAs( float *v0P, float *v1P, float *v2P,
dyeColor *colIn, int space ) {
dyeColor _col, *col;
col = &_col;
dyeColorCopy( col, colIn );
/* hope for no error */
dyeConvert( col, space );
return dyeColorGet( v0P, v1P, v2P, col );
}
dyeColor *
154 dyeColorNew( ) {
dyeColor *col;
col = ( dyeColor * )calloc( 1, sizeof( dyeColor ) );
col = dyeColorInit( col );
return col;
}
dyeColor *
163 dyeColorCopy( dyeColor *c1, dyeColor *c0 ) {
if ( c1 && c0 ) {
memcpy( c1, c0, sizeof( dyeColor ) );
}
return c1;
}
dyeColor *
172 dyeColorNix( dyeColor *col ) {
if ( col ) {
col = ( dyeColor * )airFree( col );
}
return NULL;
}
int
181 dyeColorParse( dyeColor *col, char *_str ) {
static const char me[]="dyeColorParse";
char *str;
char *colon, *valS;
float v0, v1, v2;
int spc;
if ( !( col && _str ) ) {
biffAddf( DYE, "%s: got NULL pointer", me );
return 1;
}
if ( !( str = airStrdup( _str ) ) ) {
biffAddf( DYE, "%s: couldn't strdup!", me );
return 1;
}
if ( !( colon = strchr( str, ':' ) ) ) {
biffAddf( DYE, "%s: given string \"%s\" didn't contain colon", me, str );
return 1;
}
*colon = '\0';
valS = colon+1;
if ( 3 != sscanf( valS, "%g, %g, %g", &v0, &v1, &v2 ) ) {
biffAddf( DYE, "%s: couldn't parse three floats from \"%s\"", me, valS );
return 1;
}
spc = dyeStrToSpace( str );
if ( dyeSpaceUnknown == spc ) {
biffAddf( DYE, "%s: couldn't parse colorspace from \"%s\"", me, str );
return 1;
}
str = ( char * )airFree( str );
dyeColorSet( col, spc, v0, v1, v2 );
return 0;
}
char *
218 dyeColorSprintf( char *str, dyeColor *col ) {
if ( str && col ) {
col->ii = AIR_CLAMP( 0, col->ii, 1 );
sprintf( str, "%s:%g, %g, %g", dyeSpaceToStr[col->spc[col->ii]],
col->val[col->ii][0],
col->val[col->ii][1],
col->val[col->ii][2] );
}
return str;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../dye.h"
char *me;
void
29 usage( ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <res> <scale> <out.txt> \n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
char *resS, *scS, *outS;
int i, res;
FILE *out;
float hue, R, G, B, sc;
me = argv[0];
if ( 4 != argc )
usage( );
resS = argv[1];
scS = argv[2];
outS = argv[3];
if ( 1 != sscanf( resS, "%d", &res ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as int\n", me, resS );
exit( 1 );
}
if ( 1 != sscanf( scS, "%f", &sc ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as float\n", me, scS );
exit( 1 );
}
if ( !( out = fopen( outS, "wa" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing\n", me, outS );
exit( 1 );
}
fprintf( out, "# space: RGB\n" );
for ( i=0; i<=res-1; i++ ) {
hue = AIR_AFFINE( 0, i, res, 0.0, 1.0 );
dyeHSVtoRGB( &R, &G, &B, hue, 1, 1 );
fprintf( out, "%g %g %g %g\n", hue, sc*R, sc*G, sc*B );
}
fclose( out );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../dye.h"
char *me;
void
29 usage( ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <colIn> <spaceOut> \n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
char *inS, *spcS, buff[512];
dyeColor *col;
int spc;
me = argv[0];
if ( 3 != argc )
usage( );
inS = argv[1];
spcS = argv[2];
if ( dyeColorParse( col = dyeColorNew( ), inS ) ) {
fprintf( stderr, "%s: trouble parsing \"%s\":\n%s", me, inS, biffGet( DYE ) );
exit( 1 );
}
spc = dyeStrToSpace( spcS );
if ( dyeSpaceUnknown == spc ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as colorspace\n", me, spcS );
exit( 1 );
}
if ( dyeConvert( col, spc ) ) {
fprintf( stderr, "%s: trouble converting to %s:\n%s",
me, spcS, biffGet( DYE ) );
exit( 1 );
}
printf( "%s\n", dyeColorSprintf( buff, col ) );
col = dyeColorNix( col );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../dye.h"
char *me;
void
29 usage( ) {
/* 0 1 2 3 4 ( 5 ) */
fprintf( stderr, "usage: %s <spaceIn> <imgIn> <spaceOut> <imgOut>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
char *inFN, *outFN, *inSpcS, *otSpcS, *err;
int inSpc, otSpc, hack3d;
me = argv[0];
if ( 5 != argc )
usage( );
inSpcS = argv[1];
inFN = argv[2];
otSpcS = argv[3];
outFN = argv[4];
inSpc = dyeStrToSpace( inSpcS );
if ( dyeSpaceUnknown == inSpc ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as colorspace\n", me, inSpcS );
exit( 1 );
}
otSpc = dyeStrToSpace( otSpcS );
if ( dyeSpaceUnknown == otSpc ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as colorspace\n", me, otSpcS );
exit( 1 );
}
airArray *mop;
Nrrd *nin, *nout;
float *id, *od;
mop = airMopNew( );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nin, inFN, NULL )
|| nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop );
exit( 1 );
}
if ( nrrdTypeFloat != nin->type ) {
fprintf( stderr, "%s: sorry, require type %s ( not %s )\n", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nin->type ) );
airMopError( mop );
exit( 1 );
}
if ( 2 == nin->dim && 3 == nin->axis[0].size ) {
/* as a big hack, promote to 3D */
nin->dim = 3;
nin->axis[2].size = 1;
hack3d = AIR_TRUE;
} else {
hack3d = AIR_FALSE;
}
if ( !( 3 == nin->dim && 3 == nin->axis[0].size ) ) {
fprintf( stderr, "%s: sorry, need 3D 3-by-X-by-Y array ( not %u-D %u-by )\n", me,
nin->dim, AIR_CAST( unsigned int, nin->axis[0].size ) );
airMopError( mop );
exit( 1 );
}
id = AIR_CAST( float *, nin->data );
od = AIR_CAST( float *, nout->data );
unsigned int ii, nn;
dyeColor *col;
col = dyeColorNew( );
airMopAdd( mop, col, ( airMopper )dyeColorNix, airMopAlways );
nn = AIR_CAST( unsigned int, nin->axis[1].size * nin->axis[2].size );
for ( ii=0; ii<nn; ii++ ) {
dyeColorSet( col, inSpc, id[0 + 3*ii], id[1 + 3*ii], id[2 + 3*ii] );
dyeConvert( col, otSpc );
dyeColorGet( od + 0 + 3*ii, od + 1 + 3*ii, od + 2 + 3*ii, col );
}
if ( hack3d ) {
nout->dim = 2;
}
if ( nrrdSave( outFN, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop );
exit( 1 );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../dye.h"
/*
** hist[0]: hue vs sat
** hist[1]: hue vs val
*/
void
31 imageProc( Nrrd *nhproj[3], Nrrd *nhist[2], unsigned int sH,
float *rgb, unsigned int size0, unsigned int sXY,
unsigned int overSampleNum, float overSampleScale ) {
unsigned int xyi, hi, si, vi, oi;
float rr, gg, bb, hh, ss, vv, *hist[2];
double rndA, rndB;
nrrdSetZero( nhist[0] );
nrrdSetZero( nhist[1] );
hist[0] = AIR_CAST( float *, nhist[0]->data );
hist[1] = AIR_CAST( float *, nhist[1]->data );
for ( xyi=0; xyi<sXY; xyi++ ) {
rr = AIR_CLAMP( 0, rgb[0], 255 );
gg = AIR_CLAMP( 0, rgb[1], 255 );
bb = AIR_CLAMP( 0, rgb[2], 255 );
rr = AIR_AFFINE( -1, rr, 256, 0, 1 );
gg = AIR_AFFINE( -1, gg, 256, 0, 1 );
bb = AIR_AFFINE( -1, bb, 256, 0, 1 );
dyeRGBtoHSV( &hh, &ss, &vv, rr, gg, bb );
si = airIndexClamp( 0, ss, 1, sH );
vi = airIndexClamp( 0, vv, 1, sH );
#define UPDATE_HIST( rnd ) \
hi = airIndexClamp( 0, hh + overSampleScale*( 1-ss )*( rnd ), 1, sH ); \
hist[0][hi + sH*si] += 1.0/overSampleNum; \
hist[1][hi + sH*vi] += 1.0/overSampleNum
if ( overSampleNum % 2 == 1 ) {
airNormalRand( &rndA, NULL );
UPDATE_HIST( rndA );
overSampleNum -= 1;
}
for ( oi=0; oi<overSampleNum; oi+=2 ) {
airNormalRand( &rndA, &rndB );
UPDATE_HIST( rndA );
UPDATE_HIST( rndB );
}
rgb += size0;
}
nrrdProject( nhproj[0], nhist[0], 1, nrrdMeasureHistoMean, nrrdTypeFloat );
nrrdProject( nhproj[1], nhist[1], 1, nrrdMeasureHistoMean, nrrdTypeFloat );
nrrdProject( nhproj[2], nhist[1], 1, nrrdMeasureSum, nrrdTypeFloat );
}
75 const char *mchistInfo = ( "Makes a color histogram of frames. " );
int
main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char **ninStr, *err, *outS, doneStr[13];
Nrrd *nin0, *nin, *nrgb, *nout, *nhist[2], *npreout, *nhproj[3];
float *rgb;
float *out, *preout, *hist[2], maxSum,
upSample, overSampleScale;
unsigned int size0, sX, sY, sH, ninLen, ti, overSampleNum;
NrrdResampleContext *rsmc;
NrrdKernelSpec *ksp;
me = argv[0];
mop = airMopNew( );
hopt = NULL;
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->respFileEnable = AIR_TRUE;
hestOptAdd( &hopt, "i", "images", airTypeString, 1, -1, &ninStr, NULL,
"input image sequence", &ninLen, NULL, NULL );
hestOptAdd( &hopt, "sh", "histo size", airTypeUInt, 1, 1, &sH, "500",
"histogram size" );
hestOptAdd( &hopt, "k", "kern", airTypeOther, 1, 1, &ksp,
"tent", "kernel for upsampling images",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "us", "upsampling", airTypeFloat, 1, 1, &upSample,
"1", "amount of upsampling of image" );
hestOptAdd( &hopt, "osn", "# oversmp", airTypeUInt, 1, 1, &overSampleNum,
"1", "number of sample per ( upsampled ) pixel" );
hestOptAdd( &hopt, "osc", "scaling", airTypeFloat, 1, 1, &overSampleScale,
"1", "scaling with oversampling" );
hestOptAdd( &hopt, "ms", "max sum", airTypeFloat, 1, 1, &maxSum,
"10", "per-hue histogram summation is non-linearly and "
"asymptotically clamped to this maximum" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output filename", NULL );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, mchistInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( 0 == overSampleNum ) {
fprintf( stderr, "%s: overSampleNum must be > 0\n", me );
airMopError( mop );
return 1;
}
nin0 = nrrdNew( );
airMopAdd( mop, nin0, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nin0, ninStr[0], NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't load first image:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( !( ( 3 == nin0->axis[0].size || 4 == nin0->axis[0].size )
&& 3 == nin0->dim
&& nrrdTypeUChar == nin0->type ) ) {
fprintf( stderr, "%s: 1st image not 3D ( 3-or-4 )-by-X-by-Y %s array "
"( got %u-D %s array )\n", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
nin0->dim,
airEnumStr( nrrdType, nin0->type ) );
airMopError( mop );
return 1;
}
rsmc = nrrdResampleContextNew( );
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
size0 = AIR_CAST( unsigned int, nin0->axis[0].size );
sX = AIR_CAST( unsigned int, upSample*nin0->axis[1].size );
sY = AIR_CAST( unsigned int, upSample*nin0->axis[2].size );
nrgb = nrrdNew( );
airMopAdd( mop, nrgb, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdResampleDefaultCenterSet( rsmc, nrrdCenterCell )
|| nrrdResampleInputSet( rsmc, nin0 )
|| nrrdResampleKernelSet( rsmc, 1, ksp->kernel, ksp->parm )
|| nrrdResampleKernelSet( rsmc, 2, ksp->kernel, ksp->parm )
|| nrrdResampleSamplesSet( rsmc, 1, sX )
|| nrrdResampleSamplesSet( rsmc, 2, sY )
|| nrrdResampleRangeFullSet( rsmc, 1 )
|| nrrdResampleRangeFullSet( rsmc, 2 )
|| nrrdResampleTypeOutSet( rsmc, nrrdTypeFloat )
|| nrrdResampleRenormalizeSet( rsmc, AIR_TRUE )
|| nrrdResampleExecute( rsmc, nrgb ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error resampling slice:\n%s", me, err );
airMopError( mop );
return 1;
}
nhist[0] = nrrdNew( );
airMopAdd( mop, nhist[0], ( airMopper )nrrdNuke, airMopAlways );
nhist[1] = nrrdNew( );
airMopAdd( mop, nhist[1], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nhist[0], nrrdTypeFloat, 2,
AIR_CAST( size_t, sH ),
AIR_CAST( size_t, sH ) )
|| nrrdMaybeAlloc_va( nhist[1], nrrdTypeFloat, 2,
AIR_CAST( size_t, sH ),
AIR_CAST( size_t, sH ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error allocating histos:\n%s", me, err );
airMopError( mop );
return 1;
}
nhist[0]->axis[0].min = nhist[0]->axis[1].min = 0.0;
nhist[0]->axis[0].max = nhist[0]->axis[1].max = 1.0;
nhist[1]->axis[0].min = nhist[1]->axis[1].min = 0.0;
nhist[1]->axis[0].max = nhist[1]->axis[1].max = 1.0;
nhproj[0] = nrrdNew( );
airMopAdd( mop, nhproj[0], ( airMopper )nrrdNix, airMopAlways );
nhproj[1] = nrrdNew( );
airMopAdd( mop, nhproj[1], ( airMopper )nrrdNix, airMopAlways );
nhproj[2] = nrrdNew( );
airMopAdd( mop, nhproj[2], ( airMopper )nrrdNix, airMopAlways );
printf( "working ... " );
hist[0] = AIR_CAST( float *, nhist[0]->data );
hist[1] = AIR_CAST( float *, nhist[1]->data );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
npreout = nrrdNew( );
airMopAdd( mop, npreout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( npreout, nrrdTypeFloat, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sH ),
AIR_CAST( size_t, ninLen ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error allocating pre-output:\n%s", me, err );
airMopError( mop );
return 1;
}
preout = AIR_CAST( float *, npreout->data );
for ( ti=0; ti<ninLen; ti++ ) {
printf( "%s", airDoneStr( 0, ti, ninLen, doneStr ) ); fflush( stdout );
if ( nrrdLoad( nin, ninStr[ti], NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't load image[%u]:\n%s", me, ti, err );
airMopError( mop );
return 1;
}
if ( !nrrdSameSize( nin0, nin, AIR_TRUE ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: nin[%u] not like nin[0]:\n%s", me, ti, err );
airMopError( mop );
return 1;
}
if ( nrrdResampleInputSet( rsmc, nin )
|| nrrdResampleExecute( rsmc, nrgb ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble resampling nin[%u]:\n%s", me, ti, err );
airMopError( mop );
return 1;
}
if ( nrrdWrap_va( nhproj[0], preout + 0*sH, nrrdTypeFloat, 1, AIR_CAST( size_t, sH ) ) ||
nrrdWrap_va( nhproj[1], preout + 1*sH, nrrdTypeFloat, 1, AIR_CAST( size_t, sH ) ) ||
nrrdWrap_va( nhproj[2], preout + 2*sH, nrrdTypeFloat, 1, AIR_CAST( size_t, sH ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble wrapping output[%u]:\n%s", me, ti, err );
airMopError( mop );
return 1;
}
rgb = AIR_CAST( float *, nrgb->data );
imageProc( nhproj, nhist, sH,
rgb, size0, sX*sY,
overSampleNum, overSampleScale );
preout += 3*sH;
}
printf( "%s\n", airDoneStr( 0, ti, ninLen, doneStr ) ); fflush( stdout );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sH ),
AIR_CAST( size_t, ninLen ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error allocating output:\n%s", me, err );
airMopError( mop );
return 1;
}
out = AIR_CAST( float *, nout->data );
preout = AIR_CAST( float *, npreout->data );
for ( ti=0; ti<ninLen; ti++ ) {
unsigned int hi;
float hh, vv, ss, scl;
for ( hi=0; hi<sH; hi++ ) {
hh = AIR_AFFINE( 0, hi, sH, 0, 1 );
if ( !preout[hi + 2*sH] ) {
ELL_3V_SET( out + 3*hi, 0, 0, 0 );
} else {
ss = preout[hi + 2*sH];
scl = ss/( maxSum + ss );
vv = scl*preout[hi + 1*sH];
dyeHSVtoRGB( out + 0 + 3*hi, out + 1 + 3*hi, out + 2 + 3*hi,
hh, preout[hi + 0*sH], vv );
}
}
out += 3*sH;
preout += 3*sH;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error saving output:\n%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
typedef void ( *_echoBoundsGet_t )( echoPos_t lo[3], echoPos_t hi[3],
echoObject *obj );
extern _echoBoundsGet_t _echoBoundsGet[ECHO_TYPE_NUM];
#define BNDS_TMPL( TYPE, BODY ) \
void \
34 _echo##TYPE##_bounds( echoPos_t lo[3], echoPos_t hi[3], echo##TYPE *obj ) { \
int dummy=0; \
\
do { BODY dummy=dummy;} while ( 0 ); \
lo[0] -= ECHO_EPSILON; \
lo[1] -= ECHO_EPSILON; \
lo[2] -= ECHO_EPSILON; \
hi[0] += ECHO_EPSILON; \
hi[1] += ECHO_EPSILON; \
hi[2] += ECHO_EPSILON; \
return; \
45 }
46
BNDS_TMPL( Sphere,
lo[0] = obj->pos[0] - obj->rad;
lo[1] = obj->pos[1] - obj->rad;
lo[2] = obj->pos[2] - obj->rad;
hi[0] = obj->pos[0] + obj->rad;
hi[1] = obj->pos[1] + obj->rad;
hi[2] = obj->pos[2] + obj->rad;
)
BNDS_TMPL( Cylinder,
AIR_UNUSED( obj );
ELL_3V_SET( lo, -1, -1, -1 );
ELL_3V_SET( hi, 1, 1, 1 );
)
BNDS_TMPL( Superquad,
AIR_UNUSED( obj );
ELL_3V_SET( lo, -1, -1, -1 );
ELL_3V_SET( hi, 1, 1, 1 );
)
BNDS_TMPL( Cube,
AIR_UNUSED( obj );
ELL_3V_SET( lo, -1, -1, -1 );
ELL_3V_SET( hi, 1, 1, 1 );
)
BNDS_TMPL( Triangle,
ELL_3V_COPY( lo, obj->vert[0] );
ELL_3V_MIN( lo, lo, obj->vert[1] );
ELL_3V_MIN( lo, lo, obj->vert[2] );
ELL_3V_COPY( hi, obj->vert[0] );
ELL_3V_MAX( hi, hi, obj->vert[1] );
ELL_3V_MAX( hi, hi, obj->vert[2] );
)
BNDS_TMPL( Rectangle,
echoPos_t v[3][3];
ELL_3V_COPY( lo, obj->origin );
ELL_3V_ADD2( v[0], lo, obj->edge0 );
ELL_3V_ADD2( v[1], lo, obj->edge1 );
ELL_3V_ADD2( v[2], v[0], obj->edge1 );
ELL_3V_MIN( lo, lo, v[0] );
ELL_3V_MIN( lo, lo, v[1] );
ELL_3V_MIN( lo, lo, v[2] );
ELL_3V_COPY( hi, obj->origin );
ELL_3V_MAX( hi, hi, v[0] );
ELL_3V_MAX( hi, hi, v[1] );
ELL_3V_MAX( hi, hi, v[2] );
)
BNDS_TMPL( TriMesh,
ELL_3V_COPY( lo, obj->min );
ELL_3V_COPY( hi, obj->max );
)
BNDS_TMPL( Isosurface,
AIR_UNUSED( obj );
fprintf( stderr, "_echoIsosurface_bounds: unimplemented!\n" );
)
BNDS_TMPL( AABBox,
ELL_3V_COPY( lo, obj->min );
ELL_3V_COPY( hi, obj->max );
)
BNDS_TMPL( List,
unsigned int i;
echoPos_t l[3];
echoPos_t h[3];
echoObject *o;
ELL_3V_SET( lo, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( hi, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
for ( i=0; i<obj->objArr->len; i++ ) {
o = obj->obj[i];
_echoBoundsGet[o->type]( l, h, o );
ELL_3V_MIN( lo, lo, l );
ELL_3V_MAX( hi, hi, h );
}
)
BNDS_TMPL( Split,
AIR_UNUSED( obj );
fprintf( stderr, "_echoSplit_bounds: unimplemented!\n" );
)
BNDS_TMPL( Instance,
echoPos_t a[8][4];
echoPos_t b[8][4];
echoPos_t l[3];
echoPos_t h[3];
_echoBoundsGet[obj->obj->type]( l, h, obj->obj );
ELL_4V_SET( a[0], l[0], l[1], l[2], 1 );
ELL_4V_SET( a[1], h[0], l[1], l[2], 1 );
ELL_4V_SET( a[2], l[0], h[1], l[2], 1 );
ELL_4V_SET( a[3], h[0], h[1], l[2], 1 );
ELL_4V_SET( a[4], l[0], l[1], h[2], 1 );
ELL_4V_SET( a[5], h[0], l[1], h[2], 1 );
ELL_4V_SET( a[6], l[0], h[1], h[2], 1 );
ELL_4V_SET( a[7], h[0], h[1], h[2], 1 );
ELL_4MV_MUL( b[0], obj->M, a[0] ); ELL_4V_HOMOG( b[0], b[0] );
ELL_4MV_MUL( b[1], obj->M, a[1] ); ELL_4V_HOMOG( b[1], b[1] );
ELL_4MV_MUL( b[2], obj->M, a[2] ); ELL_4V_HOMOG( b[2], b[2] );
ELL_4MV_MUL( b[3], obj->M, a[3] ); ELL_4V_HOMOG( b[3], b[3] );
ELL_4MV_MUL( b[4], obj->M, a[4] ); ELL_4V_HOMOG( b[4], b[4] );
ELL_4MV_MUL( b[5], obj->M, a[5] ); ELL_4V_HOMOG( b[5], b[5] );
ELL_4MV_MUL( b[6], obj->M, a[6] ); ELL_4V_HOMOG( b[6], b[6] );
ELL_4MV_MUL( b[7], obj->M, a[7] ); ELL_4V_HOMOG( b[7], b[7] );
ELL_3V_MIN( lo, b[0], b[1] );
ELL_3V_MIN( lo, lo, b[2] );
ELL_3V_MIN( lo, lo, b[3] );
ELL_3V_MIN( lo, lo, b[4] );
ELL_3V_MIN( lo, lo, b[5] );
ELL_3V_MIN( lo, lo, b[6] );
ELL_3V_MIN( lo, lo, b[7] );
ELL_3V_MAX( hi, b[0], b[1] );
ELL_3V_MAX( hi, hi, b[2] );
ELL_3V_MAX( hi, hi, b[3] );
ELL_3V_MAX( hi, hi, b[4] );
ELL_3V_MAX( hi, hi, b[5] );
ELL_3V_MAX( hi, hi, b[6] );
ELL_3V_MAX( hi, hi, b[7] );
)
_echoBoundsGet_t
_echoBoundsGet[ECHO_TYPE_NUM] = {
( _echoBoundsGet_t )_echoSphere_bounds,
( _echoBoundsGet_t )_echoCylinder_bounds,
( _echoBoundsGet_t )_echoSuperquad_bounds,
( _echoBoundsGet_t )_echoCube_bounds,
( _echoBoundsGet_t )_echoTriangle_bounds,
( _echoBoundsGet_t )_echoRectangle_bounds,
( _echoBoundsGet_t )_echoTriMesh_bounds,
( _echoBoundsGet_t )_echoIsosurface_bounds,
( _echoBoundsGet_t )_echoAABBox_bounds,
( _echoBoundsGet_t )_echoSplit_bounds,
( _echoBoundsGet_t )_echoList_bounds,
( _echoBoundsGet_t )_echoInstance_bounds,
};
void
echoBoundsGet( echoPos_t *lo, echoPos_t *hi, echoObject *obj ) {
_echoBoundsGet[obj->type]( lo, hi, obj );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
char _echoBuff[128] = "";
char *
30 _echoDot( int depth ) {
int i;
_echoBuff[0] = '\0';
for ( i=1; i<=depth; i++ ) {
strcat( _echoBuff, ". " );
}
return _echoBuff;
}
void
41 echoTextureLookup( echoCol_t rgba[4], Nrrd *ntext,
echoPos_t u, echoPos_t v, echoRTParm *parm ) {
int su, sv, ui, vi;
float uf, vf;
unsigned char *tdata00, *tdata10, *tdata01, *tdata11;
su = ntext->axis[1].size;
sv = ntext->axis[2].size;
if ( parm->textureNN ) {
ui = airIndex( 0.0, u, 1.0, su );
vi = airIndex( 0.0, v, 1.0, sv );
tdata00 = ( unsigned char* )( ntext->data ) + 4*( ui + su*vi );
ELL_4V_SET_TT( rgba, echoCol_t,
tdata00[0]/255.0, tdata00[1]/255.0,
tdata00[2]/255.0, tdata00[3]/255.0 );
} else {
u = AIR_AFFINE( 0.0, u, 1.0, 0.0, su-1 ); u = AIR_CLAMP( 0, u, su-1 );
v = AIR_AFFINE( 0.0, v, 1.0, 0.0, sv-1 ); v = AIR_CLAMP( 0, v, sv-1 );
u -= ( u == su-1 ); ui = ( int )u; uf = AIR_CAST( float, u - ui );
v -= ( v == sv-1 ); vi = ( int )v; vf = AIR_CAST( float, v - vi );
tdata00 = ( unsigned char* )( ntext->data ) + 4*( ui + su*vi );
tdata01 = tdata00 + 4;
tdata10 = tdata00 + 4*su;
tdata11 = tdata10 + 4;
ELL_4V_SET_TT( rgba, echoCol_t,
( ( 1-vf )*( 1-uf )*tdata00[0] + ( 1-vf )*uf*tdata01[0] +
vf*( 1-uf )*tdata10[0] + vf*uf*tdata11[0] )/255.0,
( ( 1-vf )*( 1-uf )*tdata00[1] + ( 1-vf )*uf*tdata01[1] +
vf*( 1-uf )*tdata10[1] + vf*uf*tdata11[1] )/255.0,
( ( 1-vf )*( 1-uf )*tdata00[2] + ( 1-vf )*uf*tdata01[2] +
vf*( 1-uf )*tdata10[2] + vf*uf*tdata11[2] )/255.0,
( ( 1-vf )*( 1-uf )*tdata00[3] + ( 1-vf )*uf*tdata01[3] +
vf*( 1-uf )*tdata10[3] + vf*uf*tdata11[3] )/255.0 );
}
}
void
78 echoIntxMaterialColor( echoCol_t rgba[4], echoIntx *intx, echoRTParm *parm ) {
if ( intx->obj->ntext ) {
_echoRayIntxUV[intx->obj->type]( intx );
echoTextureLookup( rgba, intx->obj->ntext, intx->u, intx->v, parm );
rgba[0] *= intx->obj->rgba[0];
rgba[1] *= intx->obj->rgba[1];
rgba[2] *= intx->obj->rgba[2];
rgba[3] *= intx->obj->rgba[3];
} else {
ELL_4V_COPY( rgba, intx->obj->rgba );
}
}
/*
******** echoIntxLightColor
**
** determines ambient, diffuse, and ( Phong ) specular contributions to lighting
** a given intersection. "ambi" and "diff" MUST be given as non-NULL,
** "spec" can be given as NULL in order to bypass specular computations
*/
void
100 echoIntxLightColor( echoCol_t ambi[3], echoCol_t diff[3], echoCol_t spec[3],
echoCol_t sp,
echoIntx *intx, echoScene *scene, echoRTParm *parm,
echoThreadState *tstate ) {
unsigned int Lidx;
int blocked;
echoRay shadRay;
echoIntx shadIntx;
echoPos_t Ldist, Ldir[3], Lpos[3], Ldot;
echoCol_t Lcol[3], fracseen;
if ( parm->shadow ) {
/* from, neer, shadow */
shadRay.shadow = AIR_TRUE;
ELL_3V_COPY( shadRay.from, intx->pos );
/* HEY: this has to be fixed: shadow rays were getting aborted
because of this in a scene of instanced superquadrics, and
so epsilon had to be increased. Something about this epsilon
has to be adjusted according to the instancing matrix */
shadRay.neer = 30*ECHO_EPSILON;
}
/* set ambient ( easy ) */
ELL_3V_COPY( ambi, scene->ambi );
/* environment contributes only to diffuse */
if ( scene->envmap ) {
echoEnvmapLookup( diff, intx->norm, scene->envmap );
} else {
ELL_3V_SET( diff, 0, 0, 0 );
}
/* lights contribute to diffuse and specular */
if ( spec ) {
ELL_3V_SET( spec, 0, 0, 0 );
}
for ( Lidx=0; Lidx<scene->lightArr->len; Lidx++ ) {
echoLightPosition( Lpos, scene->light[Lidx], tstate );
ELL_3V_SUB( Ldir, Lpos, intx->pos );
ELL_3V_NORM( Ldir, Ldir, Ldist );
Ldot = ELL_3V_DOT( Ldir, intx->norm );
/* HEY: HACK: we have to general per-object-type flag that says,
this kind of object has no notion of in-versus-out facing . . . */
if ( echoTypeRectangle == intx->obj->type ) {
Ldot = AIR_ABS( Ldot );
}
if ( Ldot <= 0 ) {
continue;
/* to next light, we aren't facing this one. NB: this means
that there aren't diffuse or specular contributions on the
backsides of even semi-transparent surfaces */
}
if ( parm->shadow ) {
ELL_3V_COPY( shadRay.dir, Ldir );
shadRay.faar = Ldist;
if ( echoRayIntx( &shadIntx, &shadRay, scene, parm, tstate ) ) {
if ( 1.0 == parm->shadow ) {
/* skip to next light, this one is obscured by something,
and we don't do any partial shadowing */
continue;
}
blocked = AIR_TRUE;
} else {
blocked = AIR_FALSE;
}
} else {
blocked = AIR_FALSE;
}
fracseen = AIR_CAST( echoCol_t, blocked ? 1.0 - parm->shadow : 1.0 );
echoLightColor( Lcol, Ldist, scene->light[Lidx], parm, tstate );
ELL_3V_SCALE_INCR_TT( diff, echoCol_t, fracseen*Ldot, Lcol );
if ( spec ) {
Ldot = ELL_3V_DOT( Ldir, intx->refl );
if ( echoTypeRectangle == intx->obj->type ) {
Ldot = AIR_ABS( Ldot );
}
if ( Ldot > 0 ) {
Ldot = pow( Ldot, sp );
ELL_3V_SCALE_INCR_TT( spec, echoCol_t, fracseen*Ldot, Lcol );
}
}
}
return;
}
void
186 _echoIntxColorPhong( INTXCOLOR_ARGS ) {
echoCol_t ambi[3], diff[3], spec[3], ka, kd, ks, sp;
ka = intx->obj->mat[echoMatterPhongKa];
kd = intx->obj->mat[echoMatterPhongKd];
ks = intx->obj->mat[echoMatterPhongKs];
sp = intx->obj->mat[echoMatterPhongSp];
echoIntxMaterialColor( rgba, intx, parm );
ELL_3V_SET( spec, 0, 0, 0 );
echoIntxLightColor( ambi, diff, ks ? spec : NULL, sp,
intx, scene, parm, tstate );
rgba[0] = rgba[0]*( ka*ambi[0] + kd*diff[0] ) + ks*spec[0];
rgba[1] = rgba[1]*( ka*ambi[1] + kd*diff[1] ) + ks*spec[1];
rgba[2] = rgba[2]*( ka*ambi[2] + kd*diff[2] ) + ks*spec[2];
return;
}
void
205 _echoIntxColorMetal( INTXCOLOR_ARGS ) {
echoCol_t ka, kd, kp, RA, RD, RS, ambi[3], diff[3], spec[4];
echoPos_t c;
echoRay reflRay;
if ( 0 && tstate->verbose ) {
fprintf( stderr, "%s%s: t = %g\n",
_echoDot( tstate->depth ), "_echoIntxColorMetal", intx->t );
}
ELL_3V_SET( spec, 0, 0, 0 );
echoIntxMaterialColor( rgba, intx, parm );
c = ELL_3V_DOT( intx->view, intx->norm );
if ( c <= 0 ) {
/* see only surface color on backside of metal */
return;
}
c = 1 - c;
c = c*c*c*c*c;
RS = intx->obj->mat[echoMatterMetalR0];
RS = AIR_CAST( echoCol_t, RS + ( 1 - RS )*c );
ka = intx->obj->mat[echoMatterMetalKa];
kd = intx->obj->mat[echoMatterMetalKd];
kp = ka + kd;
/* neer, faar, shadow, from, dir */
ELL_3V_COPY( reflRay.from, intx->pos );
ELL_3V_COPY( reflRay.dir, intx->refl );
reflRay.neer = ECHO_EPSILON;
reflRay.faar = ECHO_POS_MAX;
reflRay.shadow = AIR_FALSE;
echoRayColor( spec, &reflRay, scene, parm, tstate );
if ( kp ) {
RA = ( 1 - RS )*ka/kp;
RD = ( 1 - RS )*kd/kp;
echoIntxLightColor( ambi, diff, NULL, 0.0,
intx, scene, parm, tstate );
/* NB: surface color does attenuate reflected color ( unlike phong ) */
rgba[0] *= RA*ambi[0] + RD*diff[0] + RS*spec[0];
rgba[1] *= RA*ambi[1] + RD*diff[1] + RS*spec[1];
rgba[2] *= RA*ambi[2] + RD*diff[2] + RS*spec[2];
} else {
rgba[0] *= RS*spec[0];
rgba[1] *= RS*spec[1];
rgba[2] *= RS*spec[2];
}
return;
}
/*
** "th" = theta = angle of incidence
** "ph" = phi = angle of refraction
** "index" = ( index of outgoing material )/( index of incoming material )
*/
int
260 _echoRefract( echoPos_t T[3], echoPos_t V[3],
echoPos_t N[3], echoCol_t indexr, echoThreadState *tstate ) {
echoPos_t cosTh, cosPh, sinPhSq, cosPhSq, tmp1, tmp2;
cosTh = ELL_3V_DOT( V, N );
sinPhSq = ( 1 - cosTh*cosTh )/( indexr*indexr );
cosPhSq = 1 - sinPhSq;
if ( cosPhSq < 0 ) {
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: cosTh = %g --%g--> TIR!!\n",
_echoDot( tstate->depth ), "_echoRefract",
cosTh, indexr );
}
return AIR_FALSE;
}
/* else we do not have total internal reflection */
cosPh = sqrt( cosPhSq );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: cosTh = %g --%g--> cosPh = %g\n",
_echoDot( tstate->depth ), "_echoRefract",
cosTh, indexr, cosPh );
}
tmp1 = -1.0/indexr; tmp2 = cosTh/indexr - cosPh;
ELL_3V_SCALE_ADD2( T, tmp1, V, tmp2, N );
ELL_3V_NORM( T, T, tmp1 );
return AIR_TRUE;
}
void
289 _echoIntxColorGlass( INTXCOLOR_ARGS ) {
char me[]="_echoIntxColorGlass";
echoCol_t
ambi[3], diff[3],
ka, kd, RP, RS, RT, R0,
indexr, /* ( index of material we're going into ) /
( index of material we're leaving ) */
k[3], /* attenuation of color due to travel through medium */
matlCol[4], /* inherent color */
reflCol[4], /* color from reflected ray */
tranCol[4]; /* color from transmitted ray */
echoPos_t tmp,
negnorm[3];
double c;
echoRay tranRay, reflRay;
echoIntxMaterialColor( matlCol, intx, parm );
/* from, neer, faar, shadow */
ELL_3V_COPY( tranRay.from, intx->pos );
ELL_3V_COPY( reflRay.from, intx->pos );
tranRay.neer = reflRay.neer = ECHO_EPSILON;
tranRay.faar = reflRay.faar = ECHO_POS_MAX;
tranRay.shadow = reflRay.shadow = AIR_FALSE;
ELL_3V_COPY( reflRay.dir, intx->refl );
/* tranRay.dir set below */
indexr = intx->obj->mat[echoMatterGlassIndex];
RS = 0.0; /* this is a flag meaning: "AFAIK, there's no total int refl" */
tmp = ELL_3V_DOT( intx->norm, intx->view );
if ( tmp > 0 ) {
/* "d.n < 0": we're coming from outside the glass, and we
assume this means that we're going into a HIGHER index material,
which means there is NO total internal reflection */
_echoRefract( tranRay.dir, intx->view, intx->norm, indexr, tstate );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: V=( %g, %g, %g ), N=( %g, %g, %g ), n=%g -> T=( %g, %g, %g )\n",
_echoDot( tstate->depth ), me,
intx->view[0], intx->view[1], intx->view[2],
intx->norm[0], intx->norm[1], intx->norm[2], indexr,
tranRay.dir[0], tranRay.dir[1], tranRay.dir[2] );
}
c = tmp;
ELL_3V_SET( k, 1, 1, 1 );
} else {
/* we're coming from inside the glass */
/* the reasoning for my Beer's law implementation is this: if a
channel ( r, g, or b ) is full on ( 1.0 ), then there should be no
attenuation in its color. The more the color is below 1.0, the
more it should be damped with distance. */
k[0] = AIR_CAST( echoCol_t, exp( parm->glassC*( matlCol[0]-1 )*intx->t ) );
k[1] = AIR_CAST( echoCol_t, exp( parm->glassC*( matlCol[1]-1 )*intx->t ) );
k[2] = AIR_CAST( echoCol_t, exp( parm->glassC*( matlCol[2]-1 )*intx->t ) );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: internal refl @ t = %g -> k = %g %g %g\n",
_echoDot( tstate->depth ), me, intx->t, k[0], k[1], k[2] );
}
ELL_3V_SCALE( negnorm, -1, intx->norm );
if ( _echoRefract( tranRay.dir, intx->view, negnorm, 1/indexr, tstate ) ) {
c = -ELL_3V_DOT( tranRay.dir, negnorm );
} else {
/* its total internal reflection time! */
c = 0.0;
RS = 1.0;
}
}
if ( RS ) {
/* total internal reflection */
RT = 0;
} else {
R0 = ( indexr - 1 )/( indexr + 1 );
R0 *= R0;
c = 1 - c;
c = c*c*c*c*c;
RS = AIR_CAST( echoCol_t, R0 + ( 1-R0 )*c );
RT = 1 - RS;
}
ka = intx->obj->mat[echoMatterMetalKa];
kd = intx->obj->mat[echoMatterMetalKd];
RP = ka + kd;
if ( RP ) {
RS *= 1 - RP;
RT *= 1 - RP;
echoIntxLightColor( ambi, diff, NULL, 0.0,
intx, scene, parm, tstate );
} else {
ELL_3V_SET( ambi, 0, 0, 0 );
ELL_3V_SET( diff, 0, 0, 0 );
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: --- reflRay ( reflected )\n",
_echoDot( tstate->depth ), me );
}
echoRayColor( reflCol, &reflRay, scene, parm, tstate );
if ( RT ) {
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: --- tranRay ( refracted )\n",
_echoDot( tstate->depth ), me );
}
echoRayColor( tranCol, &tranRay, scene, parm, tstate );
} else {
ELL_3V_SET( tranCol, 0, 0, 0 );
}
rgba[0] = ( matlCol[0]*( ka*ambi[0] + kd*diff[0] ) +
k[0]*( RS*reflCol[0] + RT*tranCol[0] ) );
rgba[1] = ( matlCol[1]*( ka*ambi[1] + kd*diff[1] ) +
k[1]*( RS*reflCol[1] + RT*tranCol[1] ) );
rgba[2] = ( matlCol[2]*( ka*ambi[2] + kd*diff[2] ) +
k[2]*( RS*reflCol[2] + RT*tranCol[2] ) );
rgba[3] = 1.0;
return;
}
void
404 _echoIntxColorLight( INTXCOLOR_ARGS ) {
AIR_UNUSED( scene );
AIR_UNUSED( tstate );
echoIntxMaterialColor( rgba, intx, parm );
}
void
412 _echoIntxColorUnknown( INTXCOLOR_ARGS ) {
AIR_UNUSED( rgba );
AIR_UNUSED( intx );
AIR_UNUSED( scene );
AIR_UNUSED( parm );
fprintf( stderr, "%s%s: can't color intx with object with unset material\n",
_echoDot( tstate->depth ), "_echoIntxColorNone" );
}
_echoIntxColor_t
_echoIntxColor[ECHO_MATTER_MAX+1] = {
_echoIntxColorUnknown,
_echoIntxColorPhong,
_echoIntxColorGlass,
_echoIntxColorMetal,
_echoIntxColorLight,
};
/*
******** echoIntxFuzzify( )
**
** this modifies the refl and norm fields of a given intx
*/
void
437 echoIntxFuzzify( echoIntx *intx, echoCol_t fuzz, echoThreadState *tstate ) {
echoPos_t tmp, *jitt, oldNorm[3], perp0[3], perp1[3], jj0, jj1;
int side;
/* at some point I thought this was important to avoid bias when
going through glass, but now I'm not so sure . . . It is likely
totally moot if jitter vectors are NOT reused between pixels. */
if ( ELL_3V_DOT( intx->norm, intx->view ) > 0 ) {
jitt = tstate->jitt + 2*echoJittableNormalA;
} else {
jitt = tstate->jitt + 2*echoJittableNormalB;
}
jj0 = fuzz*jitt[0];
jj1 = fuzz*jitt[1];
ELL_3V_COPY( oldNorm, intx->norm );
side = ELL_3V_DOT( intx->refl, oldNorm ) > 0;
ell_3v_PERP( perp0, oldNorm );
ELL_3V_NORM( perp0, perp0, tmp );
ELL_3V_CROSS( perp1, perp0, oldNorm );
ELL_3V_SCALE_ADD3( intx->norm, 1, oldNorm, jj0, perp0, jj1, perp1 );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
_ECHO_REFLECT( intx->refl, intx->norm, intx->view, tmp );
if ( side != ( ELL_3V_DOT( intx->refl, oldNorm ) > 0 ) ) {
ELL_3V_SCALE_ADD3( intx->norm, 1, oldNorm, -jj0, perp0, -jj1, perp1 );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
_ECHO_REFLECT( intx->refl, intx->norm, intx->view, tmp );
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: fuzz[%g]( %g, %g, %g ) --> ( %g, %g, %g )\n",
_echoDot( tstate->depth ), "echoIntxFuzzify", fuzz,
oldNorm[0], oldNorm[1], oldNorm[2],
intx->norm[0], intx->norm[1], intx->norm[2] );
}
return;
}
void
474 echoIntxColor( echoCol_t rgba[4], echoIntx *intx,
echoScene *scene, echoRTParm *parm, echoThreadState *tstate ) {
echoCol_t fuzz;
switch( intx->obj->matter ) {
case echoMatterGlass:
fuzz = intx->obj->mat[echoMatterGlassFuzzy];
break;
case echoMatterMetal:
fuzz = intx->obj->mat[echoMatterMetalFuzzy];
break;
default:
fuzz = 0;
break;
}
if ( fuzz ) {
echoIntxFuzzify( intx, fuzz, tstate );
}
_echoIntxColor[intx->obj->matter]( rgba, intx, scene, parm, tstate );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
/* ------------------------------- jitter --------------------------- */
const char *
_echoJitterStr[ECHO_JITTER_NUM+1] = {
"( unknown_jitter )",
"none",
"grid",
"jitter",
"random"
};
const int
_echoJitterVal[ECHO_JITTER_NUM+1] = {
echoJitterUnknown,
echoJitterNone,
echoJitterGrid,
echoJitterJitter,
echoJitterRandom
};
const char *
_echoJitterDesc[ECHO_JITTER_NUM+1] = {
"unknown jitter",
"nothing- samples are ALWAYS at center of region",
"no jittering- samples are at regular grid vertices",
"normal jittering- samples are randomly located within grid cells",
"samples are randomly located within region"
};
const char *
_echoJitterStrEqv[] = {
"none",
"grid", "regular",
"jitter",
"random",
""
};
const int
_echoJitterValEqv[] = {
echoJitterNone,
echoJitterGrid, echoJitterGrid,
echoJitterJitter,
echoJitterRandom
};
const airEnum
_echoJitter = {
"jitter",
ECHO_JITTER_NUM,
_echoJitterStr, _echoJitterVal,
_echoJitterDesc,
_echoJitterStrEqv, _echoJitterValEqv,
AIR_FALSE
};
81 const airEnum *const
echoJitter = &_echoJitter;
/* ------------------------------- object type --------------------------- */
const char *
_echoTypeStr[ECHO_TYPE_NUM+1] = {
"( unknown_object )",
"sphere",
"cylinder",
"superquad",
"cube",
"triangle",
"rectangle",
"mesh",
"isosurface",
"AABoundingBox",
"split",
"list",
"instance"
};
const int
_echoTypeVal[ECHO_TYPE_NUM+1] = {
echoTypeUnknown,
echoTypeSphere,
echoTypeCylinder,
echoTypeSuperquad,
echoTypeCube,
echoTypeTriangle,
echoTypeRectangle,
echoTypeTriMesh,
echoTypeIsosurface,
echoTypeAABBox,
echoTypeSplit,
echoTypeList,
echoTypeInstance
};
const char *
_echoTypeDesc[ECHO_TYPE_NUM+1] = {
"unknown_object",
"sphere",
"axis-aligned cylinder",
"superquadric ( actually, superellipsoid )",
"unit cube, centered at the origin",
"triangle",
"rectangle",
"mesh of triangles",
"isosurface of scalar volume",
"axis-aligned bounding box",
"split",
"list",
"instance"
};
const char *
_echoTypeStrEqv[] = {
"sphere",
"cylinder", "cylind", "rod",
"superquad", "squad",
"cube", "box",
"triangle", "tri",
"rectangle", "rect",
"mesh", "tri-mesh", "trimesh",
"isosurface",
"aabbox", "AABoundingBox",
"split",
"list",
"instance",
""
};
const int
_echoTypeValEqv[] = {
echoTypeSphere,
echoTypeCylinder, echoTypeCylinder, echoTypeCylinder,
echoTypeSuperquad, echoTypeSuperquad,
echoTypeCube, echoTypeCube,
echoTypeTriangle, echoTypeTriangle,
echoTypeRectangle, echoTypeRectangle,
echoTypeTriMesh, echoTypeTriMesh, echoTypeTriMesh,
echoTypeIsosurface,
echoTypeAABBox, echoTypeAABBox,
echoTypeSplit,
echoTypeList,
echoTypeInstance
};
const airEnum
_echoType = {
"object type",
ECHO_TYPE_NUM,
_echoTypeStr, _echoTypeVal,
_echoTypeDesc,
_echoTypeStrEqv, _echoTypeValEqv,
AIR_FALSE
};
179 const airEnum *const
echoType = &_echoType;
/* ------------------------------ material types --------------------------- */
const char *
_echoMatterStr[ECHO_MATTER_MAX+1] = {
"( unknown_matter )",
"phong",
"glass",
"metal",
"light"
};
const char *
_echoMatterDesc[ECHO_MATTER_MAX+1] = {
"unknown material",
"phong shaded surface",
"glass",
"metal",
"light emitter"
};
const airEnum
_echoMatter = {
"matter",
ECHO_MATTER_MAX,
_echoMatterStr, NULL,
_echoMatterDesc,
NULL, NULL,
AIR_FALSE
};
211 const airEnum *const
echoMatter = &_echoMatter;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
/*
** TODO: do whatever possible to minimize the amount of work
** needed for shadow rays
*/
int _echoVerbose = 0;
/*
** ALL of the intersection functions are responsible for setting
** 1 ) intx->norm to a NORMALIZED direction
** 2 ) intx->t
** 3 ) intx->obj
** Specifically, these things must be set for non-shadow rays. Setting
** them for shadow rays is optional. Setting intx->u and intx->v texture
** coords is always optional.
**
** Setting intx->pos and intx->view ( normalized ) is done by echoRayIntx
*/
int
47 _echoRayIntx_Noop( RAYINTX_ARGS( Object ) ) {
AIR_UNUSED( intx );
AIR_UNUSED( ray );
AIR_UNUSED( obj );
AIR_UNUSED( parm );
AIR_UNUSED( tstate );
return 0;
}
int
58 _echoRayIntx_CubeSurf( echoPos_t *tP, int *axP, int *dirP,
echoPos_t xmin, echoPos_t xmax,
echoPos_t ymin, echoPos_t ymax,
echoPos_t zmin, echoPos_t zmax,
echoRay *ray ) {
echoPos_t txmin, tymin, tzmin, txmax, tymax, tzmax,
dx, dy, dz, ox, oy, oz, tmin, tmax;
int axmin, axmax, sgn[3];
ELL_3V_GET( dx, dy, dz, ray->dir );
ELL_3V_GET( ox, oy, oz, ray->from );
if ( dx >= 0 ) { txmin = ( xmin - ox )/dx; txmax = ( xmax - ox )/dx; sgn[0] = -1; }
else { txmin = ( xmax - ox )/dx; txmax = ( xmin - ox )/dx; sgn[0] = 1; }
if ( dy >= 0 ) { tymin = ( ymin - oy )/dy; tymax = ( ymax - oy )/dy; sgn[1] = -1; }
else { tymin = ( ymax - oy )/dy; tymax = ( ymin - oy )/dy; sgn[1] = 1; }
if ( dz >= 0 ) { tzmin = ( zmin - oz )/dz; tzmax = ( zmax - oz )/dz; sgn[2] = -1; }
else { tzmin = ( zmax - oz )/dz; tzmax = ( zmin - oz )/dz; sgn[2] = 1; }
if ( txmin > tymin ) { tmin = txmin; axmin = 0; }
else { tmin = tymin; axmin = 1; }
if ( tzmin > tmin ) { tmin = tzmin; axmin = 2; }
if ( txmax < tymax ) { tmax = txmax; axmax = 0; }
else { tmax = tymax; axmax = 1; }
if ( tzmax < tmax ) { tmax = tzmax; axmax = 2; }
if ( tmin >= tmax )
return AIR_FALSE;
*tP = tmin;
*axP = axmin;
*dirP = sgn[axmin];
if ( !AIR_IN_CL( ray->neer, tmin, ray->faar ) ) {
*tP = tmax;
*axP = axmax;
*dirP = -sgn[axmax];
if ( !AIR_IN_CL( ray->neer, tmax, ray->faar ) ) {
return AIR_FALSE;
}
}
return AIR_TRUE;
}
int
98 _echoRayIntx_CubeSolid( echoPos_t *tminP, echoPos_t *tmaxP,
echoPos_t xmin, echoPos_t xmax,
echoPos_t ymin, echoPos_t ymax,
echoPos_t zmin, echoPos_t zmax,
echoRay *ray ) {
echoPos_t txmin, tymin, tzmin, txmax, tymax, tzmax,
dx, dy, dz, ox, oy, oz, tmin, tmax;
ELL_3V_GET( dx, dy, dz, ray->dir );
ELL_3V_GET( ox, oy, oz, ray->from );
if ( dx >= 0 ) { txmin = ( xmin - ox )/dx; txmax = ( xmax - ox )/dx; }
else { txmin = ( xmax - ox )/dx; txmax = ( xmin - ox )/dx; }
if ( dy >= 0 ) { tymin = ( ymin - oy )/dy; tymax = ( ymax - oy )/dy; }
else { tymin = ( ymax - oy )/dy; tymax = ( ymin - oy )/dy; }
if ( dz >= 0 ) { tzmin = ( zmin - oz )/dz; tzmax = ( zmax - oz )/dz; }
else { tzmin = ( zmax - oz )/dz; tzmax = ( zmin - oz )/dz; }
if ( txmin > tymin ) tmin = txmin;
else tmin = tymin;
if ( tzmin > tmin ) tmin = tzmin;
if ( txmax < tymax ) tmax = txmax;
else tmax = tymax;
if ( tzmax < tmax ) tmax = tzmax;
if ( tmin >= tmax )
return AIR_FALSE;
if ( ray->faar < tmin || ray->neer > tmax )
return AIR_FALSE;
*tminP = AIR_MAX( tmin, ray->neer );
*tmaxP = AIR_MIN( tmax, ray->faar );
return AIR_TRUE;
}
int
132 _echoRayIntx_Sphere( RAYINTX_ARGS( Sphere ) ) {
echoPos_t t, A, B, C, r[3], dscr, pos[3], tmp;
AIR_UNUSED( parm );
AIR_UNUSED( tstate );
ELL_3V_SUB( r, ray->from, obj->pos );
A = ELL_3V_DOT( ray->dir, ray->dir );
B = 2*ELL_3V_DOT( ray->dir, r );
C = ELL_3V_DOT( r, r ) - obj->rad*obj->rad;
dscr = B*B - 4*A*C;
if ( dscr <= 0 ) {
/* grazes or misses ( most common case ) */
return AIR_FALSE;
}
/* else */
dscr = sqrt( dscr );
t = ( -B - dscr )/( 2*A );
if ( !AIR_IN_CL( ray->neer, t, ray->faar ) ) {
t = ( -B + dscr )/( 2*A );
if ( !AIR_IN_CL( ray->neer, t, ray->faar ) ) {
return AIR_FALSE;
}
}
/* else one of the intxs is in [neer, faar] segment */
intx->t = t;
ELL_3V_SCALE_ADD2( pos, 1, ray->from, t, ray->dir );
ELL_3V_SUB( intx->norm, pos, obj->pos );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
intx->obj = OBJECT( obj );
/* does NOT set u, v */
return AIR_TRUE;
}
void
166 _echoRayIntxUV_Sphere( echoIntx *intx ) {
echoPos_t u, v;
if ( intx->norm[0] || intx->norm[1] ) {
u = atan2( intx->norm[1], intx->norm[0] );
intx->u = AIR_AFFINE( -AIR_PI, u, AIR_PI, 0.0, 1.0 );
v = -asin( intx->norm[2] );
intx->v = AIR_AFFINE( -AIR_PI/2, v, AIR_PI/2, 0.0, 1.0 );
}
else {
intx->u = 0;
/* this is valid because if we're here, then intx->norm[2]
is either 1.0 or -1.0 */
intx->v = AIR_AFFINE( 1.0, intx->norm[2], -1.0, 0.0, 1.0 );
}
}
int
184 _echoRayIntx_Cylinder( RAYINTX_ARGS( Cylinder ) ) {
echoPos_t A, B, C, aa, bb, cc, dd, ee, ff, dscr, cylt1, cylt2, t, tmax,
twot[2], cylp1, cylp2, pos[3], tmp;
int tidx, radi0, radi1, twocap[2], cap;
AIR_UNUSED( parm );
AIR_UNUSED( tstate );
if ( !_echoRayIntx_CubeSolid( &t, &tmax,
-1-ECHO_EPSILON, 1+ECHO_EPSILON,
-1-ECHO_EPSILON, 1+ECHO_EPSILON,
-1-ECHO_EPSILON, 1+ECHO_EPSILON, ray ) ) {
return AIR_FALSE;
}
switch( obj->axis ) {
case 0:
radi0 = 1; radi1 = 2;
break;
case 1:
radi0 = 0; radi1 = 2;
break;
case 2: default:
radi0 = 0; radi1 = 1;
break;
}
aa = ray->dir[radi0];
bb = ray->dir[radi1];
ee = ray->dir[obj->axis];
cc = ray->from[radi0];
dd = ray->from[radi1];
ff = ray->from[obj->axis];
A = aa*aa + bb*bb;
B = 2*( aa*cc + bb*dd );
C = cc*cc + dd*dd - 1;
dscr = B*B - 4*A*C;
if ( dscr <= 0 ) {
/* infinite ray grazes or misses the infinite cylinder ( not
bounded to [-1, 1] along cylinder's axis ), so therefore the ray
grazes or misses the actual cylinder */
return AIR_FALSE;
}
/* else infinite ray intersects the infinite cylinder */
dscr = sqrt( dscr );
cylt1 = ( -B - dscr )/( 2*A );
cylp1 = ff + cylt1*ee;
cylt2 = ( -B + dscr )/( 2*A );
cylp2 = ff + cylt2*ee;
if ( ( cylp1 <= -1 && cylp2 <= -1 )
|| ( cylp1 >= 1 && cylp2 >= 1 ) ) {
/* both intersections with infinite cylinder lie on ONE side of the
finite extent, so there can't be an intersection */
return AIR_FALSE;
}
/* else infinite ray DOES intersect finite cylinder; we have to find
if any of the intersections are in the [neer, faar] interval */
tidx = 0;
if ( AIR_IN_CL( -1, cylp1, 1 ) ) {
twot[tidx] = cylt1;
twocap[tidx] = 0;
tidx++;
}
if ( AIR_IN_CL( -1, cylp2, 1 ) ) {
twot[tidx] = cylt2;
twocap[tidx] = 0;
tidx++;
}
if ( tidx < 2 ) {
/* at least one of the two intersections is with the endcaps */
t = ( -ff - 1 )/ee;
ELL_3V_SCALE_ADD2( pos, 1, ray->from, t, ray->dir );
aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb;
if ( cc <= 1 ) {
twot[tidx] = t;
twocap[tidx] = 1;
tidx++;
}
if ( tidx < 2 ) {
/* try other endcap */
t = ( -ff + 1 )/ee;
ELL_3V_SCALE_ADD2( pos, 1, ray->from, t, ray->dir );
aa = pos[radi0]; bb = pos[radi1]; cc = aa*aa + bb*bb;
if ( cc <= 1 ) {
twot[tidx] = t;
twocap[tidx] = 1;
tidx++;
}
}
}
if ( !tidx ) {
return AIR_FALSE;
}
if ( 2 == tidx && twot[0] > twot[1] ) {
ELL_SWAP2( twot[0], twot[1], aa );
ELL_SWAP2( twocap[0], twocap[1], cap );
}
t = twot[0];
cap = twocap[0];
if ( !AIR_IN_CL( ray->neer, t, ray->faar ) ) {
if ( 1 == tidx ) {
return AIR_FALSE;
}
t = twot[1];
cap = twocap[1];
if ( !AIR_IN_CL( ray->neer, t, ray->faar ) ) {
return AIR_FALSE;
}
}
/* else one of the intxs is in [neer, faar] segment */
intx->t = t;
ELL_3V_SCALE_ADD2( pos, 1, ray->from, t, ray->dir );
switch( obj->axis ) {
case 0:
ELL_3V_SET( intx->norm, cap*pos[0], ( 1-cap )*pos[1], ( 1-cap )*pos[2] );
break;
case 1:
ELL_3V_SET( intx->norm, ( 1-cap )*pos[0], cap*pos[1], ( 1-cap )*pos[2] );
break;
case 2: default:
ELL_3V_SET( intx->norm, ( 1-cap )*pos[0], ( 1-cap )*pos[1], cap*pos[2] );
break;
}
ELL_3V_NORM( intx->norm, intx->norm, tmp );
intx->obj = OBJECT( obj );
/* does NOT set u, v */
return AIR_TRUE;
}
int
312 _echoRayIntx_Cube( RAYINTX_ARGS( Cube ) ) {
echoPos_t t;
int ax, dir;
AIR_UNUSED( parm );
if ( !_echoRayIntx_CubeSurf( &t, &ax, &dir,
-1, 1,
-1, 1,
-1, 1, ray ) )
return AIR_FALSE;
intx->obj = ( echoObject * )obj;
intx->t = t;
switch( ax ) {
case 0: ELL_3V_SET( intx->norm, dir, 0, 0 ); break;
case 1: ELL_3V_SET( intx->norm, 0, dir, 0 ); break;
case 2: ELL_3V_SET( intx->norm, 0, 0, dir ); break;
}
intx->face = ax + 3*( dir + 1 )/2;
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: ax = %d --> norm = ( %g, %g, %g )\n",
_echoDot( tstate->depth ), "_echoRayIntx_Cube", ax,
intx->norm[0], intx->norm[1], intx->norm[2] );
}
/* does NOT set u, v */
return AIR_TRUE;
}
void
340 _echoRayIntxUV_Cube( echoIntx *intx ) {
echoPos_t x, y, z;
ELL_3V_GET( x, y, z, intx->pos );
switch( intx->face ) {
case 0:
intx->u = AIR_AFFINE( -1, y, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, -z, 1, 0.0, 1.0 );
break;
case 1:
intx->u = AIR_AFFINE( -1, -x, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, -z, 1, 0.0, 1.0 );
break;
case 2:
intx->u = AIR_AFFINE( -1, -x, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, y, 1, 0.0, 1.0 );
break;
case 3:
intx->u = AIR_AFFINE( -1, -y, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, z, 1, 0.0, 1.0 );
break;
case 4:
intx->u = AIR_AFFINE( -1, x, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, z, 1, 0.0, 1.0 );
break;
case 5:
intx->u = AIR_AFFINE( -1, x, 1, 0.0, 1.0 );
intx->v = AIR_AFFINE( -1, -y, 1, 0.0, 1.0 );
break;
}
}
/*
** TRI_INTX
**
** given a triangle in terms of origin, edge0, edge1, this will
** begin the intersection calculation:
** - sets pvec, tvec, qvec ( all of them if intx is not ruled out )
** - sets u, and rules out intx based on ( u < 0.0 || u > 1.0 )
** - sets v, and rules out intx based on COND
** - sets t, and rules out intx based on ( t < neer || t > faar )
*/
#define TRI_INTX( ray, origin, edge0, edge1, pvec, qvec, tvec, \
383 det, t, u, v, COND, NOPE ) \
384 ELL_3V_CROSS( pvec, ray->dir, edge1 ); \
385 det = ELL_3V_DOT( pvec, edge0 ); \
if ( det > -ECHO_EPSILON && det < ECHO_EPSILON ) { \
NOPE; \
} \
/* now det is the reciprocal of the determinant */ \
det = 1.0/det; \
ELL_3V_SUB( tvec, ray->from, origin ); \
u = det * ELL_3V_DOT( pvec, tvec ); \
if ( u < 0.0 || u > 1.0 ) { \
NOPE; \
} \
ELL_3V_CROSS( qvec, tvec, edge0 ); \
v = det * ELL_3V_DOT( qvec, ray->dir ); \
if ( COND ) { \
NOPE; \
} \
t = det * ELL_3V_DOT( qvec, edge1 ); \
if ( t < ray->neer || t > ray->faar ) { \
NOPE; \
}
int
_echoRayIntx_Rectangle( RAYINTX_ARGS( Rectangle ) ) {
echoPos_t pvec[3], qvec[3], tvec[3], det, t, u, v, *edge0, *edge1, tmp;
AIR_UNUSED( tstate );
if ( echoMatterLight == obj->matter
&& ( ray->shadow || !parm->renderLights ) ) {
return AIR_FALSE;
}
edge0 = obj->edge0;
edge1 = obj->edge1;
TRI_INTX( ray, obj->origin, edge0, edge1,
pvec, qvec, tvec, det, t, u, v,
( v < 0.0 || v > 1.0 ), return AIR_FALSE );
intx->t = t;
intx->u = u;
intx->v = v;
ELL_3V_CROSS( intx->norm, edge0, edge1 );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
intx->obj = OBJECT( obj );
/* DOES set u, v */
return AIR_TRUE;
}
int
_echoRayIntx_Triangle( RAYINTX_ARGS( Triangle ) ) {
echoPos_t pvec[3], qvec[3], tvec[3], det, t, u, v, edge0[3], edge1[3], tmp;
AIR_UNUSED( parm );
AIR_UNUSED( tstate );
ELL_3V_SUB( edge0, obj->vert[1], obj->vert[0] );
ELL_3V_SUB( edge1, obj->vert[2], obj->vert[0] );
TRI_INTX( ray, obj->vert[0], edge0, edge1,
pvec, qvec, tvec, det, t, u, v,
( v < 0.0 || u + v > 1.0 ), return AIR_FALSE );
intx->t = t;
intx->u = u;
intx->v = v;
ELL_3V_CROSS( intx->norm, edge0, edge1 );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
intx->obj = ( echoObject * )obj;
/* DOES set u, v */
return AIR_TRUE;
}
int
_echoRayIntx_TriMesh( RAYINTX_ARGS( TriMesh ) ) {
echoPos_t *pos, vert0[3], edge0[3], edge1[3], pvec[3], qvec[3], tvec[3],
det, t, tmax, u, v, tmp;
echoTriMesh *trim;
int i, ret;
AIR_UNUSED( parm );
trim = TRIMESH( obj );
if ( !_echoRayIntx_CubeSolid( &t, &tmax,
trim->min[0], trim->max[0],
trim->min[1], trim->max[1],
trim->min[2], trim->max[2], ray ) ) {
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: trimesh bbox ( %g, %g, %g ) --> ( %g, %g, %g ) not hit\n",
_echoDot( tstate->depth ), "_echoRayIntx_TriMesh",
trim->min[0], trim->min[1], trim->min[2],
trim->max[0], trim->max[1], trim->max[2] );
}
return AIR_FALSE;
}
/* stupid linear search for now */
ret = AIR_FALSE;
for ( i=0; i<trim->numF; i++ ) {
pos = trim->pos + 3*trim->vert[0 + 3*i];
ELL_3V_COPY( vert0, pos );
pos = trim->pos + 3*trim->vert[1 + 3*i];
ELL_3V_SUB( edge0, pos, vert0 );
pos = trim->pos + 3*trim->vert[2 + 3*i];
ELL_3V_SUB( edge1, pos, vert0 );
TRI_INTX( ray, vert0, edge0, edge1,
pvec, qvec, tvec, det, t, u, v,
( v < 0.0 || u + v > 1.0 ), continue );
if ( ray->shadow ) {
return AIR_TRUE;
}
intx->t = ray->faar = t;
ELL_3V_CROSS( intx->norm, edge0, edge1 );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
intx->obj = ( echoObject * )obj;
intx->face = i;
ret = AIR_TRUE;
}
/* does NOT set u, v */
return ret;
}
void
_echoRayIntxUV_TriMesh( echoIntx *intx ) {
echoPos_t u, v, norm[3], len;
echoTriMesh *trim;
trim = TRIMESH( intx->obj );
ELL_3V_SUB( norm, intx->pos, trim->meanvert );
ELL_3V_NORM( norm, norm, len );
if ( norm[0] || norm[1] ) {
u = atan2( norm[1], norm[0] );
intx->u = AIR_AFFINE( -AIR_PI, u, AIR_PI, 0.0, 1.0 );
v = -asin( norm[2] );
intx->v = AIR_AFFINE( -AIR_PI/2, v, AIR_PI/2, 0.0, 1.0 );
}
else {
intx->u = 0;
intx->v = AIR_AFFINE( 1.0, norm[2], -1.0, 0.0, 1.0 );
}
}
int
_echoRayIntx_AABBox( RAYINTX_ARGS( AABBox ) ) {
int ret;
echoAABBox *box;
echoPos_t t, tmax;
box = AABBOX( obj );
if ( _echoRayIntx_CubeSolid( &t, &tmax,
box->min[0], box->max[0],
box->min[1], box->max[1],
box->min[2], box->max[2], ray ) ) {
intx->boxhits++;
ret = _echoRayIntx[box->obj->type]( intx, ray, box->obj, parm, tstate );
} else {
ret = AIR_FALSE;
}
return ret;
}
int
_echoRayIntx_Split( RAYINTX_ARGS( Split ) ) {
char me[]="_echoRayIntx_Split";
echoObject *a, *b;
echoPos_t *mina, *minb, *maxa, *maxb, t, tmax;
int ret;
if ( ray->dir[obj->axis] > 0 ) {
a = obj->obj0;
mina = obj->min0;
maxa = obj->max0;
b = obj->obj1;
minb = obj->min1;
maxb = obj->max1;
}
else {
a = obj->obj1;
mina = obj->min1;
maxa = obj->max1;
b = obj->obj0;
minb = obj->min0;
maxb = obj->max0;
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: ( shadow = %d ):\n",
_echoDot( tstate->depth ), me, ray->shadow );
fprintf( stderr, "%s%s: 1st: ( %g, %g, %g ) -- ( %g, %g, %g ) ( obj %d )\n",
_echoDot( tstate->depth ), me,
mina[0], mina[1], mina[2],
maxa[0], maxa[1], maxa[2], a->type );
fprintf( stderr, "%s%s: 2nd: ( %g, %g, %g ) -- ( %g, %g, %g ) ( obj %d )\n",
_echoDot( tstate->depth ), me,
minb[0], minb[1], minb[2],
maxb[0], maxb[1], maxb[2], b->type );
}
ret = AIR_FALSE;
if ( _echoRayIntx_CubeSolid( &t, &tmax,
mina[0], maxa[0],
mina[1], maxa[1],
mina[2], maxa[2], ray ) ) {
intx->boxhits++;
if ( _echoRayIntx[a->type]( intx, ray, a, parm, tstate ) ) {
if ( ray->shadow ) {
return AIR_TRUE;
}
ray->faar = intx->t;
ret = AIR_TRUE;
}
}
if ( _echoRayIntx_CubeSolid( &t, &tmax,
minb[0], maxb[0],
minb[1], maxb[1],
minb[2], maxb[2], ray ) ) {
intx->boxhits++;
if ( _echoRayIntx[b->type]( intx, ray, b, parm, tstate ) ) {
ray->faar = intx->t;
ret = AIR_TRUE;
}
}
return ret;
}
int
_echoRayIntx_List( RAYINTX_ARGS( List ) ) {
unsigned int i;
int ret;
echoObject *kid;
ret = AIR_FALSE;
for ( i=0; i<obj->objArr->len; i++ ) {
kid = obj->obj[i];
if ( _echoRayIntx[kid->type]( intx, ray, kid, parm, tstate ) ) {
ray->faar = intx->t;
ret = AIR_TRUE;
if ( ray->shadow ) {
/* no point in testing any further */
return ret;
}
}
}
return ret;
}
int
_echoRayIntx_Instance( RAYINTX_ARGS( Instance ) ) {
echoPos_t a[4], b[4], tmp;
echoRay iray;
/*
ELL_3V_COPY( iray.from, ray->from );
ELL_3V_COPY( iray.dir, ray->dir );
*/
ELL_4V_SET( a, ray->from[0], ray->from[1], ray->from[2], 1 );
ELL_4MV_MUL( b, obj->Mi, a );
ELL_34V_HOMOG( iray.from, b );
ELL_4V_SET( a, ray->dir[0], ray->dir[1], ray->dir[2], 0 );
ELL_4MV_MUL( b, obj->Mi, a );
ELL_3V_COPY( iray.dir, b );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: dir ( %g, %g, %g )\n%s -- Mi --> "
"( %g, %g, %g, %g )\n%s --> ( %g, %g, %g )\n",
_echoDot( tstate->depth ), "_echoRayIntx_Instance",
a[0], a[1], a[2], _echoDot( tstate->depth ),
b[0], b[1], b[2], b[3], _echoDot( tstate->depth ),
iray.dir[0], iray.dir[1], iray.dir[2] );
}
iray.neer = ray->neer;
iray.faar = ray->faar;
iray.shadow = ray->shadow;
if ( _echoRayIntx[obj->obj->type]( intx, &iray, obj->obj, parm, tstate ) ) {
ELL_4V_SET( a, intx->norm[0], intx->norm[1], intx->norm[2], 0 );
ELL_4MV_TMUL( b, obj->Mi, a );
ELL_3V_COPY( intx->norm, b );
ELL_3V_NORM( intx->norm, intx->norm, tmp );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: hit a %d ( at t=%g ) with M == \n",
_echoDot( tstate->depth ), "_echoRayIntx_Instance",
obj->obj->type, intx->t );
ell_4m_PRINT( stderr, obj->M );
fprintf( stderr, "%s ... ( det = %f ), and Mi == \n",
_echoDot( tstate->depth ), ell_4m_DET( obj->M ) );
ell_4m_PRINT( stderr, obj->Mi );
}
return AIR_TRUE;
}
/* else */
return AIR_FALSE;
}
void
_echoRayIntxUV_Noop( echoIntx *intx ) {
AIR_UNUSED( intx );
}
/*
** NB: the intersections with real objects need to normalize
** intx->norm
*/
_echoRayIntx_t
_echoRayIntx[ECHO_TYPE_NUM] = {
( _echoRayIntx_t )_echoRayIntx_Sphere,
( _echoRayIntx_t )_echoRayIntx_Cylinder,
( _echoRayIntx_t )_echoRayIntx_Superquad,
( _echoRayIntx_t )_echoRayIntx_Cube,
( _echoRayIntx_t )_echoRayIntx_Triangle,
( _echoRayIntx_t )_echoRayIntx_Rectangle,
( _echoRayIntx_t )_echoRayIntx_TriMesh,
( _echoRayIntx_t )_echoRayIntx_Noop,
( _echoRayIntx_t )_echoRayIntx_AABBox,
( _echoRayIntx_t )_echoRayIntx_Split,
( _echoRayIntx_t )_echoRayIntx_List,
( _echoRayIntx_t )_echoRayIntx_Instance,
};
_echoRayIntxUV_t
_echoRayIntxUV[ECHO_TYPE_NUM] = {
_echoRayIntxUV_Sphere, /* echoTypeSphere */
_echoRayIntxUV_Noop, /* echoTypeCylinder */
_echoRayIntxUV_Noop, /* sqd.c: echoTypeSuperquad */
_echoRayIntxUV_Cube, /* echoTypeCube */
_echoRayIntxUV_Noop, /* echoTypeTriangle */
_echoRayIntxUV_Noop, /* echoTypeRectangle */
_echoRayIntxUV_TriMesh, /* echoTypeTriMesh */
_echoRayIntxUV_Noop, /* echoTypeIsosurface */
_echoRayIntxUV_Noop, /* echoTypeAABBox */
_echoRayIntxUV_Noop, /* echoTypeSplit */
_echoRayIntxUV_Noop, /* echoTypeList */
_echoRayIntxUV_Noop /* echoTypeInstance */
};
int
echoRayIntx( echoIntx *intx, echoRay *ray, echoScene *scene,
echoRTParm *parm, echoThreadState *tstate ) {
unsigned int idx;
int ret;
echoObject *kid;
echoPos_t tmp;
_echoVerbose = tstate->verbose;
ret = AIR_FALSE;
for ( idx=0; idx<scene->rendArr->len; idx++ ) {
kid = scene->rend[idx];
if ( _echoRayIntx[kid->type]( intx, ray, kid, parm, tstate ) ) {
ray->faar = intx->t;
ret = AIR_TRUE;
if ( ray->shadow ) {
/* no point in testing any further */
return ret;
}
}
}
if ( ret ) {
/* being here means we're not a shadow ray */
ELL_3V_SCALE_ADD2( intx->pos, 1, ray->from, intx->t, ray->dir );
ELL_3V_SCALE( intx->view, -1, ray->dir );
ELL_3V_NORM( intx->view, intx->view, tmp );
/* this is needed for phong materials; for glass and metal,
it is either used directly, or as a reference in fuzzification */
_ECHO_REFLECT( intx->refl, intx->norm, intx->view, tmp );
}
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
/*
******* echoLightPosition( )
**
** sets "pos" to xyz position for current sample of given light
*/
void
33 echoLightPosition( echoPos_t pos[3], echoObject *light,
echoThreadState *tstate ) {
char me[]="echoLightPos";
echoPos_t x, y;
echoRectangle *rectLight;
x = tstate->jitt[0 + 2*echoJittableLight] + 0.5;
y = tstate->jitt[1 + 2*echoJittableLight] + 0.5;
switch( light->type ) {
case echoTypeRectangle:
rectLight = RECTANGLE( light );
ELL_3V_SCALE_ADD3( pos, 1, rectLight->origin,
x, rectLight->edge0,
y, rectLight->edge1 );
break;
default:
fprintf( stderr, "%s: currently only support echoTypeRectangle lights", me );
break;
}
return;
}
/*
******* echoLightColor( )
**
** sets "col" to RGB color for current sample of given light, which
** is at distance Ldist. Knowing distance allows computation of the
** inverse square fall-off of light intensity
*/
void
63 echoLightColor( echoCol_t rgb[3], echoPos_t Ldist,
echoObject *light, echoRTParm *parm, echoThreadState *tstate ) {
echoCol_t rgba[4], falloff;
echoPos_t x, y;
x = tstate->jitt[0 + 2*echoJittableLight] + 0.5;
y = tstate->jitt[1 + 2*echoJittableLight] + 0.5;
if ( light->ntext ) {
echoTextureLookup( rgba, light->ntext, x, y, parm );
ELL_3V_COPY( rgb, rgba );
} else {
ELL_3V_COPY( rgb, light->rgba );
}
ELL_3V_SCALE( rgb, light->mat[echoMatterLightPower], rgb );
if ( light->mat[echoMatterLightUnit] ) {
falloff = AIR_CAST( echoCol_t, light->mat[echoMatterLightUnit]/Ldist );
falloff *= falloff;
ELL_3V_SCALE( rgb, falloff, rgb );
}
return;
}
void
87 echoEnvmapLookup( echoCol_t rgb[3], echoPos_t norm[3], Nrrd *envmap ) {
int qn;
float *data;
#if ECHO_POS_FLOAT
qn = limnVtoQN_f[limnQN16octa]( norm );
#else
qn = limnVtoQN_d[limnQN16octa]( norm );
#endif
data = ( float* )( envmap->data ) + 3*qn;
ELL_3V_COPY( rgb, data );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
void
28 echoListAdd( echoObject *list, echoObject *child ) {
int idx;
if ( !( list && child &&
( echoTypeList == list->type ||
echoTypeAABBox == list->type ) ) )
return;
idx = airArrayLenIncr( LIST( list )->objArr, 1 );
LIST( list )->obj[idx] = child;
return;
}
int
43 _echoPosCompare( double *A, double *B ) {
return *A < *B ? -1 : ( *A > *B ? 1 : 0 );
}
/*
******** echoListSplit( )
**
** returns a echoObjectSplit to point to the same things as pointed
** to by the given echoObjectList
*/
echoObject *
55 echoListSplit( echoScene *scene, echoObject *list, int axis ) {
echoPos_t lo[3], hi[3], loest0[3], hiest0[3],
loest1[3], hiest1[3];
double *mids;
echoObject *o, *split, *list0, *list1;
int i, splitIdx, len;
if ( !( echoTypeList == list->type ||
echoTypeAABBox == list->type ) ) {
return list;
}
len = LIST( list )->objArr->len;
if ( len <= ECHO_LEN_SMALL_ENOUGH ) {
/* there is nothing or only one object */
return list;
}
split = echoObjectNew( scene, echoTypeSplit );
list0 = echoObjectNew( scene, echoTypeList );
list1 = echoObjectNew( scene, echoTypeList );
SPLIT( split )->axis = axis;
SPLIT( split )->obj0 = list0;
SPLIT( split )->obj1 = list1;
mids = ( double * )malloc( 2 * len * sizeof( double ) );
for ( i=0; i<len; i++ ) {
o = LIST( list )->obj[i];
echoBoundsGet( lo, hi, o );
mids[0 + 2*i] = ( lo[axis] + hi[axis] )/2;
*( ( unsigned int * )( mids + 1 + 2*i ) ) = i;
}
/* overkill, I know, I know */
qsort( mids, len, 2*sizeof( double ),
( int ( * )( const void *, const void * ) )_echoPosCompare );
/*
for ( i=0; i<len; i++ ) {
printf( "%d -> %g\n", i, mids[0 + 2*i] );
}
*/
splitIdx = len/2;
/* printf( "splitIdx = %d\n", splitIdx ); */
ELL_3V_SET( loest0, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( loest1, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( hiest0, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
ELL_3V_SET( hiest1, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
airArrayLenSet( LIST( list0 )->objArr, splitIdx );
for ( i=0; i<splitIdx; i++ ) {
o = LIST( list )->obj[*( ( unsigned int * )( mids + 1 + 2*i ) )];
LIST( list0 )->obj[i] = o;
echoBoundsGet( lo, hi, o );
/*
printf( "000 lo = ( %g, %g, %g ), hi = ( %g, %g, %g )\n",
lo[0], lo[1], lo[2], hi[0], hi[1], hi[2] );
*/
ELL_3V_MIN( loest0, loest0, lo );
ELL_3V_MAX( hiest0, hiest0, hi );
}
airArrayLenSet( LIST( list1 )->objArr, len-splitIdx );
for ( i=splitIdx; i<len; i++ ) {
o = LIST( list )->obj[*( ( unsigned int * )( mids + 1 + 2*i ) )];
LIST( list1 )->obj[i-splitIdx] = o;
echoBoundsGet( lo, hi, o );
/*
printf( "111 lo = ( %g, %g, %g ), hi = ( %g, %g, %g )\n",
lo[0], lo[1], lo[2], hi[0], hi[1], hi[2] );
*/
ELL_3V_MIN( loest1, loest1, lo );
ELL_3V_MAX( hiest1, hiest1, hi );
}
/*
printf( "0: loest = ( %g, %g, %g ); hiest = ( %g, %g, %g )\n",
loest0[0], loest0[1], loest0[2],
hiest0[0], hiest0[1], hiest0[2] );
printf( "1: loest = ( %g, %g, %g ); hiest = ( %g, %g, %g )\n",
loest1[0], loest1[1], loest1[2],
hiest1[0], hiest1[1], hiest1[2] );
*/
ELL_3V_COPY( SPLIT( split )->min0, loest0 );
ELL_3V_COPY( SPLIT( split )->max0, hiest0 );
ELL_3V_COPY( SPLIT( split )->min1, loest1 );
ELL_3V_COPY( SPLIT( split )->max1, hiest1 );
/* we can't delete the list object here, we just gut it so
that there's nothing substantial left of it */
airArrayLenSet( LIST( list )->objArr, 0 );
mids = ( double * )airFree( mids );
return split;
}
echoObject *
147 echoListSplit3( echoScene *scene, echoObject *list, int depth ) {
echoObject *ret, *tmp0, *tmp1;
if ( !( echoTypeList == list->type ||
echoTypeAABBox == list->type ) )
return NULL;
if ( !depth )
return list;
ret = echoListSplit( scene, list, 0 );
#define DOIT( obj, ax ) ( ( obj ) = echoListSplit( scene, ( obj ), ( ax ) ) )
#define MORE( obj ) echoTypeSplit == ( obj )->type
if ( MORE( ret ) ) {
tmp0 = DOIT( SPLIT( ret )->obj0, 1 );
if ( MORE( tmp0 ) ) {
tmp1 = DOIT( SPLIT( tmp0 )->obj0, 2 );
if ( MORE( tmp1 ) ) {
SPLIT( tmp1 )->obj0 = echoListSplit3( scene, SPLIT( tmp1 )->obj0, depth-1 );
SPLIT( tmp1 )->obj1 = echoListSplit3( scene, SPLIT( tmp1 )->obj1, depth-1 );
}
tmp1 = DOIT( SPLIT( tmp0 )->obj1, 2 );
if ( MORE( tmp1 ) ) {
SPLIT( tmp1 )->obj0 = echoListSplit3( scene, SPLIT( tmp1 )->obj0, depth-1 );
SPLIT( tmp1 )->obj1 = echoListSplit3( scene, SPLIT( tmp1 )->obj1, depth-1 );
}
}
tmp0 = DOIT( SPLIT( ret )->obj1, 1 );
if ( MORE( tmp0 ) ) {
tmp1 = DOIT( SPLIT( tmp0 )->obj0, 2 );
if ( MORE( tmp1 ) ) {
SPLIT( tmp1 )->obj0 = echoListSplit3( scene, SPLIT( tmp1 )->obj0, depth-1 );
SPLIT( tmp1 )->obj1 = echoListSplit3( scene, SPLIT( tmp1 )->obj1, depth-1 );
}
tmp1 = DOIT( SPLIT( tmp0 )->obj1, 2 );
if ( MORE( tmp1 ) ) {
SPLIT( tmp1 )->obj0 = echoListSplit3( scene, SPLIT( tmp1 )->obj0, depth-1 );
SPLIT( tmp1 )->obj1 = echoListSplit3( scene, SPLIT( tmp1 )->obj1, depth-1 );
}
}
}
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
int
echoObjectHasMatter[ECHO_TYPE_NUM] = {
1, /* echoTypeSphere */
1, /* echoTypeCylinder */
1, /* echoTypeSuperquad */
1, /* echoTypeCube */
1, /* echoTypeTriangle */
1, /* echoTypeRectangle */
1, /* echoTypeTriMesh */
1, /* echoTypeIsosurface */
0, /* echoTypeAABBox */
0, /* echoTypeSplit */
0, /* echoTypeList */
0, /* echoTypeInstance */
};
void
44 echoColorSet( echoObject *obj,
echoCol_t R, echoCol_t G, echoCol_t B, echoCol_t A ) {
if ( obj && echoObjectHasMatter[obj->type] ) {
ELL_4V_SET( obj->rgba, R, G, B, A );
}
}
void
53 echoMatterPhongSet( echoScene *scene, echoObject *obj,
echoCol_t ka, echoCol_t kd, echoCol_t ks, echoCol_t sp ) {
if ( scene && obj && echoObjectHasMatter[obj->type] ) {
obj->matter = echoMatterPhong;
obj->mat[echoMatterPhongKa] = ka;
obj->mat[echoMatterPhongKd] = kd;
obj->mat[echoMatterPhongKs] = ks;
obj->mat[echoMatterPhongSp] = sp;
}
}
void
66 echoMatterGlassSet( echoScene *scene, echoObject *obj,
echoCol_t indexr, echoCol_t ka,
echoCol_t kd, echoCol_t fuzzy ) {
if ( scene && obj && echoObjectHasMatter[obj->type] ) {
obj->matter = echoMatterGlass;
obj->mat[echoMatterGlassIndex] = indexr;
obj->mat[echoMatterGlassKa] = ka;
obj->mat[echoMatterGlassKd] = kd;
obj->mat[echoMatterGlassFuzzy] = fuzzy;
}
}
void
80 echoMatterMetalSet( echoScene *scene, echoObject *obj,
echoCol_t R0, echoCol_t ka,
echoCol_t kd, echoCol_t fuzzy ) {
if ( scene && obj && echoObjectHasMatter[obj->type] ) {
obj->matter = echoMatterMetal;
obj->mat[echoMatterMetalR0] = R0;
obj->mat[echoMatterMetalKa] = ka;
obj->mat[echoMatterMetalKd] = kd;
obj->mat[echoMatterMetalFuzzy] = fuzzy;
}
}
void
94 echoMatterLightSet( echoScene *scene, echoObject *obj,
echoCol_t power, echoCol_t unit ) {
if ( scene && obj && echoObjectHasMatter[obj->type] ) {
obj->matter = echoMatterLight;
obj->mat[echoMatterLightPower] = power;
obj->mat[echoMatterLightUnit] = unit;
/* HEY: god forbid we should change the material of the light after this */
_echoSceneLightAdd( scene, obj );
}
}
void
107 echoMatterTextureSet( echoScene *scene, echoObject *obj, Nrrd *ntext ) {
if ( scene && obj && ntext && echoObjectHasMatter[obj->type] &&
3 == ntext->dim &&
nrrdTypeUChar == ntext->type &&
4 == ntext->axis[0].size ) {
obj->ntext = ntext;
_echoSceneNrrdAdd( scene, ntext );
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
const int
echoPresent = 42;
const char *
echoBiffKey = "echo";
echoRTParm *
34 echoRTParmNew( void ) {
echoRTParm *parm;
parm = AIR_CALLOC( 1, echoRTParm );
if ( parm ) {
parm->jitterType = echoJitterNone;
parm->reuseJitter = AIR_FALSE;
parm->permuteJitter = AIR_TRUE;
parm->textureNN = AIR_TRUE;
parm->numSamples = 1;
parm->imgResU = parm->imgResV = 256;
parm->maxRecDepth = 5;
parm->renderLights = AIR_TRUE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_TRUE;
parm->sqNRI = 15;
parm->numThreads = 1;
parm->sqTol = 0.0001;
parm->aperture = 0.0; /* pinhole camera by default */
parm->timeGamma = 6.0;
parm->boxOpac = 0.2f;
parm->shadow = 1.0;
parm->glassC = 3;
ELL_3V_SET( parm->maxRecCol, 1.0, 0.0, 1.0 );
}
return parm;
}
echoRTParm *
63 echoRTParmNix( echoRTParm *parm ) {
airFree( parm );
return NULL;
}
echoGlobalState *
70 echoGlobalStateNew( void ) {
echoGlobalState *state;
state = AIR_CALLOC( 1, echoGlobalState );
if ( state ) {
state->verbose = 0;
state->time = 0;
state->nraw = NULL;
state->cam = NULL;
state->scene = NULL;
state->parm = NULL;
state->workIdx = 0;
state->workMutex = NULL;
}
return state;
}
echoGlobalState *
88 echoGlobalStateNix( echoGlobalState *state ) {
airFree( state );
/* mutex freed at end of echoRTRender( ) */
return NULL;
}
echoThreadState *
96 echoThreadStateNew( void ) {
echoThreadState *state;
state = AIR_CALLOC( 1, echoThreadState );
if ( state ) {
state->thread = airThreadNew( );
state->verbose = 0;
state->threadIdx = -1;
state->depth = -1;
state->njitt = nrrdNew( );
state->nperm = nrrdNew( );
state->permBuff = NULL;
state->jitt = NULL;
state->chanBuff = NULL;
state->rst = airRandMTStateNew( 0 );
state->returnPtr = NULL;
}
return state;
}
echoThreadState *
117 echoThreadStateNix( echoThreadState *state ) {
if ( state ) {
state->thread = airThreadNix( state->thread );
nrrdNuke( state->njitt );
nrrdNuke( state->nperm );
state->permBuff = AIR_CAST( unsigned int *, airFree( state->permBuff ) );
state->chanBuff = AIR_CAST( echoCol_t *, airFree( state->chanBuff ) );
airFree( state );
}
return NULL;
}
echoScene *
131 echoSceneNew( void ) {
echoScene *ret;
echoPtrPtrUnion eppu;
ret = AIR_CALLOC( 1, echoScene );
if ( ret ) {
ret->cat = NULL;
ret->catArr = airArrayNew( ( eppu.obj = &( ret->cat ), eppu.v ), NULL,
sizeof( echoObject * ),
ECHO_LIST_OBJECT_INCR );
airArrayPointerCB( ret->catArr,
airNull,
( void *( * )( void * ) )echoObjectNix );
ret->rend = NULL;
ret->rendArr = airArrayNew( ( eppu.obj = &( ret->rend ), eppu.v ), NULL,
sizeof( echoObject * ),
ECHO_LIST_OBJECT_INCR );
/* no callbacks set, renderable objecs are nixed from catArr */
ret->light = NULL;
ret->lightArr = airArrayNew( ( eppu.obj = &( ret->light ), eppu.v ), NULL,
sizeof( echoObject * ),
ECHO_LIST_OBJECT_INCR );
/* no callbacks set; light objects are nixed from catArr */
ret->nrrd = NULL;
ret->nrrdArr = airArrayNew( ( eppu.nrd = &( ret->nrrd ), eppu.v ), NULL,
sizeof( Nrrd * ),
ECHO_LIST_OBJECT_INCR );
airArrayPointerCB( ret->nrrdArr,
airNull,
( void *( * )( void * ) )nrrdNuke );
ret->envmap = NULL;
ELL_3V_SET( ret->ambi, 1.0, 1.0, 1.0 );
ELL_3V_SET( ret->bkgr, 0.0, 0.0, 0.0 );
}
return ret;
}
void
169 _echoSceneLightAdd( echoScene *scene, echoObject *obj ) {
unsigned int idx;
for ( idx=0; idx<scene->lightArr->len; idx++ ) {
if ( obj == scene->light[idx] ) {
break;
}
}
if ( scene->lightArr->len == idx ) {
idx = airArrayLenIncr( scene->lightArr, 1 );
scene->light[idx] = obj;
}
}
void
184 _echoSceneNrrdAdd( echoScene *scene, Nrrd *nrrd ) {
unsigned int idx;
for ( idx=0; idx<scene->nrrdArr->len; idx++ ) {
if ( nrrd == scene->nrrd[idx] ) {
break;
}
if ( scene->nrrdArr->len == idx ) {
idx = airArrayLenIncr( scene->nrrdArr, 1 );
scene->nrrd[idx] = nrrd;
}
}
}
echoScene *
199 echoSceneNix( echoScene *scene ) {
if ( scene ) {
airArrayNuke( scene->catArr );
airArrayNuke( scene->rendArr );
airArrayNuke( scene->lightArr );
airArrayNuke( scene->nrrdArr );
/* don't touch envmap nrrd */
airFree( scene );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
void
28 _echoPosSet( echoPos_t *p3, echoPos_t *matx, echoPos_t *p4 ) {
echoPos_t a[4], b[4];
if ( matx ) {
ELL_4V_SET( a, p4[0], p4[1], p4[2], 1 );
ELL_4MV_MUL( b, matx, a );
ELL_34V_HOMOG( p3, b );
}
else {
ELL_3V_COPY( p3, p4 );
}
}
echoObject *
42 echoRoughSphereNew( echoScene *scene, int theRes, int phiRes, echoPos_t *matx ) {
echoObject *trim;
echoPos_t *_pos, *pos, tmp[3];
int *_vert, *vert, thidx, phidx, n;
echoPos_t th, ph;
trim = echoObjectNew( scene, echoTypeTriMesh );
TRIMESH( trim )->numV = 2 + ( phiRes-1 )*theRes;
TRIMESH( trim )->numF = ( 2 + 2*( phiRes-2 ) )*theRes;
_pos = pos = ( echoPos_t * )calloc( 3*TRIMESH( trim )->numV, sizeof( echoPos_t ) );
_vert = vert = ( int * )calloc( 3*TRIMESH( trim )->numF, sizeof( int ) );
ELL_3V_SET( tmp, 0, 0, 1 ); _echoPosSet( pos, matx, tmp ); pos += 3;
for ( phidx=1; phidx<phiRes; phidx++ ) {
ph = AIR_AFFINE( 0, phidx, phiRes, 0.0, AIR_PI );
for ( thidx=0; thidx<theRes; thidx++ ) {
th = AIR_AFFINE( 0, thidx, theRes, 0.0, 2*AIR_PI );
ELL_3V_SET( tmp, cos( th )*sin( ph ), sin( th )*sin( ph ), cos( ph ) );
_echoPosSet( pos, matx, tmp ); pos += 3;
}
}
ELL_3V_SET( tmp, 0, 0, -1 ); _echoPosSet( pos, matx, tmp );
for ( thidx=0; thidx<theRes; thidx++ ) {
n = AIR_MOD( thidx+1, theRes );
ELL_3V_SET( vert, 0, 1+thidx, 1+n ); vert += 3;
}
for ( phidx=0; phidx<phiRes-2; phidx++ ) {
for ( thidx=0; thidx<theRes; thidx++ ) {
n = AIR_MOD( thidx+1, theRes );
ELL_3V_SET( vert, 1+phidx*theRes+thidx, 1+( 1+phidx )*theRes+thidx,
1+phidx*theRes+n ); vert += 3;
ELL_3V_SET( vert, 1+( 1+phidx )*theRes+thidx, 1+( 1+phidx )*theRes+n,
1+phidx*theRes+n ); vert += 3;
}
}
for ( thidx=0; thidx<theRes; thidx++ ) {
n = AIR_MOD( thidx+1, theRes );
ELL_3V_SET( vert, 1+( phiRes-2 )*theRes+thidx, TRIMESH( trim )->numV-1,
1+( phiRes-2 )*theRes+n );
vert += 3;
}
echoTriMeshSet( trim, TRIMESH( trim )->numV, _pos, TRIMESH( trim )->numF, _vert );
return( trim );
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
#define NEW_TMPL( TYPE, BODY ) \
echo##TYPE * \
_echo##TYPE##_new( void ) { \
echo##TYPE *obj; \
\
32 obj = ( echo##TYPE * )calloc( 1, sizeof( echo##TYPE ) ); \
obj->type = echoType##TYPE; \
do { BODY } while ( 0 ); \
return obj; \
}
#define NIX_TMPL( TYPE, BODY ) \
echo##TYPE * \
40 _echo##TYPE##_nix( echo##TYPE *obj ) { \
int dummy=0; \
\
if ( obj ) { \
do { BODY dummy=dummy;} while ( 0 ); \
airFree( obj ); \
} \
return NULL; \
48 }
void
51 _echoMatterInit( echoObject *obj ) {
obj->matter = echoMatterUnknown;
ELL_4V_SET( obj->rgba, 0, 0, 0, 0 );
memset( obj->mat, 0, ECHO_MATTER_PARM_NUM*sizeof( echoCol_t ) );
obj->ntext = NULL;
}
NEW_TMPL( Sphere,
_echoMatterInit( OBJECT( obj ) );
ELL_3V_SET( obj->pos, 0, 0, 0 );
obj->rad = 1.0;
)
NEW_TMPL( Cylinder,
_echoMatterInit( OBJECT( obj ) );
obj->axis = 2;
)
NEW_TMPL( Superquad,
_echoMatterInit( OBJECT( obj ) );
obj->axis = 2;
obj->A = obj->B = 1;
)
NEW_TMPL( Cube,
_echoMatterInit( OBJECT( obj ) );
)
NEW_TMPL( Triangle,
_echoMatterInit( OBJECT( obj ) );
ELL_3V_SET( obj->vert[0], 0, 0, 0 );
ELL_3V_SET( obj->vert[1], 0, 0, 0 );
ELL_3V_SET( obj->vert[2], 0, 0, 0 );
)
NEW_TMPL( Rectangle,
_echoMatterInit( OBJECT( obj ) );
ELL_3V_SET( obj->origin, 0, 0, 0 );
ELL_3V_SET( obj->edge0, 0, 0, 0 );
ELL_3V_SET( obj->edge1, 0, 0, 0 );
)
NEW_TMPL( TriMesh,
_echoMatterInit( OBJECT( obj ) );
ELL_3V_SET( obj->meanvert, 0, 0, 0 );
ELL_3V_SET( obj->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( obj->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
obj->numV = obj->numF = 0;
obj->pos = NULL;
obj->vert = NULL;
)
NIX_TMPL( TriMesh,
obj->pos = ( echoPos_t * )airFree( obj->pos );
obj->vert = ( int * )airFree( obj->vert );
)
NEW_TMPL( Isosurface,
_echoMatterInit( OBJECT( obj ) );
obj->volume = NULL;
obj->value = 0.0;
/* ??? */
)
NEW_TMPL( AABBox,
obj->obj = NULL;
ELL_3V_SET( obj->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( obj->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
)
NEW_TMPL( Split,
obj->axis = -1;
ELL_3V_SET( obj->min0, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( obj->max0, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
ELL_3V_SET( obj->min1, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( obj->max1, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
obj->obj0 = obj->obj1 = NULL;
)
NEW_TMPL( List,
echoPtrPtrUnion eppu;
obj->obj = NULL;
obj->objArr = airArrayNew( ( eppu.obj = &( obj->obj ), eppu.v ), NULL,
sizeof( echoObject * ),
ECHO_LIST_OBJECT_INCR );
)
NIX_TMPL( List,
airArrayNuke( obj->objArr );
)
NEW_TMPL( Instance,
ELL_4M_IDENTITY_SET( obj->M );
ELL_4M_IDENTITY_SET( obj->Mi );
obj->obj = NULL;
)
echoObject *( *
_echoObjectNew[ECHO_TYPE_NUM] )( void ) = {
( echoObject *( * )( void ) )_echoSphere_new,
( echoObject *( * )( void ) )_echoCylinder_new,
( echoObject *( * )( void ) )_echoSuperquad_new,
( echoObject *( * )( void ) )_echoCube_new,
( echoObject *( * )( void ) )_echoTriangle_new,
( echoObject *( * )( void ) )_echoRectangle_new,
( echoObject *( * )( void ) )_echoTriMesh_new,
( echoObject *( * )( void ) )_echoIsosurface_new,
( echoObject *( * )( void ) )_echoAABBox_new,
( echoObject *( * )( void ) )_echoSplit_new,
( echoObject *( * )( void ) )_echoList_new,
( echoObject *( * )( void ) )_echoInstance_new
};
echoObject *
echoObjectNew( echoScene *scene, signed char type ) {
echoObject *ret=NULL;
int idx;
if ( scene && AIR_IN_OP( echoTypeUnknown, type, echoTypeLast ) ) {
ret = _echoObjectNew[type]( );
idx = airArrayLenIncr( scene->catArr, 1 );
scene->cat[idx] = ret;
}
return ret;
}
int
echoObjectAdd( echoScene *scene, echoObject *obj ) {
int idx;
if ( scene && obj ) {
idx = airArrayLenIncr( scene->rendArr, 1 );
scene->rend[idx] = obj;
}
return 0;
}
echoObject *( *
_echoObjectNix[ECHO_TYPE_NUM] )( echoObject * ) = {
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeSphere */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeCylinder */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeSuperquad */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeCube */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeTriangle */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeRectangle */
( echoObject *( * )( echoObject * ) )_echoTriMesh_nix, /* echoTypeTriMesh */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeIsosurface */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeAABBox */
( echoObject *( * )( echoObject * ) )airFree, /* echoTypeSplit */
( echoObject *( * )( echoObject * ) )_echoList_nix, /* echoTypeList */
( echoObject *( * )( echoObject * ) )airFree /* echoTypeInstance */
};
echoObject *
echoObjectNix( echoObject *obj ) {
if ( obj ) {
_echoObjectNix[obj->type]( obj );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
int
28 echoThreadStateInit( int threadIdx, echoThreadState *tstate,
echoRTParm *parm, echoGlobalState *gstate ) {
static const char me[]="echoThreadStateInit";
if ( !( tstate && parm && gstate ) ) {
biffAddf( ECHO, "%s: got NULL pointer", me );
return 1;
}
/* tstate->thread set by echoThreadStateNew( ) */
tstate->gstate = gstate;
/* this will probably be over-written */
tstate->verbose = gstate->verbose;
tstate->threadIdx = threadIdx;
if ( nrrdMaybeAlloc_va( tstate->nperm, nrrdTypeInt, 2,
AIR_CAST( size_t, ECHO_JITTABLE_NUM ),
AIR_CAST( size_t, parm->numSamples ) ) ) {
biffMovef( ECHO, NRRD,
"%s: couldn't allocate jitter permutation array", me );
return 1;
}
nrrdAxisInfoSet_va( tstate->nperm, nrrdAxisInfoLabel,
"jittable", "sample" );
if ( nrrdMaybeAlloc_va( tstate->njitt, echoPos_nt, 3,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, ECHO_JITTABLE_NUM ),
AIR_CAST( size_t, parm->numSamples ) ) ) {
biffMovef( ECHO, NRRD, "%s: couldn't allocate jitter array", me );
return 1;
}
nrrdAxisInfoSet_va( tstate->njitt, nrrdAxisInfoLabel,
"x, y", "jittable", "sample" );
tstate->permBuff = AIR_CAST( unsigned int *, airFree( tstate->permBuff ) );
if ( !( tstate->permBuff = AIR_CAST( unsigned int *,
calloc( parm->numSamples, sizeof( int ) ) ) ) ) {
biffAddf( ECHO, "%s: couldn't allocate permutation buffer", me );
return 1;
}
tstate->chanBuff = ( echoCol_t * )airFree( tstate->chanBuff );
if ( !( tstate->chanBuff =
( echoCol_t* )calloc( ECHO_IMG_CHANNELS * parm->numSamples,
sizeof( echoCol_t ) ) ) ) {
biffAddf( ECHO, "%s: couldn't allocate img channel sample buffer", me );
return 1;
}
airSrandMT_r( tstate->rst, AIR_CAST( unsigned int, ( parm->seedRand
? airTime( )
: threadIdx ) ) );
tstate->returnPtr = NULL;
return 0;
}
/*
******** echoJitterCompute( )
**
**
*/
void
89 echoJitterCompute( echoRTParm *parm, echoThreadState *tstate ) {
echoPos_t *jitt, w;
int s, i, j, xi, yi, n, N, *perm;
N = parm->numSamples;
n = ( int )sqrt( N );
w = 1.0/n;
/* each row in perm[] is for one sample, for going through all jittables;
each column is a different permutation of [0..parm->numSamples-1] */
perm = ( int * )tstate->nperm->data;
for ( j=0; j<ECHO_JITTABLE_NUM; j++ ) {
airShuffle_r( tstate->rst, tstate->permBuff,
parm->numSamples, parm->permuteJitter );
for ( s=0; s<N; s++ ) {
perm[j + ECHO_JITTABLE_NUM*s] = tstate->permBuff[s];
}
}
jitt = ( echoPos_t * )tstate->njitt->data;
for ( s=0; s<N; s++ ) {
for ( j=0; j<ECHO_JITTABLE_NUM; j++ ) {
i = perm[j + ECHO_JITTABLE_NUM*s];
xi = i % n;
yi = i / n;
switch( parm->jitterType ) {
case echoJitterNone:
jitt[0 + 2*j] = 0.0;
jitt[1 + 2*j] = 0.0;
break;
case echoJitterGrid:
jitt[0 + 2*j] = NRRD_POS( nrrdCenterCell, -0.5, 0.5, n, xi );
jitt[1 + 2*j] = NRRD_POS( nrrdCenterCell, -0.5, 0.5, n, yi );
break;
case echoJitterJitter:
jitt[0 + 2*j] = ( NRRD_POS( nrrdCenterCell, -0.5, 0.5, n, xi )
+ w*( airDrandMT_r( tstate->rst ) - 0.5 ) );
jitt[1 + 2*j] = ( NRRD_POS( nrrdCenterCell, -0.5, 0.5, n, yi )
+ w*( airDrandMT_r( tstate->rst ) - 0.5 ) );
break;
case echoJitterRandom:
jitt[0 + 2*j] = airDrandMT_r( tstate->rst ) - 0.5;
jitt[1 + 2*j] = airDrandMT_r( tstate->rst ) - 0.5;
break;
}
}
jitt += 2*ECHO_JITTABLE_NUM;
}
return;
}
/*
******** echoRTRenderCheck
**
** does all the error checking required of echoRTRender and
** everything that it calls
*/
int
146 echoRTRenderCheck( Nrrd *nraw, limnCamera *cam, echoScene *scene,
echoRTParm *parm, echoGlobalState *gstate ) {
static const char me[]="echoRTRenderCheck";
int tmp;
if ( !( nraw && cam && scene && parm && gstate ) ) {
biffAddf( ECHO, "%s: got NULL pointer", me );
return 1;
}
if ( limnCameraUpdate( cam ) ) {
biffMovef( ECHO, LIMN, "%s: camera trouble", me );
return 1;
}
if ( scene->envmap ) {
if ( limnEnvMapCheck( scene->envmap ) ) {
biffMovef( ECHO, LIMN, "%s: environment map not valid", me );
return 1;
}
}
if ( airEnumValCheck( echoJitter, parm->jitterType ) ) {
biffAddf( ECHO, "%s: jitter method ( %d ) invalid", me, parm->jitterType );
return 1;
}
if ( !( parm->numSamples > 0 ) ) {
biffAddf( ECHO, "%s: # samples ( %d ) invalid", me, parm->numSamples );
return 1;
}
if ( !( parm->imgResU > 0 && parm->imgResV ) ) {
biffAddf( ECHO, "%s: image dimensions ( %dx%d ) invalid", me,
parm->imgResU, parm->imgResV );
return 1;
}
if ( !AIR_EXISTS( parm->aperture ) ) {
biffAddf( ECHO, "%s: aperture doesn't exist", me );
return 1;
}
switch ( parm->jitterType ) {
case echoJitterNone:
case echoJitterRandom:
break;
case echoJitterGrid:
case echoJitterJitter:
tmp = ( int )sqrt( parm->numSamples );
if ( tmp*tmp != parm->numSamples ) {
biffAddf( ECHO,
"%s: need a square # samples for %s jitter method ( not %d )",
me, airEnumStr( echoJitter, parm->jitterType ), parm->numSamples );
return 1;
}
break;
}
/* for the time being things are hard-coded to be r, g, b, a, time */
if ( ECHO_IMG_CHANNELS != 5 ) {
biffAddf( ECHO, "%s: ECHO_IMG_CHANNELS != 5", me );
return 1;
}
/* all is well */
return 0;
}
void
210 echoChannelAverage( echoCol_t *img,
echoRTParm *parm, echoThreadState *tstate ) {
int s;
echoCol_t R, G, B, A, T;
R = G = B = A = T = 0;
for ( s=0; s<parm->numSamples; s++ ) {
R += tstate->chanBuff[0 + ECHO_IMG_CHANNELS*s];
G += tstate->chanBuff[1 + ECHO_IMG_CHANNELS*s];
B += tstate->chanBuff[2 + ECHO_IMG_CHANNELS*s];
A += tstate->chanBuff[3 + ECHO_IMG_CHANNELS*s];
T += tstate->chanBuff[4 + ECHO_IMG_CHANNELS*s];
}
img[0] = R / parm->numSamples;
img[1] = G / parm->numSamples;
img[2] = B / parm->numSamples;
img[3] = A / parm->numSamples;
img[4] = T;
return;
}
/*
******** echoRayColor
**
** This is called by echoRTRender and by the various color routines,
** following an intersection with non-phong non-light material ( the
** things that require reflection or refraction rays ). As such, it is
** never called on shadow rays.
*/
void
241 echoRayColor( echoCol_t *chan, echoRay *ray,
echoScene *scene, echoRTParm *parm, echoThreadState *tstate ) {
static const char me[]="echoRayColor";
echoCol_t rgba[4];
echoIntx intx;
tstate->depth++;
if ( tstate->depth > parm->maxRecDepth ) {
/* we've exceeded the recursion depth, so no more rays for you */
ELL_4V_SET( chan, parm->maxRecCol[0], parm->maxRecCol[1],
parm->maxRecCol[2], 1.0 );
goto done;
}
intx.boxhits = 0;
if ( !echoRayIntx( &intx, ray, scene, parm, tstate ) ) {
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: ( nothing was hit )\n", _echoDot( tstate->depth ), me );
}
/* ray hits nothing in scene */
ELL_4V_SET_TT( chan, echoCol_t,
scene->bkgr[0], scene->bkgr[1], scene->bkgr[2],
( parm->renderBoxes
? 1.0 - pow( 1.0 - parm->boxOpac, intx.boxhits )
: 0.0 ) );
goto done;
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: hit a %d ( %p ) at ( %g, %g, %g )\n"
"%s = %g along ( %g, %g, %g )\n", _echoDot( tstate->depth ), me,
intx.obj->type, AIR_CAST( void*, intx.obj ),
intx.pos[0], intx.pos[1], intx.pos[2], _echoDot( tstate->depth ),
intx.t, ray->dir[0], ray->dir[1], ray->dir[2] );
}
echoIntxColor( rgba, &intx, scene, parm, tstate );
ELL_4V_COPY( chan, rgba );
done:
tstate->depth--;
return;
}
void *
284 _echoRTRenderThreadBody( void *_arg ) {
char done[20];
int imgUi, imgVi, /* integral pixel indices */
samp; /* which sample are we doing */
echoPos_t tmp0, tmp1,
pixUsz, pixVsz, /* U and V dimensions of a pixel */
U[4], V[4], N[4], /* view space basis ( only first 3 elements used ) */
imgU, imgV, /* floating point pixel center locations */
eye[3], /* eye center before jittering */
at[3], /* ray destination ( pixel center post-jittering ) */
imgOrig[3]; /* image origin */
double time0;
echoRay ray; /* ( not a pointer ) */
echoThreadState *arg;
echoCol_t *img, *chan; /* current scanline of channel buffer array */
Nrrd *nraw; /* copies of arguments to echoRTRender . . . */
limnCamera *cam;
echoScene *scene;
echoRTParm *parm;
arg = ( echoThreadState * )_arg;
nraw = arg->gstate->nraw;
cam = arg->gstate->cam;
scene = arg->gstate->scene;
parm = arg->gstate->parm;
echoJitterCompute( arg->gstate->parm, arg );
if ( arg->gstate->verbose > 2 ) {
nrrdSave( "jitt.nrrd", arg->njitt, NULL );
}
/* set eye, U, V, N, imgOrig */
ELL_3V_COPY( eye, arg->gstate->cam->from );
ELL_4MV_ROW0_GET( U, cam->W2V );
ELL_4MV_ROW1_GET( V, cam->W2V );
ELL_4MV_ROW2_GET( N, cam->W2V );
ELL_3V_SCALE_ADD2( imgOrig, 1.0, eye, cam->vspDist, N );
/* determine size of a single pixel ( based on cell-centering ) */
pixUsz = ( cam->uRange[1] - cam->uRange[0] )/( parm->imgResU );
pixVsz = ( cam->vRange[1] - cam->vRange[0] )/( parm->imgResV );
arg->depth = 0;
ray.shadow = AIR_FALSE;
arg->verbose = AIR_FALSE;
while ( 1 ) {
if ( arg->gstate->workMutex ) {
airThreadMutexLock( arg->gstate->workMutex );
}
imgVi = arg->gstate->workIdx;
if ( arg->gstate->workIdx < parm->imgResV ) {
arg->gstate->workIdx += 1;
}
if ( !( imgVi % 5 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, imgVi, parm->imgResV-1, done ) );
fflush( stderr );
}
if ( arg->gstate->workMutex ) {
airThreadMutexUnlock( arg->gstate->workMutex );
}
if ( imgVi == parm->imgResV ) {
/* we're done! */
break;
}
imgV = NRRD_POS( nrrdCenterCell, cam->vRange[0], cam->vRange[1],
parm->imgResV, imgVi );
for ( imgUi=0; imgUi<parm->imgResU; imgUi++ ) {
imgU = NRRD_POS( nrrdCenterCell, cam->uRange[0], cam->uRange[1],
parm->imgResU, imgUi );
img = ( ( echoCol_t * )nraw->data
+ ECHO_IMG_CHANNELS*( imgUi + parm->imgResU*imgVi ) );
/* initialize things on first "scanline" */
arg->jitt = ( echoPos_t * )arg->njitt->data;
chan = arg->chanBuff;
/*
arg->verbose = ( ( 48 == imgUi && 13 == imgVi )
|| ( 49 == imgUi && 13 == imgVi ) );
*/
if ( arg->verbose ) {
fprintf( stderr, "\n" );
fprintf( stderr, "-----------------------------------------------\n" );
fprintf( stderr, "----------------- ( %3d, %3d ) ------------------\n",
imgUi, imgVi );
fprintf( stderr, "-----------------------------------------------\n\n" );
}
/* go through samples */
for ( samp=0; samp<parm->numSamples; samp++ ) {
/* set ray.from[] */
ELL_3V_COPY( ray.from, eye );
if ( parm->aperture ) {
tmp0 = parm->aperture*( arg->jitt[0 + 2*echoJittableLens] );
tmp1 = parm->aperture*( arg->jitt[1 + 2*echoJittableLens] );
ELL_3V_SCALE_ADD3( ray.from, 1, ray.from, tmp0, U, tmp1, V );
}
/* set at[] */
tmp0 = imgU + pixUsz*( arg->jitt[0 + 2*echoJittablePixel] );
tmp1 = imgV + pixVsz*( arg->jitt[1 + 2*echoJittablePixel] );
ELL_3V_SCALE_ADD3( at, 1, imgOrig, tmp0, U, tmp1, V );
/* do it! */
ELL_3V_SUB( ray.dir, at, ray.from );
ELL_3V_NORM( ray.dir, ray.dir, tmp0 );
ray.neer = 0.0;
ray.faar = ECHO_POS_MAX;
time0 = airTime( );
if ( 0 ) {
memset( chan, 0, ECHO_IMG_CHANNELS*sizeof( echoCol_t ) );
} else {
echoRayColor( chan, &ray, scene, parm, arg );
}
chan[4] = AIR_CAST( echoCol_t, airTime( ) - time0 );
/* move to next "scanline" */
arg->jitt += 2*ECHO_JITTABLE_NUM;
chan += ECHO_IMG_CHANNELS;
}
echoChannelAverage( img, parm, arg );
img += ECHO_IMG_CHANNELS;
if ( !parm->reuseJitter ) {
echoJitterCompute( parm, arg );
}
}
}
return _arg;
}
/*
******** echoRTRender
**
** top-level call to accomplish all ( ray-tracing ) rendering. As much
** error checking as possible should be done here and not in the
** lower-level functions.
*/
int
427 echoRTRender( Nrrd *nraw, limnCamera *cam, echoScene *scene,
echoRTParm *parm, echoGlobalState *gstate ) {
static const char me[]="echoRTRender";
int tid, ret;
airArray *mop;
echoThreadState *tstate[ECHO_THREAD_MAX];
if ( echoRTRenderCheck( nraw, cam, scene, parm, gstate ) ) {
biffAddf( ECHO, "%s: problem with input", me );
return 1;
}
gstate->nraw = nraw;
gstate->cam = cam;
gstate->scene = scene;
gstate->parm = parm;
mop = airMopNew( );
if ( nrrdMaybeAlloc_va( nraw, echoCol_nt, 3,
AIR_CAST( size_t, ECHO_IMG_CHANNELS ),
AIR_CAST( size_t, parm->imgResU ),
AIR_CAST( size_t, parm->imgResV ) ) ) {
biffMovef( ECHO, NRRD, "%s: couldn't allocate output image", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nraw, ( airMopper )nrrdNix, airMopOnError );
nrrdAxisInfoSet_va( nraw, nrrdAxisInfoLabel,
"r, g, b, a, t", "x", "y" );
nrrdAxisInfoSet_va( nraw, nrrdAxisInfoMin,
AIR_NAN, cam->uRange[0], cam->vRange[0] );
nrrdAxisInfoSet_va( nraw, nrrdAxisInfoMax,
AIR_NAN, cam->uRange[1], cam->vRange[1] );
gstate->time = airTime( );
if ( parm->numThreads > 1 ) {
gstate->workMutex = airThreadMutexNew( );
airMopAdd( mop, gstate->workMutex,
( airMopper )airThreadMutexNix, airMopAlways );
} else {
gstate->workMutex = NULL;
}
for ( tid=0; tid<parm->numThreads; tid++ ) {
if ( !( tstate[tid] = echoThreadStateNew( ) ) ) {
biffAddf( ECHO, "%s: failed to create thread state %d", me, tid );
airMopError( mop ); return 1;
}
if ( echoThreadStateInit( tid, tstate[tid], parm, gstate ) ) {
biffAddf( ECHO, "%s: failed to initialized thread state %d", me, tid );
airMopError( mop ); return 1;
}
airMopAdd( mop, tstate[tid], ( airMopper )echoThreadStateNix, airMopAlways );
}
fprintf( stderr, "%s: ", me ); /* prep for printing airDoneStr */
gstate->workIdx = 0;
for ( tid=0; tid<parm->numThreads; tid++ ) {
if ( ( ret = airThreadStart( tstate[tid]->thread, _echoRTRenderThreadBody,
( void * )( tstate[tid] ) ) ) ) {
biffAddf( ECHO, "%s: thread[%d] failed to start: %d", me, tid, ret );
airMopError( mop ); return 1;
}
}
for ( tid=0; tid<parm->numThreads; tid++ ) {
if ( ( ret = airThreadJoin( tstate[tid]->thread,
( void ** )( &( tstate[tid]->returnPtr ) ) ) ) ) {
biffAddf( ECHO, "%s: thread[%d] failed to join: %d", me, tid, ret );
airMopError( mop ); return 1;
}
}
gstate->time = airTime( ) - gstate->time;
fprintf( stderr, "\n%s: time = %g\n", me, gstate->time );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
void
28 echoSphereSet( echoObject *sphere,
echoPos_t x, echoPos_t y, echoPos_t z, echoPos_t rad ) {
if ( sphere && echoTypeSphere == sphere->type ) {
ELL_3V_SET( SPHERE( sphere )->pos, x, y, z );
SPHERE( sphere )->rad = rad;
}
return;
}
void
39 echoCylinderSet( echoObject *cylind,
int axis ) {
if ( cylind && echoTypeCylinder == cylind->type ) {
CYLINDER( cylind )->axis = axis;
}
return;
}
void
49 echoSuperquadSet( echoObject *squad,
int axis, echoPos_t A, echoPos_t B ) {
if ( squad && echoTypeSuperquad == squad->type ) {
SUPERQUAD( squad )->axis = axis;
SUPERQUAD( squad )->A = A;
SUPERQUAD( squad )->B = B;
}
return;
}
void
61 echoRectangleSet( echoObject *rect,
echoPos_t ogx, echoPos_t ogy, echoPos_t ogz,
echoPos_t e0x, echoPos_t e0y, echoPos_t e0z,
echoPos_t e1x, echoPos_t e1y, echoPos_t e1z ) {
if ( rect && echoTypeRectangle == rect->type ) {
ELL_3V_SET( RECTANGLE( rect )->origin, ogx, ogy, ogz );
ELL_3V_SET( RECTANGLE( rect )->edge0, e0x, e0y, e0z );
ELL_3V_SET( RECTANGLE( rect )->edge1, e1x, e1y, e1z );
}
return;
}
void
75 echoTriangleSet( echoObject *tri,
echoPos_t xx0, echoPos_t yy0, echoPos_t zz0,
echoPos_t xx1, echoPos_t yy1, echoPos_t zz1,
echoPos_t xx2, echoPos_t yy2, echoPos_t zz2 ) {
if ( tri && echoTypeTriangle == tri->type ) {
ELL_3V_SET( TRIANGLE( tri )->vert[0], xx0, yy0, zz0 );
ELL_3V_SET( TRIANGLE( tri )->vert[1], xx1, yy1, zz1 );
ELL_3V_SET( TRIANGLE( tri )->vert[2], xx2, yy2, zz2 );
}
return;
}
/*
******** echoTriMeshSet( )
**
** This has to be called any time that the locations of the points are
** changing, even if the connectivity is not changed, because of how
** the bounding box and mean vert position is calculated here.
**
** NB: the TriMesh will directly use the given pos[] and vert[] arrays,
** so don't go freeing them after they've been passed here.
*/
void
99 echoTriMeshSet( echoObject *trim,
int numV, echoPos_t *pos,
int numF, int *vert ) {
int i;
if ( trim && echoTypeTriMesh == trim->type ) {
TRIMESH( trim )->numV = numV;
TRIMESH( trim )->numF = numF;
TRIMESH( trim )->pos = pos;
TRIMESH( trim )->vert = vert;
ELL_3V_SET( TRIMESH( trim )->min, ECHO_POS_MAX, ECHO_POS_MAX, ECHO_POS_MAX );
ELL_3V_SET( TRIMESH( trim )->max, ECHO_POS_MIN, ECHO_POS_MIN, ECHO_POS_MIN );
ELL_3V_SET( TRIMESH( trim )->meanvert, 0.0, 0.0, 0.0 );
for ( i=0; i<numV; i++ ) {
ELL_3V_MIN( TRIMESH( trim )->min, TRIMESH( trim )->min, pos + 3*i );
ELL_3V_MAX( TRIMESH( trim )->max, TRIMESH( trim )->max, pos + 3*i );
ELL_3V_INCR( TRIMESH( trim )->meanvert, pos + 3*i );
}
ELL_3V_SCALE( TRIMESH( trim )->meanvert, 1.0/numV, TRIMESH( trim )->meanvert );
}
return;
}
void
123 echoInstanceSet( echoObject *inst,
echoPos_t *M, echoObject *obj ) {
if ( inst && echoTypeInstance == inst->type ) {
ell_4m_INV( INSTANCE( inst )->Mi, M );
ELL_4M_COPY( INSTANCE( inst )->M, M );
INSTANCE( inst )->obj = obj;
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "echo.h"
#include "privateEcho.h"
echoPos_t
28 _echo_SuperquadX_v( echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t xxb, yya, zza;
xxb = pow( x*x, 1/B ); yya = pow( y*y, 1/A ); zza = pow( z*z, 1/A );
return pow( yya + zza, A/B ) + xxb - 1;
}
echoPos_t
37 _echo_SuperquadY_v( echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t xxa, yyb, zza;
xxa = pow( x*x, 1/A ); yyb = pow( y*y, 1/B ); zza = pow( z*z, 1/A );
return pow( xxa + zza, A/B ) + yyb - 1;
}
echoPos_t
46 _echo_SuperquadZ_v( echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t xxa, yya, zzb;
xxa = pow( x*x, 1/A ); yya = pow( y*y, 1/A ); zzb = pow( z*z, 1/B );
return pow( xxa + yya, A/B ) + zzb - 1;
}
/* -------------------------------------------------------- */
echoPos_t
57 _echo_SuperquadX_vg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxb, yya, zza;
xxb = pow( x*x, 1/B ); yya = pow( y*y, 1/A ); zza = pow( z*z, 1/A );
R = pow( yya + zza, ( A/B )-1 );
ELL_3V_SET( grad, 2*xxb/( B*x ), 2*R*yya/( B*y ), 2*R*zza/( B*z ) );
return pow( yya + zza, A/B ) + xxb - 1;
}
echoPos_t
69 _echo_SuperquadY_vg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxa, yyb, zza;
xxa = pow( x*x, 1/A ); yyb = pow( y*y, 1/B ); zza = pow( z*z, 1/A );
R = pow( xxa + zza, ( A/B )-1 );
ELL_3V_SET( grad, 2*R*xxa/( B*x ), 2*yyb/( B*y ), 2*R*zza/( B*z ) );
return pow( xxa + zza, A/B ) + yyb - 1;
}
echoPos_t
81 _echo_SuperquadZ_vg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxa, yya, zzb;
xxa = pow( x*x, 1/A ); yya = pow( y*y, 1/A ); zzb = pow( z*z, 1/B );
R = pow( xxa + yya, ( A/B )-1 );
ELL_3V_SET( grad, 2*R*xxa/( B*x ), 2*R*yya/( B*y ), 2*zzb/( B*z ) );
return pow( xxa + yya, A/B ) + zzb - 1;
}
/* -------------------------------------------------------- */
echoPos_t
95 _echo_SuperquadX_lvg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxb, yya, zza, larg;
echoPos_t ret;
xxb = pow( x*x, 1/B ); yya = pow( y*y, 1/A ); zza = pow( z*z, 1/A );
R = pow( yya + zza, 1-( A/B ) )*xxb;
ELL_3V_SET( grad,
2/( B*x*( 1 + pow( yya + zza, A/B )/xxb ) ),
2*yya/( B*y*( yya + zza + R ) ),
2*zza/( B*z*( yya + zza + R ) ) );
larg = pow( yya + zza, A/B ) + xxb;
ret= larg > 0 ? log( larg ) : ECHO_POS_MIN;
/*
if ( !( AIR_EXISTS( grad[0] ) && AIR_EXISTS( grad[1] ) && AIR_EXISTS( grad[2] ) ) ) {
fprintf( stderr, "_echo_SuperquadX_lvg: PANIC\n" );
fprintf( stderr, "x = %g, y = %g, z = %g, A = %g, B = %g\n",
x, y, z, A, B );
fprintf( stderr, "pow( %g * %g = %g, 1/%g = %g ) = %g\n",
x, x, x*x, B, 1/B, pow( x*x, 1/B ) );
fprintf( stderr, "xxb = %g, yya = %g, zza = %g\n",
xxb, yya, zza );
fprintf( stderr, "R: pow( %g + %g = %g, 1-( %g/%g = %g ) = %g ) = %g*%g = %g\n",
yya, zza, yya + zza,
A, B, A/B, 1-( A/B ),
pow( yya + zza, 1-( A/B ) ), xxb,
pow( yya + zza, 1-( A/B ) )*xxb );
fprintf( stderr, "grad[0]: 2/( %g * %g * ( 1 + pow( %g + %g = %g, %g/%g = %g )/%g = %g ) ) = %g\n",
B, x, yya, zza, yya+zza, A, B, A/B, xxb,
pow( yya + zza, A/B )/xxb, grad[0] );
fprintf( stderr, "grad[1]: 2*%g/( %g*%g*( %g + %g + %g = %g ) = %g ) = %g\n",
yya, B, y, yya, zza, R, yya + zza + R,
B*y*( yya + zza + R ),
2*yya/( B*y*( yya + zza + R ) ) );
fprintf( stderr, "log( pow( %g + %g = %g, %g ) = %g + %g ) = %g\n",
yya, zza, yya+zza, A/B, pow( yya + zza, A/B ), xxb, ret );
fprintf( stderr, "grad = %g %g %g\n", grad[0], grad[1], grad[2] );
fprintf( stderr, "\n----------\n\n" );
}
*/
return ret;
}
echoPos_t
142 _echo_SuperquadY_lvg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxa, yyb, zza, larg;
xxa = pow( x*x, 1/A ); yyb = pow( y*y, 1/B ); zza = pow( z*z, 1/A );
R = pow( xxa + zza, 1-( A/B ) )*yyb;
ELL_3V_SET( grad,
2*xxa/( B*x*( xxa + zza + R ) ),
2/( B*y*( 1 + pow( xxa + zza, A/B )/yyb ) ),
2*zza/( B*z*( xxa + zza + R ) ) );
larg = pow( xxa + zza, A/B ) + yyb;
return larg > 0 ? log( larg ) : ECHO_POS_MIN;
}
echoPos_t
158 _echo_SuperquadZ_lvg( echoPos_t grad[3],
echoPos_t x, echoPos_t y, echoPos_t z,
echoPos_t A, echoPos_t B ) {
echoPos_t R, xxa, yya, zzb, larg;
echoPos_t ret;
xxa = pow( x*x, 1/A ); yya = pow( y*y, 1/A ); zzb = pow( z*z, 1/B );
R = pow( xxa + yya, 1-( A/B ) )*zzb;
ELL_3V_SET( grad,
2*xxa/( B*x*( xxa + yya + R ) ),
2*yya/( B*y*( xxa + yya + R ) ),
2/( B*z*( 1 + pow( xxa + yya, A/B )/zzb ) ) );
larg = pow( xxa + yya, A/B ) + zzb;
ret = larg > 0 ? log( larg ) : ECHO_POS_MIN;
/*
if ( !AIR_EXISTS( ret ) ) {
fprintf( stderr, "_echo_SuperquadZ_lvg: PANIC\n" );
fprintf( stderr, "x = %g, y = %g, z = %g, A = %g, B = %g\n",
x, y, z, A, B );
fprintf( stderr, "pow( %g*%g = %g, %g ) = %g\n",
x, x, x*x, 1/A, xxa );
fprintf( stderr, "pow( %g*%g = %g, %g ) = %g\n",
y, y, y*y, 1/A, yya );
fprintf( stderr, "log( pow( %g, %g ) = %g + %g ) = %g\n",
xxa + yya, A/B, pow( xxa + yya, A/B ), zzb, ret );
exit( 0 );
}
*/
return ret;
}
/* -------------------------------------------------------- */
int
194 _echoRayIntx_Superquad( RAYINTX_ARGS( Superquad ) ) {
char me[]="_echoRayIntx_Superquad";
echoPos_t TT=0, Tmin, Tmax, t0, t1, t2, t3, v1, v2, diff, tol,
saveTmin, Vmin, Vmax, VV=0, dV, dVmin, dVmax, tmp,
( *v )( echoPos_t, echoPos_t, echoPos_t,
echoPos_t, echoPos_t ),
( *vg )( echoPos_t[3],
echoPos_t, echoPos_t, echoPos_t,
echoPos_t, echoPos_t ),
( *lvg )( echoPos_t[3],
echoPos_t, echoPos_t, echoPos_t,
echoPos_t, echoPos_t ),
from[3], grad[3], pos[3]; /* these two used only by macros */
int iter;
if ( !_echoRayIntx_CubeSolid( &Tmin, &Tmax,
-1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON,
-1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON,
-1-2*ECHO_EPSILON, 1+2*ECHO_EPSILON, ray ) ) {
return AIR_FALSE;
}
switch( obj->axis ) {
case 0:
v = _echo_SuperquadX_v;
vg = _echo_SuperquadX_vg;
lvg = _echo_SuperquadX_lvg;
break;
case 1:
v = _echo_SuperquadY_v;
vg = _echo_SuperquadY_vg;
lvg = _echo_SuperquadY_lvg;
break;
case 2: default:
v = _echo_SuperquadZ_v;
vg = _echo_SuperquadZ_vg;
lvg = _echo_SuperquadZ_lvg;
break;
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: Tmin, Tmax = %g, %g, ax = %d\n",
_echoDot( tstate->depth ), me, Tmin, Tmax, obj->axis );
}
#define VAL( TT ) \
( ELL_3V_SCALE_ADD2( pos, 1, from, ( TT ), ray->dir ), \
v( pos[0], pos[1], pos[2], obj->A, obj->B ) )
#define VALGRAD( VV, DV, TT ) \
ELL_3V_SCALE_ADD2( pos, 1, from, ( TT ), ray->dir ); \
( VV ) = vg( grad, pos[0], pos[1], pos[2], obj->A, obj->B ); \
( DV ) = ELL_3V_DOT( grad, ray->dir )
#define LVALGRAD( VV, DV, TT ) \
ELL_3V_SCALE_ADD2( pos, 1, from, ( TT ), ray->dir ); \
( VV ) = lvg( grad, pos[0], pos[1], pos[2], obj->A, obj->B ); \
( DV ) = ELL_3V_DOT( grad, ray->dir )
#define RR 0.61803399
#define CC ( 1.0-RR )
#define SHIFT3( a, b, c, d ) ( a )=( b ); ( b )=( c ); ( c )=( d )
#define SHIFT2( a, b, c ) ( a )=( b ); ( b )=( c )
/* testing */
ELL_3V_SCALE_ADD2( from, 1, ray->from, Tmin, ray->dir );
saveTmin = Tmin;
Tmin = 0;
Tmax -= saveTmin;
/*
ELL_3V_COPY( from, ray->from );
saveTmin = 0;
*/
/* evaluate value and derivatives at Tmin and Tmax */
VALGRAD( Vmin, dVmin, Tmin );
VALGRAD( Vmax, dVmax, Tmax );
/* if the segment start and end are both positive or both negative,
and if the derivatives also don't change sign, there's no root.
Also, due to convexity, if values at start and end are negative,
then there is no root */
if ( ( Vmin*Vmax >= 0 && dVmin*dVmax >= 0 )
|| ( Vmin <= 0 && Vmax <= 0 ) ) {
return AIR_FALSE;
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: dVmin = %g, dVmax = %g, Vmin = %g, Vmax = %g\n",
_echoDot( tstate->depth ), me, dVmin, dVmax, Vmin, Vmax );
}
/* either the value changed sign, or the derivative changed sign, or
both. If, as is common, the derivatives changed sign, but the
values didn't ( which means they are both positive, due to a test
above ), we need to limit the interval by minimizing the value
until either we see a negative value, or until the minimization
converged. Based on Golden Section Search, NR pg.401 */
if ( dVmin*dVmax < 0 && Vmin*Vmax >= 0 ) {
t0 = Tmin;
t1 = RR*Tmin + CC*Tmax;
t2 = CC*Tmin + RR*Tmax;
t3 = Tmax;
v1 = VAL( t1 );
v2 = VAL( t2 );
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: \n"
" t0 = % 31.15f\n"
" t1 = % 31.15f -> v1 = % 31.15f\n"
" t2 = % 31.15f -> v2 = % 31.15f\n"
" t3 = % 31.15f\n",
_echoDot( tstate->depth ), me,
t0, t1, v1, t2, v2, t3 );
}
tol = sqrt( ECHO_POS_EPS );
while ( ( t3-t0 > tol*( t1+t2 ) ) /* still haven't converged */
&& ( v1 > 0 && v2 > 0 ) ) { /* v1 and v2 both positive */
diff = v2 - v1;
if ( v1 < v2 ) {
SHIFT3( t3, t2, t1, CC*t0 + RR*t2 );
SHIFT2( v2, v1, VAL( t1 ) );
} else {
SHIFT3( t0, t1, t2, RR*t1 + CC*t3 );
SHIFT2( v1, v2, VAL( t2 ) );
}
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: %s ---> \n"
" t0 = % 31.15f\n"
" t1 = % 31.15f -> v1 = % 31.15f\n"
" t2 = % 31.15f -> v2 = % 31.15f\n"
" t3 = % 31.15f\n",
_echoDot( tstate->depth ), me,
diff > 0 ? "v1 < v2" : "v1 > v2",
t0, t1, v1, t2, v2, t3 );
}
}
if ( v1 > 0 && v2 > 0 ) {
/* the minimization stopped, yet both v1 and v2 are still positive,
so there's no way we can have a root */
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: minimization found no root\n",
_echoDot( tstate->depth ), me );
}
return AIR_FALSE;
}
/* else either v1 or v2 <= 0, so there is a root ( actually two ).
By construction, f( t0 ) is positive, so we can now bracket the
root between t0 and t1 or t2, whichever one is associated with
a smaller value */
Tmin = t0;
/* HEY: shouldn't I just be using whichever one is closer? */
Tmax = v1 < v2 ? t1 : t2;
Vmin = VAL( Tmin );
Vmax = VAL( Tmax );
}
/* the value isn't necessarily monotonic between Tmin and Tmax, but
we know that there is only one root. Find it with newton-raphson,
using the log of function, both for values and for derivatives */
iter = 0;
TT = ( Tmin + Tmax )/2;
LVALGRAD( VV, dV, TT );
while ( iter < parm->sqNRI && AIR_ABS( VV ) > parm->sqTol
&& AIR_EXISTS( VV ) && AIR_EXISTS( dV ) ) {
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: iter = %d: TT = %g, VV = %g, dV = %g\n",
_echoDot( tstate->depth ), me, iter, TT, VV, dV );
}
TT -= VV/dV;
if ( !AIR_IN_OP( Tmin, TT, Tmax ) ) {
/* newton-raphson sent us flying out of bounds; find a tighter
[Tmin, Tmax] bracket with bisection and try again */
TT = ( Tmin + Tmax )/2;
VV = VAL( TT );
if ( Vmin*VV < 0 ) {
Tmax = TT;
Vmax = VV;
} else {
Tmin = TT;
Vmin = VV;
}
TT = ( Tmin + Tmax )/2;
}
LVALGRAD( VV, dV, TT );
iter++;
}
if ( !( AIR_EXISTS( VV ) && AIR_EXISTS( dV ) ) ) {
/* we bailed out of the loop above because
we got screwed by numerical errors
--> pretend that there was no intersection,
and HEY this will have to be debugged later */
if ( tstate->verbose ) {
fprintf( stderr, "%s%s: SORRY, numerical problems!\n",
_echoDot( tstate->depth ), me );
}
return AIR_FALSE;
}
/* else we succeeded in finding the intersection */
intx->t = TT + saveTmin;
VALGRAD( VV, dV, TT ); /* puts gradient into grad */
ELL_3V_NORM( intx->norm, grad, tmp );
intx->obj = OBJECT( obj );
/* set in intx:
yes: t, norm
no: u, v
*/
return AIR_TRUE;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../echo.h"
#include "../privateEcho.h"
#include <hest.h>
#include <ten.h>
typedef struct {
int aniso;
float anthr, gscale, lrad, ldref, llev, show, lpos[2];
} EchoGlyphParm;
void
34 rgbGen( echoCol_t rgb[3], float evec[3], float an ) {
ELL_3V_ABS( rgb, evec );
rgb[0] = AIR_AFFINE( 0.0, an, 1.0, 0.5, rgb[0] );
rgb[1] = AIR_AFFINE( 0.0, an, 1.0, 0.5, rgb[1] );
rgb[2] = AIR_AFFINE( 0.0, an, 1.0, 0.5, rgb[2] );
}
/* changed in ELL, below is only usage */
#define NOTELL_3V_AFFINE( v, i, x, I, o, O ) ( \
( v )[0] = AIR_AFFINE( ( i )[0], ( x )[0], ( I )[0], ( o )[0], ( O )[0] ), \
( v )[1] = AIR_AFFINE( ( i )[1], ( x )[1], ( I )[1], ( o )[1], ( O )[1] ), \
46 ( v )[2] = AIR_AFFINE( ( i )[2], ( x )[2], ( I )[2], ( o )[2], ( O )[2] ) )
void
makeGlyphScene( limnCam *cam, EchoParm *eparm,
Nrrd *nten, EchoGlyphParm *gparm,
EchoObject **sceneP, airArray **lightArrP ) {
char me[]="makeGlyphScene";
int xi, yi, zi, sx, sy, sz, ng;
echoPos_t x, y, z, dmat[9], MA[16], MB[16], MC[16], ident[9];
echoCol_t rgb[3];
EchoObject *glf, *inst;
float *tdata, xs, ys, zs, eval[3], evec[9], c[TEN_ANISO_MAX+1],
imin[3], imax[3], omin[3], omax[3];
EchoObject *scene, *rect;
EchoLight *light;
scene = echoObjectNew( echoObjectList );
*lightArrP = echoLightArrayNew( );
eparm->bgR = 0.5;
eparm->bgG = 0.5;
eparm->bgB = 0.5;
eparm->renderLights = AIR_FALSE;
eparm->renderBoxes = AIR_FALSE;
sx = nten->axis[1].size;
sy = nten->axis[2].size;
sz = nten->axis[3].size;
xs = nten->axis[1].spacing;
ys = nten->axis[2].spacing;
zs = nten->axis[3].spacing;
tdata = nten->data; /* we specifically requested float below */
ELL_3V_SET( imin, -1, -1, -1 );
ELL_3V_SET( imax, 1, 1, 1 );
ELL_3V_SET( omin, 0, 0, 0 );
ELL_3V_SET( omax, xs*( sx-1 ), ys*( sy-1 ), zs*( sz-1 ) );
NOTELL_3V_AFFINE( cam->from, imin, cam->from, imax, omin, omax );
NOTELL_3V_AFFINE( cam->at, imin, cam->at, imax, omin, omax );
ng = 0;
for ( zi=0; zi<sz; zi++ ) {
printf( "%s: zi = %3d/%d ... \n", me, zi, sz-1 );
z = zs * zi;
for ( yi=0; yi<sy; yi++ ) {
y = ys * yi;
for ( xi=0; xi<sx; xi++ ) {
x = xs * xi;
/* don't render tensor if confidence threshold below 0.5 */
if ( !( tdata[0] > 0.5 ) )
goto onward;
/* do eigensystem solve; don't render if aniso threshold not met */
tenEigensolve( eval, evec, tdata );
tenAnisoCalc( c, eval );
if ( !( c[gparm->aniso] > gparm->anthr ) )
goto onward;
rgbGen( rgb, evec, c[gparm->aniso] );
if ( 1 ) {
glf = echoObjectNew( echoObjectSphere );
echoObjectSphereSet( glf, 0, 0, 0, 0.5 );
}
else {
glf = echoObjectNew( echoObjectCube );
}
echoMatterMetalSet( glf, rgb[0], rgb[1], rgb[2],
0.8, 1.0, 0.0 );
echoMatterPhongSet( glf, rgb[0], rgb[1], rgb[2], 1.0,
0.1, 1.0, 0.3, 1 );
TEN_LIST2MAT( dmat, tdata );
ELL_3M_SET_IDENTITY( ident );
ELL_3M_SCALEADD( dmat, gparm->gscale*( 1-gparm->show ), ident,
gparm->gscale*gparm->show, dmat );
ELL_43M_INSET( MA, dmat );
ELL_4M_SET_TRANSLATE( MB, x, y, z );
ELL_4M_MUL( MC, MB, MA );
inst = echoObjectNew( echoObjectInstance );
echoObjectInstanceSet( inst, MC, glf, AIR_TRUE );
echoObjectListAdd( scene, inst );
ng++;
onward:
tdata += 7;
}
}
}
/* something to cast a shadow on
glf = echoObjectNew( echoObjectSphere );
echoObjectSphereSet( glf, 35+1000, 40, 35, 1000 );
echoMatterPhongSet( glf, 1, 1, 1, 1.0,
0.1, 1.0, 0.3, 1 );
inst = echoObjectNew( echoObjectInstance );
echoObjectInstanceSet( inst, MC, glf, AIR_TRUE );
echoObjectListAdd( scene, inst );
*/
printf( "%s: generated %d glyphs\n", me, ng );
if ( gparm->lrad ) {
rect = echoObjectNew( echoObjectRectangle );
echoObjectRectangleSet( rect,
AIR_AFFINE( 0, gparm->lpos[0], 1, 0, sx*xs )
- gparm->lrad,
AIR_AFFINE( 0, gparm->lpos[1], 1, 0, sy*ys )
- gparm->lrad,
0,
2*gparm->lrad, 0, 0,
0, 2*gparm->lrad, 0 );
eparm->refDistance = sz*( gparm->ldref );
printf( "llev = %g\n", gparm->llev );
echoMatterLightSet( rect, gparm->llev, gparm->llev, gparm->llev );
echoObjectListAdd( scene, rect );
light = echoLightNew( echoLightArea );
echoLightAreaSet( light, rect );
echoLightArrayAdd( *lightArrP, light );
}
else {
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 1, 1, 1,
AIR_AFFINE( 0, gparm->lpos[0], 1, -sx*xs, sx*xs ),
AIR_AFFINE( 0, gparm->lpos[1], 1, -sy*ys, sy*ys ),
AIR_AFFINE( 0, 0, 1, -sz*zs, sz*zs ) );
echoLightArrayAdd( *lightArrP, light );
}
if ( 1 ) {
printf( "%s: making BVH ... ", me ); fflush( stdout );
*sceneP = echoObjectListSplit3( scene, 8 );
printf( "done\n" );
}
else {
*sceneP = scene;
}
return;
}
189
int
echoParseTenNrrd( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[] = "echoParseTenNrrd", *nerr;
Nrrd **nrrdP;
airArray *mop;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
nrrdP = ptr;
mop = airMopNew( );
*nrrdP = nrrdNew( );
airMopAdd( mop, *nrrdP, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdLoad( *nrrdP, str ) ) {
airMopAdd( mop, nerr = biffGetDone( NRRD ), airFree, airMopOnError );
if ( strlen( nerr ) > AIR_STRLEN_HUGE - 1 )
nerr[AIR_STRLEN_HUGE - 1] = '\0';
strcpy( err, nerr );
airMopError( mop );
return 1;
}
if ( !tenValidTensor( *nrrdP, nrrdTypeFloat, AIR_TRUE ) ) {
/* why not use the given err[] as a temp buffer */
sprintf( err, "%s: \"%s\" isn't a valid tensor volume", me, str );
biffAdd( TEN, err );
airMopAdd( mop, nerr = biffGetDone( TEN ), airFree, airMopOnError );
if ( strlen( nerr ) > AIR_STRLEN_HUGE - 1 )
nerr[AIR_STRLEN_HUGE - 1] = '\0';
strcpy( err, nerr );
airMopError( mop );
return 1;
}
if ( !( AIR_EXISTS( ( *nrrdP )->axis[1].spacing ) &&
AIR_EXISTS( ( *nrrdP )->axis[2].spacing ) &&
AIR_EXISTS( ( *nrrdP )->axis[3].spacing ) ) ) {
sprintf( err, "%s: need existent spacings on x, y, z axes", me );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
hestCB echoParseTenNrrdCB = {
sizeof( Nrrd * ),
"nrrd",
echoParseTenNrrd,
( airMopper )nrrdNuke
240 };
int
main( int argc, char *argv[] ) {
airArray *mop, *lightArr;
hestOpt *opt = NULL;
limnCam *cam;
hestParm *hparm;
Nrrd *nten, *nraw, *nimg, *nppm, *ntmp, *npgm;
echoPos_t ur[2], vr[2];
int E, is[2];
char *me, *outS, *err,
info[] = "Generates cool images of tensor glyphs";
EchoParm *eparm;
EchoGlobalState *state;
EchoObject *scene;
EchoGlyphParm gparm;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hparm->respFileEnable = AIR_TRUE;
hparm->verbosity = 0;
cam = echoLimnCamNew( );
airMopAdd( mop, cam, ( airMopper )limnCamNix, airMopAlways );
cam->neer = 0;
cam->dist = 0;
cam->faar = 0;
cam->atRel = AIR_TRUE;
state = echoGlobalStateNew( );
airMopAdd( mop, state, ( airMopper )echoGlobalStateNix, airMopAlways );
eparm = echoParmNew( );
airMopAdd( mop, eparm, ( airMopper )echoParmNix, airMopAlways );
hestOptAdd( &opt, "i", "nin", airTypeOther, 1, 1, &nten, NULL,
"diffusion tensor data",
NULL, NULL, &echoParseTenNrrdCB );
hestOptAdd( &opt, "fr", "eye point", airTypeFloat, 3, 3, cam->from,
"40 40 40", "camera eye point" );
hestOptAdd( &opt, "at", "lookat", airTypeFloat, 3, 3, cam->at, "0 0 0",
"camera look-at point" );
hestOptAdd( &opt, "up", "up", airTypeFloat, 3, 3, cam->up, "0 0 1",
"camera pseudo up vector" );
hestOptAdd( &opt, "ur", "U range", echoPos_airType, 2, 2, ur, "-20 20",
"range in U direction of image plane" );
hestOptAdd( &opt, "vr", "V range", echoPos_airType, 2, 2, vr, "-20 20",
"range in V direction of image plane" );
hestOptAdd( &opt, "is", "image size", airTypeInt, 2, 2, is, "256 256",
"image dimensions" );
hestOptAdd( &opt, "ns", "# samples", airTypeInt, 1, 1, &( eparm->samples ), "1",
"# of samples per pixel ( 1 --> no jitter )" );
hestOptAdd( &opt, "o", "output", airTypeString, 1, 1, &outS, "out.ppm",
"PPM image output" );
hestOptAdd( &opt, "ap", "aperture", airTypeFloat, 1, 1, &( eparm->aperture ),
"0.0", "camera aperture ( 0.0 --> no depth of field )" );
hestOptAdd( &opt, "an", "aniso", airTypeEnum, 1, 1, &( gparm.aniso ), "fa",
"which anisotropy metric", NULL, &tenAniso );
hestOptAdd( &opt, "th", "thresh", airTypeFloat, 1, 1, &( gparm.anthr ), "0.8",
"threshold on anisotropy" );
hestOptAdd( &opt, "gs", "scale", airTypeFloat, 1, 1, &( gparm.gscale ), "0.8",
"over-all scaling on all glyphs" );
hestOptAdd( &opt, "gh", "show", airTypeFloat, 1, 1, &( gparm.show ), "1.0",
"how much to \"show\" the data:\n "
"0.0 --> all identity; 1.0 --> all data" );
hestOptAdd( &opt, "lr", "light radius", airTypeFloat, 1, 1,
&( gparm.lrad ), "5", "\"radius\" of area light over volume" );
hestOptAdd( &opt, "ld", "ref dist", airTypeFloat, 1, 1,
&( gparm.ldref ), "0.5", "\"reference distance\" for light, "
"expressed as a fraction of Z dimension of volume" );
hestOptAdd( &opt, "ll", "light level", airTypeFloat, 1, 1,
&( gparm.llev ), "1", "area light intensity" );
hestOptAdd( &opt, "lp", "light position", airTypeFloat, 2, 2,
&( gparm.lpos ), "0.5 0.5", "area light position" );
hestOptAdd( &opt, "sh", NULL, airTypeInt, 0, 0,
&( eparm->shadow ), NULL, "render shadows" );
if ( hestOptCheck( opt, &err ) ) { printf( "%s\n", err ); exit( 1 ); }
if ( 1 == argc ) {
hestInfo( stderr, argv[0], info, hparm );
hestUsage( stderr, opt, argv[0], hparm );
hestGlossary( stderr, opt, hparm );
opt = hestOptFree( opt );
hparm = hestParmFree( hparm );
exit( 1 );
}
/* parse command line */
if ( hestParse( opt, argc-1, argv+1, &err, hparm ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
hestUsage( stderr, opt, argv[0], hparm );
hestGlossary( stderr, opt, hparm );
opt = hestOptFree( opt );
hparm = hestParmFree( hparm );
exit( 1 );
}
/* finish dealing with parsed information */
cam->uRange[0] = ur[0];
cam->uRange[1] = ur[1];
cam->vRange[0] = vr[0];
cam->vRange[1] = vr[1];
eparm->imgResU = is[0];
eparm->imgResV = is[1];
eparm->jitter = ( 1 == eparm->samples ? echoJitterNone : echoJitterJitter );
/* do the glyph thing */
makeGlyphScene( cam, eparm, nten, &gparm,
&scene, &lightArr );
/* render */
nraw = nrrdNew( );
nimg = nrrdNew( );
nppm = nrrdNew( );
ntmp = nrrdNew( );
npgm = nrrdNew( );
airMopAdd( mop, nraw, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nimg, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nppm, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, npgm, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
printf( "%s: rendering ( %d samples )... ", me, eparm->samples );
fflush( stdout );
if ( !E ) E |= echoRender( nraw, cam, eparm, state, scene, lightArr );
printf( "done.\n" );
if ( !E ) E |= echoComposite( nimg, nraw, eparm );
if ( !E ) E |= echoPPM( nppm, nimg, eparm );
if ( E ) {
airMopAdd( mop, err = biffGetDone( ECHO ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
printf( "%s: render time = %g seconds ( %g fps )\n",
me, state->time, 1.0/state->time );
if ( !E ) E |= nrrdSave( "raw.nrrd", nraw, NULL );
if ( !E ) E |= nrrdSave( "out.ppm", nppm, NULL );
if ( !E ) E |= nrrdSlice( ntmp, nraw, 0, 3 );
ntmp->min = 0.0; ntmp->max = 1.0;
if ( !E ) E |= nrrdQuantize( npgm, ntmp, 8 );
if ( !E ) E |= nrrdSave( "alpha.pgm", npgm, NULL );
if ( !E ) E |= nrrdSlice( ntmp, nraw, 0, 4 );
if ( !E ) E |= nrrdHistoEq( ntmp, ntmp, NULL, 2048, 2 );
if ( !E ) E |= nrrdQuantize( npgm, ntmp, 8 );
if ( !E ) E |= nrrdSave( "time.pgm", npgm, NULL );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
hestParseFree( opt );
opt = hestOptFree( opt );
hparm = hestParmFree( hparm );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../echo.h"
int
26 main( int argc, char **argv ) {
char *me, *err;
echoScene *scene;
echoObject *sph, *rect, *list, *split;
Nrrd *nraw;
limnCamera *cam;
echoRTParm *parm;
echoGlobalState *gstate;
airArray *mop;
int I;
float R, G, B;
AIR_UNUSED( argc );
me = argv[0];
mop = airMopNew( );
scene = echoSceneNew( );
airMopAdd( mop, scene, ( airMopper )echoSceneNix, airMopAlways );
list = echoObjectNew( scene, echoTypeList );
for ( I=0; I<70; I++ ) {
sph = echoObjectNew( scene, echoTypeSphere );
R = airDrandMT( );
G = airDrandMT( );
B = airDrandMT( );
echoSphereSet( sph,
AIR_AFFINE( 0, R, 1, -0.8, 0.8 ),
AIR_AFFINE( 0, G, 1, -0.8, 0.8 ),
AIR_AFFINE( 0, B, 1, -0.8, 0.8 ),
0.15 );
echoColorSet( sph, R, G, B, 1.0 );
echoMatterPhongSet( scene, sph, 0, 1, 0, 40 );
echoListAdd( list, sph );
}
split = echoListSplit3( scene, list, 10 );
echoObjectAdd( scene, split );
/*
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -1, -1, -1,
2, 0, 0,
0, 2, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterPhongSet( scene, rect, 0, 1, 0, 40 );
echoObjectAdd( scene, rect );
*/
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -2, -2, 2,
0, 4, 0,
4, 0, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -2, -2, -2,
4, 0, 0,
0, 4, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -2, 2, -2,
4, 0, 0,
0, 0, 4 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -2, -2, -2,
0, 0, 4,
4, 0, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, -2, -2, -2,
0, 4, 0,
0, 0, 4 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, 2, -2, -2,
0, 0, 4,
0, 4, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 2 );
nraw = nrrdNew( );
cam = limnCameraNew( );
parm = echoRTParmNew( );
gstate = echoGlobalStateNew( );
airMopAdd( mop, nraw, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
airMopAdd( mop, parm, ( airMopper )echoRTParmNix, airMopAlways );
airMopAdd( mop, gstate, ( airMopper )echoGlobalStateNix, airMopAlways );
ELL_3V_SET( cam->from, 20, 20, 20 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->neer = -2;
cam->dist = 0;
cam->faar = 2;
cam->atRelative = AIR_TRUE;
cam->rightHanded = AIR_TRUE;
cam->uRange[0] = -1.6; cam->vRange[0] = -1.6;
cam->uRange[1] = 1.6; cam->vRange[1] = 1.6;
parm->imgResU = parm->imgResV = 300;
parm->numSamples = 100;
parm->jitterType = echoJitterJitter;
parm->aperture = 0.0;
parm->renderBoxes = AIR_FALSE;
if ( echoRTRender( nraw, cam, scene, parm, gstate ) ) {
airMopAdd( mop, err = biffGetDone( ECHO ), airFree, airMopAlways );
fprintf( stderr, "%s: %s\n", me, err );
airMopError( mop );
return 1;
}
nrrdSave( "nraw.nrrd", nraw, NULL );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../echo.h"
#include "../privateEcho.h"
/* bad bad bad Gordon */
void
28 _dyeHSVtoRGB( float *R, float *G, float *B,
float H, float S, float V ) {
float min, fract, vsf, mid1, mid2;
int sextant;
if ( 0 == S ) {
*R = *G = *B = V;
return;
}
/* else there is hue */
if ( 1 == H )
H = 0;
H *= 6;
sextant = ( int ) floor( H );
fract = H - sextant;
vsf = V*S*fract;
min = V*( 1 - S );
mid1 = min + vsf;
mid2 = V - vsf;
switch ( sextant ) {
case 0: { *R = V; *G = mid1; *B = min; break; }
case 1: { *R = mid2; *G = V; *B = min; break; }
case 2: { *R = min; *G = V; *B = mid1; break; }
case 3: { *R = min; *G = mid2; *B = V; break; }
case 4: { *R = mid1; *G = min; *B = V; break; }
case 5: { *R = V; *G = min; *B = mid2; break; }
}
}
#if 0
void
60 makeSceneAntialias( limnCamera *cam, echoRTParm *parm,
echoObject **sceneP, airArray **lightArrP ) {
echoObject *scene, *rect;
Nrrd *ntext;
*sceneP = scene = echoObjectNew( echoList );
*lightArrP = echoLightArrayNew( );
ELL_3V_SET( cam->from, 0, 0, 10 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 1, 0 );
cam->uRange[0] = -3.7;
cam->uRange[1] = 3.7;
cam->vRange[0] = -3.7;
cam->vRange[1] = 3.7;
parm->jitterType = echoJitterGrid;
parm->numSamples = 1;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->shadow = 1.0;
nrrdLoad( ntext = nrrdNew( ), "chirp.nrrd", NULL );
rect = echoObjectNew( echoRectangle );
echoRectangleSet( rect,
-3, -3, 0,
6, 0, 0,
0, 6, 0 );
echoMatterPhongSet( rect, 1, 1, 1, 1.0,
1.0, 0.0, 0.0, 1 );
echoMatterTextureSet( rect, ntext );
echoObjectAdd( scene, rect );
return;
}
void
102 makeSceneBVH( limnCamera *cam, echoRTParm *parm, echoObject **sceneP ) {
echoObject *sphere;
int i, N;
float r, g, b;
echoObject *scene;
double time0, time1;
*sceneP = scene = echoObjectNew( echoList );
ELL_3V_SET( cam->from, 9, 6, 0 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -3;
cam->uRange[1] = 3;
cam->vRange[0] = -3;
cam->vRange[1] = 3;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 500;
parm->imgResV = 500;
parm->aperture = 0.0;
parm->renderLights = AIR_TRUE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->shadow = 0.0;
N = 1000000;
airArrayLenSet( LIST( scene )->objArr, N );
for ( i=0; i<N; i++ ) {
sphere = echoObjectNew( echoSphere );
echoSphereSet( sphere,
4*airDrandMT( )-2, 4*airDrandMT( )-2, 4*airDrandMT( )-2, 0.005 );
_dyeHSVtoRGB( &r, &g, &b, AIR_AFFINE( 0, i, N, 0.0, 1.0 ), 1.0, 1.0 );
echoMatterPhongSet( sphere, r, g, b, 1.0,
1.0, 0.0, 0.0, 50 );
LIST( scene )->obj[i] = sphere;
}
time0 = airTime( );
*sceneP = scene = echoListSplit3( scene, 8 );
time1 = airTime( );
printf( "BVH build time = %g seconds\n", time1 - time0 );
}
void
149 makeSceneGlass( limnCamera *cam, echoRTParm *parm, echoObject **sceneP ) {
echoObject *cube, *rect;
echoObject *scene;
Nrrd *ntext;
*sceneP = scene = echoObjectNew( echoList );
ELL_3V_SET( cam->from, 2, -3, 8 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -1.0;
cam->uRange[1] = 1.0;
cam->vRange[0] = -1.0;
cam->vRange[1] = 1.0;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->shadow = 0.0;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->mrR = 1.0;
parm->mrG = 0.0;
parm->mrB = 1.0;
cube = echoObjectNew( echoCube );
printf( "cube = %p\n", cube );
echoMatterGlassSet( cube,
1.0, 1.0, 1.0,
1.5, 0.0, 0.0 );
echoObjectAdd( scene, cube );
nrrdLoad( ntext=nrrdNew( ), "psq.nrrd", NULL );
rect = echoObjectNew( echoRectangle );
printf( "rect = %p\n", rect );
echoRectangleSet( rect,
-1, -1, -0.51,
2, 0, 0,
0, 2, 0 );
echoMatterPhongSet( rect, 1.0, 1.0, 1.0, 1.0,
0.1, 0.6, 0.3, 40 );
echoMatterTextureSet( rect, ntext );
echoObjectAdd( scene, rect );
/*
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 1, 1, 1, 0, 0, 1 );
echoLightArrayAdd( lightArr, light );
*/
}
void
205 makeSceneGlass2( limnCamera *cam, echoRTParm *parm, echoObject **sceneP ) {
echoObject *cube, *rect;
echoObject *scene;
Nrrd *ntext;
echoPos_t matx[16];
*sceneP = scene = echoObjectNew( echoList );
ELL_3V_SET( cam->from, 0, 0, 100 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 1, 0 );
cam->uRange[0] = -1.0;
cam->uRange[1] = 1.0;
cam->vRange[0] = -1.0;
cam->vRange[1] = 1.0;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->shadow = 0.0;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->mrR = 1.0;
parm->mrG = 0.0;
parm->mrB = 1.0;
ELL_4M_SET_SCALE( matx, 0.5, 0.5, 0.5 );
cube = echoRoughSphere( 80, 40, matx );
/*
cube = echoObjectNew( echoSphere );
echoSphereSet( cube, 0, 0, 0, 0.5 );
*/
echoMatterGlassSet( cube,
1.0, 1.0, 1.0,
1.33333, 0.0, 0.0 );
echoObjectAdd( scene, cube );
nrrdLoad( ntext=nrrdNew( ), "check.nrrd", NULL );
rect = echoObjectNew( echoRectangle );
printf( "rect = %p\n", rect );
echoRectangleSet( rect,
-1, -1, -0.51,
2, 0, 0,
0, 2, 0 );
echoMatterPhongSet( rect, 1.0, 1.0, 1.0, 1.0,
0.0, 1.0, 0.0, 40 );
echoMatterTextureSet( rect, ntext );
echoObjectAdd( scene, rect );
/*
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 1, 1, 1, 0, 0, 1 );
echoLightArrayAdd( lightArr, light );
*/
}
#endif
void
268 makeSceneInstance( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *trim, *rect, *inst;
echoPos_t matx[16], A[16], B[16];
ELL_3V_SET( cam->from, 9*1.3, 9*1.3, 11*1.3 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -5;
cam->uRange[1] = 5;
cam->vRange[0] = -5;
cam->vRange[1] = 5;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->renderLights = AIR_TRUE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->shadow = 1.0;
ELL_4M_IDENTITY_SET( matx );
ELL_4M_SCALE_SET( B, 2.5, 1.5, 0.8 );
ELL_4M_MUL( A, B, matx ); ELL_4M_COPY( matx, A );
ELL_4M_ROTATE_X_SET( B, 0.2 );
ELL_4M_MUL( A, B, matx ); ELL_4M_COPY( matx, A );
ELL_4M_ROTATE_Y_SET( B, 0.2 );
ELL_4M_MUL( A, B, matx ); ELL_4M_COPY( matx, A );
ELL_4M_ROTATE_Y_SET( B, 0.2 );
ELL_4M_MUL( A, B, matx ); ELL_4M_COPY( matx, A );
ELL_4M_TRANSLATE_SET( B, 0, 0, 1 );
ELL_4M_MUL( A, B, matx ); ELL_4M_COPY( matx, A );
/* trim = echoRoughSphere( 50, 25, matx ); */
/*
trim = echoRoughSphere( 8, 4, matx );
echoMatterGlassSet( trim, 0.8, 0.8, 0.8,
1.3, 0.0, 0.0 );
echoMatterPhongSet( trim, 1, 1, 1, 1.0,
0.1, 0.5, 0.9, 50 );
echoObjectAdd( scene, trim );
*/
trim = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( trim, 0, 0, 0, 1 );
echoColorSet( trim, 0.8, 0.8, 0.8, 1.0 );
echoMatterGlassSet( scene, trim, 1.3, 0.0, 0.0, 0.0 );
echoMatterPhongSet( scene, trim, 0.1, 0.5, 0.9, 50 );
inst = echoObjectNew( scene, echoTypeInstance );
echoInstanceSet( inst, matx, trim );
echoObjectAdd( scene, inst );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-3.5, -3.5, -3.5,
7, 0, 0,
0, 7, 0 );
echoColorSet( trim, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.5, 0.9, 50 );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-3.5, -3.5, -3.5,
0, 7, 0,
0, 0, 7 );
echoColorSet( rect, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.5, 0.9, 50 );
echoObjectAdd( scene, rect );
/*
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-3.5, -3.5, -3.5,
0, 0, 7,
7, 0, 0 );
*/
rect = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( rect, 0, 0, 0, 1 );
echoColorSet( rect, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.5, 0.9, 50 );
inst = echoObjectNew( scene, echoTypeInstance );
ELL_4M_SCALE_SET( A, 20, 20, 20 );
ELL_4M_TRANSLATE_SET( B, 0, -( 20+3.5 ), 0 );
ELL_4M_MUL( matx, B, A );
echoInstanceSet( inst, matx, rect );
echoObjectAdd( scene, inst );
/*
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 1, 0, 0, 1, 0.001, 0.001 );
echoLightArrayAdd( lightArr, light );
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 0, 1, 0, 0.001, 1, 0.001 );
echoLightArrayAdd( lightArr, light );
light = echoLightNew( echoLightDirectional );
echoLightDirectionalSet( light, 0, 0, 1, 0.001, 0.001, 1 );
echoLightArrayAdd( lightArr, light );
*/
return;
}
void
374 makeSceneGlassTest( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *cube, *rect, *inst;
echoCol_t r=0, g=0, b=0;
Nrrd *ntext;
int i, N;
echoPos_t ma[16], mb[16];
ELL_3V_SET( cam->from, 0, 0, 10 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 1, 0, 0 );
cam->uRange[0] = -1.1;
cam->uRange[1] = 1.1;
cam->vRange[0] = -1.1;
cam->vRange[1] = 1.1;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 220;
parm->imgResV = 220;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->seedRand = AIR_FALSE;
ELL_3V_SET( scene->bkgr, 0.2, 0.3, 0.4 );
/* parm->shadow = 0.0; */
/* create scene */
N = 11;
for ( i=0; i<N; i++ ) {
cube = echoObjectNew( scene, echoTypeCube );
_dyeHSVtoRGB( &r, &g, &b, AIR_AFFINE( 0, i, N, 0.0, 1.0 ), 1.0, 1.0 );
echoColorSet( cube, r, g, b, 1 );
echoMatterGlassSet( scene, cube, 1.1, 0.0, 0.0, 0 );
inst = echoObjectNew( scene, echoTypeInstance );
ELL_4M_IDENTITY_SET( ma );
ELL_4M_SCALE_SET( mb, 1.0/( N+2 ), 0.8, 3.0/( N+2 ) );
ell_4m_POST_MUL( ma, mb );
ELL_4M_ROTATE_X_SET( mb, AIR_AFFINE( 0, i, N-1, -AIR_PI/2, AIR_PI/2 ) );
ell_4m_POST_MUL( ma, mb );
ELL_4M_TRANSLATE_SET( mb, AIR_AFFINE( 0, i, N-1, -0.8, 0.8 ), 0, 1 );
ell_4m_POST_MUL( ma, mb );
echoInstanceSet( inst, ma, cube );
echoObjectAdd( scene, inst );
}
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-1, 1, 0,
0, -2, 0,
2, 0, 0 );
echoColorSet( rect, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 1.0, 0.0, 0.0, 40 );
nrrdLoad( ntext=nrrdNew( ), "pot.png", NULL );
echoMatterTextureSet( scene, rect, ntext );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-0.25, -0.25, 10,
0.5, 0, 0,
0, 0.5, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 0 );
return;
}
void
441 makeSceneGlassMetal( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *sphere, *rect;
ELL_3V_SET( cam->from, 4, 0, 5 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, -1, 0, 0 );
cam->uRange[0] = -0.7;
cam->uRange[1] = 0.7;
cam->vRange[0] = -0.0;
cam->vRange[1] = 1.4;
parm->jitterType = echoJitterJitter;
parm->numSamples = 36;
parm->imgResU = 220;
parm->imgResV = 220;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->seedRand = AIR_FALSE;
ELL_3V_SET( scene->bkgr, 0.2, 0.3, 0.4 );
/* create scene */
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0.70, -0.3, -0.4, 0.1 );
echoColorSet( sphere, 1, 0, 0, 1.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0.66, 0.0, -0.4, 0.1 );
echoColorSet( sphere, 0, 1, 0, 1.0 );
echoMatterGlassSet( scene, sphere, 1.0, 0, 1.0, 0.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0.62, 0.3, -0.4, 0.1 );
echoColorSet( sphere, 0, 0, 1, 1.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
0.5, 0.5, 0.5,
0, -1, 0,
0, 0, -1 );
echoColorSet( rect, 1.0, 1.0, 1.0, 1.0 );
echoMatterMetalSet( scene, rect, 0.7, 0.0, 0.0, 0.2 );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-1.5, -1.5, -1,
3, 0, 0,
0, 3, 0 );
echoColorSet( rect, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
0.5-0.2, -0.2, 40,
0.4, 0, 0,
0, 0.4, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 0 );
return;
}
void
511 makeSceneTexture( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject /* *trim, */ *rect, /* *inst, */ *sphere;
Nrrd *ntext;
ELL_3V_SET( cam->from, 9, 9, 11 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -4;
cam->uRange[1] = 4;
cam->vRange[0] = -4;
cam->vRange[1] = 4;
parm->jitterType = echoJitterNone;
parm->numSamples = 1;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->shadow = 1.0;
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-2, -2, 0,
4, 0, 0,
0, 4, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterPhongSet( scene, rect, 0.1, 1, 0.9, 50 );
echoObjectAdd( scene, rect );
nrrdLoad( ntext=nrrdNew( ), "tmp.png", NULL );
echoMatterTextureSet( scene, rect, ntext );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, 0, 0, 3 );
echoColorSet( sphere, 1, 1, 1, 1 );
echoMatterPhongSet( scene, sphere, 0.1, 0.5, 0.9, 50 );
echoMatterTextureSet( scene, sphere, ntext );
echoObjectAdd( scene, sphere );
/*
ELL_4M_SET_SCALE( matx, 3, 3, 3 );
trim = echoRoughSphere( 80, 40, matx );
echoMatterPhongSet( trim, 1, 1, 1, 1.0,
0.1, 0.5, 0.9, 50 );
echoMatterTextureSet( trim, ntext );
echoObjectAdd( scene, trim );
*/
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
0, 0, 60,
0, 2, 0,
0, 0, 2 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 1 );
return;
}
void
574 makeSceneDOF( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *rect;
Nrrd *ntext;
ELL_3V_SET( cam->from, 6, 6, 20 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 1, 0 );
cam->uRange[0] = -3.3;
cam->uRange[1] = 3.3;
cam->vRange[0] = -3.3;
cam->vRange[1] = 3.3;
parm->jitterType = echoJitterJitter;
parm->numSamples = 4;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.5;
parm->renderLights = AIR_FALSE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_FALSE;
parm->maxRecDepth = 10;
parm->shadow = 1.0;
nrrdLoad( ntext = nrrdNew( ), "tmp.png", NULL );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-0.5, 1.5, -3,
2, 0, 0,
0, -2, 0 );
echoColorSet( rect, 1, 0.5, 0.5, 1 );
echoMatterPhongSet( scene, rect, 1.0, 0.0, 0.0, 1 );
echoMatterTextureSet( scene, rect, ntext );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-1, 1, 0,
2, 0, 0,
0, -2, 0 );
echoColorSet( rect, 0.5, 1, 0.5, 1 );
echoMatterPhongSet( scene, rect, 1.0, 0.0, 0.0, 1 );
echoMatterTextureSet( scene, rect, ntext );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-1.5, 0.5, 3,
2, 0, 0,
0, -2, 0 );
echoColorSet( rect, 0.5, 0.5, 1, 1 );
echoMatterPhongSet( scene, rect, 1.0, 0.0, 0.0, 1 );
echoMatterTextureSet( scene, rect, ntext );
echoObjectAdd( scene, rect );
return;
}
void
633 makeSceneShadow( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *sphere, *rect, *tri;
ELL_3V_SET( cam->from, 2, 0, 20 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, -1, 0, 0 );
cam->uRange[0] = -1.8;
cam->uRange[1] = 1.8;
cam->vRange[0] = -1.8;
cam->vRange[1] = 1.8;
parm->jitterType = echoJitterGrid;
parm->numSamples = 9;
parm->imgResU = 200;
parm->imgResV = 200;
parm->aperture = 0.0;
parm->renderLights = AIR_FALSE;
parm->shadow = 0.5;
/* create scene */
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, -1, -1, 0.2 );
echoColorSet( sphere, 0.5, 0.5, 1, 1.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, 1, -1, 0.2 );
echoColorSet( sphere, 1, 0.5, 0.5, 1.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, 0, 1, 0.2 );
echoColorSet( sphere, 0.5, 1, 0.5, 1.0 );
echoMatterPhongSet( scene, sphere, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, sphere );
tri = echoObjectNew( scene, echoTypeTriangle );
echoTriangleSet( tri,
0, -1, -1,
0, 1, -1,
0, 0, 1 );
echoColorSet( tri, 1, 1, 0, 1.0 );
echoMatterPhongSet( scene, tri, 0.1, 0.6, 0.3, 40 );
echoObjectAdd( scene, tri );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
1.7, 1.7, -2,
-3.4, 0, 0,
0, -3.4, 0 );
echoColorSet( rect, 1.0, 0.8, 1.0, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.3, 0.7, 3000 );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
1.0, 0.2, 4,
0.2, 0, 0,
0, 0.2, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 0 );
echoObjectAdd( scene, rect );
}
void
700 makeSceneSimple( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *tri, *rect, *sphere;
Nrrd *ntext;
ELL_3V_SET( cam->from, 5, -5, 9 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -3.6;
cam->uRange[1] = 3.6;
cam->vRange[0] = -3.6;
cam->vRange[1] = 3.6;
parm->jitterType = echoJitterJitter;
parm->numSamples = 9;
parm->imgResU = 300;
parm->imgResV = 300;
parm->aperture = 0.0;
parm->textureNN = AIR_FALSE;
parm->renderLights = AIR_TRUE;
parm->renderBoxes = AIR_FALSE;
parm->seedRand = AIR_TRUE;
parm->maxRecDepth = 10;
ELL_3V_SET( parm->maxRecCol, 0, 0, 0 );
parm->shadow = 1.0;
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-5, -5, -1.4,
10, 0, 0,
0, 10, 0 );
echoColorSet( rect, 1, 1, 1, 1.0 );
echoMatterPhongSet( scene, rect, 0.1, 0.5, 0.6, 50 );
if ( nrrdLoad( ntext=nrrdNew( ), "pot.png", NULL ) ) {
/* oops, no pot */
airFree( biffGetDone( NRRD ) );
nrrdNuke( ntext );
} else {
echoMatterTextureSet( scene, rect, ntext );
}
echoObjectAdd( scene, rect );
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, 0, 0, 1.85 );
echoColorSet( sphere, 1, 1, 1, 1.0 );
echoMatterGlassSet( scene, sphere, 1.5, 0.0, 0.0, 0.0 );
echoMatterMetalSet( scene, sphere, 0.8, 0.0, 1.0, 0.15 );
echoObjectAdd( scene, sphere );
tri = echoObjectNew( scene, echoTypeTriangle );
echoTriangleSet( tri,
0.1, 0.1, 2,
2, 2, 2,
0, 2, 2 );
echoColorSet( tri, 1, 0.4, 0.4, 1.0 );
echoMatterPhongSet( scene, tri, 0.4, 0.6, 0.0, 90 );
echoObjectAdd( scene, tri );
tri = echoObjectNew( scene, echoTypeTriangle );
echoTriangleSet( tri,
-0.1, 0.1, 2,
-2, 2, 2,
-2, 0, 2 );
echoColorSet( tri, 0.4, 1.0, 0.4, 1.0 );
echoMatterPhongSet( scene, tri, 0.4, 0.6, 0.0, 90 );
echoObjectAdd( scene, tri );
tri = echoObjectNew( scene, echoTypeTriangle );
echoTriangleSet( tri,
-0.1, -0.1, 2,
-2, -2, 2,
0, -2, 2 );
echoColorSet( tri, 0.4, 0.4, 1.0, 1.0 );
echoMatterPhongSet( scene, tri, 0.4, 0.6, 0.0, 90 );
echoObjectAdd( scene, tri );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
-0.5, -0.5, 10,
1.0, 0.0, 0,
0.0, 1.0, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, 0 );
echoObjectAdd( scene, rect );
return;
}
void
788 makeSceneRainLights( limnCamera *cam, echoRTParm *parm, echoScene *scene ) {
echoObject *sphere, *rect;
int i, N;
echoPos_t w;
float r=0, g=0, b=0;
ELL_3V_SET( cam->from, 2.5, 0, 5 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -1.7;
cam->uRange[1] = 1.7;
cam->vRange[0] = -1.7;
cam->vRange[1] = 1.7;
parm->jitterType = echoJitterJitter;
parm->numSamples = 36;
parm->imgResU = 1000;
parm->imgResV = 1000;
parm->numSamples = 16;
parm->imgResU = 200;
parm->imgResV = 200;
parm->aperture = 0.0;
parm->renderLights = AIR_TRUE;
parm->shadow = 0.0;
ELL_3V_SET( scene->bkgr, 0.1, 0.1, 0.1 );
/* create scene */
sphere = echoObjectNew( scene, echoTypeSphere );
echoSphereSet( sphere, 0, 0, 0, 1.0 );
echoColorSet( sphere, 1.0, 1.0, 1.0, 1.0 );
echoMatterPhongSet( scene, sphere, 0.02, 0.2, 1.0, 400 );
echoObjectAdd( scene, sphere );
N = 8;
w = 1.7/N;
for ( i=0; i<N; i++ ) {
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
w/2, AIR_AFFINE( 0, i, N-1, -1-w/2, 1-w/2 ), 1.5,
0, w, 0,
w, 0, 0 );
_dyeHSVtoRGB( &r, &g, &b, AIR_AFFINE( 0, i, N, 0.0, 1.0 ), 1.0, 1.0 );
echoColorSet( rect, r, g, b, 1 );
echoMatterLightSet( scene, rect, 1, 0 );
echoObjectAdd( scene, rect );
}
}
int
839 main( int argc, char **argv ) {
Nrrd *nraw;
limnCamera *cam;
echoRTParm *parm;
echoGlobalState *state;
echoScene *scene;
airArray *mop;
char *me, *err, *env;
int E, tmp;
AIR_UNUSED( argc );
me = argv[0];
mop = airMopNew( );
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
cam->neer = 0;
cam->dist = 0;
cam->faar = 0;
cam->atRelative = AIR_TRUE;
cam->dist = 0;
cam->rightHanded = AIR_TRUE;
parm = echoRTParmNew( );
airMopAdd( mop, parm, ( airMopper )echoRTParmNix, airMopAlways );
state = echoGlobalStateNew( );
airMopAdd( mop, state, ( airMopper )echoGlobalStateNix, airMopAlways );
scene = echoSceneNew( );
airMopAdd( mop, scene, ( airMopper )echoSceneNix, airMopAlways );
nraw = nrrdNew( );
airMopAdd( mop, nraw, ( airMopper )nrrdNuke, airMopAlways );
/* makeSceneGlass( cam, parm, scene ); */
/* makeSceneGlass2( cam, parm, scene ); */
/* makeSceneGlassMetal( cam, parm, scene ); */
/* makeSceneGlassTest( cam, parm, scene ); */
/* makeSceneBVH( cam, parm, scene ); */
/* makeSceneInstance( cam, parm, scene ); */
/* makeSceneTexture( cam, parm, scene ); */
/* makeSceneSimple( cam, parm, scene ); */
/* makeSceneRainLights( cam, parm, scene ); */
/* makeSceneAntialias( cam, parm, scene ); */
makeSceneShadow( cam, parm, scene );
/* makeSceneDOF( cam, parm, scene ); */
if ( ( env = getenv( "NT" ) ) ) {
if ( 1 == sscanf( env, "%d", &tmp ) ) {
parm->numThreads = tmp;
}
} else {
parm->numThreads = 1;
}
E = 0;
if ( !E ) E |= echoRTRender( nraw, cam, scene, parm, state );
if ( E ) {
airMopAdd( mop, err = biffGetDone( ECHO ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
printf( "render time = %g seconds ( %g fps )\n",
state->time, 1.0/state->time );
if ( !E ) E |= nrrdSave( "raw.nrrd", nraw, NULL );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009, 2008 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "elf.h"
/* Routines for estimating even-order spherical harmonics or tensor
* coefficients.
*/
/* elfCart2Thetaphi:
*
* Helper function to transform ct 3D Cartesian coordinates into
* theta ( polar angle from positive z ) and phi ( azimuth from positive x )
* Input vectors should be non-zero, but do not need to be normalized
* thetaphi needs to be pre-allocated to length 2*ct
*/
#define ELFCART2THETAPHI( TYPE, SUF ) \
36 void elfCart2Thetaphi_##SUF( TYPE *thetaphi, const TYPE *dirs, \
unsigned int ct ) { \
unsigned int i; \
for ( i=0; i<ct; i++ ) { \
TYPE r=AIR_CAST( TYPE, ELL_3V_LEN( dirs+3*i ) ); \
TYPE z=dirs[3*i+2]/r; \
if ( z>1 ) thetaphi[2*i]=0; \
else if ( z<-1 ) thetaphi[2*i]=AIR_CAST( TYPE, AIR_PI ); \
else thetaphi[2*i]=AIR_CAST( TYPE, acos( z ) ); \
thetaphi[2*i+1]=AIR_CAST( TYPE, atan2( dirs[3*i+1], dirs[3*i] ) ); \
} \
} \
ELFCART2THETAPHI( double, d )
ELFCART2THETAPHI( float, f )
/* elfESHEstimMatrix:
*
* Computes a matrix T that can be used to transform a measurement vector v
* of ct values on the sphere, at positions given by thetaphi, into a vector
* c of ESH coefficients of the desired order ( c=Tv ) such that the ESH
* series approximates the given values in a least squares sense.
58 *
59 * T needs to be pre-allocated to length len( c )*ct
* If H is non-NULL, it should be pre-allocated to length ct*ct
* In this case, the "hat" matrix will be written to H, which maps the
* measurement vector v to the corresponding model predictions
* If lambda>0, Laplace-Beltrami regularization is employed
* If w is non-NULL, it is assumed to be a weight vector with len( w )=ct
*
* Returns 0 on success
* 1 if order is unsupported
* 2 if len( v )<len( c ) ( i.e., system is underdetermined )
*/
#define ELFESHESTIMMATRIX( TYPE, SUF ) \
int elfESHEstimMatrix_##SUF( TYPE *T, TYPE *H, unsigned int order, \
const TYPE *thetaphi, \
unsigned int ct, TYPE lambda, TYPE *w ) { \
double *B, *M, *Minv; \
unsigned int N, i, j, k; \
Nrrd *nmat, *ninv; \
if ( order>tijk_max_esh_order || order%2!=0 ) \
return 1; \
N=tijk_esh_len[order/2]; \
if ( ct<N ) return 2; \
\
/* intermediate computations are always done in double precision */ \
B = ( double* ) malloc( sizeof( double )*N*ct ); \
M = ( double* ) malloc( sizeof( double )*N*N ); \
\
/* build the transformation matrix */ \
/* B has row major format -> all SHs that belong to the same \
* thetaphi are stored sequentially */ \
for ( i=0; i<ct; i++ ) { \
tijk_eval_esh_basis_d( B+N*i, order, thetaphi[2*i], thetaphi[2*i+1] ); \
} \
if ( w!=NULL ) { /* weighted fit */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<N; ++j ) { /* column index */ \
M[N*i+j]=0.0; \
for ( k=0; k<ct; ++k ) { \
M[N*i+j]+=B[N*k+i]*B[N*k+j]*w[k]; \
} \
} \
} else { /* unweighted fit */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<N; ++j ) { /* column index */ \
M[N*i+j]=0.0; \
for ( k=0; k<ct; ++k ) { \
M[N*i+j]+=B[N*k+i]*B[N*k+j]; \
} \
} \
} \
/* if desired, perform Laplace-Beltrami regularization */ \
if ( lambda>0 ) { \
unsigned int idx=0, o; \
for ( o=0; o<=order; o+=2 ) { /* order */ \
while ( idx<tijk_esh_len[o/2] ) { \
M[N*idx+idx]+=lambda*o*o*( o+1 )*( o+1 ); \
idx++; \
} \
} \
} \
/* invert what we have up to now */ \
nmat = nrrdNew( ); \
ninv = nrrdNew( ); \
nmat->dim=2; \
nmat->type=nrrdTypeDouble; \
nmat->axis[0].size=nmat->axis[1].size=N; \
nmat->data=M; \
ell_Nm_inv( ninv, nmat ); \
Minv = ( double* ) ninv->data; \
\
/* create final transformation matrix */ \
if ( w!=NULL ) { /* weighted */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<ct; ++j ) { \
unsigned int idx=ct*i+j; \
T[idx]=0.0; \
for ( k=0; k<N; ++k ) { \
T[idx]+=AIR_CAST( TYPE, Minv[N*i+k]*B[N*j+k]*w[j] ); \
} \
} \
} else { /* unweighted */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<ct; ++j ) { \
unsigned int idx=ct*i+j; \
T[idx]=0.0; \
for ( k=0; k<N; ++k ) { \
T[idx]+=AIR_CAST( TYPE, Minv[N*i+k]*B[N*j+k] ); \
} \
} \
} \
nmat = nrrdNix( nmat ); \
ninv = nrrdNuke( ninv ); \
\
if ( H!=NULL ) { /* H = BT */ \
for ( i=0; i<ct; i++ ) /* row index */ \
for ( j=0; j<ct; j++ ) { \
unsigned int idx=ct*i+j; \
H[idx]=0.0; \
for ( k=0; k<N; k++ ) { /* sum over all SH coeffs */ \
H[idx]+=AIR_CAST( TYPE, B[N*i+k]*T[k*ct+j] ); \
} \
} \
} \
\
free( M ); \
free( B ); \
return 0; \
}
ELFESHESTIMMATRIX( double, d )
ELFESHESTIMMATRIX( float, f )
/* elfTenEstimMatrix:
*
* Computes a matrix T that can be used to transform a measurement vector v
* of ct values, associated with vectors at vecs, into a vector
* t of symmetric tensor coefficients of the given type ( t=Tv ) such that the
* tensor approximates the given values in a least squares sense.
*
* T needs to be pre-allocated to length len( c )*ct
* If H is non-NULL, it should be pre-allocated to length ct*ct
* In this case, the "hat" matrix will be written to H, which maps the
* measurement vector v to the corresponding model predictions
* If w is non-NULL, it is assumed to be a weight vector with len( w )=ct
*
* Returns 0 on success
* 1 if tensor type is unsupported
* 2 if len( v )<len( c ) ( i.e., system is underdetermined )
*/
/* This is strongly modeled on elfESHEstimMatrix */
#define ELFTENESTIMMATRIX( TYPE, SUF ) \
int elfTenEstimMatrix_##SUF( TYPE *T, TYPE *H, const tijk_type *type, \
const TYPE *vecs, \
unsigned int ct, TYPE *w ) { \
double *B, *M, *Minv; \
unsigned int N, i, j, k; \
Nrrd *nmat, *ninv; \
if ( type==NULL || type->sym==NULL || ( type->dim!=2 && type->dim!=3 ) ) \
return 1; \
N=type->num; \
if ( ct<N ) return 2; \
\
/* intermediate computations are always done in double precision */ \
B = ( double* ) malloc( sizeof( double )*N*ct ); \
M = ( double* ) malloc( sizeof( double )*N*N ); \
\
/* build the transformation matrix */ \
/* B has row major format -> all coeffs that belong to the same \
* vec are stored sequentially */ \
for ( i=0; i<ct; i++ ) { \
double vec[3]; \
if ( type->dim==2 ) { \
ELL_2V_COPY( vec, vecs+2*i ); \
} else { \
ELL_3V_COPY( vec, vecs+3*i ); \
} \
( *type->sym->make_rank1_d )( B+N*i, 1.0, vec ); \
} \
for ( j=0; j<N; j++ ) { \
/* at this point, take sqrt of multiplicities to make \
* condition number for inversion rotation invariant */ \
double factor=sqrt( type->mult[j] ); \
for ( i=0; i<ct; i++ ) \
B[N*i+j]*=factor; \
} \
if ( w!=NULL ) { /* weighted fit */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<N; ++j ) { /* column index */ \
M[N*i+j]=0.0; \
for ( k=0; k<ct; ++k ) { \
M[N*i+j]+=B[N*k+i]*B[N*k+j]*w[k]; \
} \
} \
} else { /* unweighted fit */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<N; ++j ) { /* column index */ \
M[N*i+j]=0.0; \
for ( k=0; k<ct; ++k ) { \
M[N*i+j]+=B[N*k+i]*B[N*k+j]; \
} \
} \
} \
/* invert what we have up to now */ \
nmat = nrrdNew( ); \
ninv = nrrdNew( ); \
nmat->dim=2; \
nmat->type=nrrdTypeDouble; \
nmat->axis[0].size=nmat->axis[1].size=N; \
nmat->data=M; \
ell_Nm_inv( ninv, nmat ); \
Minv = ( double* ) ninv->data; \
\
/* create final transformation matrix */ \
if ( w!=NULL ) { /* weighted */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<ct; ++j ) { \
unsigned int idx=ct*i+j; \
T[idx]=0.0; \
for ( k=0; k<N; ++k ) { \
T[idx]+=AIR_CAST( TYPE, Minv[N*i+k]*B[N*j+k]*w[j] ); \
} \
} \
} else { /* unweighted */ \
for ( i=0; i<N; ++i ) /* row index */ \
for ( j=0; j<ct; ++j ) { \
unsigned int idx=ct*i+j; \
T[idx]=0.0; \
for ( k=0; k<N; ++k ) { \
T[idx]+=AIR_CAST( TYPE, Minv[N*i+k]*B[N*j+k] ); \
} \
} \
} \
nmat = nrrdNix( nmat ); \
ninv = nrrdNuke( ninv ); \
\
if ( H!=NULL ) { /* H = BT */ \
for ( i=0; i<ct; i++ ) /* row index */ \
for ( j=0; j<ct; j++ ) { \
unsigned int idx=ct*i+j; \
H[idx]=0.0; \
for ( k=0; k<N; k++ ) { /* sum over all tensor coeffs */ \
H[idx]+=AIR_CAST( TYPE, B[N*i+k]*T[k*ct+j] ); \
} \
} \
} \
\
/* divide sqrt( multiplicities ) out of T to comply with tijk's \
* ( non-premultiplied ) representation of tensors */ \
for ( i=0; i<N; i++ ) { \
double factor=1.0/sqrt( type->mult[i] ); \
for ( j=0; j<ct; j++ ) \
T[ct*i+j]*=factor; \
} \
\
free( M ); \
free( B ); \
return 0; \
}
ELFTENESTIMMATRIX( double, d )
ELFTENESTIMMATRIX( float, f )
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009, 2008 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "elf.h"
#if TEEM_LEVMAR
#include <levmar.h>
#endif
/* Routines for estimating the ball-and-multi-stick model from single-shell
* DWI data. Implements the ideas from
*
* T. Schultz, C.-F. Westin, G. Kindlmann: Multi-Diffusion-Tensor
* Fitting via Spherical Deconvolution: A Unifying Framework. MICCAI
* 2010, Part I, LNCS 6361, pp. 673-680. Springer, 2010
*/
/* elfKernelStick:
*
* Computes the deconvolution kernel corresponding to a single stick, for
* desired ESH order, bd ( b value times diffusivity ) and b0 ( non-DWI signal )
* If delta!=0, deconvolves to delta peak, else to rank-1 peak
*
* returns 0 on success, 1 if order is not supported
*/
44 int elfKernelStick_f( float *kernel, unsigned int order, float bd,
float b0, int delta ) {
double ebd=exp( bd );
double embd=exp( -bd );
double sbd=sqrt( bd );
double erfsbd=airErf( sbd );
double spi=sqrt( AIR_PI );
kernel[0]=AIR_CAST( float, b0*AIR_PI*erfsbd/sbd );
if ( order>=2 ) {
kernel[1]=AIR_CAST( float, -b0/( 4.0*bd*sbd )*embd*sqrt( 5*AIR_PI )*
( 6*sbd+( -3+2*bd )*ebd*spi*erfsbd ) );
if ( order>=4 ) {
kernel[2]=AIR_CAST( float, b0/( 32.0*bd*bd*sbd )*embd*spi*
( -30*sbd*( 21+2*bd )+9*( 35+4*bd*( -5+bd ) )*
ebd*spi*erfsbd ) );
if ( order>=6 ) { /* At order 6, noise starts to take over! */
kernel[3]=AIR_CAST( float, b0/( 128*bd*bd*bd*sbd )*embd*sqrt( 13.0 )*
( -42*sbd*( 165+4*bd*( 5+bd ) )*spi-
5*( -693+378*bd-84*bd*bd+8*bd*bd*bd )*
ebd*AIR_PI*erfsbd ) );
if ( order>=8 ) {
kernel[4]=AIR_CAST( float, b0/( 2048*bd*bd*bd*bd*sbd )*embd*sqrt( 17.0 )*
( -6*sbd*( 225225+2*bd*( 15015+2*bd*( 1925+62*bd ) ) )*spi+
35*( 19305+8*bd*( -1287+bd*( 297+2*( -18+bd )*bd ) ) )*
ebd*AIR_PI*erfsbd ) );
if ( order>8 )
return 1;
}
}
}
}
if ( delta ) {
return tijk_esh_make_kernel_delta_f( kernel, kernel, order );
}
return tijk_esh_make_kernel_rank1_f( kernel, kernel, order );
}
/* elfBallStickODF:
*
* Deconvolves single-shell DWI measurements to an ODF, using a kernel derived
* from the ball-and-stick model
*
* Output:
* odf - ESH coefficients of computed ODF
* fiso - if non-NULL, estimated isotropic volume fraction
* d - if non-NULL, estimated ADC
* Input:
* dwi - DWI measurement and parameters
* T - matrix computed by elfESHEstimMatrix
* order - desired ESH order of odf
* delta - whether to use delta peak ( set to 0 when using elfBallStickPredict )
*
* Returns 0 on success
* 1 if order is not supported
* 2 if all DWIs were larger than the B0 image
*/
100 int elfBallStickODF_f( float *odf, float *fiso, float *d,
const elfSingleShellDWI *dwi,
const float *T, unsigned int order, int delta )
{
unsigned int C = tijk_esh_len[order/2], k, l;
float mean=0, _origd=1e-20f, _d=_origd;
float *odfp = odf;
float isovf0, isovf1, _fiso;
float kernel[10]; /* should be tijk_max_esh_order/2+1,
* but ANSI C doesn't allow that :/ */
if ( order>tijk_max_esh_order || order%2!=0 )
return 1;
/* use T to estimate ESH coefficients for given signal values */
for ( k=0; k<C; k++ ) {
*odfp = 0.0;
for ( l=0; l<dwi->dwino; l++ )
*odfp += T[k*dwi->dwino+l]*dwi->dwis[l];
odfp++;
}
/* guess d and fiso based on the data */
for ( k=0; k<dwi->dwino; k++ ) {
float thisd = AIR_CAST( float, -log( dwi->dwis[k]/dwi->b0 )/dwi->b );
if ( dwi->dwis[k]!=0 && thisd>_d ) _d=thisd;
mean += dwi->dwis[k];
}
mean /= dwi->dwino;
isovf0 = AIR_CAST( float, 0.5*sqrt( AIR_PI/( dwi->b*_d ) )
*airErf( sqrt( dwi->b*_d ) ) );
isovf1 = AIR_CAST( float, exp( -dwi->b*_d ) );
_fiso = AIR_CAST( float, AIR_AFFINE( isovf0, mean/dwi->b0, isovf1, 0.0, 1.0 ) );
_fiso=AIR_CLAMP( 0.01f, _fiso, 0.99f );
if ( fiso!=NULL ) *fiso=_fiso;
if ( d!=NULL ) *d=_d;
/* create deconvolution kernel */
elfKernelStick_f( kernel, order, dwi->b*_d, dwi->b0, delta );
/* remove estimated isotropic part from the signal */
odf[0] -= AIR_CAST( float, dwi->b0 * _fiso * 2*sqrt( AIR_PI )*exp( -dwi->b*_d ) );
/* deconvolve */
tijk_esh_deconvolve_f( odf, odf, kernel, order );
if ( _d==_origd ) return 2;
else return 0;
}
/* elfBallStickPredict:
*
* Based on a rank-k decomposition of the given odf ( given as a
* symmetric tensor of type "type" ), sets vs and fs[1..k] of parms. d
* ( ADC ) and fiso are used to set d and fs[0].
* Returns 0 upon success, 1 upon error
*/
157 int elfBallStickPredict_f( elfBallStickParms *parms, float *odf,
const tijk_type *type, unsigned int k,
float d, float fiso ) {
tijk_refine_rankk_parm *rparm;
unsigned int i;
float totalfs=0;
if ( k>3 ) return 1;
rparm=tijk_refine_rankk_parm_new( );
rparm->pos=1;
if ( tijk_approx_rankk_3d_f( parms->fs+1, parms->vs, NULL,
odf, type, k, rparm ) ) {
rparm=tijk_refine_rankk_parm_nix( rparm );
return 1;
}
rparm=tijk_refine_rankk_parm_nix( rparm );
parms->d = d;
parms->fiberct = k;
parms->fs[0]=fiso;
/* normalize fs[1..3] */
for ( i=1; i<=k; i++ )
totalfs+=parms->fs[i];
for ( i=1; i<=k; i++ )
parms->fs[i]*=( 1.0f-parms->fs[0] )/totalfs;
return 0;
}
#if TEEM_LEVMAR
/* Callbacks for levmar-based optimization of ball-and-stick model */
/* pp[0]: log( d ) pp[1]: VF1 pp[2]: theta1 pp[3]: phi1
* pp[4]: VF2 pp[5]: theta2 pp[6]: phi2
* pp[7]: VF3 pp[8]: theta3 pp[9]: phi3
* one-/two-stick-models may only use a subset of these */
static void
195 _levmarBallStickCB( double *pp, double *xx, int mm, int nn, void *_data ) {
int ii, maxk=( mm-1 )/3;
elfSingleShellDWI *data = ( elfSingleShellDWI * ) _data;
float *egrad;
double adc=exp( pp[0] );
double dirs[3][3];
int k;
double vfs[4]={0.0, 0.0, 0.0, 0.0}, sumvfs;
for ( k=0; k<maxk; k++ ) {
double stheta=sin( pp[2+3*k] );
ELL_3V_SET( dirs[k], cos( pp[3+3*k] )*stheta,
sin( pp[3+3*k] )*stheta,
cos( pp[2+3*k] ) );
}
for ( k=0; k<maxk; k++ ) {
if ( pp[1+3*k]>=0 ) vfs[k+1]=1.0-0.5*exp( -pp[1+3*k] );
else vfs[k+1]=0.5*exp( pp[1+3*k] );
}
sumvfs=vfs[1]+vfs[2]+vfs[3];
if ( sumvfs>1.0 ) {
vfs[1]/=sumvfs; vfs[2]/=sumvfs; vfs[3]/=sumvfs;
} else {
vfs[0]=1.0-sumvfs;
}
egrad = data->grads;
for ( ii=0; ii<nn; ii++ ) {
xx[ii] = vfs[0]*exp( -data->b*adc ); /* ball compartment */
for ( k=0; k<maxk; k++ ) {
double dp=ELL_3V_DOT( egrad, dirs[k] );
xx[ii] += vfs[k+1]*exp( -data->b*adc*dp*dp );
}
xx[ii]*=data->b0;
egrad+=3;
}
}
static void
235 _levmarBallStickJacCB( double *p, double *jac, int m, int n, void *_data ) {
elfSingleShellDWI *data = ( elfSingleShellDWI * ) _data;
float *egrad;
double *j;
int i, k, maxk=( m-1 )/3;
double adc=exp( p[0] );
double dirs[3][3];
double stheta[3], ctheta[3], sphi[3], cphi[3];
/* pre-compute volume fractions */
double _vfs[4]={0.0, 0.0, 0.0, 0.0}, vfs[4], vfssum, svfssum;
for ( k=0; k<maxk; k++ ) {
if ( p[1+3*k]>=0 ) _vfs[k+1]=1.0-0.5*exp( -p[1+3*k] );
else _vfs[k+1]=0.5*exp( p[1+3*k] );
}
vfssum=_vfs[1]+_vfs[2]+_vfs[3];
svfssum=vfssum*vfssum;
if ( vfssum>1.0 ) {
vfs[0]=0;
vfs[1]=_vfs[1]/vfssum; vfs[2]=_vfs[2]/vfssum; vfs[3]=_vfs[3]/vfssum;
} else {
vfs[0]=1.0-vfssum;
ELL_3V_COPY( vfs+1, _vfs+1 );
}
/* directions */
for ( k=0; k<maxk; k++ ) {
stheta[k]=sin( p[2+3*k] ); ctheta[k]=cos( p[2+3*k] );
sphi[k]=sin( p[3+3*k] ); cphi[k]=cos( p[3+3*k] );
dirs[k][0]=cphi[k]*stheta[k];
dirs[k][1]=sphi[k]*stheta[k];
dirs[k][2]=ctheta[k];
}
egrad = data->grads;
j=jac;
for ( i=0; i<n; i++ ) { /* iterate over all directions */
double dps[3];
double pred[4]; /* to match vfs, pred[0] is the ball compartment */
pred[0]=data->b0*exp( -data->b*adc );
for ( k=0; k<maxk; k++ ) { /* we need these first */
dps[k]=ELL_3V_DOT( egrad, dirs[k] );
pred[k+1]=data->b0*exp( -data->b*adc*dps[k]*dps[k] );
}
j[0]=vfs[0]*pred[0]*( -data->b*adc ); /* ball contribution */
for ( k=0; k<maxk; k++ ) {
int ik; /* inner loop over k */
j[0]+=vfs[k+1]*pred[k+1]*( -data->b*adc*dps[k]*dps[k] );
if ( vfssum<1.0 ) {
if ( _vfs[k+1]>=0.5 )
j[1+3*k]=( 1.0-_vfs[k+1] )*( -pred[0]+pred[k+1] );
else
j[1+3*k]=_vfs[k+1]*( -pred[0]+pred[k+1] );
} else {
j[1+3*k]=0;
for ( ik=0; ik<maxk; ik++ ) {
j[1+3*k]+=_vfs[ik+1]*( pred[k+1]-pred[ik+1] );
}
if ( _vfs[k+1]>=0.5 )
j[1+3*k]*=( 1.0-_vfs[k+1] )/svfssum;
else
j[1+3*k]*=_vfs[k+1]/svfssum;
}
j[2+3*k]=vfs[k+1]*pred[k+1]*
( -2*data->b*adc*dps[k]*( egrad[0]*ctheta[k]*cphi[k]+
egrad[1]*ctheta[k]*sphi[k]-
egrad[2]*stheta[k] ) );
j[3+3*k]=vfs[k+1]*pred[k+1]*
( -2*data->b*adc*dps[k]*( -egrad[0]*stheta[k]*sphi[k]+
egrad[1]*stheta[k]*cphi[k] ) );
}
egrad+=3;
j+=m;
}
}
#endif /* levmar support */
/* elfBallStickOptimize:
*
* Based on an initial guess of parms, use Levenberg-Marquardt optimization
* to improve the fit w.r.t. the given DWI data.
* Requires teem to be compiled with levmar support
* Returns 0 upon success
* 1 if fiberct outside {1, 2, 3}
* 2 if levmar returned an error
* 3 if levmar support is missing in this version of teem
*/
322 int elfBallStickOptimize_f( elfBallStickParms *parms,
const elfSingleShellDWI *dwi ) {
#if TEEM_LEVMAR
double lmparms[10], *dwis;
int lmret=0;
unsigned int k;
const int parmct=3*parms->fiberct+1;
const int maxitr=200;
double opts[LM_OPTS_SZ]={LM_INIT_MU, 1e-2, 1e-8, 1e-8, 1e-8};
double info[9];
double sumfs;
double mind=1e-5, minvf=1e-3; /* some minimal values for stability */
if ( parms->fiberct==0 || parms->fiberct>3 )
return 1;
/* we need a double precision version of the dwis */
dwis = ( double* ) malloc( sizeof( double )*dwi->dwino );
for ( k=0; k<dwi->dwino; k++ )
dwis[k]=dwi->dwis[k];
/* set parameters for optimization */
lmparms[0]=log( AIR_MAX( mind, parms->d ) );
parms->fs[1]=AIR_MAX( minvf, parms->fs[1] );
if ( parms->fs[1]<0.5 ) lmparms[1]=log( 2*parms->fs[1] );
else lmparms[1]=-log( 2.0-2.0*parms->fs[1] );
lmparms[2]=acos( parms->vs[2] );
lmparms[3]=atan2( parms->vs[1], parms->vs[0] );
if ( parms->fiberct>1 ) {
parms->fs[2]=AIR_MAX( minvf, parms->fs[2] );
if ( parms->fs[2]<0.5 ) lmparms[4]=log( 2*parms->fs[2] );
else lmparms[4]=-log( 2.0-2.0*parms->fs[2] );
lmparms[5]=acos( parms->vs[5] );
lmparms[6]=atan2( parms->vs[4], parms->vs[3] );
if ( parms->fiberct>2 ) {
parms->fs[3]=AIR_MAX( minvf, parms->fs[3] );
if ( parms->fs[3]<0.5 ) lmparms[7]=log( 2*parms->fs[3] );
else lmparms[7]=-log( 2.0-2.0*parms->fs[3] );
lmparms[8]=acos( parms->vs[8] );
lmparms[9]=atan2( parms->vs[7], parms->vs[6] );
}
}
lmret = dlevmar_der( _levmarBallStickCB, _levmarBallStickJacCB,
lmparms, dwis,
parmct, dwi->dwino, maxitr, opts, info,
NULL, NULL, ( void* )dwi );
if ( lmret==-1 && ( int )info[6]==4 ) {
/* try again with larger mu */
opts[0]*=10;
lmret = dlevmar_der( _levmarBallStickCB, _levmarBallStickJacCB,
lmparms, dwis,
parmct, dwi->dwino, maxitr, opts, info,
NULL, NULL, ( void* )dwi );
}
free( dwis ); /* no longer needed */
/* output the results ( whether or not levmar signalled an error ) */
parms->d = exp( lmparms[0] );
parms->fs[0] = 0.0;
if ( lmparms[1]>=0 ) parms->fs[1]=1.0-0.5*exp( -lmparms[1] );
else parms->fs[1]=0.5*exp( lmparms[1] );
if ( parms->fiberct>1 ) {
if ( lmparms[4]>=0 ) parms->fs[2]=1.0-0.5*exp( -lmparms[4] );
else parms->fs[2]=0.5*exp( lmparms[4] );
} else {
parms->fs[2]=0.0;
}
if ( parms->fiberct>2 ) {
if ( lmparms[7]>=0 ) parms->fs[3]=1.0-0.5*exp( -lmparms[7] );
else parms->fs[3]=0.5*exp( lmparms[7] );
} else {
parms->fs[3]=0.0;
}
sumfs = parms->fs[1] + parms->fs[2] + parms->fs[3];
if ( sumfs>1.0 ) {
ELL_4V_SCALE( parms->fs, 1.0/sumfs, parms->fs );
} else {
parms->fs[0] = 1.0-sumfs;
}
for ( k=0; k<parms->fiberct; k++ ) {
double stheta = sin( lmparms[2+3*k] );
ELL_3V_SET( parms->vs+3*k,
stheta*cos( lmparms[3+3*k] ),
stheta*sin( lmparms[3+3*k] ),
cos( lmparms[2+3*k] ) );
}
/* record statistical information */
parms->stopreason = ( int ) ( info[6] )-1;
parms->sqrerr = info[1];
parms->itr = info[5];
if ( lmret==-1 ) return 2;
return 0;
#else /* no levmar support, out of luck */
( void ) parms; ( void ) dwi; /* not using the parameters in this case */
return 3;
#endif
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2010, 2009 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "elf.h"
const int
elfPresent = 42;
/* Glyphs for higher-order tensors */
/* Helper routine to estimate normals in a triangle soup in which antipodal
* vertices are subsequent */
static void
32 estimateNormalsAntipodal ( limnPolyData *glyph, const char normalize ) {
unsigned int faceno=glyph->indxNum/3;
unsigned int *faces=glyph->indx;
unsigned int f;
memset( glyph->norm, 0, sizeof( float )*3*glyph->xyzwNum );
for ( f=0; f<faceno/2; f++ ) {
float diff1[3], diff2[3], cross[3];
ELL_3V_SUB( diff1, glyph->xyzw+4*faces[3*f+1],
glyph->xyzw+4*faces[3*f] );
ELL_3V_SUB( diff2, glyph->xyzw+4*faces[3*f+2],
glyph->xyzw+4*faces[3*f] );
ELL_3V_CROSS( cross, diff1, diff2 );
ELL_3V_INCR( glyph->norm+3*faces[3*f], cross );
ELL_3V_INCR( glyph->norm+3*faces[3*f+1], cross );
ELL_3V_INCR( glyph->norm+3*faces[3*f+2], cross );
/* same for anti-face */
if ( faces[3*f]%2==0 )
ELL_3V_SUB( glyph->norm+3*faces[3*f]+3,
glyph->norm+3*faces[3*f]+3, cross );
else
ELL_3V_SUB( glyph->norm+3*faces[3*f]-3,
glyph->norm+3*faces[3*f]-3, cross );
if ( faces[3*f+1]%2==0 )
ELL_3V_SUB( glyph->norm+3*faces[3*f+1]+3,
glyph->norm+3*faces[3*f+1]+3, cross );
else
ELL_3V_SUB( glyph->norm+3*faces[3*f+1]-3,
glyph->norm+3*faces[3*f+1]-3, cross );
if ( faces[3*f+2]%2==0 )
ELL_3V_SUB( glyph->norm+3*faces[3*f+2]+3,
glyph->norm+3*faces[3*f+2]+3, cross );
else
ELL_3V_SUB( glyph->norm+3*faces[3*f+2]-3,
glyph->norm+3*faces[3*f+2]-3, cross );
}
if ( normalize ) {
float len;
unsigned int i;
for ( i=0; i<glyph->normNum; i++ ) {
ELL_3V_NORM_TT( glyph->norm + 3*i, float, glyph->norm + 3*i, len );
}
}
}
/*
******** elfGlyphPolar
**
** Turns a unit sphere into a polar plot that depicts a given tensor.
**
** Input:
** glyph is expected to represent the unit sphere
** antipodal can be set to a non-zero value if antipodal points on the sphere
** are subsequent in the input. It will lead to faster processing.
** ( this can be used together with limnPolyDataIcoSphere )
** ten is an input tensor of type type
** clamp - if nonzero, negative values will be clamped to zero
** normalize - if nonzero, surface normals will be rescaled to unit length
** posColor[4] - RGBA color code for positive values ( if desired, else NULL )
** negColor[4] - RGBA color code for negative values ( if desired, else NULL )
**
** Output:
** glyph is the polar plot that corresponds to ten.
** If the input shape was anything other than a unit sphere, the output
** shape is undefined
** Normals are only updated when they were allocated in the input
** When colors were present in the input, they are replaced by a color
** coding of sign ( if pos/negColor!=NULL ) or a pointwise XYZ-RGB map ( else )
** When isdef!=NULL, *isdef is set to 0 if we found evidence that the given
** input tensor is not positive definite
** The return value is the radius of the glyph's bounding sphere
*/
float
104 elfGlyphPolar( limnPolyData *glyph, const char antipodal,
const float *ten, const tijk_type *type,
char *isdef, const char clamp, const char normalize,
const unsigned char *posColor,
const unsigned char *negColor ) {
float *verts=glyph->xyzw;
float max=0;
unsigned int i, infoBitFlag;
char def=1;
infoBitFlag = limnPolyDataInfoBitFlag( glyph );
for ( i=0; i<glyph->xyzwNum; i++ ) {
float val=( *type->sym->s_form_f )( ten, verts );
/* if RGBA is allocated, take care of coloring */
if ( infoBitFlag & ( 1 << limnPolyDataInfoRGBA ) ) {
/* color by sign */
if ( val<0 && negColor!=NULL ) {
ELL_4V_COPY( glyph->rgba+4*i, negColor );
if ( antipodal ) ELL_4V_COPY( glyph->rgba+4*i+4, negColor );
} else if ( val>0 && posColor!=NULL ) {
ELL_4V_COPY( glyph->rgba+4*i, posColor );
if ( antipodal ) ELL_4V_COPY( glyph->rgba+4*i+4, posColor );
} else {
/* RGB encode the vertex coordinates */
ELL_4V_SET_TT( glyph->rgba+4*i, unsigned char,
255*fabs( verts[0] ), 255*fabs( verts[1] ),
255*fabs( verts[2] ), 255 );
if ( antipodal ) {
ELL_4V_COPY( glyph->rgba+4*i+4, glyph->rgba+4*i );
}
}
}
if ( val<0 ) {
def=0;
if ( clamp ) val=0;
else val=-val;
}
if ( val>max ) max=val;
ELL_3V_SCALE( verts, val, verts );
if ( antipodal ) {
ELL_3V_SCALE( verts+4, -1.0f, verts );
verts+=4; i++;
}
verts+=4;
}
if ( isdef!=NULL ) *isdef=def;
if ( infoBitFlag & ( 1 << limnPolyDataInfoNorm ) ) {
/* take care of normals */
if ( antipodal &&
glyph->primNum==1 &&
glyph->type[0]==limnPrimitiveTriangles ) {
/* we can use our specialized, more efficient code */
estimateNormalsAntipodal( glyph, normalize );
} else { /* use standard limn routine */
limnPolyDataVertexNormals( glyph );
}
}
return max;
}
/*
******** elfGlyphHOME
**
** Turns a unit sphere into a HOME glyph that depicts a given tensor.
**
** Input:
** glyph is expected to represent the unit sphere
** antipodal can be set to a non-zero value if antipodal points on the sphere
** are subsequent in the input. It will lead to faster processing.
** ( this can be used together with limnPolyDataIcoSphere )
** ten is an input tensor of type type
** normalize - if nonzero, normals will be rescaled to unit length
**
** Output:
** glyph is the HOME glyph that corresponds to ten.
** If the input shape was anything other than a unit sphere, the output
** shape is undefined
** If the input tensor does not have a rank-k decomposition with positive
** coefficients, the output shape may self-intersect
** Normals are only updated when they were allocated in the input
** When colors were present in the input, they are replaced by a
** pointwise XYZ-RGB map
** When isdef!=NULL, *isdef is set to 0 if we found evidence that the given
** input tensor is not positive definite
** The return value is the radius of the glyph's bounding sphere, or -1
** upon error ( odd tensor order; HOME glyph is only defined for even orders )
*/
float
193 elfGlyphHOME( limnPolyData *glyph, const char antipodal,
const float *ten, const tijk_type *type,
char *isdef, const char normalize ) {
float *verts=glyph->xyzw;
float max=0;
unsigned int i, infoBitFlag;
char def=1;
if ( type->order%2==1 ) /* sanity check */
return -1;
infoBitFlag = limnPolyDataInfoBitFlag( glyph );
for ( i=0; i<glyph->xyzwNum; i++ ) {
float HOMEpos[3], len;
( *type->sym->v_form_f )( HOMEpos, ten, verts );
if ( ELL_3V_DOT( HOMEpos, verts )<0 ) def=0;
ELL_3V_COPY( verts, HOMEpos );
len=AIR_CAST( float, ELL_3V_LEN( HOMEpos ) );
if ( len>max ) max=len;
/* if RGBA is allocated, take care of coloring */
if ( infoBitFlag & ( 1 << limnPolyDataInfoRGBA ) ) {
float c[3];
if ( len>1e-18 )
ELL_3V_SET_TT( c, float, fabs( verts[0]/len ),
fabs( verts[1]/len ), fabs( verts[2]/len ) );
else
ELL_3V_SET( c, 0, 0, 0 );
/* RGB encode the vertex coordinates */
ELL_4V_SET_TT( glyph->rgba+4*i, unsigned char,
255*c[0], 255*c[1], 255*c[2], 255 );
if ( antipodal ) {
ELL_4V_COPY( glyph->rgba+4*i+4, glyph->rgba+4*i );
}
}
if ( antipodal ) {
ELL_3V_SCALE( verts+4, -1.0f, verts );
verts+=4; i++;
}
verts+=4;
}
if ( isdef!=NULL ) *isdef=def;
if ( infoBitFlag & ( 1 << limnPolyDataInfoNorm ) ) {
/* take care of normals */
if ( antipodal &&
glyph->primNum==1 &&
glyph->type[0]==limnPrimitiveTriangles ) {
/* we can use our specialized faster code */
estimateNormalsAntipodal( glyph, normalize );
} else { /* use standard limn routine */
limnPolyDataVertexNormals( glyph );
}
}
return max;
}
/*
******** elfGlyphKDE
**
** Turns a unit sphere into a polar plot that depicts Kernel Density
** Estimate ( KDE ) of input vectors
**
** Input:
** glyph is expected to represent the unit sphere
** antipodal can be set to a non-zero value if antipodal points on the sphere
** are subsequent in the input, and gamma is even. It will lead to
** faster processing.
** ( this can be used together with limnPolyDataIcoSphere )
** vecs is a set of n_vecs 3D input vectors ( PDF samples )
** KDE uses dp kernel with exponent gamma
** normalize - if nonzero, surface normals will be rescaled to unit length
**
** Output:
** glyph is the polar plot that corresponds to KDE of vecs.
** If the input shape was anything other than a unit sphere, the output
** shape is undefined
**
** NOTE ON NORMALIZATION:
** Normalization is such that the maximum radius of the glyph ( when all
** samples agree ) is one. To instead normalize the KDE such that it
** integrates to unity, multiply by ( 2*gamma+1 )/( 4*pi )
**
** Normals are only updated when they were allocated in the input
** When colors were present in the input, they are replaced by a pointwise
** XYZ-RGB map
** The return value is the radius of the glyph's bounding sphere
*/
float
280 elfGlyphKDE( limnPolyData *glyph, const char antipodal,
const float *vecs, const size_t n_vecs,
const float _gamma, const char normalize ) {
float *verts=glyph->xyzw;
float max=0;
unsigned int i, j, infoBitFlag;
infoBitFlag = limnPolyDataInfoBitFlag( glyph );
for ( i=0; i<glyph->xyzwNum; i++ ) {
/* compute value by looping over all vecs */
double val=0;
for ( j=0; j<n_vecs; j++ ) {
double dp = ELL_3V_DOT( verts, vecs+3*j );
val+=pow( dp, _gamma );
}
val/=n_vecs;
/* if RGBA is allocated, take care of coloring */
if ( infoBitFlag & ( 1 << limnPolyDataInfoRGBA ) ) {
/* RGB encode the vertex coordinates */
ELL_4V_SET_TT( glyph->rgba+4*i, unsigned char,
255*fabs( verts[0] ), 255*fabs( verts[1] ),
255*fabs( verts[2] ), 255 );
if ( antipodal ) {
ELL_4V_COPY( glyph->rgba+4*i+4, glyph->rgba+4*i );
}
}
if ( val>max ) max=AIR_CAST( float, val );
ELL_3V_SCALE_TT( verts, float, val, verts );
if ( antipodal ) {
ELL_3V_SCALE( verts+4, -1.0f, verts );
verts+=4; i++;
}
verts+=4;
}
if ( infoBitFlag & ( 1 << limnPolyDataInfoNorm ) ) {
/* take care of normals */
if ( antipodal &&
glyph->primNum==1 &&
glyph->type[0]==limnPrimitiveTriangles ) {
/* we can use our specialized, more efficient code */
estimateNormalsAntipodal( glyph, normalize );
} else { /* use standard limn routine */
limnPolyDataVertexNormals( glyph );
}
}
return max;
}
/*
******** elfColorGlyphMaxima
**
** Maximum-based coloring of tensor glyphs, as described in Section 4 of
** Schultz/Kindlmann, EuroVis 2010
**
** Input:
** glyph is a tensor glyph ( generated by elfGlyph* )
** antipodal can be set to a non-zero value if antipodal points on the sphere
** are subsequent in the input. It will lead to faster processing.
** neighbors is an array that, for each vertex in glyph, holds the indices
** of its neighbors ( adjacent through an edge ). Each vertex has
** nbstride entries, padded with -1s if it really has less neighbors
** ten is the tensor depicted by the glyph
** type is the tensor type
** modulate - non-zero values lead to modulation by peak sharpness
** gamma - allows you to "bump up" the peak sharpness by the power of gamma
**
** Output:
** glyph is colored according to its maxima
** returns zero upon success ( fails if memory cannot be allocated )
*/
int
352 elfColorGlyphMaxima( limnPolyData *glyph, const char antipodal,
const int *neighbors, unsigned int nbstride,
const float *ten, const tijk_type *type,
const char modulate, const float _gamma ) {
float *sqrdist, *verts, *newcol;
char *processed, *id_ct, *diff_ct;
int *path;
unsigned int infoBitFlag, i, j;
airArray *mop;
infoBitFlag = limnPolyDataInfoBitFlag( glyph );
if ( limnPolyDataAlloc( glyph, /* make sure we have a color buffer */
infoBitFlag | ( 1 << limnPolyDataInfoRGBA ),
glyph->xyzwNum, glyph->indxNum, glyph->primNum ) ) {
return 1;
}
/* do the memory allocation */
mop = airMopNew( );
if ( mop==NULL ) return 1;
sqrdist = ( float * ) malloc( sizeof( float )*glyph->xyzwNum );
airMopAdd( mop, sqrdist, airFree, airMopAlways );
processed = ( char * ) malloc( sizeof( char )*glyph->xyzwNum );
airMopAdd( mop, processed, airFree, airMopAlways );
path = ( int * ) malloc( sizeof( int )*glyph->xyzwNum );
airMopAdd( mop, path, airFree, airMopAlways );
newcol = ( float * ) malloc( sizeof( float )*3*glyph->xyzwNum );
airMopAdd( mop, newcol, airFree, airMopAlways );
id_ct = ( char * ) malloc( sizeof( char )*glyph->xyzwNum );
airMopAdd( mop, id_ct, airFree, airMopAlways );
diff_ct = ( char * ) malloc( sizeof( char )*glyph->xyzwNum );
airMopAdd( mop, diff_ct, airFree, airMopAlways );
if ( sqrdist==NULL || processed==NULL || path==NULL || newcol==NULL ||
id_ct==NULL || diff_ct==NULL ) {
airMopError( mop );
return 1;
}
/* initialize sqrdist / processed / path */
verts=glyph->xyzw;
if ( antipodal ) {
for ( i=0; i<glyph->xyzwNum; i+=2 ) {
sqrdist[i]=sqrdist[i+1]=ELL_3V_DOT( verts, verts );
verts+=8;
}
} else {
for ( i=0; i<glyph->xyzwNum; i++ ) {
sqrdist[i]=ELL_3V_DOT( verts, verts );
verts+=4;
}
}
memset( processed, 0, sizeof( char )*glyph->xyzwNum );
memset( path, -1, sizeof( int )*glyph->xyzwNum );
/* go through all vertices; ascend until we get to a maximum or a
* previously processed vertex */
for ( i=0; i<glyph->xyzwNum; i++ ) {
unsigned int pathno=0;
int vert=i;
char foundmax=0;
unsigned char color[3];
if ( processed[i] ) continue;
path[pathno++]=vert;
while ( !foundmax ) {
int bestnb=-1;
float maxdist=0;
for ( j=0; j<nbstride; ++j ) {
int nb=neighbors[nbstride*vert+j];
float dist;
if ( nb==-1 ) break;
dist=sqrdist[nb]-sqrdist[vert];
if ( dist>maxdist ) {
maxdist=dist; bestnb=nb;
}
}
if ( bestnb==-1 ) { /* we are at an unprocessed maximum */
/* find appropriate color */
float vertdir[3];
float norm;
float modfactor=1.0;
ELL_3V_COPY( vertdir, glyph->xyzw+4*vert );
norm=AIR_CAST( float, ELL_3V_LEN( vertdir ) );
if ( norm>1e-18 ) {
ELL_3V_SCALE( vertdir, 1.0f/norm, vertdir );
if ( modulate ) {
/* modulate by peak strength */
float hess[7], val, evals[3];
/* compute second derivatives */
( *type->sym->hess_f )( hess+1, ten, vertdir );
val=( *type->sym->s_form_f )( ten, vertdir );
tenEigensolve_f( evals, NULL, hess );
if ( evals[1]>=0 || val<1e-10 ) {
modfactor=0.0;
} else {
modfactor=-evals[1]/( type->order*val );
if ( modfactor>1.0 ) modfactor=1.0;
else modfactor=AIR_CAST( float, pow( modfactor, _gamma ) );
}
}
} else {
ELL_3V_SET( vertdir, 0, 0, 0 );
}
ELL_3V_SET( color,
( unsigned char ) ( AIR_LERP( modfactor, 1.0,
fabs( vertdir[0] ) )*255 ),
( unsigned char ) ( AIR_LERP( modfactor, 1.0,
fabs( vertdir[1] ) )*255 ),
( unsigned char ) ( AIR_LERP( modfactor, 1.0,
fabs( vertdir[2] ) )*255 ) );
foundmax=1;
} else {
if ( processed[bestnb] ) {
ELL_3V_COPY( color, glyph->rgba+4*bestnb );
foundmax=1;
} else { /* add bestnb to the path and proceed */
path[pathno++]=bestnb;
vert=bestnb;
}
}
} /* end looking for maximum */
/* copy color to all vertices on the path */
for ( j=0; j<pathno; ++j ) {
processed[path[j]]=1;
ELL_4V_SET( glyph->rgba+4*path[j], color[0], color[1], color[2], 255 );
if ( antipodal ) {
int antip=path[j]+1;
if ( antip%2==0 ) antip-=2;
processed[antip]=1;
ELL_4V_COPY( glyph->rgba+4*antip, glyph->rgba+4*path[j] );
}
}
if ( antipodal ) i++; /* we can skip over the next one */
}
/* make coloring smoother by averaging */
memset( newcol, 0, sizeof( float )*3*glyph->xyzwNum );
memset( id_ct, 0, sizeof( char )*glyph->xyzwNum );
memset( diff_ct, 0, sizeof( char )*glyph->xyzwNum );
for ( i=0; i<glyph->xyzwNum; i++ ) {
for ( j=0; j<nbstride; j++ ) {
int nb=neighbors[nbstride*i+j];
if ( nb<0 ) break;
if ( ( unsigned int )nb<i ) continue; /* process each pair only once */
if ( ELL_3V_EQUAL( glyph->rgba+4*i, glyph->rgba+4*nb ) ) {
id_ct[i]++;
id_ct[nb]++;
} else {
ELL_3V_INCR( newcol+3*i, glyph->rgba+4*nb );
diff_ct[i]++;
ELL_3V_INCR( newcol+3*nb, glyph->rgba+4*i );
diff_ct[nb]++;
}
}
}
for ( i=0; i<glyph->xyzwNum; i++ ) {
if ( diff_ct[i]>0 ) {
ELL_3V_SCALE_INCR_TT( newcol+3*i, float, 1.0+id_ct[i], glyph->rgba+4*i );
ELL_3V_SCALE_TT( glyph->rgba+4*i, unsigned char,
1.0/( 1.0+id_ct[i]+diff_ct[i] ),
newcol+3*i );
if ( antipodal ) {
ELL_3V_COPY( glyph->rgba+4*i+4, glyph->rgba+4*i );
i++;
}
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "elf.h"
/* Creates an elfMaximaContext, which can then be used to find all
* maxima of a symmetric even-order 3D tensor of the given type.
* Returns NULL if type is not even-order 3D symmetric.
*
* The initial maximum sampling is at the vertices of a subdivided
* icosahedron - level=3 ( 321 unique directions ) should be
* sufficient. Larger levels reduce the risk of missing one of two ( or
* more ) very close maxima, at increased computational cost. */
32 elfMaximaContext *elfMaximaContextNew( const tijk_type *type,
unsigned int level ) {
elfMaximaContext *retval;
limnPolyData *sphere;
unsigned int vert;
if ( type==NULL || type->dim!=3 || type->sym==NULL || type->order%2!=0 )
return NULL; /* elfMaxima cannot be used with this tensor type */
sphere = limnPolyDataNew( );
limnPolyDataIcoSphere( sphere, 0, level );
retval = ( elfMaximaContext* ) malloc( sizeof( elfMaximaContext ) );
retval->num = sphere->xyzwNum;
retval->type = type;
retval->parm = NULL;
retval->refine = 1;
/* extract neighborhood info ( needed to take discrete maxima ) */
limnPolyDataNeighborArray( &( retval->neighbors ), &( retval->nbstride ), sphere );
/* copy over vertices in single and double precision */
retval->vertices_f = ( float* ) malloc( sizeof( float )*3*( sphere->xyzwNum/2 ) );
for ( vert=0; vert<sphere->xyzwNum; vert+=2 ) {
ELL_3V_COPY( retval->vertices_f+3*( vert/2 ), sphere->xyzw+4*vert );
}
retval->vertices_d = NULL;
sphere=limnPolyDataNix( sphere );
return retval;
}
58 elfMaximaContext *elfMaximaContextNix( elfMaximaContext *emc ) {
if ( emc!=NULL ) {
free( emc->neighbors );
free( emc->vertices_f );
if ( emc->vertices_d!=NULL )
free( emc->vertices_d );
if ( emc->parm!=NULL )
tijk_refine_rank1_parm_nix( emc->parm );
free( emc );
}
return NULL;
}
/* can be used to set the parameters for refining the found local
* maxima. Note that the elfMaximaContext will "take over possession"
* of the parm struct, i.e., it will be nix'ed along with the context
* or when setting another parm */
75 void elfMaximaParmSet( elfMaximaContext *emc,
tijk_refine_rank1_parm *parm ) {
if ( emc!=NULL ) {
if ( emc->parm!=NULL )
tijk_refine_rank1_parm_nix( emc->parm );
emc->parm=parm;
}
}
/* By default, discrete maxima are refined via optimization on the sphere.
* Set this to zero for faster, but less accurate results. */
86 void elfMaximaRefineSet( elfMaximaContext *emc, int refine ) {
if ( emc!=NULL ) {
emc->refine = refine;
}
}
/* Finds discrete maxima of the tensor ( based on emc ) and refines them,
* storing magnitudes in ( malloc'ed ) *ls and *vs. Returns the number of
* distinct maxima, or -1 on error. ls are sorted in descending order.
*/
96 int elfMaximaFind_d( double **ls, double **vs, const double *ten,
elfMaximaContext *emc ) {
unsigned int i;
int retval;
double *vals;
airHeap *heap;
if ( ls==NULL || vs==NULL || ten==NULL || emc==NULL )
return -1;
if ( emc->vertices_d==NULL ) { /* we need to allocate these */
emc->vertices_d = ( double* ) malloc( sizeof( double )*3*( emc->num/2 ) );
for ( i=0; i<emc->num/2; i++ ) {
ELL_3V_COPY( emc->vertices_d+3*i, emc->vertices_f+3*i );
}
}
/* evaluate all unique directions */
vals = ( double* ) malloc( sizeof( double )*( emc->num/2 ) );
for ( i=0; i<emc->num/2; i++ ) {
vals[i]=( *emc->type->sym->s_form_d )( ten, emc->vertices_d+3*i );
}
heap = airHeapNew( sizeof( double )*3, 20 );
/* identify discrete maxima */
for ( i=0; i<emc->num/2; i++ ) {
unsigned int ni=0;
int ismax=1, nb;
while ( ni<emc->nbstride && ( nb=emc->neighbors[emc->nbstride*2*i+ni] )!=-1 ) {
if ( vals[i]<=vals[nb/2] ) {
ismax=0;
break;
}
ni++;
}
if ( ismax ) {
double s=vals[i], v[3];
ELL_3V_COPY( v, emc->vertices_d+3*i );
if ( emc->refine ) /* refine further */
tijk_refine_max_3d_d( &s, v, ten, emc->type, emc->parm );
/* add to heap */
airHeapInsert( heap, -s, v );
}
}
/* allocate arrays and return what we have found */
retval=airHeapLength( heap );
if ( retval>0 ) {
*ls = ( double* ) malloc( sizeof( double )*retval );
*vs = ( double* ) malloc( sizeof( double )*3*retval );
for ( i=0; i<( unsigned int )retval; i++ ) {
( *ls )[i]=-airHeapFrontPop( heap, ( *vs )+3*i );
}
}
heap=airHeapNix( heap );
free( vals );
return retval;
}
/* Mostly copy-pasted from above :-/
*/
152 int elfMaximaFind_f( float **ls, float **vs, const float *ten,
elfMaximaContext *emc ) {
unsigned int i;
int retval;
float *vals;
airHeap *heap;
if ( ls==NULL || vs==NULL || ten==NULL || emc==NULL )
return -1;
/* evaluate all unique directions */
vals = ( float* ) malloc( sizeof( float )*( emc->num/2 ) );
for ( i=0; i<emc->num/2; i++ ) {
vals[i]=( *emc->type->sym->s_form_f )( ten, emc->vertices_f+3*i );
}
heap = airHeapNew( sizeof( float )*3, 20 );
/* identify discrete maxima */
for ( i=0; i<emc->num/2; i++ ) {
unsigned int ni=0;
int ismax=1, nb;
while ( ni<emc->nbstride && ( nb=emc->neighbors[emc->nbstride*2*i+ni] )!=-1 ) {
if ( vals[i]<=vals[nb/2] ) {
ismax=0;
break;
}
ni++;
}
if ( ismax ) {
float s=vals[i], v[3];
ELL_3V_COPY( v, emc->vertices_f+3*i );
if ( emc->refine ) /* refine further */
tijk_refine_max_3d_f( &s, v, ten, emc->type, emc->parm );
/* add to heap */
airHeapInsert( heap, -s, v );
}
}
/* allocate arrays and return what we have found */
retval=airHeapLength( heap );
if ( retval>0 ) {
*ls = ( float* ) malloc( sizeof( float )*retval );
*vs = ( float* ) malloc( sizeof( float )*3*retval );
for ( i=0; i<( unsigned int )retval; i++ ) {
( *ls )[i]=AIR_CAST( float, -airHeapFrontPop( heap, ( *vs )+3*i ) );
}
}
heap=airHeapNix( heap );
free( vals );
return retval;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
/*
******** ell_cubic( ):
**
** finds real roots of x^3 + A*x^2 + B*x + C.
**
** records the found real roots in the given root array.
**
** returns information about the roots according to ellCubicRoot enum,
** the set the following values in given root[] array:
** ell_cubic_root_single: root[0], root[1] == root[2] == AIR_NAN
** ell_cubic_root_triple: root[0] == root[1] == root[2]
** ell_cubic_root_single_double: single root[0]; double root[1] == root[2]
** or double root[0] == root[1], single root[2]
** ell_cubic_root_three: root[0], root[1], root[2]
**
** The values stored in root[] are, in a change from the past, sorted
** in descending order! No need to sort them any more!
**
** This does NOT use biff
*/
int
50 ell_cubic( double root[3], double A, double B, double C, int newton ) {
char me[]="ell_cubic";
double epsilon = 1.0E-11, AA, Q, R, QQQ, D, sqrt_D, der,
u, v, x, theta, t, sub;
/*
printf( "%s: A B C = %g %g %g\n", me, A, B, C );
*/
sub = A/3.0;
AA = A*A;
Q = ( AA/3.0 - B )/3.0;
R = ( -2.0*A*AA/27.0 + A*B/3.0 - C )/2.0;
QQQ = Q*Q*Q;
D = R*R - QQQ;
/*
printf( " R = %15.30f\n Q = %15.30f\n QQQ = %15.30f\n D = %15.30f\n",
R, Q, QQQ, D );
*/
if ( D < -epsilon ) {
/* three distinct roots- this is the most common case, it has
been tested the most, its code should go first */
theta = acos( R/sqrt( QQQ ) )/3.0;
t = 2*sqrt( Q );
/* yes, these are sorted, because the C definition of acos says
that it returns values in in [0, pi] */
root[0] = t*cos( theta ) - sub;
root[1] = t*cos( theta - 2*AIR_PI/3.0 ) - sub;
root[2] = t*cos( theta + 2*AIR_PI/3.0 ) - sub;
/*
if ( !AIR_EXISTS( root[0] ) ) {
fprintf( stderr, "%s: %g %g %g --> nan!!!\n", me, A, B, C );
}
*/
return ell_cubic_root_three;
}
else if ( D > epsilon ) {
double nr, fnr;
/* one real solution, except maybe also a "rescued" double root */
sqrt_D = sqrt( D );
u = airCbrt( sqrt_D+R );
v = -airCbrt( sqrt_D-R );
x = u+v - sub;
if ( !newton ) {
root[0] = x;
root[1] = root[2] = AIR_NAN;
return ell_cubic_root_single;
}
/* else refine x, the known root, with newton-raphson, so as to get the
most accurate possible calculation for nr, the possible new root */
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
x -= ( der = ( 3*x + 2*A )*x + B, ( ( x/der + A/der )*x + B/der )*x + C/der );
nr = -( A + x )/2.0;
fnr = ( ( nr + A )*nr + B )*nr + C; /* the polynomial evaluated at nr */
/*
if ( ell_debug ) {
fprintf( stderr, "%s: root = %g -> %g, nr=% 20.15f\n"
" fnr=% 20.15f\n", me,
x, ( ( ( x + A )*x + B )*x + C ), nr, fnr );
}
*/
if ( fnr < -epsilon || fnr > epsilon ) {
root[0] = x;
root[1] = root[2] = AIR_NAN;
return ell_cubic_root_single;
}
else {
if ( ell_debug ) {
fprintf( stderr, "%s: rescued double root:% 20.15f\n", me, nr );
}
if ( x > nr ) {
root[0] = x;
root[1] = nr;
root[2] = nr;
} else {
root[0] = nr;
root[1] = nr;
root[2] = x;
}
return ell_cubic_root_single_double;
}
}
else {
/* else D is in the interval [-epsilon, +epsilon] */
if ( R < -epsilon || epsilon < R ) {
/* one double root and one single root */
u = airCbrt( R );
if ( u > 0 ) {
root[0] = 2*u - sub;
root[1] = -u - sub;
root[2] = -u - sub;
} else {
root[0] = -u - sub;
root[1] = -u - sub;
root[2] = 2*u - sub;
}
return ell_cubic_root_single_double;
}
else {
/* one triple root */
root[0] = root[1] = root[2] = -sub;
return ell_cubic_root_triple;
}
}
/* shouldn't ever get here */
/* return ell_cubic_root_unknown; */
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
/* lop A
fprintf( stderr, "_ellAlign3: ----------\n" );
fprintf( stderr, "_ellAlign3: v0 = %g %g %g\n", ( v+0 )[0], ( v+0 )[1], ( v+0 )[2] );
fprintf( stderr, "_ellAlign3: v3 = %g %g %g\n", ( v+3 )[0], ( v+3 )[1], ( v+3 )[2] );
fprintf( stderr, "_ellAlign3: v6 = %g %g %g\n", ( v+6 )[0], ( v+6 )[1], ( v+6 )[2] );
fprintf( stderr, "_ellAlign3: d = %g %g %g -> %d %d %d\n",
d0, d1, d2, Mi, ai, bi );
fprintf( stderr, "_ellAlign3: pre dot signs ( 03, 06, 36 ): %d %d %d\n",
airSgn( ELL_3V_DOT( v+0, v+3 ) ),
airSgn( ELL_3V_DOT( v+0, v+6 ) ),
airSgn( ELL_3V_DOT( v+3, v+6 ) ) );
*/
/* lop B
fprintf( stderr, "_ellAlign3: v0 = %g %g %g\n", ( v+0 )[0], ( v+0 )[1], ( v+0 )[2] );
fprintf( stderr, "_ellAlign3: v3 = %g %g %g\n", ( v+3 )[0], ( v+3 )[1], ( v+3 )[2] );
fprintf( stderr, "_ellAlign3: v6 = %g %g %g\n", ( v+6 )[0], ( v+6 )[1], ( v+6 )[2] );
fprintf( stderr, "_ellAlign3: post dot signs %d %d %d\n",
airSgn( ELL_3V_DOT( v+0, v+3 ) ),
airSgn( ELL_3V_DOT( v+0, v+6 ) ),
airSgn( ELL_3V_DOT( v+3, v+6 ) ) );
if ( airSgn( ELL_3V_DOT( v+0, v+3 ) ) < 0
|| airSgn( ELL_3V_DOT( v+0, v+6 ) ) < 0
|| airSgn( ELL_3V_DOT( v+3, v+6 ) ) < 0 ) {
exit( 1 );
}
*/
/*
******** ell_quadratic( )
**
** finds real roots of A*x^2 + B*x + C.
**
** records the found roots in the given root array, and returns a
** value from the ell_quadratic_root* enum:
**
** ell_quadratic_root_two:
** two distinct roots root[0] > root[1]
** ell_quadratic_root_complex:
** two complex conjugate roots at root[0] +/- i*root[1]
** ell_quadratic_root_double:
** a repeated root root[0] == root[1]
**
** HEY simple as this code may seem, it has definitely numerical
** issues that have not been explored or fixed, such as what if A is
** near 0. Also correctly handling the transition from double root to
** complex roots needs to be re-thought, as well as this issue:
** http://people.csail.mit.edu/bkph/articles/Quadratics.pdf Should
** also understand http://www.cs.berkeley.edu/~wkahan/Qdrtcs.pdf
**
** This does NOT use biff
*/
int
80 ell_quadratic( double root[2], double A, double B, double C ) {
/* static const char me[]="ell_quadratic"; */
int ret;
double disc, rd, tmp, eps=1.0E-12;
disc = B*B - 4*A*C;
if ( disc > 0 ) {
rd = sqrt( disc );
root[0] = ( -B + rd )/( 2*A );
root[1] = ( -B - rd )/( 2*A );
if ( root[0] < root[1] ) {
ELL_SWAP2( root[0], root[1], tmp );
}
ret = ell_quadratic_root_two;
} else if ( disc < -eps ) {
root[0] = -B/( 2*A );
root[1] = sqrt( -disc )/( 2*A );
ret = ell_quadratic_root_complex;
} else {
/* 0 == disc or only *very slightly* negative */
root[0] = root[1] = -B/( 2*A );
ret = ell_quadratic_root_double;
}
return ret;
}
int
107 ell_2m_eigenvalues_d( double eval[2], const double m[4] ) {
double A, B, C;
int ret;
A = 1;
B = -m[0] - m[3];
C = m[0]*m[3] - m[1]*m[2];
ret = ell_quadratic( eval, A, B, C );
return ret;
}
void
119 ell_2m_1d_nullspace_d( double ans[2], const double _n[4] ) {
/* static const char me[]="ell_2m_1d_nullspace_d"; */
double n[4], dot, len, rowv[2];
ELL_4V_COPY( n, _n );
dot = ELL_2V_DOT( n + 2*0, n + 2*1 );
/*
fprintf( stderr, "!%s: n = {{%g, %g}, {%g, %g}}\n", me,
n[0], n[1], n[2], n[3] );
fprintf( stderr, "!%s: dot = %g\n", me, dot );
*/
if ( dot > 0 ) {
ELL_2V_ADD2( rowv, n + 2*0, n + 2*1 );
} else {
ELL_2V_SUB( rowv, n + 2*0, n + 2*1 );
}
/* fprintf( stderr, "!%s: rowv = %g %g\n", me, rowv[0], rowv[1] ); */
/* have found good description of what's perpendicular nullspace,
so now perpendicularize it */
ans[0] = rowv[1];
ans[1] = -rowv[0];
ELL_2V_NORM( ans, ans, len );
/*
if ( !( AIR_EXISTS( ans[0] ) && AIR_EXISTS( ans[1] ) ) ) {
fprintf( stderr, "!%s: bad! %g %g\n", me, ans[0], ans[1] );
}
*/
return;
}
/*
******** ell_2m_eigensolve_d
**
** Eigensolve 2x2 matrix, which may be asymmetric
*/
int
155 ell_2m_eigensolve_d( double eval[2], double evec[4], const double m[4] ) {
/* static const char me[]="ell_2m_eigensolve_d"; */
double nul[4], ident[4] = {1, 0, 0, 1};
int ret;
ret = ell_2m_eigenvalues_d( eval, m );
/*
fprintf( stderr, "!%s: m = {{%.17g, %.17g}, {%.17g, %.17g}} -> "
"%s evals ( %.17g, %.17g )\n", me, m[0], m[1], m[2], m[3],
airEnumStr( ell_quadratic_root, ret ), eval[0], eval[1] );
*/
switch ( ret ) {
case ell_quadratic_root_two:
ELL_4V_SCALE_ADD2( nul, 1.0, m, -eval[0], ident );
ell_2m_1d_nullspace_d( evec + 2*0, nul );
/*
fprintf( stderr, "!%s: eval=%.17g -> nul {{%.17g, %.17g}, {%.17g, %.17g}} "
"-> evec %.17g %.17g\n", me, eval[0],
nul[0], nul[1], nul[2], nul[3],
( evec + 2*0 )[0], ( evec + 2*0 )[1] );
*/
ELL_4V_SCALE_ADD2( nul, 1.0, m, -eval[1], ident );
ell_2m_1d_nullspace_d( evec + 2*1, nul );
/*
fprintf( stderr, "!%s: eval=%.17g -> nul {{%.17g, %.17g}, {%.17g, %.17g}} "
"-> evec %.17g %.17g\n", me, eval[1],
nul[0], nul[1], nul[2], nul[3],
( evec + 2*1 )[0], ( evec + 2*1 )[1] );
*/
break;
case ell_quadratic_root_double:
/* fprintf( stderr, "!%s: double eval=%.17g\n", me, eval[0] ); */
ELL_4V_SCALE_ADD2( nul, 1.0, m, -eval[0], ident );
/*
fprintf( stderr, "!%s: nul = {{%.17g, %.17g}, {%.17g, %.17g}} ( len %.17g )\n",
me, nul[0], nul[1], nul[2], nul[3], ELL_4V_LEN( nul ) );
*/
if ( ELL_4V_DOT( nul, nul ) ) {
/* projecting out the nullspace produced non-zero matrix,
( possibly from an asymmetric matrix ) so there is real
orientation to recover */
ell_2m_1d_nullspace_d( evec + 2*0, nul );
ELL_2V_COPY( evec + 2*1, evec + 2*0 );
} else {
/* so this was isotropic symmetric; invent orientation */
ELL_2V_SET( evec + 2*0, 1, 0 );
ELL_2V_SET( evec + 2*1, 0, 1 );
}
break;
case ell_quadratic_root_complex:
/* HEY punting for now */
ELL_2V_SET( evec + 2*0, 0.5, 0 );
ELL_2V_SET( evec + 2*1, 0, 0.5 );
break;
default:
/* fprintf( stderr, "%s: unexpected solution indicator %d\n", me, ret ); */
break;
}
return ret;
}
void
218 _ell_align3_d( double v[9] ) {
double d0, d1, d2;
int Mi, ai, bi;
d0 = ELL_3V_DOT( v+0, v+0 );
d1 = ELL_3V_DOT( v+3, v+3 );
d2 = ELL_3V_DOT( v+6, v+6 );
Mi = ELL_MAX3_IDX( d0, d1, d2 );
ai = ( Mi + 1 ) % 3;
bi = ( Mi + 2 ) % 3;
/* lop A */
if ( ELL_3V_DOT( v+3*Mi, v+3*ai ) < 0 ) {
ELL_3V_SCALE( v+3*ai, -1, v+3*ai );
}
if ( ELL_3V_DOT( v+3*Mi, v+3*bi ) < 0 ) {
ELL_3V_SCALE( v+3*bi, -1, v+3*bi );
}
/* lob B */
/* we can't guarantee that dot( v+3*ai, v+3*bi ) > 0 . . . */
}
/*
** leaves v+3*0 untouched, but makes sure that v+3*0, v+3*1, and v+3*2
** are mutually orthogonal. Also leaves the magnitudes of all
** vectors unchanged.
*/
void
245 _ell_3m_enforce_orthogonality( double v[9] ) {
double d00, d10, d11, d20, d21, d22, scl, tv[3];
d00 = ELL_3V_DOT( v+3*0, v+3*0 );
d10 = ELL_3V_DOT( v+3*1, v+3*0 );
d11 = ELL_3V_DOT( v+3*1, v+3*1 );
ELL_3V_SCALE_ADD2( tv, 1, v+3*1, -d10/d00, v+3*0 );
scl = sqrt( d11/ELL_3V_DOT( tv, tv ) );
ELL_3V_SCALE( v+3*1, scl, tv );
d20 = ELL_3V_DOT( v+3*2, v+3*0 );
d21 = ELL_3V_DOT( v+3*2, v+3*1 );
d22 = ELL_3V_DOT( v+3*2, v+3*2 );
ELL_3V_SCALE_ADD3( tv, 1, v+3*2, -d20/d00, v+3*0, -d21/d00, v+3*1 );
scl = sqrt( d22/ELL_3V_DOT( tv, tv ) );
ELL_3V_SCALE( v+3*2, scl, tv );
return;
}
/*
** makes sure that v+3*2 has a positive dot product with
** cross product of v+3*0 and v+3*1
*/
void
268 _ell_3m_make_right_handed_d( double v[9] ) {
double x[3];
ELL_3V_CROSS( x, v+3*0, v+3*1 );
if ( 0 > ELL_3V_DOT( x, v+3*2 ) ) {
ELL_3V_SCALE( v+3*2, -1, v+3*2 );
}
}
/* lop A
fprintf( stderr, "=== pre ===\n" );
fprintf( stderr, "crosses: %g %g %g\n", ( t+0 )[0], ( t+0 )[1], ( t+0 )[2] );
fprintf( stderr, " %g %g %g\n", ( t+3 )[0], ( t+3 )[1], ( t+3 )[2] );
fprintf( stderr, " %g %g %g\n", ( t+6 )[0], ( t+6 )[1], ( t+6 )[2] );
fprintf( stderr, "cross dots: %g %g %g\n",
ELL_3V_DOT( t+0, t+3 ), ELL_3V_DOT( t+0, t+6 ), ELL_3V_DOT( t+3, t+6 ) );
*/
/* lop B
fprintf( stderr, "=== post ===\n" );
fprintf( stderr, "crosses: %g %g %g\n", ( t+0 )[0], ( t+0 )[1], ( t+0 )[2] );
fprintf( stderr, " %g %g %g\n", ( t+3 )[0], ( t+3 )[1], ( t+3 )[2] );
fprintf( stderr, " %g %g %g\n", ( t+6 )[0], ( t+6 )[1], ( t+6 )[2] );
fprintf( stderr, "cross dots: %g %g %g\n",
ELL_3V_DOT( t+0, t+3 ), ELL_3V_DOT( t+0, t+6 ), ELL_3V_DOT( t+3, t+6 ) );
*/
/*
******** ell_3m_1d_nullspace_d( )
**
** the given matrix is assumed to have a nullspace of dimension one.
** A normalized vector which spans the nullspace is put into ans.
**
** The given nullspace matrix is NOT modified.
**
** This does NOT use biff
*/
void
306 ell_3m_1d_nullspace_d( double ans[3], const double _n[9] ) {
double t[9], n[9], norm;
ELL_3M_TRANSPOSE( n, _n );
/* find the three cross-products of pairs of column vectors of n */
ELL_3V_CROSS( t+0, n+0, n+3 );
ELL_3V_CROSS( t+3, n+0, n+6 );
ELL_3V_CROSS( t+6, n+3, n+6 );
/* lop A */
_ell_align3_d( t );
/* lop B */
/* add them up ( longer, hence more accurate, should dominate ) */
ELL_3V_ADD3( ans, t+0, t+3, t+6 );
/* normalize */
ELL_3V_NORM( ans, ans, norm );
return;
}
/*
******** ell_3m_2d_nullspace_d( )
**
** the given matrix is assumed to have a nullspace of dimension two.
**
** The given nullspace matrix is NOT modified
**
** This does NOT use biff
*/
void
336 ell_3m_2d_nullspace_d( double ans0[3], double ans1[3], const double _n[9] ) {
double n[9], tmp[3], norm;
ELL_3M_TRANSPOSE( n, _n );
_ell_align3_d( n );
ELL_3V_ADD3( tmp, n+0, n+3, n+6 );
ELL_3V_NORM( tmp, tmp, norm );
/* any two vectors which are perpendicular to the ( supposedly 1D )
span of the column vectors span the nullspace */
ell_3v_perp_d( ans0, tmp );
ELL_3V_NORM( ans0, ans0, norm );
ELL_3V_CROSS( ans1, tmp, ans0 );
return;
}
/*
******** ell_3m_eigenvalues_d( )
**
** finds eigenvalues of given matrix.
**
** returns information about the roots according to ellCubeRoot enum,
** see header for ellCubic for details.
**
** given matrix is NOT modified
**
** This does NOT use biff
**
** Doing the frobenius normalization proved successfull in avoiding the
** the creating of NaN eigenvalues when the coefficients of the matrix
** were really large ( > 50000 ). Also, when the matrix norm was really
** small, the comparison to "epsilon" in ell_cubic mistook three separate
** roots for a single and a double, with this matrix in particular:
** 1.7421892 0.0137642 0.0152975
** 0.0137642 1.7565432 -0.0062296
** 0.0152975 -0.0062296 1.7700019
** ( actually, this is prior to tenEigensolve's isotropic removal )
**
** HEY: tenEigensolve_d and tenEigensolve_f start by removing the
** isotropic part of the tensor. It may be that that smarts should
** be migrated here, but GLK is uncertain how it would change the
** handling of non-symmetric matrices.
*/
int
381 ell_3m_eigenvalues_d( double _eval[3], const double _m[9], const int newton ) {
double A, B, C, scale, frob, m[9], eval[3];
int roots;
frob = ELL_3M_FROB( _m );
scale = frob ? 1.0/frob : 1.0;
ELL_3M_SCALE( m, scale, _m );
/*
printf( "!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "ell_3m_eigenvalues_d",
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8] );
*/
/*
** from gordon with mathematica; these are the coefficients of the
** cubic polynomial in x: det( x*I - M ). The full cubic is
** x^3 + A*x^2 + B*x + C.
*/
A = -m[0] - m[4] - m[8];
B = m[0]*m[4] - m[3]*m[1]
+ m[0]*m[8] - m[6]*m[2]
+ m[4]*m[8] - m[7]*m[5];
C = ( m[6]*m[4] - m[3]*m[7] )*m[2]
+ ( m[0]*m[7] - m[6]*m[1] )*m[5]
+ ( m[3]*m[1] - m[0]*m[4] )*m[8];
/*
printf( "!%s: A B C = %g %g %g\n", "ell_3m_eigenvalues_d", A, B, C );
*/
roots = ell_cubic( eval, A, B, C, newton );
/* no longer need to sort here */
ELL_3V_SCALE( _eval, 1.0/scale, eval );
return roots;
}
void
414 _ell_3m_evecs_d( double evec[9], double eval[3], int roots,
const double m[9] ) {
double n[9], e0=0, e1=0.0, e2=0.0, t /* , tmpv[3] */ ;
ELL_3V_GET( e0, e1, e2, eval );
/* if ( ell_debug ) {
printf( "ell_3m_evecs_d: numroots = %d\n", numroots );
} */
/* we form m - lambda*I by doing a memcpy from m, and then
( repeatedly ) over-writing the diagonal elements */
ELL_3M_COPY( n, m );
switch ( roots ) {
case ell_cubic_root_three:
/* if ( ell_debug ) {
printf( "ell_3m_evecs_d: evals: %20.15f %20.15f %20.15f\n",
eval[0], eval[1], eval[2] );
} */
ELL_3M_DIAG_SET( n, m[0]-e0, m[4]-e0, m[8]-e0 );
ell_3m_1d_nullspace_d( evec+0, n );
ELL_3M_DIAG_SET( n, m[0]-e1, m[4]-e1, m[8]-e1 );
ell_3m_1d_nullspace_d( evec+3, n );
ELL_3M_DIAG_SET( n, m[0]-e2, m[4]-e2, m[8]-e2 );
ell_3m_1d_nullspace_d( evec+6, n );
_ell_3m_enforce_orthogonality( evec );
_ell_3m_make_right_handed_d( evec );
ELL_3V_SET( eval, e0, e1, e2 );
break;
case ell_cubic_root_single_double:
ELL_SORT3( e0, e1, e2, t );
if ( e0 > e1 ) {
/* one big ( e0 ) , two small ( e1, e2 ) : more like a cigar */
ELL_3M_DIAG_SET( n, m[0]-e0, m[4]-e0, m[8]-e0 );
ell_3m_1d_nullspace_d( evec+0, n );
ELL_3M_DIAG_SET( n, m[0]-e1, m[4]-e1, m[8]-e1 );
ell_3m_2d_nullspace_d( evec+3, evec+6, n );
}
else {
/* two big ( e0, e1 ), one small ( e2 ): more like a pancake */
ELL_3M_DIAG_SET( n, m[0]-e0, m[4]-e0, m[8]-e0 );
ell_3m_2d_nullspace_d( evec+0, evec+3, n );
ELL_3M_DIAG_SET( n, m[0]-e2, m[4]-e2, m[8]-e2 );
ell_3m_1d_nullspace_d( evec+6, n );
}
_ell_3m_enforce_orthogonality( evec );
_ell_3m_make_right_handed_d( evec );
ELL_3V_SET( eval, e0, e1, e2 );
break;
case ell_cubic_root_triple:
/* one triple root; use any basis as the eigenvectors */
ELL_3V_SET( evec+0, 1, 0, 0 );
ELL_3V_SET( evec+3, 0, 1, 0 );
ELL_3V_SET( evec+6, 0, 0, 1 );
ELL_3V_SET( eval, e0, e1, e2 );
break;
case ell_cubic_root_single:
/* only one real root */
ELL_3M_DIAG_SET( n, m[0]-e0, m[4]-e0, m[8]-e0 );
ell_3m_1d_nullspace_d( evec+0, n );
ELL_3V_SET( evec+3, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( evec+6, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( eval, e0, AIR_NAN, AIR_NAN );
break;
}
/* if ( ell_debug ) {
printf( "ell_3m_evecs_d ( numroots = %d ): evecs: \n", numroots );
ELL_3MV_MUL( tmpv, m, evec[0] );
printf( " ( %g:%g ): %20.15f %20.15f %20.15f\n",
eval[0], ELL_3V_DOT( evec[0], tmpv ),
evec[0][0], evec[0][1], evec[0][2] );
ELL_3MV_MUL( tmpv, m, evec[1] );
printf( " ( %g:%g ): %20.15f %20.15f %20.15f\n",
eval[1], ELL_3V_DOT( evec[1], tmpv ),
evec[1][0], evec[1][1], evec[1][2] );
ELL_3MV_MUL( tmpv, m, evec[2] );
printf( " ( %g:%g ): %20.15f %20.15f %20.15f\n",
eval[2], ELL_3V_DOT( evec[2], tmpv ),
evec[2][0], evec[2][1], evec[2][2] );
} */
return;
}
/*
******** ell_3m_eigensolve_d( )
**
** finds eigenvalues and eigenvectors of given matrix m
**
** returns information about the roots according to ellCubeRoot enum,
** see header for ellCubic for details. When eval[i] is set, evec+3*i
** is set to a corresponding eigenvector. The eigenvectors are
** ( evec+0 )[], ( evec+3 )[], and ( evec+6 )[]
**
** NOTE: Even in the post-Teem-1.7 switch from column-major to
** row-major- its still the case that the eigenvectors are at
** evec+0, evec+3, evec+6: this means that they USED to be the
** "columns" of the matrix, and NOW they're the rows.
**
** The eigenvalues ( and associated eigenvectors ) are sorted in
** descending order.
**
** This does NOT use biff
*/
int
517 ell_3m_eigensolve_d( double eval[3], double evec[9],
const double m[9], const int newton ) {
int roots;
/* if ( ell_debug ) {
printf( "ell_3m_eigensolve_d: input matrix:\n" );
printf( "{{%20.15f, \t%20.15f, \t%20.15f}, \n", m[0], m[1], m[2] );
printf( " {%20.15f, \t%20.15f, \t%20.15f}, \n", m[3], m[4], m[5] );
printf( " {%20.15f, \t%20.15f, \t%20.15f}};\n", m[6], m[7], m[8] );
} */
roots = ell_3m_eigenvalues_d( eval, m, newton );
_ell_3m_evecs_d( evec, eval, roots, m );
return roots;
}
/* ____________________________ 3m2sub ____________________________ */
/*
******** ell_3m2sub_eigenvalues_d
**
** for doing eigensolve of the upper-left 2x2 matrix sub-matrix of a
** 3x3 matrix. The other entries are assumed to be zero. A 0 root is
** put last ( in eval[2] ), possibly in defiance of the usual eigenvalue
** ordering.
*/
int
544 ell_3m2sub_eigenvalues_d( double eval[3], const double _m[9] ) {
double A, B, m[4], D, Dsq, eps=1.0E-11;
int roots;
/* static const char me[]="ell_3m2sub_eigenvalues_d"; */
m[0] = _m[0];
m[1] = _m[1];
m[2] = _m[3];
m[3] = _m[4];
/* cubic characteristic equation is L^3 + A*L^2 + B*L = 0 */
A = -m[0] - m[3];
B = m[0]*m[3] - m[1]*m[2];
Dsq = A*A - 4*B;
/*
fprintf( stderr, "!%s: m = {{%f, %f}, {%f, %f}} -> A=%f B=%f Dsq=%.17f %s 0 ( %.17f )\n", me,
m[0], m[1], m[2], m[3], A, B, Dsq,
( Dsq > 0 ? ">" : ( Dsq < 0 ? "<" : "==" ) ), eps );
fprintf( stderr, "!%s: Dsq = \n", me );
airFPFprintf_d( stderr, Dsq );
fprintf( stderr, "!%s: eps = \n", me );
airFPFprintf_d( stderr, eps );
ell_3m_print_d( stderr, _m );
*/
if ( Dsq > eps ) {
D = sqrt( Dsq );
eval[0] = ( -A + D )/2;
eval[1] = ( -A - D )/2;
eval[2] = 0;
roots = ell_cubic_root_three;
} else if ( Dsq < -eps ) {
/* no quadratic roots; only the implied zero */
ELL_3V_SET( eval, AIR_NAN, AIR_NAN, 0 );
roots = ell_cubic_root_single;
} else {
/* a quadratic double root */
ELL_3V_SET( eval, -A/2, -A/2, 0 );
roots = ell_cubic_root_single_double;
}
/*
fprintf( stderr, "!%s: Dsq=%f, roots=%d ( %f %f %f )\n",
me, Dsq, roots, eval[0], eval[1], eval[2] );
*/
return roots;
}
void
591 _ell_22v_enforce_orthogonality( double uu[2], double _vv[2] ) {
double dot, vv[2], len;
dot = ELL_2V_DOT( uu, _vv );
ELL_2V_SCALE_ADD2( vv, 1, _vv, -dot, uu );
ELL_2V_NORM( _vv, vv, len );
return;
}
/*
** NOTE: assumes that eval and roots have come from
** ell_3m2sub_eigenvalues_d( m )
*/
void
605 _ell_3m2sub_evecs_d( double evec[9], double eval[3], int roots,
const double m[9] ) {
double n[4];
static const char me[]="_ell_3m2sub_evecs_d";
if ( ell_cubic_root_three == roots ) {
/* set off-diagonal entries once */
n[1] = m[1];
n[2] = m[3];
/* find first evec */
n[0] = m[0] - eval[0];
n[3] = m[4] - eval[0];
ell_2m_1d_nullspace_d( evec + 3*0, n );
( evec + 3*0 )[2] = 0;
/* find second evec */
n[0] = m[0] - eval[1];
n[3] = m[4] - eval[1];
ell_2m_1d_nullspace_d( evec + 3*1, n );
( evec + 3*1 )[2] = 0;
_ell_22v_enforce_orthogonality( evec + 3*0, evec + 3*1 );
/* make right-handed */
ELL_3V_CROSS( evec + 3*2, evec + 3*0, evec + 3*1 );
} else if ( ell_cubic_root_single_double == roots ) {
/* can pick any 2D basis */
ELL_3V_SET( evec + 3*0, 1, 0, 0 );
ELL_3V_SET( evec + 3*1, 0, 1, 0 );
ELL_3V_SET( evec + 3*2, 0, 0, 1 );
} else {
/* ell_cubic_root_single == roots, if assumptions are met */
ELL_3V_SET( evec + 3*0, AIR_NAN, AIR_NAN, 0 );
ELL_3V_SET( evec + 3*1, AIR_NAN, AIR_NAN, 0 );
ELL_3V_SET( evec + 3*2, 0, 0, 1 );
}
if ( !ELL_3M_EXISTS( evec ) ) {
fprintf( stderr, "%s: given m = \n", me );
ell_3m_print_d( stderr, m );
fprintf( stderr, "%s: got roots = %s ( %d ) and evecs = \n", me,
airEnumStr( ell_cubic_root, roots ), roots );
ell_3m_print_d( stderr, evec );
}
return;
}
int
649 ell_3m2sub_eigensolve_d( double eval[3], double evec[9],
const double m[9] ) {
int roots;
roots = ell_3m2sub_eigenvalues_d( eval, m );
_ell_3m2sub_evecs_d( evec, eval, roots, m );
return roots;
}
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3m2sub ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
/*
******** ell_3m_svd_d
**
** singular value decomposition:
** mat = uu * diag( sval ) * vv
**
** singular values are square roots of eigenvalues of mat * mat^T
** columns of uu are eigenvectors of mat * mat^T
** rows of vv are eigenvectors of mat^T * mat
**
** returns info about singular values according to ellCubeRoot enum
**
** HEY: I think this does the wrong thing when given a symmetric
** matrix with negative eigenvalues . . .
*/
int
677 ell_3m_svd_d( double uu[9], double sval[3], double vv[9],
const double mat[9], const int newton ) {
double trn[9], msqr[9], eval[3], evec[9];
int roots;
ELL_3M_TRANSPOSE( trn, mat );
ELL_3M_MUL( msqr, mat, trn );
roots = ell_3m_eigensolve_d( eval, evec, msqr, newton );
sval[0] = sqrt( eval[0] );
sval[1] = sqrt( eval[1] );
sval[2] = sqrt( eval[2] );
ELL_3M_TRANSPOSE( uu, evec );
ELL_3M_MUL( msqr, trn, mat );
_ell_3m_evecs_d( vv, eval, roots, msqr );
return roots;
}
/*
** NOTE: profiling showed that about a quarter of the execution time of
** ell_6ms_eigensolve_d( ) is spent here; so reconsider its need and
** implementation . . . ( fabs vs. AIR_ABS( ) made no difference )
*/
static void
701 _maxI_sum_find( unsigned int maxI[2], double *sumon, double *sumoff,
double mat[6][6] ) {
double maxm, tmp;
unsigned int rrI, ccI;
/* we hope that all these loops are unrolled by the optimizer */
*sumon = *sumoff = 0.0;
for ( rrI=0; rrI<6; rrI++ ) {
*sumon += AIR_ABS( mat[rrI][rrI] );
}
maxm = -1;
maxI[0] = maxI[1] = 0;
for ( rrI=0; rrI<5; rrI++ ) {
for ( ccI=rrI+1; ccI<6; ccI++ ) {
tmp = AIR_ABS( mat[rrI][ccI] );
*sumoff += tmp;
if ( tmp > maxm ) {
maxm = tmp;
maxI[0] = rrI;
maxI[1] = ccI;
}
}
}
/*
if ( 1 ) {
double nrm, trc;
nrm = trc = 0;
for ( rrI=0; rrI<6; rrI++ ) {
trc += mat[rrI][rrI];
nrm += mat[rrI][rrI]*mat[rrI][rrI];
}
for ( rrI=0; rrI<5; rrI++ ) {
for ( ccI=rrI+1; ccI<6; ccI++ ) {
nrm += 2*mat[rrI][ccI]*mat[rrI][ccI];
}
}
fprintf( stderr, "---------------- invars = %g %g\n", trc, nrm );
}
*/
return;
}
static int
746 _compar( const void *A_void, const void *B_void ) {
const double *A, *B;
A = AIR_CAST( const double *, A_void );
B = AIR_CAST( const double *, B_void );
return ( A[0] < B[0] ? 1 : ( A[0] > B[0] ? -1 : 0 ) );
}
/*
******* ell_6ms_eigensolve_d
**
** uses Jacobi iterations to find eigensystem of 6x6 symmetric matrix,
** given in sym[21], to within convergence threshold eps. Puts
** eigenvalues, in descending order, in eval[6], and corresponding
** eigenvectors in _evec+0, _evec+6, . . ., _evec+30. NOTE: you can
** pass a NULL _evec if eigenvectors aren't needed.
**
** does NOT use biff
*/
int
765 ell_6ms_eigensolve_d( double eval[6], double _evec[36],
const double sym[21], const double eps ) {
/* char me[]="ell_6ms_eigensolve_d"; */
double mat[2][6][6], evec[2][6][6], sumon, sumoff, evtmp[12];
unsigned int cur, rrI, ccI, maxI[2], iter;
if ( !( eval && sym && eps >= 0 ) ) {
return 1;
}
/* copy symmetric matrix sym[] into upper tris of mat[0][][] & mat[1][][] */
mat[0][0][0] = sym[ 0];
mat[0][0][1] = sym[ 1];
mat[0][0][2] = sym[ 2];
mat[0][0][3] = sym[ 3];
mat[0][0][4] = sym[ 4];
mat[0][0][5] = sym[ 5];
mat[0][1][1] = sym[ 6];
mat[0][1][2] = sym[ 7];
mat[0][1][3] = sym[ 8];
mat[0][1][4] = sym[ 9];
mat[0][1][5] = sym[10];
mat[0][2][2] = sym[11];
mat[0][2][3] = sym[12];
mat[0][2][4] = sym[13];
mat[0][2][5] = sym[14];
mat[0][3][3] = sym[15];
mat[0][3][4] = sym[16];
mat[0][3][5] = sym[17];
mat[0][4][4] = sym[18];
mat[0][4][5] = sym[19];
mat[0][5][5] = sym[20];
if ( _evec ) {
/* initialize evec[0]; */
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=0; ccI<6; ccI++ ) {
evec[0][ccI][rrI] = ( rrI == ccI );
}
}
}
/*
fprintf( stderr, "!%s( INIT ): m = [", me );
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=0; ccI<6; ccI++ ) {
fprintf( stderr, "%f%s",
( rrI <= ccI ? mat[0][rrI][ccI] : mat[0][ccI][rrI] ),
ccI<5 ? ", " : ( rrI<5 ? ";" : "]" ) );
}
fprintf( stderr, "\n" );
}
*/
maxI[0] = maxI[1] = UINT_MAX; /* quiet warnings about using maxI unset */
_maxI_sum_find( maxI, &sumon, &sumoff, mat[0] );
cur = 1; /* fake out anticipating first line of loop */
iter = 0;
while ( sumoff/sumon > eps ) {
double th, tt, cc, ss;
const unsigned int P = maxI[0];
const unsigned int Q = maxI[1];
//make sure that P and Q are within the bounds for mat[2][6][6]
if( P >=6 || Q >= 6 ){
break;
}
/*
fprintf( stderr, "!%s( %u ): sumoff/sumon = %g/%g = %g > %g\n", me, iter,
sumoff, sumon, sumoff/sumon, eps );
*/
cur = 1 - cur;
th = ( mat[cur][Q][Q] - mat[cur][P][P] )/( 2*mat[cur][P][Q] );
tt = ( th > 0 ? +1 : -1 )/( AIR_ABS( th ) + sqrt( th*th + 1 ) );
cc = 1/sqrt( tt*tt + 1 );
ss = cc*tt;
/*
fprintf( stderr, "!%s( %u ): maxI = ( P, Q ) = ( %u, %u ) --> ss=%f, cc=%f\n",
me, iter, P, Q, ss, cc );
fprintf( stderr, " r = [" );
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=0; ccI<6; ccI++ ) {
fprintf( stderr, "%g%s",
( rrI == ccI
? ( rrI == P || rrI == Q ? cc : 1.0 )
: ( rrI == P && ccI == Q
? ss
: ( rrI == Q && ccI == P
? -ss
: 0 ) ) ),
ccI<5 ? ", " : ( rrI<5 ? ";" : "]" ) );
}
fprintf( stderr, "\n" );
}
*/
/* initialize by copying whole matrix */
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=rrI; ccI<6; ccI++ ) {
mat[1-cur][rrI][ccI] = mat[cur][rrI][ccI];
}
}
/* perform Jacobi rotation */
for ( rrI=0; rrI<P; rrI++ ) {
mat[1-cur][rrI][P] = cc*mat[cur][rrI][P] - ss*mat[cur][rrI][Q];
}
for ( ccI=P+1; ccI<6; ccI++ ) {
mat[1-cur][P][ccI] = cc*mat[cur][P][ccI] - ss*( Q <= ccI
? mat[cur][Q][ccI]
: mat[cur][ccI][Q] );
}
for ( rrI=0; rrI<Q; rrI++ ) {
mat[1-cur][rrI][Q] = ss*( rrI <= P
? mat[cur][rrI][P]
: mat[cur][P][rrI] ) + cc*mat[cur][rrI][Q];
}
for ( ccI=Q+1; ccI<6; ccI++ ) {
mat[1-cur][Q][ccI] = ss*mat[cur][P][ccI] + cc*mat[cur][Q][ccI];
}
/* set special entries */
mat[1-cur][P][P] = mat[cur][P][P] - tt*mat[cur][P][Q];
mat[1-cur][Q][Q] = mat[cur][Q][Q] + tt*mat[cur][P][Q];
mat[1-cur][P][Q] = 0.0;
if ( _evec ) {
/* NOTE: the eigenvectors use transpose of indexing of mat */
/* start by copying all */
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=0; ccI<6; ccI++ ) {
evec[1-cur][ccI][rrI] = evec[cur][ccI][rrI];
}
}
for ( rrI=0; rrI<6; rrI++ ) {
evec[1-cur][P][rrI] = cc*evec[cur][P][rrI] - ss*evec[cur][Q][rrI];
evec[1-cur][Q][rrI] = ss*evec[cur][P][rrI] + cc*evec[cur][Q][rrI];
}
}
_maxI_sum_find( maxI, &sumon, &sumoff, mat[1-cur] );
/*
fprintf( stderr, "!%s( %u ): m = [", me, iter );
for ( rrI=0; rrI<6; rrI++ ) {
for ( ccI=0; ccI<6; ccI++ ) {
fprintf( stderr, "%f%s",
( rrI <= ccI ? mat[1-cur][rrI][ccI] : mat[1-cur][ccI][rrI] ),
ccI<5 ? ", " : ( rrI<5 ? ";" : "]" ) );
}
fprintf( stderr, "\n" );
}
*/
iter++;
}
/* 1-cur is index of final solution */
/* sort evals */
for ( ccI=0; ccI<6; ccI++ ) {
evtmp[0 + 2*ccI] = mat[1-cur][ccI][ccI];
evtmp[1 + 2*ccI] = ccI;
}
qsort( evtmp, 6, 2*sizeof( double ), _compar );
/* copy out solution */
for ( ccI=0; ccI<6; ccI++ ) {
eval[ccI] = evtmp[0 + 2*ccI];
if ( _evec ) {
unsigned eeI;
for ( rrI=0; rrI<6; rrI++ ) {
eeI = AIR_CAST( unsigned int, evtmp[1 + 2*ccI] );
_evec[rrI + 6*ccI] = evec[1-cur][eeI][rrI];
}
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
int
28 ell_Nm_check( Nrrd *mat, int doNrrdCheck ) {
static const char me[]="ell_Nm_check";
if ( doNrrdCheck ) {
if ( nrrdCheck( mat ) ) {
biffMovef( ELL, NRRD, "%s: basic nrrd validity check failed", me );
return 1;
}
} else {
if ( !mat ) {
biffAddf( ELL, "%s: got NULL pointer", me );
return 1;
}
}
if ( !( 2 == mat->dim ) ) {
biffAddf( ELL, "%s: nrrd must be 2-D ( not %d-D )", me, mat->dim );
return 1;
}
if ( !( nrrdTypeDouble == mat->type ) ) {
biffAddf( ELL, "%s: nrrd must be type %s ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, mat->type ) );
return 1;
}
return 0;
}
/*
******** ell_Nm_tran
**
** M N
** N [trn] <-- M [mat]
*/
int
63 ell_Nm_tran( Nrrd *ntrn, Nrrd *nmat ) {
static const char me[]="ell_Nm_tran";
double *mat, *trn;
size_t MM, NN, mm, nn;
if ( !( ntrn && !ell_Nm_check( nmat, AIR_FALSE ) ) ) {
biffAddf( ELL, "%s: NULL or invalid args", me );
return 1;
}
if ( ntrn == nmat ) {
biffAddf( ELL, "%s: sorry, can't work in-place yet", me );
return 1;
}
/*
if ( nrrdAxesSwap( ntrn, nmat, 0, 1 ) ) {
biffMovef( ELL, NRRD, "%s: trouble", me );
return 1;
}
*/
NN = nmat->axis[0].size;
MM = nmat->axis[1].size;
if ( nrrdMaybeAlloc_va( ntrn, nrrdTypeDouble, 2,
MM, NN ) ) {
biffMovef( ELL, NRRD, "%s: trouble", me );
return 1;
}
mat = AIR_CAST( double *, nmat->data );
trn = AIR_CAST( double *, ntrn->data );
for ( nn=0; nn<NN; nn++ ) {
for ( mm=0; mm<MM; mm++ ) {
trn[mm + MM*nn] = mat[nn + NN*mm];
}
}
return 0;
}
/*
******** ell_Nm_mul
**
** Currently, only useful for matrix-matrix multiplication
**
** matrix-matrix: M N
** L [A] . M [B]
*/
int
109 ell_Nm_mul( Nrrd *nAB, Nrrd *nA, Nrrd *nB ) {
static const char me[]="ell_Nm_mul";
double *A, *B, *AB, tmp;
size_t LL, MM, NN, ll, mm, nn;
char stmp[4][AIR_STRLEN_SMALL];
if ( !( nAB && !ell_Nm_check( nA, AIR_FALSE )
&& !ell_Nm_check( nB, AIR_FALSE ) ) ) {
biffAddf( ELL, "%s: NULL or invalid args", me );
return 1;
}
if ( nAB == nA || nAB == nB ) {
biffAddf( ELL, "%s: can't do in-place multiplication", me );
return 1;
}
LL = nA->axis[1].size;
MM = nA->axis[0].size;
NN = nB->axis[0].size;
if ( MM != nB->axis[1].size ) {
biffAddf( ELL, "%s: size mismatch: %s-by-%s times %s-by-%s", me,
airSprintSize_t( stmp[0], LL ),
airSprintSize_t( stmp[1], MM ),
airSprintSize_t( stmp[2], nB->axis[1].size ),
airSprintSize_t( stmp[3], NN ) );
return 1;
}
if ( nrrdMaybeAlloc_va( nAB, nrrdTypeDouble, 2,
NN, LL ) ) {
biffMovef( ELL, NRRD, "%s: trouble", me );
return 1;
}
A = ( double* )( nA->data );
B = ( double* )( nB->data );
AB = ( double* )( nAB->data );
for ( ll=0; ll<LL; ll++ ) {
for ( nn=0; nn<NN; nn++ ) {
tmp = 0;
for ( mm=0; mm<MM; mm++ ) {
tmp += A[mm + MM*ll]*B[nn + NN*mm];
}
AB[ll*NN + nn] = tmp;
}
}
return 0;
}
/*
** _ell_LU_decomp( )
**
** in-place LU decomposition
*/
int
162 _ell_LU_decomp( double *aa, size_t *indx, size_t NN ) {
static const char me[]="_ell_LU_decomp";
int ret=0;
size_t ii, imax=0, jj, kk;
double big, sum, tmp;
double *vv;
if ( !( vv = ( double* )calloc( NN, sizeof( double ) ) ) ) {
biffAddf( ELL, "%s: couldn't allocate vv[]!", me );
ret = 1; goto seeya;
}
/* find vv[i]: max of abs of everything in column i */
for ( ii=0; ii<NN; ii++ ) {
big = 0.0;
for ( jj=0; jj<NN; jj++ ) {
if ( ( tmp=AIR_ABS( aa[ii*NN + jj] ) ) > big ) {
big = tmp;
}
}
if ( !big ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( ELL, "%s: singular matrix since column %s all zero", me,
airSprintSize_t( stmp, ii ) );
ret = 1; goto seeya;
}
vv[ii] = big;
}
for ( jj=0; jj<NN; jj++ ) {
/* for aa[ii][jj] in lower triangle ( below diagonal ), subtract from
aa[ii][jj] the dot product of all elements to its left with elements
above it ( starting at the top ) */
for ( ii=0; ii<jj; ii++ ) {
sum = aa[ii*NN + jj];
for ( kk=0; kk<ii; kk++ ) {
sum -= aa[ii*NN + kk]*aa[kk*NN + jj];
}
aa[ii*NN + jj] = sum;
}
/* for aa[ii][jj] in upper triangle ( including diagonal ), subtract from
aa[ii][jj] the dot product of all elements above it with elements to
its left ( starting from the left ) */
big = 0.0;
for ( ii=jj; ii<NN; ii++ ) {
sum = aa[ii*NN + jj];
for ( kk=0; kk<jj; kk++ ) {
sum -= aa[ii*NN + kk]*aa[kk*NN + jj];
}
aa[ii*NN + jj] = sum;
/* imax column is one in which abs( aa[i][j] )/vv[i] */
if ( ( tmp = AIR_ABS( sum )/vv[ii] ) >= big ) {
big = tmp;
imax = ii;
}
}
/* unless we're on the imax column, swap this column the with imax column,
and permute vv[] accordingly */
if ( jj != imax ) {
/* could record parity # of permutes here */
for ( kk=0; kk<NN; kk++ ) {
tmp = aa[imax*NN + kk];
aa[imax*NN + kk] = aa[jj*NN + kk];
aa[jj*NN + kk] = tmp;
}
tmp = vv[imax];
vv[imax] = vv[jj];
vv[jj] = tmp;
}
indx[jj] = imax;
if ( aa[jj*NN + jj] == 0.0 ) {
aa[jj*NN + jj] = ELL_EPS;
}
/* divide everything right of a[jj][jj] by a[jj][jj] */
if ( jj != NN ) {
tmp = 1.0/aa[jj*NN + jj];
for ( ii=jj+1; ii<NN; ii++ ) {
aa[ii*NN + jj] *= tmp;
}
}
}
seeya:
airFree( vv );
return ret;
}
/*
** _ell_LU_back_sub
**
** given the matrix and index array from _ellLUDecomp generated from
** some matrix M, solves for x in the linear equation Mx = b, and
** puts the result back into b
*/
void
261 _ell_LU_back_sub( double *aa, size_t *indx, double *bb, size_t NN ) {
size_t ii, jj;
double sum;
/* Forward substitution, with lower triangular matrix */
for ( ii=0; ii<NN; ii++ ) {
sum = bb[indx[ii]];
bb[indx[ii]] = bb[ii];
for ( jj=0; jj<ii; jj++ ) {
sum -= aa[ii*NN + jj]*bb[jj];
}
bb[ii] = sum;
}
/* Backward substitution, with upper triangular matrix */
for ( ii=NN; ii>0; ii-- ) {
sum = bb[ii-1];
for ( jj=ii; jj<NN; jj++ ) {
sum -= aa[( ii-1 )*NN + jj]*bb[jj];
}
bb[ii-1] = sum / aa[( ii-1 )*NN + ( ii-1 )];
}
return;
}
/*
** _ell_inv
**
** Invert NNxNN matrix based on LU-decomposition
**
** The given matrix is copied, turned into its LU-decomposition, and
** then repeated backsubstitution is used to get successive columns of
** the inverse.
*/
int
296 _ell_inv( double *inv, double *_mat, size_t NN ) {
static const char me[]="_ell_inv";
size_t ii, jj, *indx=NULL;
double *col=NULL, *mat=NULL;
int ret=0;
if ( !( ( col = ( double* )calloc( NN, sizeof( double ) ) ) &&
( mat = ( double* )calloc( NN*NN, sizeof( double ) ) ) &&
( indx = ( size_t* )calloc( NN, sizeof( size_t ) ) ) ) ) {
biffAddf( ELL, "%s: couldn't allocate all buffers", me );
ret = 1; goto seeya;
}
memcpy( mat, _mat, NN*NN*sizeof( double ) );
if ( _ell_LU_decomp( mat, indx, NN ) ) {
biffAddf( ELL, "%s: trouble", me );
ret = 1; goto seeya;
}
for ( jj=0; jj<NN; jj++ ) {
memset( col, 0, NN*sizeof( double ) );
col[jj] = 1.0;
_ell_LU_back_sub( mat, indx, col, NN );
/* set column jj of inv to result of backsub */
for ( ii=0; ii<NN; ii++ ) {
inv[ii*NN + jj] = col[ii];
}
}
seeya:
airFree( col ); airFree( mat ); airFree( indx );
return ret;
}
/*
******** ell_Nm_inv
**
** computes the inverse of given matrix in nmat, and puts the
** inverse in the ( maybe allocated ) ninv. Does not touch the
** values in nmat.
*/
int
338 ell_Nm_inv( Nrrd *ninv, Nrrd *nmat ) {
static const char me[]="ell_Nm_inv";
double *mat, *inv;
size_t NN;
if ( !( ninv && !ell_Nm_check( nmat, AIR_FALSE ) ) ) {
biffAddf( ELL, "%s: NULL or invalid args", me );
return 1;
}
NN = nmat->axis[0].size;
if ( !( NN == nmat->axis[1].size ) ) {
char stmp[2][AIR_STRLEN_SMALL];
biffAddf( ELL, "%s: need a square matrix, not %s-by-%s", me,
airSprintSize_t( stmp[0], nmat->axis[1].size ),
airSprintSize_t( stmp[1], NN ) );
return 1;
}
if ( nrrdMaybeAlloc_va( ninv, nrrdTypeDouble, 2,
NN, NN ) ) {
biffMovef( ELL, NRRD, "%s: trouble", me );
return 1;
}
inv = ( double* )( ninv->data );
mat = ( double* )( nmat->data );
if ( _ell_inv( inv, mat, NN ) ) {
biffAddf( ELL, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** ell_Nm_pseudo_inv( )
**
** determines the pseudoinverse of the given matrix M by using the formula
** P = ( M^T * M )^( -1 ) * M^T
**
** I'll get an SVD-based solution working later, since that gives a more
** general solution
*/
int
381 ell_Nm_pseudo_inv( Nrrd *ninv, Nrrd *nA ) {
static const char me[]="ell_Nm_pseudo_inv";
Nrrd *nAt, *nAtA, *nAtAi;
int ret=0;
if ( !( ninv && !ell_Nm_check( nA, AIR_FALSE ) ) ) {
biffAddf( ELL, "%s: NULL or invalid args", me );
return 1;
}
nAt = nrrdNew( );
nAtA = nrrdNew( );
nAtAi = nrrdNew( );
if ( ell_Nm_tran( nAt, nA )
|| ell_Nm_mul( nAtA, nAt, nA )
|| ell_Nm_inv( nAtAi, nAtA )
|| ell_Nm_mul( ninv, nAtAi, nAt ) ) {
biffAddf( ELL, "%s: trouble", me );
ret = 1; goto seeya;
}
seeya:
nrrdNuke( nAt ); nrrdNuke( nAtA ); nrrdNuke( nAtAi );
return ret;
}
/*
******** ell_Nm_wght_pseudo_inv( )
**
** determines a weighted least squares solution via
** P = ( A^T * W * A )^( -1 ) * A^T * W
*/
int
413 ell_Nm_wght_pseudo_inv( Nrrd *ninv, Nrrd *nA, Nrrd *nW ) {
static const char me[]="ell_Nm_wght_pseudo_inv";
Nrrd *nAt, *nAtW, *nAtWA, *nAtWAi;
int ret=0;
if ( !( ninv && !ell_Nm_check( nA, AIR_FALSE )
&& !ell_Nm_check( nW, AIR_FALSE ) ) ) {
biffAddf( ELL, "%s: NULL or invalid args", me );
return 1;
}
nAt = nrrdNew( );
nAtW = nrrdNew( );
nAtWA = nrrdNew( );
nAtWAi = nrrdNew( );
if ( ell_Nm_tran( nAt, nA )
|| ell_Nm_mul( nAtW, nAt, nW )
|| ell_Nm_mul( nAtWA, nAtW, nA )
|| ell_Nm_inv( nAtWAi, nAtWA )
|| ell_Nm_mul( ninv, nAtWAi, nAtW ) ) {
biffAddf( ELL, "%s: trouble", me );
ret = 1; goto seeya;
}
seeya:
nrrdNuke( nAt ); nrrdNuke( nAtW ); nrrdNuke( nAtWA ); nrrdNuke( nAtWAi );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
void
28 ell_3m_mul_f( float m3[9], const float _m1[9], const float _m2[9] ) {
float m1[9], m2[9];
ELL_3M_COPY( m1, _m1 );
ELL_3M_COPY( m2, _m2 );
ELL_3M_MUL( m3, m1, m2 );
}
void
37 ell_3m_mul_d( double m3[9], const double _m1[9], const double _m2[9] ) {
double m1[9], m2[9];
ELL_3M_COPY( m1, _m1 );
ELL_3M_COPY( m2, _m2 );
ELL_3M_MUL( m3, m1, m2 );
}
void
46 ell_3m_pre_mul_f( float _m[9], const float x[9] ) {
float m[9];
ELL_3M_MUL( m, _m, x );
ELL_3M_COPY( _m, m );
}
void
53 ell_3m_pre_mul_d( double _m[9], const double x[9] ) {
double m[9];
ELL_3M_MUL( m, _m, x );
ELL_3M_COPY( _m, m );
}
void
60 ell_3m_post_mul_f( float _m[9], const float x[9] ) {
float m[9];
ELL_3M_MUL( m, x, _m );
ELL_3M_COPY( _m, m );
}
void
67 ell_3m_post_mul_d( double _m[9], const double x[9] ) {
double m[9];
ELL_3M_MUL( m, x, _m );
ELL_3M_COPY( _m, m );
}
float
74 ell_3m_det_f( float m[9] ) {
return ELL_3M_DET( m );
}
double
79 ell_3m_det_d( double m[9] ) {
return ELL_3M_DET( m );
}
void
84 ell_3m_inv_f( float i[9], const float m[9] ) {
float det;
ELL_3M_INV( i, m, det );
}
void
91 ell_3m_inv_d( double i[9], const double m[9] ) {
double det;
ELL_3M_INV( i, m, det );
}
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void
102 ell_4m_mul_f( float m3[16], const float _m1[16], const float _m2[16] ) {
float m1[16], m2[16];
ELL_4M_COPY( m1, _m1 );
ELL_4M_COPY( m2, _m2 );
ELL_4M_MUL( m3, m1, m2 );
}
void
111 ell_4m_mul_d( double m3[16], const double _m1[16], const double _m2[16] ) {
double m1[16], m2[16];
ELL_4M_COPY( m1, _m1 );
ELL_4M_COPY( m2, _m2 );
ELL_4M_MUL( m3, m1, m2 );
}
void
120 ell_4m_pre_mul_f( float _m[16], const float x[16] ) {
float m[16];
ELL_4M_MUL( m, _m, x );
ELL_4M_COPY( _m, m );
}
void
127 ell_4m_pre_mMul_d( double _m[16], const double x[16] ) {
double m[16];
ELL_4M_MUL( m, _m, x );
ELL_4M_COPY( _m, m );
}
void
134 ell_4m_post_mul_f( float _m[16], const float x[16] ) {
float m[16];
ELL_4M_MUL( m, x, _m );
ELL_4M_COPY( _m, m );
}
void
141 ell_4m_post_mul_d( double _m[16], const double x[16] ) {
double m[16];
ELL_4M_MUL( m, x, _m );
ELL_4M_COPY( _m, m );
}
float
148 ell_4m_det_f( float m[16] ) {
return ELL_4M_DET( m );
}
double
153 ell_4m_det_d( double m[16] ) {
return ELL_4M_DET( m );
}
#define _4INV \
det = ELL_4M_DET( m ); \
i[ 0] = _ELL_3M_DET( ( m )[ 5], ( m )[ 6], ( m )[ 7], \
160 ( m )[ 9], ( m )[10], ( m )[11], \
( m )[13], ( m )[14], ( m )[15] )/det; \
i[ 1] = -_ELL_3M_DET( ( m )[ 1], ( m )[ 2], ( m )[ 3], \
( m )[ 9], ( m )[10], ( m )[11], \
( m )[13], ( m )[14], ( m )[15] )/det; \
i[ 2] = _ELL_3M_DET( ( m )[ 1], ( m )[ 2], ( m )[ 3], \
( m )[ 5], ( m )[ 6], ( m )[ 7], \
167 ( m )[13], ( m )[14], ( m )[15] )/det; \
i[ 3] = -_ELL_3M_DET( ( m )[ 1], ( m )[ 2], ( m )[ 3], \
( m )[ 5], ( m )[ 6], ( m )[ 7], \
( m )[ 9], ( m )[10], ( m )[11] )/det; \
i[ 4] = -_ELL_3M_DET( ( m )[ 4], ( m )[ 6], ( m )[ 7], \
( m )[ 8], ( m )[10], ( m )[11], \
( m )[12], ( m )[14], ( m )[15] )/det; \
174 i[ 5] = _ELL_3M_DET( ( m )[ 0], ( m )[ 2], ( m )[ 3], \
( m )[ 8], ( m )[10], ( m )[11], \
( m )[12], ( m )[14], ( m )[15] )/det; \
i[ 6] = -_ELL_3M_DET( ( m )[ 0], ( m )[ 2], ( m )[ 3], \
( m )[ 4], ( m )[ 6], ( m )[ 7], \
( m )[12], ( m )[14], ( m )[15] )/det; \
i[ 7] = _ELL_3M_DET( ( m )[ 0], ( m )[ 2], ( m )[ 3], \
( m )[ 4], ( m )[ 6], ( m )[ 7], \
( m )[ 8], ( m )[10], ( m )[11] )/det; \
i[ 8] = _ELL_3M_DET( ( m )[ 4], ( m )[ 5], ( m )[ 7], \
( m )[ 8], ( m )[ 9], ( m )[11], \
( m )[12], ( m )[13], ( m )[15] )/det; \
i[ 9] = -_ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 3], \
( m )[ 8], ( m )[ 9], ( m )[11], \
( m )[12], ( m )[13], ( m )[15] )/det; \
i[10] = _ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 3], \
( m )[ 4], ( m )[ 5], ( m )[ 7], \
( m )[12], ( m )[13], ( m )[15] )/det; \
i[11] = -_ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 3], \
( m )[ 4], ( m )[ 5], ( m )[ 7], \
( m )[ 8], ( m )[ 9], ( m )[11] )/det; \
i[12] = -_ELL_3M_DET( ( m )[ 4], ( m )[ 5], ( m )[ 6], \
( m )[ 8], ( m )[ 9], ( m )[10], \
( m )[12], ( m )[13], ( m )[14] )/det; \
198 i[13] = _ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 2], \
( m )[ 8], ( m )[ 9], ( m )[10], \
( m )[12], ( m )[13], ( m )[14] )/det; \
i[14] = -_ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 2], \
( m )[ 4], ( m )[ 5], ( m )[ 6], \
( m )[12], ( m )[13], ( m )[14] )/det; \
i[15] = _ELL_3M_DET( ( m )[ 0], ( m )[ 1], ( m )[ 2], \
( m )[ 4], ( m )[ 5], ( m )[ 6], \
( m )[ 8], ( m )[ 9], ( m )[10] )/det
void
ell_4m_inv_f( float i[16], const float m[16] ) {
float det;
_4INV;
}
void
ell_4m_inv_d( double i[16], const double m[16] ) {
double det;
_4INV;
}
void
ell_6m_mul_d( double AB[36], const double A[36], const double B[36] ) {
unsigned int ll, mm, nn;
double tmp;
if ( !( AB && A && B ) ) {
return;
}
for ( ll=0; ll<6; ll++ ) {
for ( nn=0; nn<6; nn++ ) {
tmp = 0;
for ( mm=0; mm<6; mm++ ) {
tmp += A[mm + 6*ll]*B[nn + 6*mm];
}
AB[nn + 6*ll] = tmp;
}
}
return;
}
/*
** Thanks to:
** http://jgt.akpeters.com/papers/MollerHughes99/code.html
*/
void
ell_3m_rotate_between_d( double rot[9], double from[3], double to[3] ) {
double vv[3];
double e, h, f;
if ( !( rot && from && to ) ) {
return;
}
ELL_3V_CROSS( vv, from, to );
e = ELL_3V_DOT( from, to );
f = AIR_ABS( e );
if ( f > 0.9999999 ) { /* "from" and "to"-vector almost parallel */
double tu[3], tv[3]; /* temporary storage vectors */
double xx[3]; /* vector most nearly orthogonal to "from" */
double c1, c2, c3; /* coefficients for later use */
int i, j;
xx[0] = AIR_ABS( from[0] );
xx[1] = AIR_ABS( from[1] );
xx[2] = AIR_ABS( from[2] );
if ( xx[0] < xx[1] ) {
if ( xx[0] < xx[2] ) {
xx[0] = 1.0; xx[1] = xx[2] = 0.0;
} else {
xx[2] = 1.0; xx[0] = xx[1] = 0.0;
}
} else {
if ( xx[1] < xx[2] ) {
xx[1] = 1.0; xx[0] = xx[2] = 0.0;
} else {
xx[2] = 1.0; xx[0] = xx[1] = 0.0;
}
}
tu[0] = xx[0] - from[0]; tu[1] = xx[1] - from[1]; tu[2] = xx[2] - from[2];
tv[0] = xx[0] - to[0]; tv[1] = xx[1] - to[1]; tv[2] = xx[2] - to[2];
c1 = 2.0 / ELL_3V_DOT( tu, tu );
c2 = 2.0 / ELL_3V_DOT( tv, tv );
c3 = c1 * c2 * ELL_3V_DOT( tu, tv );
for ( i = 0; i < 3; i++ ) {
for ( j = 0; j < 3; j++ ) {
rot[3*i + j] = - c1 * tu[i] * tu[j]
- c2 * tv[i] * tv[j]
+ c3 * tv[i] * tu[j];
}
rot[3*i + i] += 1.0;
}
} else { /* the most common case, unless "from"="to", or "from"=-"to" */
double hvx, hvz, hvxy, hvxz, hvyz;
h = 1.0/( 1.0 + e ); /* optimization by Gottfried Chen */
hvx = h * vv[0];
hvz = h * vv[2];
hvxy = hvx * vv[1];
hvxz = hvx * vv[2];
hvyz = hvz * vv[1];
rot[3*0 + 0] = e + hvx * vv[0];
rot[3*0 + 1] = hvxy - vv[2];
rot[3*0 + 2] = hvxz + vv[1];
rot[3*1 + 0] = hvxy + vv[2];
rot[3*1 + 1] = e + h * vv[1] * vv[1];
rot[3*1 + 2] = hvyz - vv[0];
rot[3*2 + 0] = hvxz - vv[1];
rot[3*2 + 1] = hvyz + vv[0];
rot[3*2 + 2] = e + hvz * vv[2];
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
/*
** we use the name ellPresent ( even though ell_present would be
** properly consistent with the ell library name convention ) because
** we want to facilitate systematic testing of all libraries
*/
const int
ellPresent = 42;
const char *
ell_biff_key = "ell";
/*
******** ell_debug
**
** some functions may use this value to control printing of
** verbose debugging information
*/
int ell_debug = 0;
const char *
_ell_quadratic_root_str[] = {
"( unknown ell_quadratic_root )",
"two",
"double",
"complex"
};
const char *
_ell_quadratic_root_desc[] = {
"( unknown ell_quadratic_root )",
"two distinct roots",
"one double root",
"complex conjugate roots",
};
airEnum
_ell_quadratic_root = {
"quadratic root solutions",
ELL_QUADRATIC_ROOT_MAX,
_ell_quadratic_root_str, NULL,
_ell_quadratic_root_desc,
NULL, NULL,
AIR_FALSE
};
72 const airEnum *const
ell_quadratic_root = &_ell_quadratic_root;
const char *
_ell_cubic_root_str[] = {
"( unknown ell_cubic_root )",
"single",
"triple",
"single and double",
"three distinct"
};
const char *
_ell_cubic_root_desc[] = {
"( unknown ell_cubic_root )",
"one single root",
"one triple root",
"a single and a double root",
"three distinct roots"
};
airEnum
_ell_cubic_root = {
"cubic root solutions",
ELL_CUBIC_ROOT_MAX,
_ell_cubic_root_str, NULL,
_ell_cubic_root_desc,
NULL, NULL,
AIR_FALSE
};
103 const airEnum *const
ell_cubic_root = &_ell_cubic_root;
void
108 ell_3m_print_f( FILE *f, const float s[9] ) {
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
s[0], s[1], s[2] );
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
s[3], s[4], s[5] );
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
s[6], s[7], s[8] );
}
void
119 ell_3v_print_f( FILE *f, const float s[3] ) {
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
s[0], s[1], s[2] );
}
void
126 ell_3m_print_d( FILE *f, const double s[9] ) {
fprintf( f, "% 31.15f % 31.15f % 31.15f\n",
s[0], s[1], s[2] );
fprintf( f, "% 31.15f % 31.15f % 31.15f\n",
s[3], s[4], s[5] );
fprintf( f, "% 31.15f % 31.15f % 31.15f\n",
s[6], s[7], s[8] );
}
void
137 ell_3v_print_d( FILE *f, const double s[3] ) {
fprintf( f, "% 31.15f % 31.15f % 31.15f\n",
s[0], s[1], s[2] );
}
void
144 ell_4m_print_f( FILE *f, const float s[16] ) {
fprintf( f, "% 15.7f % 15.7f % 15.7f % 15.7f\n",
s[ 0], s[ 1], s[ 2], s[ 3] );
fprintf( f, "% 15.7f % 15.7f % 15.7f % 15.7f\n",
s[ 4], s[ 5], s[ 6], s[ 7] );
fprintf( f, "% 15.7f % 15.7f % 15.7f % 15.7f\n",
s[ 8], s[ 9], s[10], s[11] );
fprintf( f, "% 15.7f % 15.7f % 15.7f % 15.7f\n",
s[12], s[13], s[14], s[15] );
}
void
157 ell_4v_print_f( FILE *f, const float s[4] ) {
fprintf( f, "% 15.7f % 15.7f % 15.7f % 15.7f\n",
s[0], s[1], s[2], s[3] );
}
void
164 ell_4m_print_d( FILE *f, const double s[16] ) {
fprintf( f, "% 31.15f % 31.15f % 31.15f % 31.15f\n",
s[ 0], s[ 1], s[ 2], s[ 3] );
fprintf( f, "% 31.15f % 31.15f % 31.15f % 31.15f\n",
s[ 4], s[ 5], s[ 6], s[ 7] );
fprintf( f, "% 31.15f % 31.15f % 31.15f % 31.15f\n",
s[ 8], s[ 9], s[10], s[11] );
fprintf( f, "% 31.15f % 31.15f % 31.15f % 31.15f\n",
s[12], s[13], s[14], s[15] );
}
void
177 ell_4v_print_d( FILE *f, const double s[4] ) {
fprintf( f, "% 31.15f % 31.15f % 31.15f % 31.15f\n",
s[0], s[1], s[2], s[3] );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
#define W 0
#define X 1
#define Y 2
#define Z 3
/*
0 1 2
3 4 5
6 7 8
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
*/
/*
** note: this will always produce a unit length quaternion, by the
** ELL_4V_NORM( q, q, len ) at the end ( NOTE: actually that's been
** expanded out to deal with warnings about precision loss with
** double->float conversion ). However, for proper rotation matrices,
** that normalization should be far from a divide by zero, so it
** should be stable. It *IS* necessary, since it accomplishes
** division by w, x, y, or z, whichever's squared magnitude is biggest
*/
#define _ELL_M_TO_Q( type, i0, i1, i2, i3, i4, i5, i6, i7, i8 ) \
type s[4], wx, wy, wz, xy, xz, yz, len; \
int mi; \
54 \
s[W] = 1 + m[i0] + m[i4] + m[i8]; \
s[X] = 1 + m[i0] - m[i4] - m[i8]; \
s[Y] = 1 - m[i0] + m[i4] - m[i8]; \
s[Z] = 1 - m[i0] - m[i4] + m[i8]; \
wx = m[i7] - m[i5]; \
wy = m[i2] - m[i6]; \
61 wz = m[i3] - m[i1]; \
xy = m[i3] + m[i1]; \
xz = m[i6] + m[i2]; \
yz = m[i7] + m[i5]; \
mi = s[W] > s[X] ? W : X; \
mi = s[mi] > s[Y] ? mi : Y; \
67 mi = s[mi] > s[Z] ? mi : Z; \
switch ( mi ) { \
case W: \
ELL_4V_SET( q, s[W], wx, wy, wz ); \
break; \
case X: \
ELL_4V_SET( q, wx, s[X], xy, xz ); \
74 break; \
case Y: \
ELL_4V_SET( q, wy, xy, s[Y], yz ); \
break; \
case Z: \
ELL_4V_SET( q, wz, xz, yz, s[Z] ); \
break; \
}
void
ell_3m_to_q_f( float q[4], const float m[9] ) {
_ELL_M_TO_Q( float, 0, 1, 2, 3, 4, 5, 6, 7, 8 );
len = AIR_CAST( float, ELL_4V_LEN( q ) );
ELL_4V_SCALE( q, 1.0f/len, q );
}
90 void
ell_3m_to_q_d( double q[4], const double m[9] ) {
_ELL_M_TO_Q( double, 0, 1, 2, 3, 4, 5, 6, 7, 8 );
ELL_4V_NORM( q, q, len );
}
void
ell_4m_to_q_f( float q[4], const float m[16] ) {
98 _ELL_M_TO_Q( float, 0, 1, 2, 4, 5, 6, 8, 9, 10 );
len = AIR_CAST( float, ELL_4V_LEN( q ) );
ELL_4V_SCALE( q, 1.0f/len, q );
}
void
ell_4m_to_q_d( double q[4], const double m[16] ) {
_ELL_M_TO_Q( double, 0, 1, 2, 4, 5, 6, 8, 9, 10 );
ELL_4V_NORM( q, q, len );
}
/*
** note: normalizes the quaternion on the way in, to insure
** creation of a proper rotation matrix. Without the normalization
112 ** the coefficients in the matrix would be off by a factor of
** w*w + x*x + y*y + z*z
**
** See NOTE below about the non-use of ELL_4V_NORM( u, q, w )
*/
#define _ELL_Q_TO_3M( type ) \
ELL_4V_GET( w, x, y, z, u ); \
ELL_3V_SET( m+0, \
120 w*w + x*x - y*y - z*z, \
2*( x*y - w*z ), \
2*( x*z + w*y ) ); \
ELL_3V_SET( m+3, \
2*( x*y + w*z ), \
w*w - x*x + y*y - z*z, \
2*( y*z - w*x ) ); \
ELL_3V_SET( m+6, \
2*( x*z - w*y ), \
2*( y*z + w*x ), \
w*w - x*x - y*y + z*z )
void
ell_q_to_3m_f( float m[9], const float q[4] ) {
float u[4], w=0.0, x=0.0, y=0.0, z=0.0;
w = AIR_CAST( float, ELL_4V_LEN( q ) );
ELL_4V_SCALE( u, 1.0f/w, q );
_ELL_Q_TO_3M( float );
138 }
void
ell_q_to_3m_d( double m[9], const double q[4] ) {
double u[4], w=0.0, x=0.0, y=0.0, z=0.0;
143 ELL_4V_NORM( u, q, w );
_ELL_Q_TO_3M( double );
}
/*
** HEY: the first two lines of this replace ELL_4V_NORM( u, q, w ). The
** replacement was needed to avoid warnings about precision loss with
** double->float converstion. Macros are indeed problematic . . .
*/
#define _ELL_Q_TO_4M( type ) \
ELL_4V_GET( w, x, y, z, u ); \
154 ELL_4V_SET( m+0, \
w*w + x*x - y*y - z*z, \
2*( x*y - w*z ), \
2*( x*z + w*y ), \
0 ); \
159 ELL_4V_SET( m+4, \
2*( x*y + w*z ), \
w*w - x*x + y*y - z*z, \
2*( y*z - w*x ), \
0 ); \
164 ELL_4V_SET( m+8, \
2*( x*z - w*y ), \
2*( y*z + w*x ), \
w*w - x*x - y*y + z*z, \
0 ); \
ELL_4V_SET( m+12, 0, 0, 0, 1 )
void
172 ell_q_to_4m_f( float m[16], const float q[4] ) {
float u[4], w=0.0, x=0.0, y=0.0, z=0.0;
w = AIR_CAST( float, ELL_3V_LEN( q ) );
ELL_4V_SCALE( u, 1.0f/w, q );
_ELL_Q_TO_4M( float );
}
void
180 ell_q_to_4m_d( double m[16], const double q[4] ) {
double u[4], w=0.0, x=0.0, y=0.0, z=0.0;
ELL_4V_NORM( u, q, w );
_ELL_Q_TO_4M( double );
}
/*
** note: by the use of atan2, this does NOT assume a
188 ** a unit-length quaternion. The axis output, however,
** will always be unit length, even if the quaternion was
** purely real ( rotation angle is zero )
**
** HEY: there are two instances here of non-use of ELL_3V_NORM
** to avoid warnings about precision loss with type conversion
*/
#define _ELL_Q_TO_AA( type ) \
196 type len, angle; \
\
len = AIR_CAST( type, ELL_3V_LEN( q+1 ) ); \
angle = AIR_CAST( type, atan2( len, q[0] ) ); \
if ( len ) { \
ELL_3V_SCALE( axis, 1.0f/len, q+1 ); \
len = AIR_CAST( type, ELL_3V_LEN( axis ) ); \
ELL_3V_SCALE( axis, 1.0f/len, axis ); \
204 } else { \
ELL_3V_SET( axis, 1, 0, 0 ); \
} \
return 2*angle
float
ell_q_to_aa_f( float axis[3], const float q[4] ) {
_ELL_Q_TO_AA( float );
212 }
double
ell_q_to_aa_d( double axis[3], const double q[4] ) {
_ELL_Q_TO_AA( double );
}
/*
220 ** note: assuming that axis is unit length, this produces a
** a unit length quaternion
*/
#define _ELL_AA_TO_Q( type ) \
type sa; \
\
sa = AIR_CAST( type, sin( angle/2 ) ); \
ELL_4V_SET( q, \
228 AIR_CAST( type, cos( angle/2 ) ), AIR_CAST( type, sa*axis[0] ), \
AIR_CAST( type, sa*axis[1] ), AIR_CAST( type, sa*axis[2] ) )
void
ell_aa_to_q_f( float q[4], const float angle, const float axis[3] ) {
233 _ELL_AA_TO_Q( float );
}
void
ell_aa_to_q_d( double q[4], const double angle, const double axis[3] ) {
238 _ELL_AA_TO_Q( double );
}
float
ell_3m_to_aa_f( float axis[3], const float m[9] ) {
float q[4];
244
ell_3m_to_q_f( q, m );
return ell_q_to_aa_f( axis, q );
}
double
ell_3m_to_aa_d( double axis[3], const double m[9] ) {
double q[4];
253 ell_3m_to_q_d( q, m );
return ell_q_to_aa_d( axis, q );
}
float
ell_4m_to_aa_f( float axis[3], const float m[16] ) {
float q[4];
261 ell_4m_to_q_f( q, m );
return ell_q_to_aa_f( axis, q );
}
double
ell_4m_to_aa_d( double axis[3], const double m[16] ) {
double q[4];
ell_4m_to_q_d( q, m );
return ell_q_to_aa_d( axis, q );
}
void
ell_aa_to_3m_f( float m[9], const float angle, const float axis[3] ) {
float q[4];
ell_aa_to_q_f( q, angle, axis );
ell_q_to_3m_f( m, q );
279 }
void
ell_aa_to_3m_d( double m[9], const double angle, const double axis[3] ) {
double q[4];
ell_aa_to_q_d( q, angle, axis );
ell_q_to_3m_d( m, q );
}
288
void
ell_aa_to_4m_f( float m[16], const float angle, const float axis[3] ) {
float q[4];
ell_aa_to_q_f( q, angle, axis );
ell_q_to_4m_f( m, q );
}
void
ell_aa_to_4m_d( double m[16], const double angle, const double axis[3] ) {
double q[4];
ell_aa_to_q_d( q, angle, axis );
ell_q_to_4m_d( m, q );
}
304
void
ell_q_mul_f( float q3[4], const float q1[4], const float q2[4] ) {
ELL_Q_MUL( q3, q1, q2 );
}
309
void
ell_q_mul_d( double q3[4], const double q1[4], const double q2[4] ) {
ELL_Q_MUL( q3, q1, q2 );
}
314
void
ell_q_inv_f( float qi[4], const float q[4] ) {
float N;
ELL_Q_INV( qi, q, N );
}
void
ell_q_inv_d( double qi[4], const double q[4] ) {
double N;
324 ELL_Q_INV( qi, q, N );
}
/*
** div( a, b ) = a^-1 * b
*/
void
ell_q_div_f( float q3[4], const float q1[4], const float q2[4] ) {
float N, q1i[4];
ELL_Q_INV( q1i, q1, N );
ELL_Q_MUL( q3, q1i, q2 );
}
void
ell_q_div_d( double q3[4], const double q1[4], const double q2[4] ) {
double N, q1i[4];
ELL_Q_INV( q1i, q1, N );
343 ELL_Q_MUL( q3, q1i, q2 );
}
/*
** this is good for *ALL* quaternions, any length, including zero.
348 ** the behavior on the zero quaternion is governed by the behavior
** of the log( ) and atan2( ) functions in the math library
**
** the basic insight is that doing conversion to angle/axis,
** and doing the atan2( l2( x, y, z ), w ),
** and that doing a logarithm, are all basically the same thing
*/
void
357 ell_q_log_f( float q2[4], const float q1[4] ) {
float a, b, axis[3];
a = AIR_CAST( float, log( ELL_4V_LEN( q1 ) ) );
b = ell_q_to_aa_f( axis, q1 )/2.0f;
ELL_4V_SET( q2, a, b*axis[0], b*axis[1], b*axis[2] );
363 }
void
ell_q_log_d( double q2[4], const double q1[4] ) {
double a, b, axis[3];
a = log( ELL_4V_LEN( q1 ) );
b = ell_q_to_aa_d( axis, q1 )/2.0;
371 ELL_4V_SET( q2, a, b*axis[0], b*axis[1], b*axis[2] );
}
/*
** this is good for *ALL* quaternions, any length, including zero
** NOTE: one non-use of ELL_3V_NORM to avoid warnings about
** precision loss from type conversion
*/
#define _ELL_Q_EXP( type ) \
type ea, b, sb, axis[3], tmp; \
\
ea = AIR_CAST( type, exp( q1[0] ) ); \
b = AIR_CAST( type, ELL_3V_LEN( q1+1 ) ); \
if ( b ) { \
ELL_3V_SCALE( axis, 1.0f/b, q1+1 ); \
tmp = AIR_CAST( type, ELL_3V_LEN( axis ) ); \
ELL_3V_SCALE( axis, 1.0f/tmp, axis ); \
} else { \
ELL_3V_SET( axis, 1.0f, 0.0f, 0.0f ); \
} \
sb = AIR_CAST( type, sin( b ) ); \
ELL_4V_SET( q2, AIR_CAST( type, ea*cos( b ) ), ea*sb*axis[0], \
ea*sb*axis[1], ea*sb*axis[2] )
void
ell_q_exp_f( float q2[4], const float q1[4] ) {
_ELL_Q_EXP( float );
}
void
ell_q_exp_d( double q2[4], const double q1[4] ) {
_ELL_Q_EXP( double );
}
void
ell_q_pow_f( float q2[4], const float q1[4], const float p ) {
float len, angle, axis[3];
len = AIR_CAST( float, pow( ELL_4V_LEN( q1 ), p ) );
angle = ell_q_to_aa_f( axis, q1 );
ell_aa_to_q_f( q2, p*angle, axis );
ELL_4V_SCALE( q2, len, q2 );
}
void
ell_q_pow_d( double q2[4], const double q1[4], const double p ) {
double len, angle, axis[3];
len = pow( ELL_4V_LEN( q1 ), p );
angle = ell_q_to_aa_d( axis, q1 );
ell_aa_to_q_d( q2, p*angle, axis );
ELL_4V_SCALE( q2, len, q2 );
}
/*
** by the wonders of quaternions, this rotation will be the
** same regardless of the quaternion length. This is in
** contrast to doing rotation by first converting to matrix,
** in which an explicit normalization is required. There is
** a divide here ( in ELL_Q_INV ), but there's no sqrt( ).
*/
#define _ELL_Q3V_ROT( type ) \
type n, a[4], b[4], c[4]; \
\
ELL_4V_SET( a, 0, v1[0], v1[1], v1[2] ); \
ELL_Q_INV( b, q, n ); \
437 ELL_Q_MUL( c, a, b ); \
ELL_Q_MUL( a, q, c ); \
ELL_3V_COPY( v2, a+1 )
void
ell_q_3v_rotate_f( float v2[3], const float q[4], const float v1[3] ) {
_ELL_Q3V_ROT( float );
}
void
ell_q_3v_rotate_d( double v2[3], const double q[4], const double v1[3] ) {
_ELL_Q3V_ROT( double );
}
/*
** we start by ignoring the last ( homogenous ) coordinate of
** the vector, but then we copy it to the output
*/
void
ell_q_4v_rotate_f( float v2[4], const float q[4], const float v1[4] ) {
_ELL_Q3V_ROT( float );
v2[3] = v1[3];
}
void
ell_q_4v_rotate_d( double v2[4], const double q[4], const double v1[4] ) {
_ELL_Q3V_ROT( double );
v2[3] = v1[3];
}
/* #define _ELL_Q_AVG_ITER_MAX 30 */
int
ell_q_avg4_d( double m[4], unsigned int *iterP,
const double _q1[4], const double _q2[4],
const double _q3[4], const double _q4[4],
const double _wght[4],
const double eps, const unsigned int maxIter ) {
static const char me[]="ell_q_avg4_d";
double N, elen, a[4], b[4], c[4], d[4],
tmp[4], la[4], lb[4], lc[4], ld[4], u[4], wght[4];
unsigned int iter;
/* *iterP optional */
if ( !( m && _q1 && _q2 && _q3 && _q4 && _wght ) ) {
biffAddf( ELL, "%s: got NULL pointer", me );
return 1;
}
if ( !( eps >= 0 ) ) {
biffAddf( ELL, "%s: need eps >= 0 ( not %g )", me, eps );
return 1;
}
/* normalize ( wrt L2 ) all given quaternions */
ELL_4V_NORM( a, _q1, N );
ELL_4V_NORM( b, _q2, N );
ELL_4V_NORM( c, _q3, N );
ELL_4V_NORM( d, _q4, N );
/* normalize ( wrt L1 ) the given weights */
ELL_4V_COPY( wght, _wght );
N = wght[0] + wght[1] + wght[2] + wght[3];
ELL_4V_SCALE( wght, 1.0/N, wght );
/* initialize mean to normalized euclidean mean */
ELL_4V_SCALE_ADD4( m, wght[0], a, wght[1], b, wght[2], c, wght[3], d );
ELL_4V_NORM( m, m, N );
iter = 0;
do {
/* take log of everyone */
ell_q_div_d( tmp, m, a ); ell_q_log_d( la, tmp );
ell_q_div_d( tmp, m, b ); ell_q_log_d( lb, tmp );
ell_q_div_d( tmp, m, c ); ell_q_log_d( lc, tmp );
ell_q_div_d( tmp, m, d ); ell_q_log_d( ld, tmp );
/* average, and find length */
ELL_4V_SCALE_ADD4( u, wght[0], la, wght[1], lb, wght[2], lc, wght[3], ld );
elen = ELL_4V_LEN( u );
/* use exp to put it back on S^3 */
ell_q_exp_d( tmp, u ); ell_q_mul_d( m, m, tmp );
iter++;
} while ( ( !maxIter || iter < maxIter ) && elen > eps );
if ( elen > eps ) {
biffAddf( ELL, "%s: still have error %g after max %d iters", me,
elen, maxIter );
return 1;
}
if ( iterP ) {
*iterP = iter;
}
return 0;
}
/*
** unlike the thing above, this one assumes that the quaternions in qq
** have already been normalized, and, that the sum of wght[] is 1.0
*/
int
ell_q_avgN_d( double mm[4], unsigned int *iterP,
const double *qq, double *qlog,
const double *wght, const unsigned int NN,
const double eps, const unsigned int maxIter ) {
static const char me[]="ell_q_avgN_d";
double tmp, qdiv[4], elen;
unsigned int ii, iter;
/* iterP optional */
/* wght optional, to signify equal 1/NN weighting for all */
if ( !( mm && qq ) ) {
biffAddf( ELL, "%s: got NULL pointer", me );
return 1;
}
if ( !( eps >= 0 ) ) {
biffAddf( ELL, "%s: need eps >= 0 ( not %g )", me, eps );
return 1;
}
/* initialize with euclidean mean */
ELL_4V_SET( mm, 0, 0, 0, 0 );
for ( ii=0; ii<NN; ii++ ) {
double ww;
ww = wght ? wght[ii] : 1.0/NN;
ELL_4V_SCALE_INCR( mm, ww, qq + 4*ii );
}
ELL_4V_NORM( mm, mm, tmp );
iter = 0;
do {
double logavg[4], qexp[4];
/* take log of everyone */
for ( ii=0; ii<NN; ii++ ) {
ell_q_div_d( qdiv, mm, qq + 4*ii ); /* div = mm^1 * qq[ii] */
ell_q_log_d( qlog + 4*ii, qdiv );
}
/* average, and find length */
ELL_4V_SET( logavg, 0, 0, 0, 0 );
for ( ii=0; ii<NN; ii++ ) {
double ww;
ww = wght ? wght[ii] : 1.0/NN;
ELL_4V_SCALE_INCR( logavg, ww, qlog + 4*ii );
}
elen = ELL_4V_LEN( logavg );
/* use exp to put it back on S^3 */
ell_q_exp_d( qexp, logavg );
ell_q_mul_d( mm, mm, qexp );
iter++;
} while ( ( !maxIter || iter < maxIter ) && elen > eps );
if ( elen > eps ) {
biffAddf( ELL, "%s: still have error %g ( > eps %g ) after max %d iters", me,
elen, eps, maxIter );
return 1;
}
if ( iterP ) {
*iterP = iter;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *es6Info = ( "Tests ell_6ms_eigensolve_d" );
int
28 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
Nrrd *nin, *_nin;
double *in, sym[21], eval[6], evec[36];
unsigned int rr, cc, ii, jj;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "matrix", airTypeOther, 1, 1, &_nin, NULL,
"6x6 matrix", NULL, NULL, nrrdHestNrrd );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, es6Info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( !( 2 == _nin->dim
&& 6 == _nin->axis[0].size
&& 6 == _nin->axis[1].size ) ) {
fprintf( stderr, "%s: didn't get 2-D 6x6 matrix ( got %u-D %ux%u )\n", me,
_nin->dim,
AIR_CAST( unsigned int, _nin->axis[0].size ),
AIR_CAST( unsigned int, _nin->axis[1].size ) );
airMopError( mop ); return 1;
}
nrrdConvert( nin, _nin, nrrdTypeDouble );
in = AIR_CAST( double *, nin->data );
sym[ 0] = in[ 0];
sym[ 1] = in[ 1];
sym[ 2] = in[ 2];
sym[ 3] = in[ 3];
sym[ 4] = in[ 4];
sym[ 5] = in[ 5];
sym[ 6] = in[ 7];
sym[ 7] = in[ 8];
sym[ 8] = in[ 9];
sym[ 9] = in[10];
sym[10] = in[11];
sym[11] = in[14];
sym[12] = in[15];
sym[13] = in[16];
sym[14] = in[17];
sym[15] = in[21];
sym[16] = in[22];
sym[17] = in[23];
sym[18] = in[28];
sym[19] = in[29];
sym[20] = in[35];
for ( rr=1; rr<6; rr++ ) {
for ( cc=0; cc<rr; cc++ ) {
in[cc + 6*rr] = in[rr + 6*cc];
}
}
fprintf( stderr, "m = [" );
for ( rr=0; rr<6; rr++ ) {
for ( cc=0; cc<6; cc++ ) {
fprintf( stderr, "%g%s", in[cc + 6*rr],
cc<5 ? ", " : ( rr<5 ? ";" : "]" ) );
}
fprintf( stderr, "\n" );
}
ell_6ms_eigensolve_d( eval, evec, sym, FLT_MIN/10 );
for ( ii=0; ii<6; ii++ ) {
fprintf( stderr, "eval[%u] = %g:\n ", ii, eval[ii] );
for ( jj=0; jj<6; jj++ ) {
fprintf( stderr, " %g", evec[jj + 6*ii] );
}
fprintf( stderr, "\n" );
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *interInfo = ( "Recreates memories of college physics" );
typedef struct {
FILE *file;
double psc;
double bbox[4];
double maxX, maxY, yscale;
} wheelPS;
#define WPS_X( x ) AIR_AFFINE( wps->bbox[0], ( x ), wps->bbox[2], 0, wps->maxX )
#define WPS_Y( y ) AIR_AFFINE( wps->bbox[1], ( y ), wps->bbox[3], 0, wps->maxY )
#define WPS_S( s ) AIR_DELTA( wps->bbox[1], ( s ), wps->bbox[3], 0, wps->maxY )
void
40 wheelPreamble( wheelPS *wps ) {
wps->maxX = wps->psc*( wps->bbox[2] - wps->bbox[0] );
wps->maxY = wps->psc*( wps->bbox[3] - wps->bbox[1] );
fprintf( wps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
fprintf( wps->file, "%%%%Creator: limn\n" );
fprintf( wps->file, "%%%%Pages: 1\n" );
fprintf( wps->file, "%%%%BoundingBox: 0 0 %d %d\n",
( int )( wps->maxX ),
( int )( wps->maxY ) );
fprintf( wps->file, "%%%%EndComments\n" );
fprintf( wps->file, "%%%%EndProlog\n" );
fprintf( wps->file, "%%%%Page: 1 1\n" );
fprintf( wps->file, "gsave\n" );
fprintf( wps->file, "0 0 moveto\n" );
fprintf( wps->file, "%g 0 lineto\n", wps->maxX );
fprintf( wps->file, "%g %g lineto\n", wps->maxX, wps->maxY );
fprintf( wps->file, "0 %g lineto\n", wps->maxY );
fprintf( wps->file, "closepath\n" );
fprintf( wps->file, "clip\n" );
fprintf( wps->file, "gsave newpath\n" );
fprintf( wps->file, "1 setlinejoin\n" );
fprintf( wps->file, "1 setlinecap\n" );
fprintf( wps->file, "/M {moveto} bind def\n" );
fprintf( wps->file, "/L {lineto} bind def\n" );
fprintf( wps->file, "/W {setlinewidth} bind def\n" );
fprintf( wps->file, "/F {fill} bind def\n" );
fprintf( wps->file, "/S {stroke} bind def\n" );
fprintf( wps->file, "/CP {closepath} bind def\n" );
fprintf( wps->file, "/RGB {setrgbcolor} bind def\n" );
fprintf( wps->file, "/Gr {setgray} bind def\n" );
fprintf( wps->file, "\n" );
return;
}
void
77 wheelWidth( wheelPS *wps, double width ) {
fprintf( wps->file, "%g W\n", width );
return;
}
void
84 wheelGray( wheelPS *wps, double gray ) {
fprintf( wps->file, "%g Gr\n", gray );
return;
}
void
91 wheelArrow( wheelPS *wps, double x0, double y0, double x1, double y1,
double alen, double awidth ) {
double len, dir[2], perp[2];
dir[0] = x0 - x1;
dir[1] = y0 - y1;
ELL_2V_NORM( dir, dir, len );
ELL_2V_SET( perp, -dir[1], dir[0] );
fprintf( wps->file, "%g %g M\n", WPS_X( x0 ), WPS_Y( y0 ) );
fprintf( wps->file, "%g %g L S\n",
WPS_X( x1 + alen*dir[0]/2 ),
WPS_Y( y1 + alen*dir[1]/2 ) );
if ( alen && awidth ) {
if ( len < alen ) {
awidth *= len/alen;
alen = len;
}
fprintf( wps->file, "%g %g M\n",
WPS_X( x1 + alen*dir[0] + awidth*perp[0] ),
WPS_Y( y1 + alen*dir[1] + awidth*perp[1] ) );
fprintf( wps->file, "%g %g L\n", WPS_X( x1 ), WPS_Y( y1 ) );
fprintf( wps->file, "%g %g L CP F\n",
WPS_X( x1 + alen*dir[0] - awidth*perp[0] ),
WPS_Y( y1 + alen*dir[1] - awidth*perp[1] ) );
}
return;
}
void
120 wheelEpilog( wheelPS *wps ) {
fprintf( wps->file, "grestore\n" );
fprintf( wps->file, "grestore\n" );
fprintf( wps->file, "%%%%Trailer\n" );
return;
}
int
129 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
int fidx, aidx, num, frames;
double psc, width[2], arrowWidth, lineWidth, angle, seglen,
x0, y0, x1, y1, cc, ss;
wheelPS wps;
char filename[AIR_STRLEN_MED];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "w", "arrowWidth lineWidth", airTypeDouble, 2, 2, width,
"1.0 0.2", "widths" );
hestOptAdd( &hopt, "n", "number", airTypeInt, 1, 1, &num, "10",
"number of arrows" );
hestOptAdd( &hopt, "f", "frames", airTypeInt, 1, 1, &frames, "10",
"number of frames" );
hestOptAdd( &hopt, "psc", "scale", airTypeDouble, 1, 1, &psc, "200",
"scaling from world space to PostScript points" );
hestOptAdd( &hopt, "o", "prefix", airTypeString, 1, 1, &outS, NULL,
"prefix of file names" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, interInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
for ( fidx=0; fidx<frames; fidx++ ) {
sprintf( filename, "%s%03d.eps", outS, fidx );
if ( !( wps.file = airFopen( filename, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't open output file\n", me );
airMopError( mop ); return 1;
}
lineWidth = width[0];
arrowWidth = width[1];
wps.psc = psc;
ELL_4V_SET( wps.bbox, -0.45, -0.85, 1.1, 0.85 );
wheelPreamble( &wps );
fprintf( wps.file, "0 setlinecap\n" );
wheelGray( &wps, 0.4 );
wheelWidth( &wps, lineWidth );
x0 = 0;
y0 = 0;
seglen = 1.0/num;
angle = AIR_AFFINE( 0, fidx, frames, 0, 2*AIR_PI );
for ( aidx=1; aidx<=num; aidx++ ) {
cc = cos( angle*aidx )*seglen;
ss = sin( angle*aidx )*seglen;
x1 = x0 + 0.90*cc;
y1 = y0 + 0.90*ss;
wheelArrow( &wps, x0, y0, x1, y1, arrowWidth, arrowWidth*0.4 );
x0 += cc;
y0 += ss;
}
wheelGray( &wps, 0.0 );
wheelArrow( &wps, 0, 0, x0, y0, arrowWidth, arrowWidth*0.4 );
wheelEpilog( &wps );
airFclose( wps.file );
}
airMopOkay( mop );
exit( 0 );
}
/*
mkdir inter04
test/inter -w 4 0.08 -o inter04/ -n 4 -f 300
mkdir inter08
test/inter -w 2.5 0.04 -o inter08/ -n 8 -f 300
mkdir inter16
test/inter -w 2.2 0.02 -o inter16/ -n 16 -f 300
mkdir inter32
test/inter -w 2 0.01 -o inter32/ -n 32 -f 300
mkdir inter
foreach I ( 000 001 002 003 004 005 006 007 008 009 \
010 011 012 013 014 015 016 017 018 019 \
020 021 022 023 024 025 026 027 028 029 \
030 031 032 033 034 035 036 037 038 039 \
040 041 042 043 044 045 046 047 048 049 \
050 051 052 053 054 055 056 057 058 059 \
060 061 062 063 064 065 066 067 068 069 \
070 071 072 073 074 075 076 077 078 079 \
080 081 082 083 084 085 086 087 088 089 \
090 091 092 093 094 095 096 097 098 099 \
100 101 102 103 104 105 106 107 108 109 \
110 111 112 113 114 115 116 117 118 119 \
120 121 122 123 124 125 126 127 128 129 \
130 131 132 133 134 135 136 137 138 139 \
140 141 142 143 144 145 146 147 148 149 \
150 151 152 153 154 155 156 157 158 159 \
160 161 162 163 164 165 166 167 168 169 \
170 171 172 173 174 175 176 177 178 179 \
180 181 182 183 184 185 186 187 188 189 \
190 191 192 193 194 195 196 197 198 199 \
200 201 202 203 204 205 206 207 208 209 \
210 211 212 213 214 215 216 217 218 219 \
220 221 222 223 224 225 226 227 228 229 \
230 231 232 233 234 235 236 237 238 239 \
240 241 242 243 244 245 246 247 248 249 \
250 251 252 253 254 255 256 257 258 259 \
260 261 262 263 264 265 266 267 268 269 \
270 271 272 273 274 275 276 277 278 279 \
280 281 282 283 284 285 286 287 288 289 \
290 291 292 293 294 295 296 297 298 299 )
echo $I
foreach J ( 04 08 16 32 )
eps2ppm inter{$J}/${I}.eps 250 \
| unu slice -a 0 -p 0 \
| unu resample -s x0.2 x0.2 -o inter${J}/${I}.png
end
unu pad -i inter04/${I}.png -min 0 0 -max M+1 M -b pad -v 128 \
| unu join -i - inter08/${I}.png -a 0 \
| unu pad -min 0 0 -max M M+1 -b pad -v 128 -o a.png
unu pad -i inter16/${I}.png -min 0 0 -max M+1 M -b pad -v 128 \
| unu join -i - inter32/${I}.png -a 0 -o b.png
unu join -i a.png b.png -a 1 \
| unu 2op - 255 - -o inter/${I}.png
end
rm -f a.png b.png
*/
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *invInfo = ( "Tests ell_Nm_inv and ell_Nm_pseudo_inv, "
"and ell_{3, 4}inv_d where possible " );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *outS, *err;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
Nrrd *nin, *nmat, *ninv, *nidn;
int ( *func )( Nrrd *, Nrrd * );
double m3[9], m4[16];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "matrix", airTypeOther, 1, 1, &nin, NULL,
"transform( s ) to apply to image",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, invInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
ninv = nrrdNew( );
airMopAdd( mop, ninv, ( airMopper )nrrdNuke, airMopAlways );
nidn = nrrdNew( );
airMopAdd( mop, nidn, ( airMopper )nrrdNuke, airMopAlways );
nmat = nrrdNew( );
airMopAdd( mop, nmat, ( airMopper )nrrdNuke, airMopAlways );
nrrdConvert( nmat, nin, nrrdTypeDouble );
if ( 3 == nmat->axis[0].size && 3 == nmat->axis[1].size ) {
ell_3m_inv_d( m3, ( double * )nmat->data );
fprintf( stderr, "%s: input:\n", me );
ell_3m_print_d( stderr, ( double * )nmat->data );
fprintf( stderr, "%s: inverse:\n", me );
ell_3m_print_d( stderr, m3 );
}
if ( 4 == nmat->axis[0].size && 4 == nmat->axis[1].size ) {
ell_4m_inv_d( m4, ( double * )nmat->data );
fprintf( stderr, "%s: input:\n", me );
ell_4m_print_d( stderr, ( double * )nmat->data );
fprintf( stderr, "%s: inverse:\n", me );
ell_4m_print_d( stderr, m4 );
}
func = ( nmat->axis[0].size == nmat->axis[1].size
? ell_Nm_inv
: ell_Nm_pseudo_inv );
if ( func( ninv, nmat ) ) {
airMopAdd( mop, err = biffGetDone( ELL ), airFree, airMopAlways );
fprintf( stderr, "%s: problem inverting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, ninv, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *mulInfo = ( "Tests ell_Nm_mul" );
int
28 main( int argc, const char *argv[] ) {
const char *me;
char *outS, *err;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
Nrrd *_ninA, *_ninB, *ninA, *ninB, *nmul;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "matrix", airTypeOther, 1, 1, &_ninA, NULL,
"first matrix",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, NULL, "matrix", airTypeOther, 1, 1, &_ninB, NULL,
"first matrix",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, mulInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
ninA = nrrdNew( );
airMopAdd( mop, ninA, ( airMopper )nrrdNuke, airMopAlways );
ninB = nrrdNew( );
airMopAdd( mop, ninB, ( airMopper )nrrdNuke, airMopAlways );
nmul = nrrdNew( );
airMopAdd( mop, nmul, ( airMopper )nrrdNuke, airMopAlways );
nrrdConvert( ninA, _ninA, nrrdTypeDouble );
nrrdConvert( ninB, _ninB, nrrdTypeDouble );
if ( ell_Nm_mul( nmul, ninA, ninB ) ) {
airMopAdd( mop, err = biffGetDone( ELL ), airFree, airMopAlways );
fprintf( stderr, "%s: problem inverting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nmul, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *rot2aaInfo = ( "converts a list of rotation matrices to a list of "
"angle axis specifications" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *outS, *err;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
Nrrd *_nmat, *nmat;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "input matrices", airTypeOther, 1, 1, &_nmat, NULL,
"list of rotation matrices, 2-D array with one matrix "
"( in ROW-major order ) per scanline", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, NULL, "output filename", airTypeString, 1, 1, &outS, "-",
"file to write EPS output to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, rot2aaInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 2 == _nmat->dim && 9 == _nmat->axis[0].size ) ) {
fprintf( stderr, "%s: need a 2-D 9-by-N nrrd\n", me );
airMopError( mop ); return 1;
}
nmat = nrrdNew( );
airMopAdd( mop, nmat, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nmat, _nmat, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't convert to double:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "../ell.h"
char *me;
void
30 usage( ) {
/* 0 1 2 3 ( 4 ) */
fprintf( stderr, "usage: %s <a> <b> <c>\n", me );
exit( 1 );
}
int
37 main( int argc, char *argv[] ) {
char *aS, *bS, *cS;
float a, b, c, t;
me = argv[0];
if ( 4 != argc ) {
usage( );
}
aS = argv[1];
bS = argv[2];
cS = argv[3];
if ( 3 != ( sscanf( aS, "%g", &a ) +
sscanf( bS, "%g", &b ) +
sscanf( cS, "%g", &c ) ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\", \"%s\", \"%s\" as floats\n",
me, aS, bS, cS );
usage( );
}
printf( "%g %g %g ( max idx %d; min idx %d ) --> ", a, b, c,
ELL_MAX3_IDX( a, b, c ), ELL_MIN3_IDX( a, b, c ) );
ELL_SORT3( a, b, c, t );
printf( "%g %g %g\n", a, b, c );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include "../ell.h"
char *me;
#define CA 1
int
32 main( int argc, char *argv[] ) {
float angleA_f, axisA_f[3], angleB_f, axisB_f[3],
qA_f[4], qB_f[4], qC_f[4],
mat3A_f[9], mat4A_f[16], mat3B_f[9], mat4B_f[16], mat3C_f[9], mat4C_f[16],
pntA_f[4], pntB_f[4], pntC_f[4];
double angleA_d, axisA_d[3], angleB_d, axisB_d[3],
qA_d[4], qB_d[4], qC_d[4],
mat3A_d[9], mat4A_d[16], mat3B_d[9], mat4B_d[16], mat3C_d[9], mat4C_d[16],
pntA_d[4], pntB_d[4], pntC_d[4];
int I, N;
double tmp, det, frob;
me = argv[0];
N = 100000;
AIR_UNUSED( pntA_d );
AIR_UNUSED( pntB_d );
AIR_UNUSED( pntC_d );
AIR_UNUSED( mat4C_d );
AIR_UNUSED( mat3C_d );
AIR_UNUSED( mat4B_d );
AIR_UNUSED( mat3B_d );
AIR_UNUSED( mat4A_d );
AIR_UNUSED( mat3A_d );
AIR_UNUSED( qC_d );
AIR_UNUSED( qB_d );
AIR_UNUSED( qA_d );
AIR_UNUSED( axisB_d );
AIR_UNUSED( angleB_d );
AIR_UNUSED( axisA_d );
AIR_UNUSED( angleA_d );
AIR_UNUSED( argc );
for ( I=0; I<N; I++ ) {
/* make a rotation ( as a quaternion ) */
ELL_3V_SET( axisA_f, 2*airDrandMT( )-1, 2*airDrandMT( )-1, 2*airDrandMT( )-1 );
ELL_3V_NORM( axisA_f, axisA_f, tmp ); /* yea, not uniform, so what */
angleA_f = AIR_PI*( 2*airDrandMT( )-1 );
ell_aa_to_q_f( qA_f, angleA_f, axisA_f );
/* convert to AA and back, and back */
angleB_f = ell_q_to_aa_f( axisB_f, qA_f );
if ( ELL_3V_DOT( axisB_f, axisA_f ) < 0 ) {
ELL_3V_SCALE( axisB_f, -1, axisB_f );
angleB_f *= -1;
}
ELL_3V_SUB( axisA_f, axisA_f, axisB_f );
printf( " aa -> q -> aa error: %g, %g\n",
CA + AIR_ABS( angleA_f - angleB_f ), CA + ELL_3V_LEN( axisA_f ) );
/* convert to 3m and back, and back */
ell_q_to_3m_f( mat3A_f, qA_f );
ell_3m_to_q_f( qB_f, mat3A_f );
if ( ELL_4V_DOT( qA_f, qB_f ) < 0 ) {
ELL_4V_SCALE( qB_f, -1, qB_f );
}
ELL_4V_SUB( qC_f, qA_f, qB_f );
ELL_Q_TO_3M( mat3B_f, qA_f );
ELL_3M_SUB( mat3C_f, mat3B_f, mat3A_f );
printf( " q -> 3m -> q error: %g, %g\n",
CA + ELL_4V_LEN( qC_f ), CA + ELL_3M_FROB( mat3C_f ) );
/* convert to 4m and back, and back */
ell_q_to_4m_f( mat4A_f, qA_f );
ell_4m_to_q_f( qB_f, mat4A_f );
if ( ELL_4V_DOT( qA_f, qB_f ) < 0 ) {
ELL_4V_SCALE( qB_f, -1, qB_f );
}
ELL_4V_SUB( qC_f, qA_f, qB_f );
ELL_Q_TO_4M( mat4B_f, qA_f );
ELL_4M_SUB( mat4C_f, mat4B_f, mat4A_f );
printf( " q -> 4m -> q error: %g, %g\n",
CA + ELL_4V_LEN( qC_f ), CA + ELL_4M_FROB( mat4C_f ) );
/* make a point that we'll rotate */
ELL_3V_SET( pntA_f, 2*airDrandMT( )-1, 2*airDrandMT( )-1, 2*airDrandMT( )-1 );
/* effect rotation in two different ways, and compare results */
ELL_3MV_MUL( pntB_f, mat3A_f, pntA_f );
ell_q_3v_rotate_f( pntC_f, qA_f, pntA_f );
ELL_3V_SUB( pntA_f, pntB_f, pntC_f );
printf( " rotation error = %g\n", CA + ELL_3V_LEN( pntA_f ) );
/* mix up inversion with conversion */
ell_3m_inv_f( mat3C_f, mat3A_f );
ell_3m_to_q_f( qB_f, mat3C_f );
ell_q_mul_f( qC_f, qA_f, qB_f );
if ( ELL_4V_DOT( qA_f, qC_f ) < 0 ) {
ELL_4V_SCALE( qC_f, -1, qC_f );
}
printf( " inv mul = %g %g %g %g\n", qC_f[0],
CA + qC_f[1], CA + qC_f[2], CA + qC_f[3] );
ell_q_inv_f( qC_f, qB_f );
ELL_4V_SUB( qC_f, qB_f, qB_f );
printf( " inv diff = %g %g %g %g\n", CA + qC_f[0],
CA + qC_f[1], CA + qC_f[2], CA + qC_f[3] );
/* exp and log */
ell_q_log_f( qC_f, qA_f );
ell_q_log_f( qB_f, qC_f );
ell_q_exp_f( qC_f, qB_f );
ell_q_exp_f( qB_f, qC_f );
ELL_4V_SUB( qC_f, qB_f, qA_f );
printf( " exp/log diff = %g %g %g %g\n", CA + qC_f[0],
CA + qC_f[1], CA + qC_f[2], CA + qC_f[3] );
/* pow, not very exhaustive */
ell_q_to_3m_f( mat3A_f, qA_f );
ell_3m_post_mul_f( mat3A_f, mat3A_f );
ell_3m_post_mul_f( mat3A_f, mat3A_f );
ell_q_pow_f( qB_f, qA_f, 4 );
ell_q_to_3m_f( mat3B_f, qB_f );
ELL_3M_SUB( mat3B_f, mat3B_f, mat3A_f );
printf( " pow diff = %g\n", CA + ELL_3M_FROB( mat3B_f ) );
if ( ELL_3M_FROB( mat3B_f ) > 2 ) {
printf( " start q = %g %g %g %g\n", qA_f[0], qA_f[1], qA_f[2], qA_f[3] );
angleA_f = ell_q_to_aa_f( axisA_f, qA_f );
printf( " --> aa = %g ( %g %g %g )\n", angleA_f,
axisA_f[0], axisA_f[1], axisA_f[2] );
printf( " q^3 = %g %g %g %g\n", qB_f[0], qB_f[1], qB_f[2], qB_f[3] );
angleA_f = ell_q_to_aa_f( axisA_f, qB_f );
printf( " --> aa = %g ( %g %g %g )\n", angleA_f,
axisA_f[0], axisA_f[1], axisA_f[2] );
exit( 1 );
}
/* make sure it looks like a rotation matrix */
ell_q_to_3m_f( mat3A_f, qA_f );
det = ELL_3M_DET( mat3A_f );
frob = ELL_3M_FROB( mat3A_f );
ELL_3M_TRANSPOSE( mat3B_f, mat3A_f );
ell_3m_inv_f( mat3C_f, mat3A_f );
ELL_3M_SUB( mat3C_f, mat3B_f, mat3C_f );
printf( " det = %g; size = %g; err = %g\n", det, frob*frob/3,
CA + ELL_3M_FROB( mat3C_f ) );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ell.h"
char *wheelInfo = ( "Makes pictures of the eigenvalue wheel" );
void
28 wheelTenToGeom( double geom[3], double a, double b, double c,
double d, double e, double f ) {
double A, B, C, Q, QQQ, R, Th;
A = -a - d - f;
B = a*d + a*f + d*f - b*b - c*c - e*e;
C = c*c*d + a*e*e + b*b*f - 2*b*c*e - a*d*f;
Q = ( A*A - 3*B )/9;
R = ( 9*A*B - 27*C - 2*A*A*A )/54;
QQQ = Q*Q*Q;
Th = QQQ ? acos( R/sqrt( QQQ ) )/3 : 0;
geom[0] = -A/3;
geom[1] = 2*sqrt( Q );
geom[2] = 180.0*Th/AIR_PI;
return;
}
void
46 wheelABCToGeom( double *geom, double A, double B, double C ) {
double Q, R, QQQ, Th;
Q = ( A*A - 3*B )/9;
R = ( 9*A*B - 27*C - 2*A*A*A )/54;
QQQ = Q*Q*Q;
Th = QQQ ? acos( R/sqrt( QQQ ) )/3 : 0;
geom[0] = -A/3;
geom[1] = 2*sqrt( Q );
geom[2] = 180.0*Th/AIR_PI;
return;
}
void
60 wheelGeomToRoot( double xroot[3], double yroot[3], double geom[3] ) {
double Th;
Th = AIR_PI*geom[2]/180.0;
xroot[0] = geom[0] + geom[1]*cos( Th );
xroot[1] = geom[0] + geom[1]*cos( Th - 2*AIR_PI/3 );
xroot[2] = geom[0] + geom[1]*cos( Th + 2*AIR_PI/3 );
yroot[0] = geom[1]*sin( Th );
yroot[1] = geom[1]*sin( Th - 2*AIR_PI/3 );
yroot[2] = geom[1]*sin( Th + 2*AIR_PI/3 );
return;
}
void
74 wheelGeomToABC( double ABC[3], double center, double radius, double angle ) {
double geom[3], x[3], yroot[3];
ELL_3V_SET( geom, center, radius, angle );
wheelGeomToRoot( x, yroot, geom );
ELL_3V_SET( ABC,
-x[0] - x[1] - x[2],
x[0]*x[1] + x[1]*x[2] + x[0]*x[2],
-x[0]*x[1]*x[2] );
return;
}
void
87 wheelGeomToRNTH( double rnth[3], double center, double radius, double angle ) {
double mu1, mu2;
mu1 = center;
mu2 = radius*radius/2;
rnth[0] = sqrt( mu2 )/( sqrt( 2 )*mu1 );
rnth[1] = sqrt( 3*( mu1*mu1 + mu2 ) );
rnth[2] = angle;
}
typedef struct {
FILE *file;
double psc;
double bbox[4];
double maxX, maxY, yscale;
} wheelPS;
#define WPS_X( x ) AIR_AFFINE( wps->bbox[0], ( x ), wps->bbox[2], 0, wps->maxX )
#define WPS_Y( y ) AIR_AFFINE( wps->bbox[1], ( y ), wps->bbox[3], 0, wps->maxY )
#define WPS_S( s ) AIR_DELTA( wps->bbox[1], ( s ), wps->bbox[3], 0, wps->maxY )
void
110 wheelPreamble( wheelPS *wps ) {
wps->maxX = wps->psc*( wps->bbox[2] - wps->bbox[0] );
wps->maxY = wps->psc*( wps->bbox[3] - wps->bbox[1] );
fprintf( wps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
fprintf( wps->file, "%%%%Creator: limn\n" );
fprintf( wps->file, "%%%%Pages: 1\n" );
fprintf( wps->file, "%%%%BoundingBox: 0 0 %d %d\n",
( int )( wps->maxX ),
( int )( wps->maxY ) );
fprintf( wps->file, "%%%%EndComments\n" );
fprintf( wps->file, "%%%%EndProlog\n" );
fprintf( wps->file, "%%%%Page: 1 1\n" );
fprintf( wps->file, "gsave\n" );
fprintf( wps->file, "0 0 moveto\n" );
fprintf( wps->file, "%g 0 lineto\n", wps->maxX );
fprintf( wps->file, "%g %g lineto\n", wps->maxX, wps->maxY );
fprintf( wps->file, "0 %g lineto\n", wps->maxY );
fprintf( wps->file, "closepath\n" );
fprintf( wps->file, "clip\n" );
fprintf( wps->file, "gsave newpath\n" );
fprintf( wps->file, "1 setlinejoin\n" );
fprintf( wps->file, "1 setlinecap\n" );
fprintf( wps->file, "/M {moveto} bind def\n" );
fprintf( wps->file, "/L {lineto} bind def\n" );
fprintf( wps->file, "/W {setlinewidth} bind def\n" );
fprintf( wps->file, "/F {fill} bind def\n" );
fprintf( wps->file, "/S {stroke} bind def\n" );
fprintf( wps->file, "/CP {closepath} bind def\n" );
fprintf( wps->file, "/RGB {setrgbcolor} bind def\n" );
fprintf( wps->file, "/Gr {setgray} bind def\n" );
fprintf( wps->file, "\n" );
return;
}
void
147 wheelWidth( wheelPS *wps, double width ) {
fprintf( wps->file, "%g W\n", width );
return;
}
void
154 wheelGray( wheelPS *wps, double gray ) {
fprintf( wps->file, "%g Gr\n", gray );
return;
}
void
161 wheelLabel( wheelPS *wps, double x, double y, char *str ) {
fprintf( wps->file, "%g %g M ( %s ) show\n", WPS_X( x ), WPS_Y( y ), str );
return;
}
void
168 wheelGraph( wheelPS *wps, double a, double d, double f ) {
double A, B, C;
int xi;
double x, y;
A = -a - d - f;
B = a*d + a*f + d*f;
C = -a*d*f;
for ( xi=0; xi<=99; xi++ ) {
x = AIR_AFFINE( 0, xi, 99, wps->bbox[0], wps->bbox[2] );
y = ( ( ( x + A )*x + B )*x + C )/2;
fprintf( wps->file, "%g %g %s\n", WPS_X( x ), WPS_Y( wps->yscale*y ),
xi ? "L" : "M" );
}
fprintf( wps->file, "S\n" );
return;
}
void
187 wheelLine( wheelPS *wps, double x0, double y0, double x1, double y1 ) {
fprintf( wps->file, "%g %g M\n", WPS_X( x0 ), WPS_Y( y0 ) );
fprintf( wps->file, "%g %g L S\n", WPS_X( x1 ), WPS_Y( y1 ) );
return;
}
void
195 wheelCircle( wheelPS *wps, double xc, double yc, double rad ) {
fprintf( wps->file, "%g %g %g 0 360 arc closepath S\n",
WPS_X( xc ), WPS_Y( yc ), WPS_S( rad ) );
return;
}
void
203 wheelArc( wheelPS *wps, double xc, double yc, double rad,
double angle1, double angle2 ) {
fprintf( wps->file, "newpath %g %g %g %g %g arc S\n",
WPS_X( xc ), WPS_Y( yc ), WPS_S( rad ), angle1, angle2 );
}
void
211 wheelDot( wheelPS *wps, double x, double y, double rad ) {
fprintf( wps->file, "%g %g %g 0 360 arc closepath F S\n",
WPS_X( x ), WPS_Y( y ), WPS_S( rad ) );
return;
}
void
219 wheelEpilog( wheelPS *wps ) {
fprintf( wps->file, "grestore\n" );
fprintf( wps->file, "grestore\n" );
fprintf( wps->file, "%%%%Trailer\n" );
return;
}
int
228 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
double tval[6], ABC[3], geom[3], rnth[3], RA, norm, mu2, tmpr=0, tmpa=0,
xroot[3], yroot[3], bbox[4], htick, htth, psc;
wheelPS wps;
int correct, labels, drawRA;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "t", "a b c d e f", airTypeDouble, 6, 6, tval,
"nan nan nan nan nan nan nan",
"six values of symmetric tensors" );
hestOptAdd( &hopt, "ABC", "A B C", airTypeDouble, 3, 3, ABC, "nan nan nan",
"directly give coefficients of cubic polynomial "
"( and override info from \"-t\" )" );
hestOptAdd( &hopt, "g", "c rad th", airTypeDouble, 3, 3, geom, "nan nan nan",
"directly give center, radius, and angle ( in degrees ) of wheel "
"( and override info from \"-t\" and \"-ABC\"" );
hestOptAdd( &hopt, "p", "RA norm th", airTypeDouble, 3, 3, rnth,
"nan nan nan",
"directly give RA, norm, and angle ( in degrees ) of tensor "
"( and override info from \"-t\", \"-ABC\", and \"-geom\"" );
hestOptAdd( &hopt, "correct", NULL, airTypeInt, 0, 0, &correct, NULL,
"when using \"-g\", be honest about what the estimated "
"acos( sqrt( 2 )*skew )/3 is going to be" );
hestOptAdd( &hopt, "labels", NULL, airTypeInt, 0, 0, &labels, NULL,
"put little labels on things; fix with psfrag in LaTeX" );
hestOptAdd( &hopt, "RA", NULL, airTypeInt, 0, 0, &drawRA, NULL,
"draw extra geometry associated with RA" );
hestOptAdd( &hopt, "htick", "pos", airTypeDouble, 1, 1, &htick, "nan",
"location of single tick mark on horizontal axis" );
hestOptAdd( &hopt, "htth", "thick", airTypeDouble, 1, 1, &htth, "3",
"thickness of horizontal tick" );
hestOptAdd( &hopt, "bb", "bbox", airTypeDouble, 4, 4, bbox,
"nan nan nan nan", "bounding box, in world space around the "
"region of the graph that should be drawn to EPS" );
hestOptAdd( &hopt, "ysc", "scale", airTypeDouble, 1, 1, &( wps.yscale ), "0.5",
"scaling on Y axis for drawing graph of characteristic "
"polynomial, or \"0\" to turn this off." );
hestOptAdd( &hopt, "psc", "scale", airTypeDouble, 1, 1, &psc, "100",
"scaling from world space to PostScript points" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write EPS output to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, wheelInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( wps.file = airFopen( outS, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't open output file\n", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, wps.file, ( airMopper )airFclose, airMopAlways );
if ( AIR_EXISTS( rnth[0] ) ) {
RA = rnth[0];
norm = rnth[1];
mu2 = ( norm*norm/3 )*( 2*RA*RA/( 1 + 2*RA*RA ) );
geom[0] = sqrt( mu2 )/( sqrt( 2 )*RA );
geom[1] = sqrt( 2*mu2 );
geom[2] = rnth[2];
wheelGeomToRoot( xroot, yroot, geom );
wheelGeomToABC( ABC, geom[0], geom[1], geom[2] );
} else if ( AIR_EXISTS( geom[0] ) ) {
wheelGeomToRoot( xroot, yroot, geom );
if ( correct ) {
tval[0] = xroot[0];
tval[1] = tval[2] = 0;
tval[3] = xroot[1];
tval[4] = 0;
tval[5] = xroot[2];
wheelTenToGeom( geom,
tval[0], tval[1], tval[2], tval[3], tval[4], tval[5] );
wheelGeomToRoot( xroot, yroot, geom );
}
wheelGeomToABC( ABC, geom[0], geom[1], geom[2] );
wheelGeomToRNTH( rnth, geom[0], geom[1], geom[2] );
} else if ( AIR_EXISTS( ABC[0] ) ) {
wheelABCToGeom( geom, ABC[0], ABC[1], ABC[2] );
wheelGeomToRNTH( rnth, geom[0], geom[1], geom[2] );
wheelGeomToRoot( xroot, yroot, geom );
} else {
wheelTenToGeom( geom, tval[0], tval[1], tval[2], tval[3], tval[4], tval[5] );
wheelGeomToRoot( xroot, yroot, geom );
wheelGeomToRNTH( rnth, geom[0], geom[1], geom[2] );
wheelGeomToABC( ABC, geom[0], geom[1], geom[2] );
}
fprintf( stderr, "%s: RNTH: %g %g %g\n", me, rnth[0], rnth[1], rnth[2] );
fprintf( stderr, "%s: ABC: %g %g %g\n", me, ABC[0], ABC[1], ABC[2] );
fprintf( stderr, "%s: xroot: %g %g %g\n",
me, xroot[0], xroot[1], xroot[2] );
fprintf( stderr, "%s: geom: %g %g %g\n", me, geom[0], geom[1], geom[2] );
if ( !AIR_EXISTS( bbox[0] ) ) {
bbox[0] = geom[0] - 1.2*geom[1];
bbox[1] = - 1.2*geom[1];
bbox[2] = geom[0] + 1.2*geom[1];
bbox[3] = + 1.2*geom[1];
fprintf( stderr, "%s: bbox %g %g %g %g\n", me,
bbox[0], bbox[1], bbox[2], bbox[3] );
}
wps.psc = psc;
ELL_4V_COPY( wps.bbox, bbox );
wheelPreamble( &wps );
/* graph */
if ( wps.yscale ) {
wheelWidth( &wps, 4 );
wheelGray( &wps, 0.5 );
wheelGraph( &wps, xroot[0], xroot[1], xroot[2] );
}
/* axis */
wheelWidth( &wps, 2 );
wheelGray( &wps, 0.0 );
wheelLine( &wps, bbox[0], 0, bbox[2], 0 );
/* circle */
wheelWidth( &wps, 3 );
wheelCircle( &wps, geom[0], 0, geom[1] );
/* spokes */
wheelWidth( &wps, 4 );
wheelLine( &wps, geom[0], 0, xroot[0], yroot[0] );
wheelLine( &wps, geom[0], 0, xroot[1], yroot[1] );
wheelLine( &wps, geom[0], 0, xroot[2], yroot[2] );
/* dots at spoke ends */
wheelDot( &wps, xroot[0], yroot[0], 0.025*geom[1] );
wheelDot( &wps, xroot[1], yroot[1], 0.025*geom[1] );
wheelDot( &wps, xroot[2], yroot[2], 0.025*geom[1] );
/* lines from dots to roots */
wheelWidth( &wps, 2 );
fprintf( wps.file, "gsave\n" );
fprintf( wps.file, "[2 4] 0 setdash\n" );
wheelLine( &wps, xroot[0], 0, xroot[0], yroot[0] );
wheelLine( &wps, xroot[1], 0, xroot[1], yroot[1] );
wheelLine( &wps, xroot[2], 0, xroot[2], yroot[2] );
fprintf( wps.file, "grestore\n" );
/* tickmarks */
wheelWidth( &wps, 6 );
wheelLine( &wps, xroot[0], -0.02*geom[1], xroot[0], 0.02*geom[1] );
wheelLine( &wps, xroot[1], -0.02*geom[1], xroot[1], 0.02*geom[1] );
wheelLine( &wps, xroot[2], -0.02*geom[1], xroot[2], 0.02*geom[1] );
if ( AIR_EXISTS( htick ) ) {
wheelWidth( &wps, htth );
wheelLine( &wps, htick, -0.04, htick, 0.04 );
}
/* RA angle */
if ( drawRA ) {
wheelWidth( &wps, 3 );
wheelLine( &wps, 0.0, 0.0, geom[0], geom[1] );
wheelWidth( &wps, 2 );
fprintf( wps.file, "gsave\n" );
fprintf( wps.file, "[2 4] 0 setdash\n" );
wheelLine( &wps, geom[0], geom[1], geom[0], 0 );
fprintf( wps.file, "grestore\n" );
}
/* labels, if wanted */
if ( labels ) {
fprintf( wps.file, "/Helvetica findfont 20 scalefont setfont\n" );
wheelLabel( &wps, geom[0], 0, "center" );
wheelLine( &wps, geom[0], -0.02*geom[1], geom[0], 0.02*geom[1] );
wheelLabel( &wps, ( geom[0] + xroot[0] )/1.8, yroot[0]/1.8, "radius" );
wheelWidth( &wps, 2 );
wheelArc( &wps, geom[0], 0, geom[1]/2, 0, geom[2] );
wheelLabel( &wps, geom[0] + geom[1]*cos( AIR_PI*geom[2]/180/2 )/2.5,
geom[1]*sin( AIR_PI*geom[2]/180/2 )/2.5, "theta" );
if ( drawRA ) {
tmpr = sqrt( geom[0]*geom[0] + geom[1]*geom[1] );
tmpa = atan( 2.0*rnth[0] );
wheelWidth( &wps, 2 );
wheelArc( &wps, 0, 0, 0.2*tmpr, 0, 180*tmpa/AIR_PI );
wheelLabel( &wps, 0.2*tmpr*cos( tmpa/2 ), 0.2*tmpr*sin( tmpa/2 ), "phi" );
}
wheelLabel( &wps, xroot[0], yroot[0], "spoke0" );
wheelLabel( &wps, xroot[1], yroot[1], "spoke-" );
wheelLabel( &wps, xroot[2], yroot[2], "spoke+" );
wheelLabel( &wps, xroot[0], 0, "root0" );
wheelLabel( &wps, xroot[1], 0, "root-" );
wheelLabel( &wps, xroot[2], 0, "root+" );
}
wheelEpilog( &wps );
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ell.h"
void
28 ell_4v_norm_f( float bv[4], const float av[4] ) {
float len;
len = AIR_CAST( float, ELL_4V_LEN( av ) );
ELL_4V_SCALE( bv, 1.0f/len, av );
return;
}
#define PERP \
idx = 0; \
if ( b[0]*b[0] < b[1]*b[1] ) \
idx = 1; \
if ( b[idx]*b[idx] < b[2]*b[2] ) \
idx = 2; \
switch ( idx ) { \
case 0: \
ELL_3V_SET( a, b[1] - b[2], -b[0], b[0] ); \
break; \
case 1: \
47 ELL_3V_SET( a, -b[1], b[0] - b[2], b[1] ); \
break; \
case 2: \
ELL_3V_SET( a, -b[2], b[2], b[0] - b[1] ); \
break; \
}
/*
******** ell_3v_perp_f( )
**
** Given a 3-vector, produce one which is perpendicular.
58 ** Output length won't be same as input length, but it will always
** be non-zero, if input length is non-zero. This does NOT try to
** produce a unit-length vector, regardless of the length of the input.
*/
void
ell_3v_perp_f( float a[3], const float b[3] ) {
64 int idx;
PERP;
}
/*
******** ell_3v_perp_d( )
**
** same as above, but for doubles
72 */
void
ell_3v_perp_d( double a[3], const double b[3] ) {
int idx;
PERP;
}
void
80 ell_3mv_mul_f( float v2[3], const float m[9], const float v1[3] ) {
float tmp[3];
ELL_3MV_MUL( tmp, m, v1 );
ELL_3V_COPY( v2, tmp );
}
void
88 ell_3mv_mul_d( double v2[3], const double m[9], const double v1[3] ) {
double tmp[3];
ELL_3MV_MUL( tmp, m, v1 );
ELL_3V_COPY( v2, tmp );
}
void
ell_4mv_mul_f( float v2[4], const float m[16], const float v1[4] ) {
float tmp[4];
99 ELL_4MV_MUL( tmp, m, v1 );
ELL_4V_COPY( v2, tmp );
}
void
ell_4mv_mul_d( double v2[4], const double m[16], const double v1[4] ) {
double tmp[4];
ELL_4MV_MUL( tmp, m, v1 );
ELL_4V_COPY( v2, tmp );
}
/*
** hat tip to http://www.plunk.org/~hatch/rightway.php
*/
float
ell_3v_angle_f( const float _uu[3], const float _vv[3] ) {
116 float tmp[3], len, uu[3], vv[3], ret;
ELL_3V_NORM_TT( uu, float, _uu, len );
ELL_3V_NORM_TT( vv, float, _vv, len );
if ( ELL_3V_DOT( uu, vv ) < 0.0 ) {
ELL_3V_ADD2( tmp, uu, vv );
ret = AIR_CAST( float, AIR_PI - 2*asin( ELL_3V_LEN( tmp )/2.0 ) );
} else {
ELL_3V_SUB( tmp, uu, vv );
ret = AIR_CAST( float, 2*asin( ELL_3V_LEN( tmp )/2.0 ) );
}
return ret;
}
/* HEY: copy and paste */
double
ell_3v_angle_d( const double _uu[3], const double _vv[3] ) {
133 double tmp[3], len, uu[3], vv[3], ret;
ELL_3V_NORM( uu, _uu, len );
ELL_3V_NORM( vv, _vv, len );
if ( ELL_3V_DOT( uu, vv ) < 0.0 ) {
ELL_3V_ADD2( tmp, uu, vv );
ret = AIR_PI - 2*asin( ELL_3V_LEN( tmp )/2.0 );
} else {
ELL_3V_SUB( tmp, uu, vv );
ret = 2*asin( ELL_3V_LEN( tmp )/2.0 );
}
return ret;
}
/* HEY: copy and paste */
float
ell_2v_angle_f( const float _uu[2], const float _vv[2] ) {
150 float tmp[2], len, uu[2], vv[2], ret;
ELL_2V_NORM_TT( uu, float, _uu, len );
ELL_2V_NORM_TT( vv, float, _vv, len );
if ( ELL_2V_DOT( uu, vv ) < 0.0 ) {
ELL_2V_ADD2( tmp, uu, vv );
ret = AIR_CAST( float, AIR_PI - 2*asin( ELL_2V_LEN( tmp )/2.0 ) );
} else {
ELL_2V_SUB( tmp, uu, vv );
ret = AIR_CAST( float, 2*asin( ELL_2V_LEN( tmp )/2.0 ) );
}
return ret;
}
/* HEY: copy and paste */
double
ell_2v_angle_d( const double _uu[2], const double _vv[2] ) {
double tmp[2], len, uu[2], vv[2], ret;
169 ELL_2V_NORM( uu, _uu, len );
ELL_2V_NORM( vv, _vv, len );
if ( ELL_2V_DOT( uu, vv ) < 0.0 ) {
ELL_2V_ADD2( tmp, uu, vv );
ret = AIR_PI - 2*asin( ELL_2V_LEN( tmp )/2.0 );
} else {
ELL_2V_SUB( tmp, uu, vv );
ret = 2*asin( ELL_2V_LEN( tmp )/2.0 );
}
return ret;
}
/*
** input vectors have to be normalized!
*/
double
ell_3v_area_spherical_d( const double avec[3],
const double bvec[3],
const double cvec[3] ) {
double axb[3], bxc[3], cxa[3], A, B, C, tmp;
190 ELL_3V_CROSS( axb, avec, bvec );
ELL_3V_CROSS( bxc, bvec, cvec );
ELL_3V_CROSS( cxa, cvec, avec );
ELL_3V_NORM( axb, axb, tmp );
ELL_3V_NORM( bxc, bxc, tmp );
ELL_3V_NORM( cxa, cxa, tmp );
A = AIR_PI - ell_3v_angle_d( axb, cxa );
B = AIR_PI - ell_3v_angle_d( bxc, axb );
C = AIR_PI - ell_3v_angle_d( cxa, bxc );
return A + B + C - AIR_PI;
}
/*
** all input vectors {a, b, c}vec, dir must be normalized
*/
void
ell_3v_barycentric_spherical_d( double bary[3],
const double av[3],
const double bv[3],
const double cv[3],
const double vv[3] ) {
double sum;
bary[0] = ell_3v_area_spherical_d( vv, bv, cv );
bary[1] = ell_3v_area_spherical_d( vv, cv, av );
bary[2] = ell_3v_area_spherical_d( vv, av, bv );
sum = bary[0] + bary[1] + bary[2];
if ( sum ) {
ELL_3V_SCALE( bary, 1.0/sum, bary );
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
typedef union {
void **vd;
gagePerVolume ***pvl;
} perVolumeUnion;
/*
******** gageContextNew( )
**
** doesn't use biff
*/
gageContext *
39 gageContextNew( ) {
gageContext *ctx;
perVolumeUnion pvu;
int ii;
ctx = AIR_CALLOC( 1, gageContext );
if ( ctx ) {
ctx->verbose = gageDefVerbose;
gageParmReset( &ctx->parm );
for ( ii=gageKernelUnknown+1; ii<gageKernelLast; ii++ ) {
ctx->ksp[ii] = NULL;
}
ctx->pvl = NULL;
ctx->pvlNum = 0;
pvu.pvl = &( ctx->pvl );
ctx->pvlArr = airArrayNew( pvu.vd, &( ctx->pvlNum ),
sizeof( gagePerVolume* ),
GAGE_PERVOLUME_ARR_INCR );
gageKernelReset( ctx ); /* placed here for logic of kernel flag */
ctx->shape = gageShapeNew( );
for ( ii=gageCtxFlagUnknown+1; ii<gageCtxFlagLast; ii++ ) {
ctx->flag[ii] = AIR_FALSE;
}
ctx->stackPos = NULL;
ctx->stackFsl = NULL;
ctx->stackFw = NULL;
for ( ii=0; ii<=GAGE_DERIV_MAX; ii++ ) {
ctx->needD[ii] = AIR_FALSE;
}
for ( ii=gageKernelUnknown+1; ii<gageKernelLast; ii++ ) {
ctx->needK[ii] = AIR_FALSE;
}
ctx->radius = 0;
ctx->fsl = ctx->fw = NULL;
ctx->off = NULL;
gagePointReset( &ctx->point );
strcpy( ctx->errStr, "" );
ctx->errNum = gageErrNone;
ctx->edgeFrac = 0;
}
return ctx;
}
/*
******** gageContextCopy( )
**
** gives you a new context, which behaves the same as the given context,
** with newly allocated pervolumes attached. With the avoidance of
** padding to create a private copy of the volume, the gageContext is
** light-weight enough that there is no reason that this function can't
** return an independent and fully functioning copy of the context ( whereas
** before you weren't allowed to do anything but gageProbe( ) on the
** copied context ).
*/
gageContext * /*Teem: biff if ( !ret ) */
94 gageContextCopy( gageContext *ctx ) {
static const char me[]="gageContextCopy";
gageContext *ntx;
unsigned int fd, pvlIdx;
perVolumeUnion pvu;
int ki;
ntx = AIR_CALLOC( 1, gageContext );
if ( !ntx ) {
biffAddf( GAGE, "%s: couldn't make a gageContext", me );
return NULL;
}
/* we should probably restrict ourselves to gage API calls, but given the
constant state of gage construction, this seems much simpler.
Pointers are fixed below */
memcpy( ntx, ctx, sizeof( gageContext ) );
for ( ki=gageKernelUnknown+1; ki<gageKernelLast; ki++ ) {
ntx->ksp[ki] = nrrdKernelSpecCopy( ctx->ksp[ki] );
}
pvu.pvl = &( ntx->pvl );
ntx->pvlArr = airArrayNew( pvu.vd, &( ntx->pvlNum ),
sizeof( gagePerVolume* ),
GAGE_PERVOLUME_ARR_INCR );
airArrayLenSet( ntx->pvlArr, ctx->pvlNum );
if ( !ntx->pvl ) {
biffAddf( GAGE, "%s: couldn't allocate new pvl array", me );
return NULL;
}
for ( pvlIdx=0; pvlIdx<ntx->pvlNum; pvlIdx++ ) {
ntx->pvl[pvlIdx] = _gagePerVolumeCopy( ctx->pvl[pvlIdx], 2*ctx->radius );
if ( !ntx->pvl[pvlIdx] ) {
biffAddf( GAGE, "%s: trouble copying pervolume %u", me, pvlIdx );
return NULL;
}
}
if ( ctx->stackPos && ctx->stackFsl && ctx->stackFw ) {
ntx->stackPos = AIR_CALLOC( ctx->pvlNum-1, double );
ntx->stackFsl = AIR_CALLOC( ctx->pvlNum-1, double );
ntx->stackFw = AIR_CALLOC( ctx->pvlNum-1, double );
if ( !( ntx->stackPos && ntx->stackFsl && ntx->stackFw ) ) {
biffAddf( GAGE, "%s: couldn't allocate stack Pos, Fsl, Fw", me );
return NULL;
}
for ( pvlIdx=0; pvlIdx<ntx->pvlNum-1; pvlIdx++ ) {
ntx->stackPos[pvlIdx] = ctx->stackPos[pvlIdx];
ntx->stackFsl[pvlIdx] = ctx->stackFsl[pvlIdx];
ntx->stackFw[pvlIdx] = ctx->stackFw[pvlIdx];
}
} else {
ntx->stackPos = NULL;
ntx->stackFsl = NULL;
ntx->stackFw = NULL;
}
ntx->shape = gageShapeCopy( ctx->shape );
fd = 2*ntx->radius;
ntx->fsl = AIR_CALLOC( fd*3, double );
ntx->fw = AIR_CALLOC( fd*3*( GAGE_KERNEL_MAX+1 ), double );
ntx->off = AIR_CALLOC( fd*fd*fd, unsigned int );
if ( !( ntx->fsl && ntx->fw && ntx->off ) ) {
biffAddf( GAGE, "%s: couldn't allocate new filter caches for fd=%d",
me, fd );
return NULL;
}
/* the content of the offset array needs to be copied because
it won't be refilled simply by calls to gageProbe( ) */
memcpy( ntx->off, ctx->off, fd*fd*fd*sizeof( unsigned int ) );
/* make sure gageProbe( ) has to refill caches */
gagePointReset( &ntx->point );
return ntx;
}
/*
******** gageContextNix( )
**
** responsible for freeing and clearing up everything hanging off a
** context so that things can be returned to the way they were prior
** to gageContextNew( ).
**
** does not use biff
*/
gageContext *
177 gageContextNix( gageContext *ctx ) {
/* static const char me[]="gageContextNix"; */
unsigned int pvlIdx;
if ( ctx ) {
gageKernelReset( ctx );
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
gagePerVolumeNix( ctx->pvl[pvlIdx] );
/* no point in doing a detach, the whole context is going bye-bye */
}
airArrayNuke( ctx->pvlArr );
ctx->shape = gageShapeNix( ctx->shape );
ctx->stackPos = AIR_CAST( double *, airFree( ctx->stackPos ) );
ctx->stackFsl = AIR_CAST( double *, airFree( ctx->stackFsl ) );
ctx->stackFw = AIR_CAST( double *, airFree( ctx->stackFw ) );
ctx->fw = AIR_CAST( double *, airFree( ctx->fw ) );
ctx->fsl = AIR_CAST( double *, airFree( ctx->fsl ) );
ctx->off = AIR_CAST( unsigned int *, airFree( ctx->off ) );
}
airFree( ctx );
return NULL;
}
/*
******** gageKernelSet( )
**
** sets one kernel in a gageContext; but the value of this function
** is all the error checking it does.
**
** Refers to ctx->checkIntegrals and acts appropriately.
**
** Does use biff.
*/
int
211 gageKernelSet( gageContext *ctx,
int which, const NrrdKernel *k, const double *kparm ) {
static const char me[]="gageKernelSet";
int numParm;
double support, integral;
if ( !( ctx && k && kparm ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( gageKernel, which ) ) {
biffAddf( GAGE, "%s: \"which\" ( %d ) not in range [%d, %d]", me,
which, gageKernelUnknown+1, gageKernelLast-1 );
return 1;
}
if ( ctx->verbose ) {
fprintf( stderr, "%s: which = %d -> %s\n", me, which,
airEnumStr( gageKernel, which ) );
}
numParm = k->numParm;
if ( !( AIR_IN_CL( 0, numParm, NRRD_KERNEL_PARMS_NUM ) ) ) {
biffAddf( GAGE, "%s: kernel's numParm ( %d ) not in range [%d, %d]",
me, numParm, 0, NRRD_KERNEL_PARMS_NUM );
return 1;
}
support = k->support( kparm );
if ( !( support > 0 ) ) {
biffAddf( GAGE, "%s: kernel's support ( %g ) not > 0", me, support );
return 1;
}
if ( ctx->parm.checkIntegrals ) {
integral = k->integral( kparm );
if ( gageKernel00 == which ||
gageKernel10 == which ||
gageKernel20 == which ||
gageKernelStack == which ) {
if ( !( integral > 0 ) ) {
biffAddf( GAGE, "%s: reconstruction kernel's integral ( %g ) not > 0.0",
me, integral );
return 1;
}
} else {
/* its a derivative, so integral must be near zero */
if ( !( AIR_ABS( integral ) <= ctx->parm.kernelIntegralNearZero ) ) {
char str[AIR_STRLEN_LARGE]="";
nrrdKernelSprint( str, k, kparm );
biffAddf( GAGE, "%s: derivative %s kernel ( %s ) integral %g not within "
"%g of 0.0", me, airEnumStr( gageKernel, which ), str,
integral, ctx->parm.kernelIntegralNearZero );
return 1;
}
}
}
/* okay, enough enough, go set the kernel */
if ( !ctx->ksp[which] ) {
ctx->ksp[which] = nrrdKernelSpecNew( );
}
nrrdKernelSpecSet( ctx->ksp[which], k, kparm );
ctx->flag[gageCtxFlagKernel] = AIR_TRUE;
return 0;
}
/*
******** gageKernelReset( )
**
** reset kernels ( including the stack ksp ) and parameters.
*/
void
281 gageKernelReset( gageContext *ctx ) {
/* static const char me[]="gageKernelReset"; */
int i;
if ( ctx ) {
for( i=gageKernelUnknown+1; i<gageKernelLast; i++ ) {
ctx->ksp[i] = nrrdKernelSpecNix( ctx->ksp[i] );
}
ctx->flag[gageCtxFlagKernel] = AIR_TRUE;
}
return;
}
/*
******** gageParmSet( )
**
** for setting the boolean-ish flags in the context in a safe and
** intelligent manner, since changing some of them can have many
** consequences
*/
void
302 gageParmSet( gageContext *ctx, int which, double val ) {
static const char me[]="gageParmSet";
unsigned int pvlIdx;
switch ( which ) {
case gageParmVerbose:
ctx->verbose = AIR_CAST( int, val );
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s( %p ): ctx->verbose now %d\n", me,
AIR_CAST( void *, ctx ), ctx->verbose );
}
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
ctx->pvl[pvlIdx]->verbose = AIR_CAST( int, val );
if ( ctx->pvl[pvlIdx]->verbose > 3 ) {
fprintf( stderr, "%s: ctx->pvl[%u]->verbose now %d\n", me, pvlIdx,
ctx->pvl[pvlIdx]->verbose );
}
}
break;
case gageParmRenormalize:
ctx->parm.renormalize = val ? AIR_TRUE : AIR_FALSE;
/* we have to make sure that any existing filter weights
are not re-used; because gageUpdage( ) is not called mid-probing,
we don't use the flag machinery. Instead we just invalidate
the last known fractional probe locations */
gagePointReset( &ctx->point );
break;
case gageParmCheckIntegrals:
ctx->parm.checkIntegrals = val ? AIR_TRUE : AIR_FALSE;
/* no flags to set, simply affects future calls to gageKernelSet( ) */
break;
case gageParmK3Pack:
ctx->parm.k3pack = val ? AIR_TRUE : AIR_FALSE;
ctx->flag[gageCtxFlagK3Pack] = AIR_TRUE;
break;
case gageParmGradMagCurvMin:
ctx->parm.gradMagCurvMin = val;
/* no flag to set, simply affects future calls to gageProbe( ) */
break;
case gageParmCurvNormalSide:
ctx->parm.curvNormalSide = AIR_CAST( int, val );
/* no flag to set, simply affects future calls to gageProbe( ) */
break;
case gageParmKernelIntegralNearZero:
ctx->parm.kernelIntegralNearZero = val;
/* no flag to set, simply affects future calls to gageKernelSet( ) */
break;
case gageParmDefaultCenter:
ctx->parm.defaultCenter = AIR_CAST( int, val );
/* no flag to set, I guess, although the value here effects the
action of _gageShapeSet when called by gagePerVolumeAttach . . . */
break;
case gageParmStackUse:
ctx->parm.stackUse = AIR_CAST( int, val );
/* no flag to set, right? simply affects future calls to gageProbe( )? */
/* HEY: no? because if you're turning on the stack behavior, you now
should be doing the error checking to make sure that all the pvls
have the same kind! This should be caught by gageUpdate( ), which is
supposed to be called after changing anything, prior to gageProbe( ) */
break;
case gageParmStackNormalizeRecon:
ctx->parm.stackNormalizeRecon = AIR_CAST( int, val );
break;
case gageParmStackNormalizeDeriv:
ctx->parm.stackNormalizeDeriv = AIR_CAST( int, val );
break;
case gageParmStackNormalizeDerivBias:
ctx->parm.stackNormalizeDerivBias = val;
break;
case gageParmOrientationFromSpacing:
ctx->parm.orientationFromSpacing = AIR_CAST( int, val );
/* affects future calls to _gageShapeSet */
break;
case gageParmGenerateErrStr:
ctx->parm.generateErrStr = AIR_CAST( int, val );
break;
case gageParmTwoDimZeroZ:
ctx->parm.twoDimZeroZ = AIR_CAST( int, val );
break;
default:
fprintf( stderr, "\n%s: sorry, which = %d not valid\n\n", me, which );
break;
}
return;
}
/*
******** gagePerVolumeIsAttached( )
**
*/
int
393 gagePerVolumeIsAttached( const gageContext *ctx, const gagePerVolume *pvl ) {
int ret;
unsigned int pvlIdx;
ret = AIR_FALSE;
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
if ( pvl == ctx->pvl[pvlIdx] ) {
ret = AIR_TRUE;
}
}
return ret;
}
/*
******** gagePerVolumeAttach( )
**
** attaches a pervolume to a context, which actually involves
** very little work
*/
int
413 gagePerVolumeAttach( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="gagePerVolumeAttach";
gageShape *shape;
unsigned int newidx;
if ( !( ctx && pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( gagePerVolumeIsAttached( ctx, pvl ) ) {
biffAddf( GAGE, "%s: given pervolume already attached", me );
return 1;
}
if ( 0 == ctx->pvlNum ) {
/* the volume "shape" is context state that we set now, because unlike
everything else ( handled by gageUpdate( ) ), it does not effect
the kind or amount of padding done */
if ( _gageShapeSet( ctx, ctx->shape, pvl->nin, pvl->kind->baseDim ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
ctx->flag[gageCtxFlagShape] = AIR_TRUE;
} else {
/* have to check to that new pvl matches first one. Since all
attached pvls were at some point the "new" one, they all
should match each other */
shape = gageShapeNew( );
if ( _gageShapeSet( ctx, shape, pvl->nin, pvl->kind->baseDim ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
if ( !gageShapeEqual( ctx->shape, "existing context", shape, "new volume" ) ) {
biffAddf( GAGE, "%s: trouble", me );
gageShapeNix( shape ); return 1;
}
gageShapeNix( shape );
}
/* here we go */
newidx = airArrayLenIncr( ctx->pvlArr, 1 );
if ( !ctx->pvl ) {
biffAddf( GAGE, "%s: couldn't increase length of pvl", me );
return 1;
}
ctx->pvl[newidx] = pvl;
pvl->verbose = ctx->verbose;
return 0;
}
/*
******** gagePerVolumeDetach( )
**
** detaches a pervolume from a context, but does nothing else
** with the pervolume; caller may want to call gagePerVolumeNix
** if this pervolume will no longer be used
*/
int
471 gagePerVolumeDetach( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="gagePerVolumeDetach";
unsigned int pvlIdx, foundIdx=0;
if ( !( ctx && pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !gagePerVolumeIsAttached( ctx, pvl ) ) {
biffAddf( GAGE, "%s: given pervolume not currently attached", me );
return 1;
}
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
if ( pvl == ctx->pvl[pvlIdx] ) {
foundIdx = pvlIdx;
}
}
for ( pvlIdx=foundIdx+1; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
ctx->pvl[pvlIdx-1] = ctx->pvl[pvlIdx];
}
ctx->pvl[ctx->pvlNum-1] = NULL;
airArrayLenIncr( ctx->pvlArr, -1 );
if ( 0 == ctx->pvlNum ) {
/* leave things the way that they started */
gageShapeReset( ctx->shape );
ctx->flag[gageCtxFlagShape] = AIR_TRUE;
}
return 0;
}
/*
** gageIv3Fill( )
**
** based on ctx's shape and radius, and the ( xi, yi, zi ) determined from
** the probe location, fills the iv3 cache in the given pervolume
**
** This function is a bottleneck for so many things, so forcing off
** the verbose comments seems like one way of trying to speed it up.
** However, temporarily if-def'ing out unused branches of the
** "switch( pvl->kind->valLen )", and #define'ing fddd to 64 ( for 4x4x4
** neighborhoods ) did not noticeably speed anything up.
**
*/
void
515 gageIv3Fill( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="gageIv3Fill";
int lx, ly, lz, hx, hy, hz, _xx, _yy, _zz;
unsigned int xx, yy, zz,
fr, cacheIdx, dataIdx, fddd;
unsigned int sx, sy, sz;
char *data, *here;
unsigned int tup;
sx = ctx->shape->size[0];
sy = ctx->shape->size[1];
sz = ctx->shape->size[2];
fr = ctx->radius;
/* idx[0]-1: see Thu Jan 14 comment in filter.c */
lx = ctx->point.idx[0]-1 - ( fr - 1 );
ly = ctx->point.idx[1]-1 - ( fr - 1 );
lz = ctx->point.idx[2]-1 - ( fr - 1 );
hx = lx + 2*fr - 1;
hy = ly + 2*fr - 1;
hz = lz + 2*fr - 1;
fddd = 2*fr*2*fr*2*fr;
if ( ctx->verbose > 1 ) {
fprintf( stderr, "%s: ___ hello; s %u %u %u; fr %u\n", me,
sx, sy, sz, fr );
fprintf( stderr, "%s: point.idx %u %u %u\n", me,
ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2] );
fprintf( stderr, "%s: l %d %d %d; h %d %d %d; fddd %u\n", me,
lx, ly, lz, hx, hy, hz, fddd );
}
data = ( char* )pvl->nin->data;
if ( lx >= 0 && ly >= 0 && lz >= 0
&& hx < AIR_CAST( int, sx )
&& hy < AIR_CAST( int, sy )
&& hz < AIR_CAST( int, sz ) ) {
/* all the samples we need are inside the existing volume */
dataIdx = lx + sx*( ly + sy*( lz ) );
if ( ctx->verbose > 1 ) {
fprintf( stderr, "%s: hello, valLen = %d, pvl->nin = %p, data = %p\n",
me, pvl->kind->valLen,
AIR_CVOIDP( pvl->nin ), pvl->nin->data );
}
here = data + dataIdx*pvl->kind->valLen*nrrdTypeSize[pvl->nin->type];
if ( ctx->verbose > 1 ) {
fprintf( stderr, "%s: size = ( %u, %u, %u );\n"
"%s: fd = %d; coord = ( %u, %u, %u ) --> dataIdx = %d\n",
me, sx, sy, sz, me, 2*fr,
ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2],
dataIdx );
fprintf( stderr, "%s: here = %p; iv3 = %p; off[0, 1, 2, 3, 4, 5, 6, 7] = "
"%d, %d, %d, %d, %d, %d, %d, %d\n",
me, here, AIR_CAST( void*, pvl->iv3 ),
ctx->off[0], ctx->off[1], ctx->off[2], ctx->off[3],
ctx->off[4], ctx->off[5], ctx->off[6], ctx->off[7] );
}
switch( pvl->kind->valLen ) {
case 1:
for ( cacheIdx=0; cacheIdx<fddd; cacheIdx++ ) {
pvl->iv3[cacheIdx] = pvl->lup( here, ctx->off[cacheIdx] );
}
break;
/* NOTE: the tuple axis is being shifted from the fastest to
the slowest axis, to anticipate component-wise filtering
operations */
case 3:
for ( cacheIdx=0; cacheIdx<fddd; cacheIdx++ ) {
pvl->iv3[cacheIdx + fddd*0] = pvl->lup( here, 0 + 3*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*1] = pvl->lup( here, 1 + 3*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*2] = pvl->lup( here, 2 + 3*ctx->off[cacheIdx] );
}
break;
case 7:
/* this might come in handy for tenGage . . . */
for ( cacheIdx=0; cacheIdx<fddd; cacheIdx++ ) {
pvl->iv3[cacheIdx + fddd*0] = pvl->lup( here, 0 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*1] = pvl->lup( here, 1 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*2] = pvl->lup( here, 2 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*3] = pvl->lup( here, 3 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*4] = pvl->lup( here, 4 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*5] = pvl->lup( here, 5 + 7*ctx->off[cacheIdx] );
pvl->iv3[cacheIdx + fddd*6] = pvl->lup( here, 6 + 7*ctx->off[cacheIdx] );
}
break;
default:
for ( cacheIdx=0; cacheIdx<fddd; cacheIdx++ ) {
for ( tup=0; tup<pvl->kind->valLen; tup++ ) {
pvl->iv3[cacheIdx + fddd*tup] =
pvl->lup( here, tup + pvl->kind->valLen*ctx->off[cacheIdx] );
}
}
break;
}
ctx->edgeFrac = 0;
} else {
unsigned int edgeNum, dataStride, valLen;
/* the query requires samples which don't actually lie
within the volume- more care has to be taken */
double *iv3;
cacheIdx = 0;
edgeNum = 0;
valLen = pvl->kind->valLen;
dataStride = AIR_UINT( valLen*nrrdTypeSize[pvl->nin->type] );
iv3 = pvl->iv3;
if ( 1 == sz ) {
/* working with 2D images is now common enough that we try to make
simplifications for that ( HEY copy and paste ). We first do the
needed lup( )s to fill first slice of iv3 ... */
for ( _yy=ly; _yy<=hy; _yy++ ) {
yy = AIR_CLAMP( 0, _yy, AIR_CAST( int, sy-1 ) );
for ( _xx=lx; _xx<=hx; _xx++ ) {
xx = AIR_CLAMP( 0, _xx, AIR_CAST( int, sx-1 ) );
edgeNum += ( ( 1 != sy && ( AIR_CAST( int, yy ) != _yy ) )
|| ( AIR_CAST( int, xx ) != _xx ) );
dataIdx = xx + sx*yy;
here = data+dataIdx*dataStride;
for ( tup=0; tup<valLen; tup++ ) {
iv3[cacheIdx + fddd*tup] = pvl->lup( here, tup );
}
cacheIdx++;
}
}
/* ... and then copy from first slice into rest of iv3 */
for ( _zz=lz+1; _zz<=hz; _zz++ ) {
unsigned int z0ci;
z0ci = 0;
for ( _yy=ly; _yy<=hy; _yy++ ) {
for ( _xx=lx; _xx<=hx; _xx++ ) {
for ( tup=0; tup<valLen; tup++ ) {
iv3[cacheIdx + fddd*tup] = iv3[z0ci + fddd*tup];
}
cacheIdx++;
z0ci++;
}
}
}
/* we would have been outside for all the other z slices besides
z=0, but don't report this if the whole point is to pretend
that we're working with 2D data */
if ( !( ctx->parm.twoDimZeroZ ) ) {
edgeNum += ( 2*fr - 1 )*2*fr*2*fr;
}
} else {
/* sz > 1 */
for ( _zz=lz; _zz<=hz; _zz++ ) {
zz = AIR_CLAMP( 0, _zz, AIR_CAST( int, sz-1 ) );
for ( _yy=ly; _yy<=hy; _yy++ ) {
yy = AIR_CLAMP( 0, _yy, AIR_CAST( int, sy-1 ) );
for ( _xx=lx; _xx<=hx; _xx++ ) {
xx = AIR_CLAMP( 0, _xx, AIR_CAST( int, sx-1 ) );
edgeNum += ( ( AIR_CAST( int, zz ) != _zz )
|| ( AIR_CAST( int, yy ) != _yy )
|| ( AIR_CAST( int, xx ) != _xx ) );
dataIdx = xx + sx*( yy + sy*zz );
here = data+dataIdx*pvl->kind->valLen*nrrdTypeSize[pvl->nin->type];
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: ( %d, %d, %d ) --clamp--> ( %u, %u, %u )\n", me,
_xx, _yy, _zz, xx, yy, zz );
fprintf( stderr, " --> dataIdx = %d; data = %p -> here = %p\n",
dataIdx, data, here );
}
for ( tup=0; tup<pvl->kind->valLen; tup++ ) {
iv3[cacheIdx + fddd*tup] = pvl->lup( here, tup );
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: iv3[%u + %u*%u=%u] = %g\n", me,
cacheIdx, fddd, tup, cacheIdx + fddd*tup,
iv3[cacheIdx + fddd*tup] );
}
}
cacheIdx++;
}
}
}
}
ctx->edgeFrac = AIR_CAST( double, edgeNum )/fddd;
}
if ( ctx->verbose > 1 ) {
fprintf( stderr, "%s: ^^^ bye\n", me );
}
return;
}
/*
** _gageProbe
**
** how to do probing. ( x, y, z ) position is *index space* position.
** Note, however, that derivatives ( gradients and hessians ) will
** effectively be computed in *world space*.
**
** doesn't actually do much more than call callbacks in the gageKind
** structs of the attached pervolumes
**
** NOTE: the stack filter weights are ( like the spatial filter weights )
** computed inside _gageLocationSet( )
*/
int
709 _gageProbe( gageContext *ctx, double _xi, double _yi, double _zi, double _si ) {
static const char me[]="_gageProbe";
unsigned int oldIdx[4], oldNnz=0, pvlIdx;
int idxChanged;
if ( !ctx ) {
return 1;
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: hello( %g, %g, %g, %g ) _____________ \n", me,
_xi, _yi, _zi, _si );
}
ELL_4V_COPY( oldIdx, ctx->point.idx );
oldNnz = ctx->point.stackFwNonZeroNum;
if ( _gageLocationSet( ctx, _xi, _yi, _zi, _si ) ) {
/* we're outside the volume; leave ctx->errNum and ctx->errStr set;
as they have just been set by _gageLocationSet( ) */
/* GLK had added but not checked in the following line;
the logic of this has to be studied further */
/* ctx->edgeFrac = 0.666; */
return 1;
}
/* if necessary, refill the iv3 cache */
idxChanged = ( oldIdx[0] != ctx->point.idx[0]
|| oldIdx[1] != ctx->point.idx[1]
|| oldIdx[2] != ctx->point.idx[2] );
if ( ctx->parm.stackUse ) {
idxChanged |= oldIdx[3] != ctx->point.idx[3];
/* this is subtle ( and the source of a difficult bug ): even if
point.idx[3] has not changed, you can still have a change in
which of the stackFw[] are non-zero, which in turn determines
which iv3s have to be refilled. For example, changing _si
from 0.0 to 0.1, using tent or hermite reconstruction, will
newly require pvl[1]'s iv3 to be refilled. To catch this kind
of situation, we could keep a list of which iv3s are active and
look for changes in that, or, we could just look for changes in
point.idx[3] AND changes in stackFwNonZeroNum */
idxChanged |= oldNnz != ctx->point.stackFwNonZeroNum;
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: oldIdx %u %u %u %u, point.idx %u %u %u %u --> %d\n",
me, oldIdx[0], oldIdx[1], oldIdx[2], oldIdx[3],
ctx->point.idx[0], ctx->point.idx[1],
ctx->point.idx[2], ctx->point.idx[3], idxChanged );
}
if ( idxChanged ) {
if ( !ctx->parm.stackUse ) {
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: gageIv3Fill( pvl[%u/%u] %s ): .......\n", me,
pvlIdx, ctx->pvlNum, ctx->pvl[pvlIdx]->kind->name );
}
gageIv3Fill( ctx, ctx->pvl[pvlIdx] );
}
} else {
for ( pvlIdx=0; pvlIdx<ctx->pvlNum-1; pvlIdx++ ) {
/* note that we only fill the cache for the stack samples that
have a non-zero weight. HEY, however, it would be nice to
only refill the iv3 that we have to, based on the change in
scale, instead of refilling all of them in the support of
the stack recon */
if ( ctx->stackFw[pvlIdx] ) {
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: stackFw[%u] == %g -> iv3fill needed\n", me,
pvlIdx, ctx->stackFw[pvlIdx] );
}
gageIv3Fill( ctx, ctx->pvl[pvlIdx] );
} else {
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: stackFw[%u] == %g -> NO iv3fill\n", me,
pvlIdx, ctx->stackFw[pvlIdx] );
}
}
}
}
}
if ( ctx->parm.stackUse ) {
unsigned int baseIdx, vi;
baseIdx = ctx->pvlNum - 1;
if ( ctx->verbose > 3 ) {
for ( vi=0; vi<baseIdx; vi++ ) {
fprintf( stderr, "%s: ( stack ) pvl[%u]'s value cache at "
"coords = %u, %u, %u:\n", me, vi,
ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2] );
ctx->pvl[vi]->kind->iv3Print( stderr, ctx, ctx->pvl[vi] );
}
}
_gageStackBaseIv3Fill( ctx );
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: ( stack ) base pvl's value cache at "
"coords = %u, %u, %u:\n", me,
ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2] );
ctx->pvl[baseIdx]->kind->iv3Print( stderr, ctx, ctx->pvl[baseIdx] );
}
ctx->pvl[baseIdx]->kind->filter( ctx, ctx->pvl[baseIdx] );
ctx->pvl[baseIdx]->kind->answer( ctx, ctx->pvl[baseIdx] );
} else {
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: pvl[%u/%u %s]'s value cache at "
"coords = %u, %u, %u:\n", me, pvlIdx, ctx->pvlNum,
ctx->pvl[pvlIdx]->kind->name,
ctx->point.idx[0], ctx->point.idx[1], ctx->point.idx[2] );
ctx->pvl[pvlIdx]->kind->iv3Print( stderr, ctx, ctx->pvl[pvlIdx] );
}
ctx->pvl[pvlIdx]->kind->filter( ctx, ctx->pvl[pvlIdx] );
ctx->pvl[pvlIdx]->kind->answer( ctx, ctx->pvl[pvlIdx] );
}
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: bye ^^^^^^^^^^^^^ \n\n", me );
}
return 0;
}
/*
******** gageProbe( )
**
** bummer: the non-stack gageProbe function is now just a wrapper
** around the stack-based gageProbe
*/
int
833 gageProbe( gageContext *ctx, double xi, double yi, double zi ) {
return _gageProbe( ctx, xi, yi, zi, 0.0 );
}
int
839 _gageProbeSpace( gageContext *ctx, double xx, double yy, double zz, double ss,
int indexSpace, int clamp ) {
static const char me[]="_gageProbeSpace";
unsigned int *size;
double xi, yi, zi, si;
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: hi; pos=( %g, %g, %g, %g ) in %s space %s clamping\n", me,
xx, yy, zz, ss, indexSpace ? "index" : "world",
clamp ? "WITH" : "w/out" );
}
size = ctx->shape->size;
if ( indexSpace ) {
xi = xx;
yi = yy;
zi = zz;
if ( ctx->parm.stackUse ) {
si = ss;
} else {
si = 0;
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: staying at ipos ( %g, %g, %g )\n", me,
xi, yi, zi );
}
} else {
/* have to convert from world to index. NOTE: the [4]s here are
for homogeneous coordinates, not for anything stack-related */
double icoord[4]; double wcoord[4];
ELL_4V_SET( wcoord, xx, yy, zz, 1 );
ELL_4MV_MUL( icoord, ctx->shape->WtoI, wcoord );
ELL_4V_HOMOG( icoord, icoord );
xi = icoord[0];
yi = icoord[1];
zi = icoord[2];
if ( ctx->parm.stackUse ) {
unsigned int sidx;
/* HEY: this is a stupid linear search! */
if ( ss < ctx->stackPos[0] ) {
/* we'll extrapolate from stackPos[0] and [1]. um, actually the
extrapolation is overkill because we're either going to clamp
out-of-range scale index positions, or they'll cause a
gageErrStackBounds error downstream */
sidx = 0;
} else if ( ss > ctx->stackPos[ctx->pvlNum-2] ) {
/* extrapolate from stackPos[ctx->pvlNum-3] and [ctx->pvlNum-2];
gageStackPerVolumeAttach ensures that we there are at least
two blurrings pvls and one base pvl */
sidx = ctx->pvlNum-3;
} else {
for ( sidx=0; sidx<ctx->pvlNum-2; sidx++ ) {
if ( AIR_IN_CL( ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1] ) ) {
break;
}
}
if ( sidx == ctx->pvlNum-2 ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: search failure for ss = %g", me, ss );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrStackSearch;
return 1;
}
}
si = AIR_AFFINE( ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1],
sidx, sidx+1 );
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: si = affine( %g, %g, %g -> %u %u ) = %g\n", me,
ctx->stackPos[sidx], ss, ctx->stackPos[sidx+1],
sidx, sidx+1, si );
}
} else {
si = 0;
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: wpos ( %g, %g, %g ) --> ipos ( %g, %g, %g )\n", me,
xx, yy, zz, xi, yi, zi );
}
}
if ( clamp ) {
if ( nrrdCenterNode == ctx->shape->center ) {
xi = AIR_CLAMP( 0, xi, size[0]-1 );
yi = AIR_CLAMP( 0, yi, size[1]-1 );
zi = AIR_CLAMP( 0, zi, size[2]-1 );
} else {
xi = AIR_CLAMP( -0.5, xi, size[0]-0.5 );
yi = AIR_CLAMP( -0.5, yi, size[1]-0.5 );
zi = AIR_CLAMP( -0.5, zi, size[2]-0.5 );
}
if ( ctx->parm.stackUse ) {
si = AIR_CLAMP( 0, si, ctx->pvlNum-2 );
}
if ( ctx->verbose > 3 ) {
fprintf( stderr, "%s: with clamping --> ipos ( %g, %g, %g )\n", me,
xi, yi, zi );
}
}
return _gageProbe( ctx, xi, yi, zi, si );
}
int
941 gageProbeSpace( gageContext *ctx, double xx, double yy, double zz,
int indexSpace, int clamp ) {
return _gageProbeSpace( ctx, xx, yy, zz, AIR_NAN, indexSpace, clamp );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
int
28 gageDeconvolve( Nrrd *_nout, double *lastDiffP,
const Nrrd *nin, const gageKind *kind,
const NrrdKernelSpec *ksp, int typeOut,
unsigned int maxIter, int saveAnyway,
double step, double epsilon, int verbose ) {
static const char me[]="gageDeconvolve";
gageContext *ctx[2];
gagePerVolume *pvl[2];
double *out[2], *val[2], alpha, ( *lup )( const void *, size_t ), meandiff=0;
const double *ans[2];
Nrrd *nout[2];
airArray *mop;
unsigned int sx, sy, sz, xi, yi, zi, anslen, thiz=0, last, inIdx, iter;
int E, valItem;
if ( !( _nout && lastDiffP && nin && kind && ksp ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( nrrdTypeDefault == typeOut
|| !airEnumValCheck( nrrdType, typeOut ) ) ) {
biffAddf( GAGE, "%s: typeOut %d not valid", me, typeOut );
return 1;
}
if ( !( maxIter >= 1 ) ) {
biffAddf( GAGE, "%s: need maxIter >= 1 ( not %u )", me, maxIter );
return 1;
}
if ( !( epsilon >= 0 ) ) {
biffAddf( GAGE, "%s: need epsilon >= 0.0 ( not %g )", me, epsilon );
return 1;
}
/* this once changed from 0 to 1, but is unlikely to change again */
valItem = 1;
mop = airMopNew( );
for ( iter=0; iter<2; iter++ ) {
nout[iter] = nrrdNew( );
airMopAdd( mop, nout[iter], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nout[iter], nin, nrrdTypeDouble ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't allocate working buffer %u",
me, iter );
airMopError( mop ); return 1;
}
ctx[iter] = gageContextNew( );
airMopAdd( mop, ctx[iter], ( airMopper )gageContextNix, airMopAlways );
E = 0;
if ( !E ) E |= !( pvl[iter] = gagePerVolumeNew( ctx[iter], nout[iter], kind ) );
if ( !E ) E |= gagePerVolumeAttach( ctx[iter], pvl[iter] );
if ( !E ) E |= gageKernelSet( ctx[iter], gageKernel00,
ksp->kernel, ksp->parm );
if ( !E ) E |= gageQueryItemOn( ctx[iter], pvl[iter], valItem );
if ( !E ) E |= gageUpdate( ctx[iter] );
if ( E ) {
biffAddf( GAGE, "%s: trouble setting up context %u", me, iter );
airMopError( mop ); return 1;
}
out[iter] = AIR_CAST( double*, nout[iter]->data );
ans[iter] = gageAnswerPointer( ctx[iter], pvl[iter], valItem );
}
anslen = kind->table[valItem].answerLength;
lup = nrrdDLookup[nin->type];
alpha = ksp->kernel->eval1_d( 0.0, ksp->parm );
sx = ctx[0]->shape->size[0];
sy = ctx[0]->shape->size[1];
sz = ctx[0]->shape->size[2];
for ( iter=0; iter<maxIter; iter++ ) {
thiz = ( iter+1 ) % 2;
last = ( iter+0 ) % 2;
val[thiz] = out[thiz];
val[last] = out[last];
inIdx = 0;
meandiff = 0;
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
unsigned int ai;
double in, aa;
gageProbe( ctx[last], xi, yi, zi );
for ( ai=0; ai<anslen; ai++ ) {
in = lup( nin->data, ai + anslen*inIdx );
aa = ans[last][ai];
val[thiz][ai] = val[last][ai] + step*( in - aa )/alpha;
meandiff += 2*( in - aa )*( in - aa )/( DBL_EPSILON + in*in + aa*aa );
}
val[thiz] += anslen;
val[last] += anslen;
++inIdx;
}
}
}
meandiff /= sx*sy*sz;
if ( verbose ) {
fprintf( stderr, "%s: iter %u meandiff = %g\n", me, iter, meandiff );
}
if ( meandiff < epsilon ) {
/* we have indeed converged while iter < maxIter */
break;
}
}
if ( iter == maxIter ) {
if ( !saveAnyway ) {
biffAddf( GAGE, "%s: failed to converge in %u iterations, meandiff = %g",
me, maxIter, meandiff );
airMopError( mop ); return 1;
} else {
if ( verbose ) {
fprintf( stderr, "%s: at maxIter %u; err %g still > thresh %g\n", me,
iter, meandiff, epsilon );
}
}
}
if ( nrrdClampConvert( _nout, nout[thiz], ( nrrdTypeDefault == typeOut
? nin->type
: typeOut ) ) ) {
biffAddf( GAGE, "%s: couldn't create output", me );
airMopError( mop ); return 1;
}
*lastDiffP = meandiff;
airMopOkay( mop );
return 0;
}
/*
*******************************
** all the following functionality should at some point be
** pushed down to nrrd . . .
*/
static void
164 deconvLine( double *line, size_t llen, const NrrdKernelSpec *ksp ) {
/* Add as many other parameters to this as you want,
like number and location of poles, or whatever other
buffers you think you need ( they should be passed,
not allocated and freed on a per-line basis ) */
/* comment these out when there is a real function body */
AIR_UNUSED( line );
AIR_UNUSED( llen );
AIR_UNUSED( ksp );
return;
}
static int
180 deconvTrivial( const NrrdKernelSpec *ksp ) {
int ret;
/* HEY this will be much easier once kernels have a way of
advertising whether or not they interpolate */
if ( 1 == ksp->parm[0] &&
( ksp->kernel == nrrdKernelHann ||
ksp->kernel == nrrdKernelBlackman ||
ksp->kernel == nrrdKernelBox ||
ksp->kernel == nrrdKernelCheap ||
ksp->kernel == nrrdKernelTent ) ) {
ret = AIR_TRUE;
} else {
ret = AIR_FALSE;
}
return ret;
}
int
199 gageDeconvolveSeparableKnown( const NrrdKernelSpec *ksp ) {
int ret;
if ( !ksp ) {
ret = 0;
} else if ( deconvTrivial( ksp )
|| nrrdKernelBSpline3 == ksp->kernel
|| nrrdKernelBSpline5 == ksp->kernel ) {
ret = 1;
} else {
ret = 0;
}
return ret;
}
int
215 gageDeconvolveSeparable( Nrrd *nout, const Nrrd *nin,
const gageKind *kind,
const NrrdKernelSpec *ksp,
int typeOut ) {
static const char me[]="gageDeconvolveSeparable";
double *line, ( *lup )( const void *, size_t ),
( *ins )( void *, size_t, double );
airArray *mop;
size_t lineLen, sx, sy, sz, idx, ii, jj;
unsigned int vi, valLen;
if ( !( nout && nin && kind && ksp ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( nrrdTypeDefault == typeOut
|| !airEnumValCheck( nrrdType, typeOut ) ) ) {
biffAddf( GAGE, "%s: typeOut %d not valid", me, typeOut );
return 1;
}
if ( !gageDeconvolveSeparableKnown( ksp ) ) {
biffAddf( GAGE, "%s: separable deconv not known for %s kernel",
me, ksp->kernel->name );
return 1;
}
if ( gageKindVolumeCheck( kind, nin ) ) {
biffAddf( GAGE, "%s: given volume doesn't fit %s kind",
me, kind->name );
return 1;
}
if ( nrrdTypeDefault == typeOut
? nrrdCopy( nout, nin )
: nrrdConvert( nout, nin, typeOut ) ) {
biffMovef( GAGE, NRRD, "%s: problem allocating output", me );
return 1;
}
if ( deconvTrivial( ksp ) ) {
/* if there's no real work for the deconvolution, then by
copying the values we're already done; bye */
return 0;
}
valLen = kind->valLen;
sx = nin->axis[kind->baseDim + 0].size;
sy = nin->axis[kind->baseDim + 1].size;
sz = nin->axis[kind->baseDim + 2].size;
lineLen = sx;
lineLen = AIR_MAX( lineLen, sy );
lineLen = AIR_MAX( lineLen, sz );
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nout->type];
mop = airMopNew( );
line = AIR_CALLOC( lineLen*valLen, double );
airMopAdd( mop, line, airFree, airMopAlways );
/* process along X scanlines */
for ( jj=0; jj<sy*sz; jj++ ) {
/* xi = 0, yi = jj%sy, zi = jj/sy
==> xi + sx*( yi + sy*zi )
== 0 + sx*( jj%sy + sy*( jj/sy ) ) == 0 + sx*jj */
idx = 0 + valLen*( 0 + sx*jj );
for ( ii=0; ii<sx; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
line[ii + sx*vi] = lup( nin->data, idx + vi + valLen*ii );
}
}
for ( vi=0; vi<valLen; vi++ ) {
deconvLine( line + sx*vi, sx, ksp );
}
for ( ii=0; ii<sx; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
ins( nout->data, idx + vi + valLen*ii, line[ii + sx*vi] );
}
}
}
/* process along Y scanlines */
for ( jj=0; jj<sx*sz; jj++ ) {
/* xi = jj%sx, yi = 0, zi = jj/sx
==> xi + sx*( yi + sy*zi )
== jj%sx + sx*( 0 + sy*jj/sx ) */
idx = 0 + valLen*( ( jj%sx ) + sx*( 0 + sy*( jj/sx ) ) );
for ( ii=0; ii<sy; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
line[ii + sy*vi] = lup( nin->data, idx + vi + valLen*sx*ii );
}
}
for ( vi=0; vi<valLen; vi++ ) {
deconvLine( line + sy*vi, sy, ksp );
}
for ( ii=0; ii<sx; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
ins( nout->data, idx + vi + valLen*sx*ii, line[ii + sy*vi] );
}
}
}
/* process along Z scanlines */
for ( jj=0; jj<sx*sy; jj++ ) {
/* xi = jj%sx, yi = jj/sx, zi = 0
==> xi + sx*( yi + sy*zi )
== jj%sx + sx*( jj/sx + sy*0 )
== jj%sx + sx*( jj/sx ) == jj */
idx = 0 + valLen*jj;
for ( ii=0; ii<sz; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
line[ii + sz*vi] = lup( nin->data, idx + vi + valLen*sx*sy*ii );
}
}
for ( vi=0; vi<valLen; vi++ ) {
deconvLine( line + sz*vi, sz, ksp );
}
for ( ii=0; ii<sx; ii++ ) {
for ( vi=0; vi<valLen; vi++ ) {
ins( nout->data, idx + vi + valLen*sx*sy*ii, line[ii + sz*vi] );
}
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
const char *
gageBiffKey = "gage";
int
gageDefVerbose = 0;
double
gageDefGradMagCurvMin = 0.0;
int
gageDefRenormalize = AIR_FALSE;
int
gageDefCheckIntegrals = AIR_TRUE;
int
gageDefK3Pack = AIR_TRUE;
int
gageDefCurvNormalSide = 1;
double
gageDefKernelIntegralNearZero = 0.0001;
int
gageDefDefaultCenter = nrrdCenterCell;
int
gageDefStackUse = AIR_FALSE; /* GLK doesn't see any reasonable circumstance
where this could be AIR_TRUE, since it
significantly changes the behavior of gage */
int
gageDefStackNormalizeRecon = AIR_FALSE;
int
gageDefStackNormalizeDeriv = AIR_FALSE;
double
gageDefStackNormalizeDerivBias = 0.0;
int
gageDefOrientationFromSpacing = AIR_FALSE;
/* Before teem 1.10.0, gage behaved inconsistently: Derivatives were
taken as if orientationFromSpacing were TRUE, the index space to
world space mapping acted as if it were FALSE. Now, you have the
choice and get consistent results in either case. */
int
gageDefGenerateErrStr = AIR_TRUE;
/* Before Teem 1.11, error strings were always sprintf, which can easily
become a bottleneck in some situations, but this should still stay
the default behavior */
int
gageDefTwoDimZeroZ = AIR_FALSE; /* no way this can default to true */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
** sets the filter sample location ( fsl ) array based on fractional
** probe location ctx->point->frac
**
** One possible rare surpise: if a filter is not continuous with 0
** at the end of its support, and if the sample location is at the
** highest possible point ( xi == N-2, xf = 1.0 ), then the filter
** weights may not be the desired ones. Forward differencing ( via
** nrrdKernelForwDiff ) is a good example of this.
*/
void
38 _gageFslSet( gageContext *ctx ) {
int fr, i;
double *fslx, *fsly, *fslz;
double xf, yf, zf;
fr = ctx->radius;
fslx = ctx->fsl + 0*2*fr;
fsly = ctx->fsl + 1*2*fr;
fslz = ctx->fsl + 2*2*fr;
xf = ctx->point.frac[0];
yf = ctx->point.frac[1];
zf = ctx->point.frac[2];
switch ( fr ) {
case 1:
fslx[0] = xf; fslx[1] = xf-1;
fsly[0] = yf; fsly[1] = yf-1;
fslz[0] = zf; fslz[1] = zf-1;
break;
case 2:
fslx[0] = xf+1; fslx[1] = xf; fslx[2] = xf-1; fslx[3] = xf-2;
fsly[0] = yf+1; fsly[1] = yf; fsly[2] = yf-1; fsly[3] = yf-2;
fslz[0] = zf+1; fslz[1] = zf; fslz[2] = zf-1; fslz[3] = zf-2;
break;
default:
/* filter radius bigger than 2 */
for ( i=-fr+1; i<=fr; i++ ) {
fslx[i+fr-1] = xf-i;
fsly[i+fr-1] = yf-i;
fslz[i+fr-1] = zf-i;
}
break;
}
return;
}
/*
** renormalize weights of a reconstruction kernel with
** constraint: the sum of the weights must equal the continuous
** integral of the kernel
*/
void
79 _gageFwValueRenormalize( gageContext *ctx, int wch ) {
double integral, sumX, sumY, sumZ, *fwX, *fwY, *fwZ;
int i, fd;
fd = 2*ctx->radius;
fwX = ctx->fw + 0 + fd*( 0 + 3*wch );
fwY = ctx->fw + 0 + fd*( 1 + 3*wch );
fwZ = ctx->fw + 0 + fd*( 2 + 3*wch );
integral = ctx->ksp[wch]->kernel->integral( ctx->ksp[wch]->parm );
sumX = sumY = sumZ = 0;
for ( i=0; i<fd; i++ ) {
sumX += fwX[i];
sumY += fwY[i];
sumZ += fwZ[i];
}
for ( i=0; i<fd; i++ ) {
fwX[i] *= integral/sumX;
fwY[i] *= integral/sumY;
fwZ[i] *= integral/sumZ;
}
return;
}
/*
** renormalize weights of a derivative kernel with
** constraint: the sum of the weights must be zero, but
** sign of individual weights must be preserved
*/
void
108 _gageFwDerivRenormalize( gageContext *ctx, int wch ) {
char me[]="_gageFwDerivRenormalize";
double negX, negY, negZ, posX, posY, posZ, fixX, fixY, fixZ,
*fwX, *fwY, *fwZ;
int i, fd;
fd = 2*ctx->radius;
fwX = ctx->fw + 0 + fd*( 0 + 3*wch );
fwY = ctx->fw + 0 + fd*( 1 + 3*wch );
fwZ = ctx->fw + 0 + fd*( 2 + 3*wch );
negX = negY = negZ = 0;
posX = posY = posZ = 0;
for ( i=0; i<fd; i++ ) {
if ( fwX[i] <= 0 ) { negX += -fwX[i]; } else { posX += fwX[i]; }
if ( fwY[i] <= 0 ) { negY += -fwY[i]; } else { posY += fwY[i]; }
if ( fwZ[i] <= 0 ) { negZ += -fwZ[i]; } else { posZ += fwZ[i]; }
}
/* fix is the sqrt( ) of factor by which the positive values
are too big. negative values are scaled up by fix;
positive values are scaled down by fix */
fixX = sqrt( posX/negX );
fixY = sqrt( posY/negY );
fixZ = sqrt( posZ/negZ );
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: fixX = % 10.4f, fixY = % 10.4f, fixX = % 10.4f\n",
me, ( float )fixX, ( float )fixY, ( float )fixZ );
}
for ( i=0; i<fd; i++ ) {
if ( fwX[i] <= 0 ) { fwX[i] *= fixX; } else { fwX[i] /= fixX; }
if ( fwY[i] <= 0 ) { fwY[i] *= fixY; } else { fwY[i] /= fixY; }
if ( fwZ[i] <= 0 ) { fwZ[i] *= fixZ; } else { fwZ[i] /= fixZ; }
}
return;
}
void
144 _gageFwSet( gageContext *ctx, unsigned int sidx, double sfrac ) {
char me[]="_gageFwSet";
int kidx;
unsigned int fd;
fd = 2*ctx->radius;
for ( kidx=gageKernelUnknown+1; kidx<gageKernelLast; kidx++ ) {
if ( !ctx->needK[kidx] || kidx==gageKernelStack ) {
continue;
}
/* we evaluate weights for all three axes with one call */
ctx->ksp[kidx]->kernel->evalN_d( ctx->fw + fd*3*kidx, ctx->fsl,
fd*3, ctx->ksp[kidx]->parm );
}
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: filter weights after kernel evaluation:\n", me );
_gagePrint_fslw( stderr, ctx );
}
if ( ctx->parm.renormalize ) {
for ( kidx=gageKernelUnknown+1; kidx<gageKernelLast; kidx++ ) {
if ( !ctx->needK[kidx] || kidx==gageKernelStack ) {
continue;
}
switch ( kidx ) {
case gageKernel00:
case gageKernel10:
case gageKernel20:
_gageFwValueRenormalize( ctx, kidx );
break;
default:
_gageFwDerivRenormalize( ctx, kidx );
break;
}
}
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: filter weights after renormalization:\n", me );
_gagePrint_fslw( stderr, ctx );
}
}
if ( ctx->parm.stackUse && ctx->parm.stackNormalizeDeriv ) {
unsigned int jj;
double scl, norm, *fwX, *fwY, *fwZ;
scl = AIR_AFFINE( 0.0, sfrac, 1.0,
ctx->stackPos[sidx],
ctx->stackPos[sidx+1] );
#if 0
double ( *dgeval )( double x, const double *parm ),
dgparm[2] = {0, 3};
dgeval = nrrdKernelDiscreteGaussian->eval1_d;
dgparm[0] = scl;
/* from Eq. ( 120 ) in T. Lindeberg. "Feature Detection with Automatic
Scale Selection." International Journal of Computer Vision,
1998, 30, 77-116 */
/* 0.7978845608 ~= sqrt( 2 )/sqrt( pi ) */
norm = 0.7978845608/( dgeval( 0.0, dgparm ) + dgeval( 1.0, dgparm ) );
#endif
/* really simple; no lindeberg normalization, possible bias */
norm = scl + ctx->parm.stackNormalizeDerivBias;
fd = 2*ctx->radius;
kidx = gageKernel11;
fwX = ctx->fw + 0 + fd*( 0 + 3*kidx );
fwY = ctx->fw + 0 + fd*( 1 + 3*kidx );
fwZ = ctx->fw + 0 + fd*( 2 + 3*kidx );
for ( jj=0; jj<fd; jj++ ) {
fwX[jj] *= norm;
fwY[jj] *= norm;
fwZ[jj] *= norm;
}
kidx = gageKernel22;
fwX = ctx->fw + 0 + fd*( 0 + 3*kidx );
fwY = ctx->fw + 0 + fd*( 1 + 3*kidx );
fwZ = ctx->fw + 0 + fd*( 2 + 3*kidx );
for ( jj=0; jj<fd; jj++ ) {
fwX[jj] *= norm*norm;
fwY[jj] *= norm*norm;
fwZ[jj] *= norm*norm;
}
}
return;
}
/*
** _gageLocationSet
**
** updates probe location in general context, and things which
** depend on it:
** fsl, fw
**
** ( _xi, _yi, _zi ) is *index* space position in the volume
** _si is the index-space position in the stack, the value is ignored
** if there is no stack behavior
**
** does NOT use biff, but returns 1 on error and 0 if all okay
** Currently only error is probing outside volume, which sets
** ctx->errNum and sprints message into ctx->errStr.
*/
int
246 _gageLocationSet( gageContext *ctx,
double xif, double yif, double zif, double sif ) {
char me[]="_gageProbeLocationSet";
unsigned int top[3], /* "top" x, y, z: highest valid index in volume */
idx[4];
int sdiff; /* computed integral positions in volume */
double frac[4], min, max[3];
/* **** bounds checking **** */
top[0] = ctx->shape->size[0] - 1;
top[1] = ctx->shape->size[1] - 1;
top[2] = ctx->shape->size[2] - 1;
if ( nrrdCenterNode == ctx->shape->center ) {
min = 0;
max[0] = top[0];
max[1] = top[1];
max[2] = top[2];
} else {
min = -0.5;
max[0] = AIR_CAST( double, top[0] ) + 0.5;
max[1] = AIR_CAST( double, top[1] ) + 0.5;
max[2] = AIR_CAST( double, top[2] ) + 0.5;
}
if ( !( AIR_IN_CL( min, xif, max[0] ) &&
AIR_IN_CL( min, yif, max[1] ) &&
AIR_IN_CL( min, zif, max[2] ) ) ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: position ( %g, %g, %g ) outside ( %s-centered ) "
"bounds [%g, %g]x[%g, %g]x[%g, %g]",
me, xif, yif, zif,
airEnumStr( nrrdCenter, ctx->shape->center ),
min, max[0], min, max[1], min, max[2] );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrBoundsSpace;
return 1;
}
if ( ctx->parm.stackUse ) {
if ( !( AIR_IN_CL( 0, sif, ctx->pvlNum-2 ) ) ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: stack position %g outside ( %s-centered ) "
"bounds [0, %u]", me, sif,
airEnumStr( nrrdCenter, nrrdCenterNode ), ctx->pvlNum-2 );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrBoundsStack;
return 1;
}
}
/* **** computing integral and fractional sample locations **** */
/* Thu Jan 14 19:46:53 CST 2010: detected that along the low edge
( next to sample 0 ) in cell centered, the rounding behavior of
AIR_CAST( unsigned int, xif ), namely [-0.5, 0] --> 0, meant that
the low edge was not treated symmetrically with the high edge.
This motivated the change from using idx to store the lower
corner of the containing voxel, to the upper corner. So, the new
"idx" is always +1 of what the old idx was. Code here and in
ctx.c ( since idx is saved into ctx->point.idx ) has been changed
accordingly */
ELL_3V_SET( idx,
AIR_CAST( unsigned int, xif+1 ), /* +1: see above */
AIR_CAST( unsigned int, yif+1 ),
AIR_CAST( unsigned int, zif+1 ) );
if ( ctx->verbose > 5 ) {
fprintf( stderr, "%s: ( %g, %g, %g, %g ) -%s-> mm [%g, %g/%g/%g]\n"
" --> idx %u %u %u\n",
me, xif, yif, zif, sif,
airEnumStr( nrrdCenter, ctx->shape->center ),
min, max[0], max[1], max[2], idx[0], idx[1], idx[2] );
}
/* these can only can kick in for node-centered, because that's when
max[] has an integral value */
idx[0] -= ( idx[0]-1 == max[0] );
idx[1] -= ( idx[1]-1 == max[1] );
idx[2] -= ( idx[2]-1 == max[2] );
if ( ctx->verbose > 5 ) {
fprintf( stderr, "%s: ----> idx %u %u %u\n",
me, idx[0], idx[1], idx[2] );
}
ELL_3V_SET( frac,
xif - ( AIR_CAST( float, idx[0] )-1 ),
yif - ( AIR_CAST( float, idx[1] )-1 ),
zif - ( AIR_CAST( float, idx[2] )-1 ) );
ELL_3V_COPY( ctx->point.idx, idx ); /* not idx[3], yet */
if ( ctx->parm.stackUse ) {
idx[3] = AIR_CAST( unsigned int, sif );
idx[3] -= ( idx[3] == ctx->pvlNum-2 );
frac[3] = sif - idx[3];
sdiff = ( ctx->point.idx[3] + ctx->point.frac[3] != sif );
} else {
idx[3] = 0;
frac[3] = 0;
sdiff = AIR_FALSE;
}
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: \n"
" pos ( % 15.7f, % 15.7f, % 15.7f, % 15.7f ) \n"
" -> i( %5d, %5d, %5d, %5d ) \n"
" + f( % 15.7f, % 15.7f, % 15.7f, % 15.7f ) \n",
me, xif, yif, zif, sif, idx[0], idx[1], idx[2], idx[3],
frac[0], frac[1], frac[2], frac[3] );
}
/* **** compute *spatial* fsl and fw ****
these have to be reconsidered if anything changes about the
fractional spatial position, or ( if no fractional spatial change ),
movement along scale AND using normalization based on scale */
if ( ctx->point.frac[0] != frac[0]
|| ctx->point.frac[1] != frac[1]
|| ctx->point.frac[2] != frac[2]
|| ( ctx->parm.stackUse && sdiff && ctx->parm.stackNormalizeDeriv ) ) {
/* We don't yet record the scale position in ctx->point because
that's done below while setting stackFsl and stackFw. So, have
to pass stack pos info to _gageFwSet( ) */
ELL_3V_COPY( ctx->point.frac, frac );
/* these may take some time ( especially if using renormalization ),
hence the conditional above */
_gageFslSet( ctx );
_gageFwSet( ctx, idx[3], frac[3] );
}
/* **** compute *stack* fsl and fw **** */
if ( ctx->verbose > 2 && ctx->parm.stackUse ) {
fprintf( stderr, "%s: point.frac[3] %f + idx[3] %u = %f %s sif %f\n", me,
ctx->point.frac[3], ctx->point.idx[3],
ctx->point.frac[3] + ctx->point.idx[3],
( sdiff ? "*NOT ==*" : "==" ), sif );
}
if ( !ctx->parm.stackUse ) {
ctx->point.idx[3] = idx[3];
ctx->point.frac[3] = frac[3];
ctx->point.stackFwNonZeroNum = 0;
} else if ( sdiff ) {
double sum;
unsigned int ii, nnz;
NrrdKernelSpec *sksp;
/* node-centered sampling of stack indices from 0 to ctx->pvlNum-2 */
/* HEY: we really are quite far from implementing arbitrary
nrrdBoundary behaviors here, and centering is stuck on node! */
/* HEY: honestly, the whole idea that it still makes sense to do
low-level operations in index space, when the world-space locations
of the samples can be non-uniform, is a little suspect. This is
all legit for nrrdKernelTent and nrrdKernelHermiteScaleSpaceFlag,
but is pretty fishy otherwise */
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
ctx->stackFsl[ii] = sif - ii;
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: ctx->stackFsl[%u] = %g\n",
me, ii, ctx->stackFsl[ii] );
}
}
sksp = ctx->ksp[gageKernelStack];
sksp->kernel->evalN_d( ctx->stackFw, ctx->stackFsl,
ctx->pvlNum-1, sksp->parm );
if ( ctx->verbose > 2 ) {
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
fprintf( stderr, "%s: ctx->stackFw[%u] = %g\n",
me, ii, ctx->stackFw[ii] );
}
}
/* compute stackFwNonZeroNum whether or not parm.stackNormalizeRecon! */
nnz = 0;
if ( ctx->parm.stackNormalizeRecon ) {
sum = 0;
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
nnz += !!ctx->stackFw[ii];
sum += ctx->stackFw[ii];
}
if ( !sum ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: integral of stackFw[] is zero; "
"can't do stack reconstruction", me );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrStackIntegral;
return 1;
}
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
ctx->stackFw[ii] /= sum;
}
if ( ctx->verbose > 2 ) {
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
fprintf( stderr, "%s: ctx->stackFw[%u] = %g\n", me,
ii, ctx->stackFw[ii] );
}
}
} else {
for ( ii=0; ii<ctx->pvlNum-1; ii++ ) {
nnz += !!ctx->stackFw[ii];
}
if ( !nnz ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: all stackFw[] weights are zero; "
"can't do stack reconstruction", me );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrStackIntegral;
return 1;
}
}
ctx->point.idx[3] = idx[3];
ctx->point.frac[3] = frac[3];
ctx->point.stackFwNonZeroNum = nnz;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
******** gageKindCheck
**
** some some basic checking of the gageEntryItem array ( the "table" ) for
** the sorts of mistakes that may be introduced by its hand-coding, although
** theoretically this is good for dynamically-generated gageKinds as well.
*/
int
35 gageKindCheck( const gageKind *kind ) {
static const char me[]="gageKindCheck";
int pitem, pindex, alen;
int ii, pi;
gageItemEntry *item;
if ( !kind ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( kind->itemMax > GAGE_ITEM_MAX ) {
biffAddf( GAGE, "%s: kind \"%s\" item max %d > GAGE_ITEM_MAX %d",
me, kind->name, kind->itemMax, GAGE_ITEM_MAX );
return 1;
}
for ( ii=1; ii<=kind->itemMax; ii++ ) {
item = kind->table + ii;
if ( ii != item->enumVal ) {
biffAddf( GAGE,
"%s: \"%s\"-kind \"%s\" ( item %d ) has enumVal %d ( not %d )",
me, kind->name, airEnumStr( kind->enm, ii ),
ii, item->enumVal, ii );
return 1;
}
alen = item->answerLength;
if ( !( 1 <= alen ) ) {
if ( kind->dynamicAlloc ) {
biffAddf( GAGE, "%s: ( dynamic ) \"%s\"-kind \"%s\" ( item %d ) "
"answerLength ( %d ) not set?",
me, kind->name, airEnumStr( kind->enm, ii ), ii, alen );
} else {
biffAddf( GAGE, "%s: \"%s\"-kind \"%s\" ( item %d ) has invalid "
"answerLength %d",
me, kind->name, airEnumStr( kind->enm, ii ), ii, alen );
}
return 1;
}
if ( !( AIR_IN_CL( 0, item->needDeriv, GAGE_DERIV_MAX ) ) ) {
biffAddf( GAGE,
"%s: \"%s\"-kind \"%s\" ( item %d ) has invalid needDeriv %d "
"( not in [0, %u] )\n",
me, kind->name, airEnumStr( kind->enm, ii ),
ii, item->needDeriv, GAGE_DERIV_MAX );
return 1;
}
for ( pi=0; pi<GAGE_ITEM_PREREQ_MAXNUM; pi++ ) {
if ( !( 0 <= item->prereq[pi] ) ) {
if ( kind->dynamicAlloc ) {
biffAddf( GAGE, "%s: ( dynamic ) \"%s\"-kind \"%s\" ( item %d ) "
"prereq %d ( %d ) not set?",
me, kind->name, airEnumStr( kind->enm, ii ), ii,
pi, item->prereq[pi] );
} else {
biffAddf( GAGE, "%s: \"%s\"-kind \"%s\" ( item %d ) has invalid "
"prereq %d ( %d )",
me, kind->name, airEnumStr( kind->enm, ii ), ii,
pi, item->prereq[pi] );
}
return 1;
}
}
pitem = item->parentItem;
pindex = item->parentIndex;
if ( 0 != pitem ) {
if ( 1 == ii ) {
biffAddf( GAGE, "%s: first item ( index 1 ) of \"%s\"-kind can't "
"be a sub-item ( wanted parent index %d )",
me, kind->name, pitem );
return 1;
}
if ( !( AIR_IN_CL( 1, pitem, kind->itemMax ) ) ) {
biffAddf( GAGE, "%s: item %d of \"%s\"-kind wants parent item %d "
"outside valid range [0..%d]",
me, ii, kind->name, pitem, kind->itemMax );
return 1;
}
if ( 0 != kind->table[pitem].parentItem ) {
biffAddf( GAGE, "%s: item %d of \"%s\"-kind has parent %d which "
"wants to have parent %d: can't have sub-sub-items",
me, ii, kind->name, pitem, kind->table[pitem].parentItem );
return 1;
}
if ( !( 0 <= pindex
&& ( ( unsigned int )pindex + alen
<= kind->table[pitem].answerLength ) ) ) {
biffAddf( GAGE,
"%s: item %d of \"%s\"-kind wants index range [%d, %d] "
"of parent %d, which isn't in valid range [0, %d]",
me, ii, kind->name,
pindex, pindex + alen - 1,
pitem, kind->table[pitem].answerLength - 1 );
return 1;
}
}
}
return 0;
}
unsigned int
134 gageKindTotalAnswerLength( const gageKind *kind ) {
static const char me[]="gageKindTotalAnswerLength";
char *err;
unsigned int alen;
int ii;
if ( gageKindCheck( kind ) ) {
err = biffGetDone( GAGE );
fprintf( stderr, "%s: PANIC:\n %s", me, err );
free( err ); exit( 1 );
}
alen = 0;
for ( ii=1; ii<=kind->itemMax; ii++ ) {
alen += ( 0 == kind->table[ii].parentItem
? kind->table[ii].answerLength
: 0 );
}
return alen;
}
/*
** _gageKindAnswerOffset
**
** return the location of the item in the master answer array
**
** I don't think this will work if there are sub-sub-items
*/
int
162 _gageKindAnswerOffset( const gageKind *kind, int item ) {
int parent, ii;
if ( 1 >= item ) {
/* the first item always has zero offset */
return 0;
}
/* else we're not the first */
parent = kind->table[item].parentItem;
if ( 0 != parent ) {
/* we're a sub-item */
return ( kind->table[item].parentIndex
+ _gageKindAnswerOffset( kind, parent ) );
}
/* else we're not a sub-item: find the first previous non-sub-item */
ii = item-1;
while ( 0 != kind->table[ii].parentItem ) {
/* gageKindCheck ensures that item 1 is not a sub-item */
ii--;
}
return ( kind->table[ii].answerLength
+ _gageKindAnswerOffset( kind, ii ) );
}
unsigned int
189 gageKindAnswerLength( const gageKind *kind, int item ) {
static const char me[]="gageKindAnswerLength";
char *err;
if ( gageKindCheck( kind ) ) {
err = biffGetDone( GAGE );
fprintf( stderr, "%s: PANIC:\n %s", me, err );
free( err ); exit( 1 );
}
return ( !airEnumValCheck( kind->enm, item )
? kind->table[item].answerLength
: 0 );
}
int
205 gageKindAnswerOffset( const gageKind *kind, int item ) {
static const char me[]="gageKindAnswerOffset";
char *err;
if ( gageKindCheck( kind ) ) {
err = biffGetDone( GAGE );
fprintf( stderr, "%s: PANIC:\n %s", me, err );
free( err ); exit( 1 );
}
return _gageKindAnswerOffset( kind, item );
}
/*
** so that you can see if a given volume will work as the given kind
*/
int
222 gageKindVolumeCheck( const gageKind *kind, const Nrrd *nrrd ) {
static const char me[]="gageKindVolumeCheck";
if ( !( kind && nrrd ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nrrd ) ) {
biffMovef( GAGE, NRRD, "%s: problem with nrrd", me );
return 1;
}
if ( !( nrrd->dim == 3 + kind->baseDim ) ) {
biffAddf( GAGE, "%s: nrrd should be %u-D, not %u-D",
me, 3 + kind->baseDim, nrrd->dim );
return 1;
}
if ( nrrdTypeBlock == nrrd->type ) {
biffAddf( GAGE, "%s: can't handle %s-type volumes", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( kind->baseDim ) {
char stmp[AIR_STRLEN_SMALL];
if ( 1 == kind->baseDim ) {
if ( kind->valLen != nrrd->axis[0].size ) {
biffAddf( GAGE, "%s: %s kind needs %u axis 0 values, not %s", me,
kind->name, kind->valLen,
airSprintSize_t( stmp, nrrd->axis[0].size ) );
return 1;
}
} else {
/* actually there is yet to be a kind in Teem for which
kind->baseDim > 1, but this code would work in that case */
unsigned int axi;
size_t numsub; /* number of samples sub base dim */
numsub = 1;
for ( axi=0; axi<kind->baseDim; axi++ ) {
numsub *= nrrd->axis[axi].size;
}
if ( kind->valLen != numsub ) {
biffAddf( GAGE, "%s: %s kind needs %u values below baseDim axis %u, "
"not %s", me, kind->name, kind->valLen, kind->baseDim,
airSprintSize_t( stmp, numsub ) );
return 1;
}
}
}
/* this eventually calls _gageShapeSet( ), which, for purely historical
reasons, does the brunt of the error checking, some of which is almost
certainly redundant with checks above . . . */
if ( gageVolumeCheck( NULL, nrrd, kind ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
const int
gagePresent = 42;
/*
******** gageZeroNormal[]
**
** this is the vector to supply when someone wants the normalized
** version of a vector with zero length. We could be nasty and
** set this to {AIR_NAN, AIR_NAN, AIR_NAN}, but simply passing
** NANs around can make things fantastically slow . . .
*/
double
gageZeroNormal[3] = {0, 0, 0};
const char *
_gageKernelStr[] = {
"( unknown_kernel )",
"00",
"10",
"11",
"20",
"21",
"22",
/* "33", */
/* "44", */
"stack"
};
const char *
_gageKernelDesc[] = {
"unknown kernel",
"kernel for reconstructing values",
"kernel for reconstruction values when doing 1st derivatives",
"kernel for measuring 1st derivatives when doing 1st derivatives",
"kernel for reconstruction values when doing 2nd derivatives",
"kernel for measuring 1st derivatives when doing 2nd derivatives",
"kernel for measuring 2nd derivatives when doing 2nd derivatives",
/* "kernel for measuring 3rd derivatives when doing 3rd derivatives", */
/* "kernel for measuring 4th derivatives when doing 4th derivatives", */
"kernel for reconstruction across a stack"
};
const char *
_gageKernelStrEqv[] = {
"00", "k00",
"10", "k10",
"11", "k11",
"20", "k20",
"21", "k21",
"22", "k22",
/* "33", "k33", */
/* "44", "k44", */
"stack", "ss", "kss",
""
};
const int
_gageKernelValEqv[] = {
gageKernel00, gageKernel00,
gageKernel10, gageKernel10,
gageKernel11, gageKernel11,
gageKernel20, gageKernel20,
gageKernel21, gageKernel21,
gageKernel22, gageKernel22,
/* gageKernel33, gageKernel33, */
/* gageKernel44, gageKernel44, */
gageKernelStack, gageKernelStack, gageKernelStack
};
const airEnum
_gageKernel_enum = {
"kernel",
GAGE_KERNEL_MAX,
_gageKernelStr, NULL,
_gageKernelDesc,
_gageKernelStrEqv, _gageKernelValEqv,
AIR_FALSE
};
105 const airEnum *const
gageKernel = &_gageKernel_enum;
void
109 gageParmReset( gageParm *parm ) {
if ( parm ) {
parm->renormalize = gageDefRenormalize;
parm->checkIntegrals = gageDefCheckIntegrals;
parm->k3pack = gageDefK3Pack;
parm->gradMagCurvMin = gageDefGradMagCurvMin;
parm->curvNormalSide = gageDefCurvNormalSide;
parm->kernelIntegralNearZero = gageDefKernelIntegralNearZero;
parm->defaultCenter = gageDefDefaultCenter;
parm->stackUse = gageDefStackUse;
parm->stackNormalizeRecon = gageDefStackNormalizeRecon;
parm->stackNormalizeDeriv = gageDefStackNormalizeDeriv;
parm->stackNormalizeDerivBias = gageDefStackNormalizeDerivBias;
parm->orientationFromSpacing = gageDefOrientationFromSpacing;
parm->generateErrStr = gageDefGenerateErrStr;
parm->twoDimZeroZ = gageDefTwoDimZeroZ;
}
return;
}
void
131 gagePointReset( gagePoint *point ) {
if ( point ) {
unsigned int big;
/* learned: can't initialize the floating point to AIR_NAN,
non-dot-net windows compilers proclaim that QNAN == x
for any existent x!!! For some reason though, infinity
is handled correctly */
ELL_4V_SET( point->frac,
AIR_POS_INF, AIR_POS_INF, AIR_POS_INF, AIR_POS_INF );
big = AIR_CAST( unsigned int, -1 );
ELL_4V_SET( point->idx, big, big, big, big );
point->stackFwNonZeroNum = 0;
}
return;
}
void
149 gageItemSpecInit( gageItemSpec *isp ) {
if ( isp ) {
isp->item = -1;
isp->kind = NULL;
}
return;
}
gageItemSpec *
159 gageItemSpecNew( void ) {
gageItemSpec *isp;
isp = ( gageItemSpec * )calloc( 1, sizeof( gageItemSpec ) );
gageItemSpecInit( isp );
return isp;
}
gageItemSpec *
168 gageItemSpecNix( gageItemSpec *isp ) {
if ( isp ) {
airFree( isp );
}
return NULL;
}
const char *
_gageErrStr[GAGE_ERR_MAX+1] = {
"( unknown gageErr )",
"none",
"space bounds",
"stack bounds",
"stack integral",
"stack search",
"stack unused"
};
const airEnum
_gageErr = {
"gageErr",
GAGE_ERR_MAX,
_gageErrStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
196 const airEnum *const
gageErr = &_gageErr;
const char *
_gageItemPackPartStr[] = {
"( unknown_pack_part )",
"scalar",
"gradvec",
"gradmag",
"normal",
"hessian",
"hesseval0",
"hesseval1",
"hesseval2",
"hessevec0",
"hessevec1",
"hessevec2"
};
const char *
_gageItemPackPartDesc[] = {
"unknown pack part",
"the base scalar F",
"gradient ( vector ) of F",
"magnitude of the gradient of F",
"normalized gradient ( vector ) of F",
"Hessian of F",
"1st eigenvalue of Hessian of F",
"2nd eigenvalue of Hessian of F",
"3rd eigenvalue of Hessian of F",
"1st eigenvector of Hessian of F",
"2nd eigenvector of Hessian of F",
"3rd eigenvector of Hessian of F"
};
const char *
_gageItemPackPartStrEqv[] = {
"scalar", "scl",
"gradvec", "gvec",
"gradmag", "gmag",
"normal", "norm",
"hessian", "hess",
"hesseval0", "heval0",
"hesseval1", "heval1",
"hesseval2", "heval2",
"hessevec0", "hevec0",
"hessevec1", "hevec1",
"hessevec2", "hevec2",
""
};
const int
_gageItemPackPartValEqv[] = {
gageItemPackPartScalar, gageItemPackPartScalar,
gageItemPackPartGradVec, gageItemPackPartGradVec,
gageItemPackPartGradMag, gageItemPackPartGradMag,
gageItemPackPartNormal, gageItemPackPartNormal,
gageItemPackPartHessian, gageItemPackPartHessian,
gageItemPackPartHessEval0, gageItemPackPartHessEval0,
gageItemPackPartHessEval1, gageItemPackPartHessEval1,
gageItemPackPartHessEval2, gageItemPackPartHessEval2,
gageItemPackPartHessEvec0, gageItemPackPartHessEvec0,
gageItemPackPartHessEvec1, gageItemPackPartHessEvec1,
gageItemPackPartHessEvec2, gageItemPackPartHessEvec2,
};
const airEnum
_gageItemPackPart_enum = {
"pack part",
GAGE_ITEM_PACK_PART_MAX,
_gageItemPackPartStr, NULL,
_gageItemPackPartDesc,
_gageItemPackPartStrEqv, _gageItemPackPartValEqv,
AIR_FALSE
};
271 const airEnum *const
gageItemPackPart = &_gageItemPackPart_enum;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
** in the 1.11 release this file was not actually linked in;
** but it remains in the repo as notes on the first pass
** at implementing this functionality. More thinking needed!
*/
/* The "/ *Teem:" ( without space ) comments in here are an experiment */
gageMultiItem * /*Teem: error if ( !ret ) */
36 gageMultiItemNew( const gageKind *kind ) {
gageMultiItem *gmi = NULL;
if ( kind && ( gmi = AIR_CALLOC( 1, gageMultiItem ) ) ) {
gmi->kind = kind;
gmi->item = NULL;
gmi->ansDir = NULL;
gmi->ansLen = NULL;
gmi->nans = nrrdNew( );
}
return gmi;
}
gageMultiItem * /*Teem: no error */
50 gageMultiItemNix( gageMultiItem *gmi ) {
if ( gmi ) {
airFree( gmi->item );
airFree( gmi );
}
return NULL;
}
gageMultiItem * /*Teem: no error */
60 gageMultiItemNuke( gageMultiItem *gmi ) {
if ( gmi ) {
nrrdNuke( gmi->nans );
}
return gageMultiItemNix( gmi );
}
int /*Teem: biff if ( ret ) */
69 gageMultiItemSet( gageMultiItem *gmi, const int *item, unsigned int itemNum ) {
static const char me[]="gageMultiItemSet";
unsigned int ii;
if ( !( gmi && item ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !itemNum ) {
biffAddf( GAGE, "%s: can't set zero items", me );
return 1;
}
gmi->item = AIR_CAST( int *, airFree( gmi->item ) );
if ( !( gmi->item = AIR_CALLOC( itemNum, int ) ) ) {
biffAddf( GAGE, "%s: couldn't allocate %u ints for items", me, itemNum );
return 1;
}
for ( ii=0; ii<itemNum; ii++ ) {
if ( airEnumValCheck( gmi->kind->enm, item[ii] ) ) {
biffAddf( GAGE, "%s: item[%u] %d not a valid %s value", me,
ii, item[ii], gmi->kind->enm->name );
return 1;
}
gmi->item[ii] = item[ii];
fprintf( stderr, "!%s: item[%u] = %d %s\n", me, ii, item[ii],
airEnumStr( gmi->kind->enm, item[ii] ) );
}
return 0;
}
/*
******** gageMultiItemSet_va
**
** How to set ( in one call ) the multiple items in a gageMultiItem.
** These are the items for which the answers will be collected into
** a single nrrd on output ( maximizing memory locality ).
*/
int /*Teem: biff if ( ret ) */
109 gageMultiItemSet_va( gageMultiItem *gmi, unsigned int itemNum,
... /* itemNum items */ ) {
static const char me[]="gageMultiItemSet_va";
int *item;
unsigned int ii;
va_list ap;
airArray *mop;
if ( !gmi ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !itemNum ) {
biffAddf( GAGE, "%s: can't set zero items", me );
return 1;
}
if ( !( item = AIR_CALLOC( itemNum, int ) ) ) {
biffAddf( GAGE, "%s: couldn't allocate %u ints for items", me, itemNum );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, item, airFree, airMopAlways );
/* consume items from var args */
va_start( ap, itemNum );
for ( ii=0; ii<itemNum; ii++ ) {
item[ii] = va_arg( ap, int );
}
va_end( ap );
if ( gageMultiItemSet( gmi, item, itemNum ) ) {
biffAddf( GAGE, "%s: problem setting items", me );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
/* ----------------------------------------------------------- */
gageMultiQuery * /*Teem: error if ( !ret ) */
152 gageMultiQueryNew( const gageContext *gctx ) {
gageMultiQuery *gmq = NULL;
if ( gctx && ( gmq = AIR_CALLOC( 1, gageMultiQuery ) ) ) {
gmq->pvlNum = gctx->pvlNum;
gmq->mitmNum = AIR_CALLOC( gmq->pvlNum, unsigned int );
gmq->mitm = AIR_CALLOC( gmq->pvlNum, gageMultiItem ** );
gmq->nidx = nrrdNew( );
if ( !( gmq->mitmNum && gmq->mitm && gmq->nidx ) ) {
/* bail */
airFree( gmq->mitmNum );
airFree( gmq->mitm );
nrrdNuke( gmq->nidx );
airFree( gmq ); gmq=NULL;
} else {
/* allocated everything ok */
unsigned int qi;
for ( qi=0; qi<gmq->pvlNum; qi++ ) {
gmq->mitm[qi] = NULL;
}
}
}
return gmq;
}
/*
******** gageMultiQueryAdd_va
**
** add multi-items for one particular pvl ( pvlIdx )
*/
int /*Teem: biff if ( ret ) */
183 gageMultiQueryAdd_va( gageContext *gctx,
gageMultiQuery *gmq, unsigned int pvlIdx,
unsigned int mitmNum,
... /* mitmNum gageMultiItem* */ ) {
static const char me[]="gageMultiQueryAdd_va";
unsigned int qi;
va_list ap;
if ( !gmq ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( pvlIdx < gmq->pvlNum ) ) {
biffAddf( GAGE, "%s: pvlIdx %u not in valid range [0, %u]", me,
pvlIdx, gmq->pvlNum-1 );
return 1;
}
gmq->mitmNum[pvlIdx] = mitmNum;
gmq->mitm[pvlIdx] = AIR_CALLOC( mitmNum, gageMultiItem* );
/* consume and add items to context */
va_start( ap, mitmNum );
for ( qi=0; qi<mitmNum; qi++ ) {
gmq->mitm[pvlIdx][qi] = va_arg( ap, gageMultiItem* );
}
va_end( ap );
AIR_UNUSED( gctx );
return 0;
}
int /*Teem: biff if ( ret ) */
216 gageMultiProbe( gageContext *gctx, gageMultiQuery *gmq,
const gageMultiInput *minput ) {
AIR_UNUSED( gmq );
AIR_UNUSED( gctx );
AIR_UNUSED( minput );
return 0;
}
gageMultiQuery * /*Teem: no error */
226 gageMultiQueryNix( gageMultiQuery *gmq ) {
AIR_UNUSED( gmq );
return NULL;
}
/*
** here the difference between nix and nuke is where the
** nans in gageMultiItem is freed?
*/
gageMultiQuery * /*Teem: no error */
237 gageMultiQueryNuke( gageMultiQuery *gmq ) {
AIR_UNUSED( gmq );
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
static int debugging = 0;
static int debugii;
*/
static airArray *debugReconErrArr = NULL;
static double *debugReconErr = NULL;
static char *debugReconErrName = NULL;
/*
** learned:
**
** -- debug high/discontinuous errors at the low sigmas: was because
** cut-off was insufficient to prevent some discontinuous change in
** kernel values: increased minimum support in the kernel itself, and
** now using larger cut-offs.
**
** -- also, separately from this problem, you can have minima in the
** inf error ( in imgMeasr ) *not* at sample points, apparently simply
** because of how the hermite interpolation works ( but this is
** troubling )
**
** -- do now have a different minimization scheme for allMeasr=Linf,
** but this may still be a work in progress. Recognizing that this is
** essentially seeking to find a uniform re-parameterization of
** something with a hidden non-uniform parameterization, we could
** probably implement a simple global warping of control points within
** the implied non-uniform domain.
*/
/* this limits how big the kernel can get with a single evaluation
of nrrdKernelDiscreteGaussian; there are some numerical issues
with large kernels that need ironing out */
#define GOOD_SIGMA_MAX 5
#define N -1
/*
** NOTE: The idea for this table originated with Raul San Jose Estepar;
** GLK recomputed it optimizing for 3D recon, but
** NOTE: there are probably still be bugs in this; look at the
** "HEY: bug?" notes below, the same problem occurs elsewhere
**
** Basic indexing idea: [sigma max][total # samples][which sample]
**
** "sigma max" can't be 0; smallest value is 1
** ==> index with ( sigma max )-1
** biggest value is GAGE_OPTIMSIG_SIGMA_MAX,
** ==> biggest index is GAGE_OPTIMSIG_SIGMA_MAX-1
** ==> allocate for GAGE_OPTIMSIG_SIGMA_MAX
**
** "total # samples" can't be 0, or 1, smallest value is 2
** ==> index with ( total # samples )-2
** biggest value is GAGE_OPTIMSIG_SAMPLES_MAXNUM
** ==> biggest index is GAGE_OPTIMSIG_SAMPLES_MAXNUM-2
** ==> allocate for GAGE_OPTIMSIG_SAMPLES_MAXNUM-1
**
** "which sample" ranges from 0 to GAGE_OPTIMSIG_SAMPLES_MAXNUM-1
** ==> allocate for GAGE_OPTIMSIG_SAMPLES_MAXNUM
*/
static double
_optimSigTable[GAGE_OPTIMSIG_SIGMA_MAX][GAGE_OPTIMSIG_SAMPLES_MAXNUM-1][GAGE_OPTIMSIG_SAMPLES_MAXNUM] = {
{
{0, 1, N, N, N, N, N, N, N, N, N},
{0, 0.5279398, 1, N, N, N, N, N, N, N, N},
{0, 0.30728838, 0.59967405, 1, N, N, N, N, N, N, N},
{0, 0.25022203, 0.47050092, 0.69525677, 1, N, N, N, N, N, N},
{0, 0.17127343, 0.39234546, 0.56356072, 0.75660759, 1, N, N, N, N, N},
{0, 0.16795139, 0.37100673, 0.51324213, 0.65655005, 0.81952846, 1, N, N, N, N},
{0, 0.1662873, 0.34969759, 0.46556041, 0.55324608, 0.68717259, 0.83465695, 1, N, N, N},
{0, 0.12720504, 0.22565289, 0.28316727, 0.44209728, 0.58615023, 0.75034028, 0.87391609, 1, N, N},
{0, 0.12836272 /* HEY: bug? should be < 0.12720504 */, 0.22926401, 0.27715567, 0.43546647, 0.56471503, 0.69411868, 0.80830419, 0.89314467, 1, N},
{0, 0.13169055 /* HEY: bug? should be < 0.12720504 */, 0.23498112, 0.26570156, 0.42672107, 0.54272485, 0.62969965, 0.73375762, 0.76996493, 0.89293921, 1}
}, {
{0, 2, N, N, N, N, N, N, N, N, N},
{0, 0.75118494, 2, N, N, N, N, N, N, N, N},
{0, 0.55478472, 1.1535828, 2, N, N, N, N, N, N, N},
{0, 0.49007216, 0.8412028, 1.308665, 2, N, N, N, N, N, N},
{0, 0.29460263, 0.57445061, 0.93797231, 1.368475, 2, N, N, N, N, N},
{0, 0.2506085, 0.49080029, 0.73882496, 1.069332, 1.4497081, 2, N, N, N, N},
{0, 0.18255657, 0.42056954, 0.62766695, 0.87999368, 1.1692151, 1.5175625, 2, N, N, N},
{0, 0.17582123, 0.40522173, 0.58696139, 0.79624867, 1.0485514, 1.2950466, 1.5977446, 2, N, N},
{0, 0.17304537, 0.39376548, 0.56427032, 0.75127059, 0.96672511, 1.187861, 1.4141362, 1.6921321, 2, N},
{0, 0.16970521, 0.38116929, 0.53575242, 0.69498152, 0.88430929, 1.0844854, 1.2899524, 1.5211773, 1.7645421, 2}
}, {
{0, 3, N, N, N, N, N, N, N, N, N},
{0, 0.92324787, 3, N, N, N, N, N, N, N, N},
{0, 0.59671402, 1.3871731, 3, N, N, N, N, N, N, N},
{0, 0.53303385, 1.0274624, 1.6725048, 3, N, N, N, N, N, N},
{0, 0.47298154, 0.79659319, 1.2379739, 1.8434249, 3, N, N, N, N, N},
{0, 0.29337707, 0.56664073, 0.94871783, 1.3666322, 1.949043, 3, N, N, N, N},
{0, 0.25583801, 0.52919179, 0.78387552, 1.1250161, 1.516176, 2.0632432, 3, N, N, N},
{0, 0.25013804, 0.48255014, 0.72428173, 1.0308567, 1.3638159, 1.7629964, 2.2885511, 3, N, N},
{0, 0.25038671, 0.46448985, 0.67336935, 0.94502586, 1.2324173, 1.5780864, 1.9883285, 2.5002999, 3, N},
{0, 0.25034565, 0.44725224, 0.63590652, 0.8669008, 1.1130947, 1.3942779, 1.7180597, 2.1408446, 2.5466051, 3}
}, {
{0, 4, N, N, N, N, N, N, N, N, N},
{0, 1.0342592, 4, N, N, N, N, N, N, N, N},
{0, 0.6341188, 1.5414433, 4, N, N, N, N, N, N, N},
{0, 0.5523203, 1.1400089, 1.9595566, 4, N, N, N, N, N, N},
{0, 0.51082283, 0.91567439, 1.4275582, 2.2504199, 4, N, N, N, N, N},
{0, 0.46390373, 0.76406777, 1.1620381, 1.6579833, 2.470386, 4, N, N, N, N},
{0, 0.29957226, 0.58226484, 0.90447241, 1.318499, 1.8011117, 2.5972142, 4, N, N, N},
{0, 0.29072434, 0.5657317, 0.8687849, 1.2413157, 1.7351674, 2.2752147, 3.1038468, 4, N, N},
{0, 0.25000414, 0.5027808, 0.75375289, 1.0744231, 1.4267329, 1.8665372, 2.4665236, 3.2203004, 4, N},
{0, 0.19010291, 0.44269502, 0.66081244, 0.95829803, 1.2627038, 1.6005131, 2.0043969, 2.6440792, 3.2979164, 4}
}, {
{0, 5, N, N, N, N, N, N, N, N, N},
{0, 1.1176668, 5, N, N, N, N, N, N, N, N},
{0, 0.66791451, 1.6688319, 5, N, N, N, N, N, N, N},
{0, 0.56513244, 1.2151262, 2.2046661, 5, N, N, N, N, N, N},
{0, 0.51955444, 0.96157616, 1.5293243, 2.5639, 5, N, N, N, N, N},
{0, 0.50639188, 0.83235806, 1.2596023, 1.8475783, 2.8751452, 5, N, N, N, N},
{0, 0.30821687, 0.60048282, 1.0057166, 1.4351804, 2.0372179, 3.0747592, 5, N, N, N},
{0, 0.28437388, 0.560866, 0.92278755, 1.3049414, 1.7620444, 2.4607313, 3.5198457, 5, N, N},
{0, 0.26883101, 0.53947717, 0.84076571, 1.1986721, 1.6077875, 2.165575, 2.9591467, 3.931181, 5, N},
{0, 0.25029126, 0.50162876, 0.75587535, 1.0861237, 1.4452776, 1.8865763, 2.5002809, 3.2476835, 4.0337272, 5}
}, {
{0, 6, N, N, N, N, N, N, N, N, N},
{0, 1.185726, 6, N, N, N, N, N, N, N, N},
{0, 0.69637311, 1.7772807, 6, N, N, N, N, N, N, N},
{0, 0.57470578, 1.2709187, 2.4227901, 6, N, N, N, N, N, N},
{0, 0.52996641, 1.0128419, 1.632214, 2.8718762, 6, N, N, N, N, N},
{0, 0.50426048, 0.87729794, 1.3428378, 2.0053113, 3.2981832, 6, N, N, N, N},
{0, 0.46658435, 0.76617205, 1.1726109, 1.6950468, 2.5514688, 4.1463666, 6, N, N, N},
{0, 0.50030917, 0.78596908, 1.1486269, 1.5887094, 2.2150676, 3.2805684, 4.4828262, 6, N, N},
{0, 0.27919531, 0.56878412, 0.88591647, 1.2631332, 1.7201432, 2.3851209, 3.392889, 4.6255312, 6, N},
{0, 0.25088972, 0.50369233, 0.78494686, 1.1030188, 1.482311, 1.9812444, 2.6906328, 3.734978, 4.7532525, 6}
}, {
{0, 7, N, N, N, N, N, N, N, N, N},
{0, 1.2437892, 7, N, N, N, N, N, N, N, N},
{0, 0.72099203, 1.8771845, 7, N, N, N, N, N, N, N},
{0, 0.58251196, 1.3139123, 2.6157444, 7, N, N, N, N, N, N},
{0, 0.5371021, 1.0473768, 1.7166929, 3.1448426, 7, N, N, N, N, N},
{0, 0.51312029, 0.92989367, 1.4221185, 2.2125893, 3.6739931, 7, N, N, N, N},
{0, 0.50083971, 0.84841007, 1.2561073, 1.8532455, 2.8668625, 4.7535434, 7, N, N, N},
{0, 0.3375614, 0.63945627, 1.0301709, 1.4884938, 2.073761, 3.1614799, 5.0744987, 7, N, N},
{0, 0.29428458, 0.58668923, 0.93714356, 1.3736334, 1.8300356, 2.6405344, 3.9042048, 5.3097196, 7, N},
{0, 0.25234449, 0.52068585, 0.79422623, 1.1273863, 1.5991755, 2.1453006, 2.8984315, 4.1899557, 5.4597921, 7}
}, {
{0, 8, N, N, N, N, N, N, N, N, N},
{0, 1.2942501, 8, N, N, N, N, N, N, N, N},
{0, 0.74332041, 1.9693407, 8, N, N, N, N, N, N, N},
{0, 0.58823597, 1.3483386, 2.7880962, 8, N, N, N, N, N, N},
{0, 0.56661958, 1.2263036, 1.9593971, 3.6037345, 8, N, N, N, N, N},
{0, 0.52106231, 0.97026396, 1.486012, 2.3670862, 4.1632919, 8, N, N, N, N},
{0, 0.50727636, 0.86810225, 1.3293955, 2.0115428, 3.1358411, 5.3943086, 8, N, N, N},
{0, 0.47202346, 0.77812189, 1.1608884, 1.6648751, 2.4694417, 3.9094045, 5.7665443, 8, N, N},
{0, 0.37446901, 0.66116196, 1.038642, 1.4625595, 2.0528309, 2.9814169, 4.4429126, 5.9815373, 8, N},
{0, 0.26310974, 0.54373014, 0.84282249, 1.2090484, 1.6551158, 2.3275802, 3.3196113, 4.7216973, 6.1578932, 8}
}, {
{0, 9, N, N, N, N, N, N, N, N, N},
{0, 1.3413963, 9, N, N, N, N, N, N, N, N},
{0, 0.76222414, 2.0542119, 9, N, N, N, N, N, N, N},
{0, 0.59559792, 1.3777219, 2.946173, 9, N, N, N, N, N, N},
{0, 0.56240517, 1.1527119, 1.9145473, 3.6841569, 9, N, N, N, N, N},
{0, 0.52387071, 0.98832464, 1.5376476, 2.5417714, 4.4669261, 9, N, N, N, N},
{0, 0.50359035, 0.87327009, 1.3558764, 2.0646384, 3.3180211, 5.9420524, 9, N, N, N},
{0, 0.50140077, 0.83020425, 1.256588, 1.7709454, 2.7100441, 4.4434023, 6.3934889, 9, N, N},
{0, 0.36521655, 0.65757704, 1.0627806, 1.5081434, 2.1497617, 3.1920822, 4.870122, 6.6418982, 9, N},
{0, 0.31160679, 0.59032226, 0.94745982, 1.3620865, 1.8115216, 2.6007423, 3.8324564, 5.2064519, 6.8468728, 9}
}, {
{0, 10, N, N, N, N, N, N, N, N, N},
{0, 1.3838946, 10, N, N, N, N, N, N, N, N},
{0, 0.77946955, 2.1342247, 10, N, N, N, N, N, N, N},
{0, 0.60070014, 1.4040204, 3.0944126, 10, N, N, N, N, N, N},
{0, 0.55609542, 1.1508646, 1.9495349, 3.9375696, 10, N, N, N, N, N},
{0, 0.5350194, 1.031119, 1.6607633, 2.8520992, 5.4718146, 10, N, N, N, N},
{0, 0.5083549, 0.90783268, 1.4059756, 2.1796026, 3.571064, 6.5497985, 10, N, N, N},
{0, 0.50199872, 0.85233968, 1.2647815, 1.8777326, 2.8592849, 4.7821364, 7.0110598, 10, N, N},
{0, 0.46663594, 0.75212663, 1.1302133, 1.6134665, 2.3560972, 3.6558499, 5.3189116, 7.2945781, 10, N},
{0, 0.3789258, 0.64023608, 1.0374272, 1.4685256, 2.0717783, 3.0241971, 4.2591534, 5.6669927, 7.5286098, 10}
}, {
{0, 11, N, N, N, N, N, N, N, N, N},
{0, 1.4234025, 11, N, N, N, N, N, N, N, N},
{0, 0.79513794, 2.2098076, 11, N, N, N, N, N, N, N},
{0, 0.60728961, 1.4287171, 3.2358651, 11, N, N, N, N, N, N},
{0, 0.55890071, 1.165283, 2.0149148, 4.1530919, 11, N, N, N, N, N},
{0, 0.55071467, 1.0660659, 1.7177736, 3.0094495, 6.0395317, 11, N, N, N, N},
{0, 0.5066433, 0.89661205, 1.4050072, 2.2117786, 3.7080047, 7.0954437, 11, N, N, N},
{0, 0.50242329, 0.86727452, 1.3264461, 1.9118301, 2.9509099, 5.1184769, 7.624764, 11, N, N},
{0, 0.47785854, 0.78873962, 1.1769236, 1.6880652, 2.4978926, 4.0288033, 5.7288432, 7.9420485, 11, N},
{0, 0.50532979, 0.79486167, 1.1706896, 1.6148115, 2.2648265, 3.3499777, 4.5595574, 6.116312, 8.2049971, 11}
}
};
/*
** this is only for retreiving part of the table above
*/
int
217 gageOptimSigSet( double *scale, unsigned int num, unsigned int sigmaMax ) {
static const char me[]="gageOptimSigSet";
unsigned int si;
if ( !scale ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_CL( 2, num, GAGE_OPTIMSIG_SAMPLES_MAXNUM ) ) {
biffAddf( GAGE,
"%s: requested # sigma samples %u not in known range [2, %u]",
me, num, GAGE_OPTIMSIG_SAMPLES_MAXNUM );
return 1;
}
if ( !AIR_IN_CL( 1, sigmaMax, GAGE_OPTIMSIG_SIGMA_MAX ) ) {
biffAddf( GAGE, "%s: requested sigma max %u not in known range [1, %u]",
me, sigmaMax, GAGE_OPTIMSIG_SIGMA_MAX );
return 1;
}
for ( si=0; si<num; si++ ) {
scale[si] = _optimSigTable[sigmaMax-1][num-2][si];
}
return 0;
}
/* ------- from here down is the stuff for computing the table ------ */
/* rho is a stand-in for tau - and something that will likely change
based on the findings from using this code; the idea is that it
reflects the needed density of samples for optimal scale-space
reconstruction. In order to be used for the internal workings of
the sigma optimization, its important that the conversion between
sigma and rho be accurately invertible. */
/*
**
** This is a decent approximation of tau( sigma ), made of a slightly
** scaled version of the taylor expansion of tau( sigma=0 ) which meets
** up with the large-scale approximation of tau from Lindeberg.
** However, because its so flat at sigma=0, its not really invertible
** there, so its a poor basis for computations that are parameterized
** by rho. Keeping it around for reference.
static double
_RhoOfSig( double sig ) {
double rho;
if ( sig < 1.05189095 ) {
rho = sig*sig*( 0.2775733212544225 + 0.13078298856958057*sig*sig );
} else {
double tee;
tee = sig*sig;
rho = 0.53653222368715360118 + log( tee )/2.0 + log( 1.0 - 1.0/( 8.0*tee ) );
}
return rho;
}
static double
_SigOfRho( double rho ) {
double sig;
if ( rho < 0.46724360022171363 ) {
sig = 0.00033978812426865065 *
sqrt( -9.191366355042886e6 + 245.3752559286824 *
sqrt( 1.403132301e9 + 9.526961876920057e9*rho ) );
} else {
double ee, tee;
ee = exp( 2.0*rho );
tee = 0.0063325739776461107152*( 27.0*ee + 2*AIR_PI*AIR_PI
+ 3.0*sqrt( 81.0*ee*ee
+ 12*ee*AIR_PI*AIR_PI ) );
sig = sqrt( tee );
}
return sig;
}
*/
static double
296 _RhoOfSig( double sig ) {
return log( sig + 1 );
}
static double
302 _SigOfRho( double rho ) {
return exp( rho )-1;
}
/*
** allocates context, with error checking
** does use biff
*/
312 gageOptimSigContext *gageOptimSigContextNew( unsigned int dim,
unsigned int sampleNumMax,
unsigned int trueImgNum,
double sigmaMin, double sigmaMax,
double cutoff ) {
static const char me[]="gageOptimSigContextNew";
gageOptimSigContext *oscx;
unsigned int support, ii;
double kparm[2];
oscx = AIR_CALLOC( 1, gageOptimSigContext );
if ( !oscx ) {
biffAddf( GAGE, "%s: couldn't allocate context", me );
return NULL;
}
if ( !AIR_IN_CL( 1, dim, 3 ) ) {
biffAddf( GAGE, "%s: dim %u not 1, 2, or 3", me, dim );
return NULL;
}
if ( !( sampleNumMax >= 3 ) ) {
biffAddf( GAGE, "%s: sampleNumMax %u not >= 3", me, sampleNumMax );
return NULL;
}
if ( !( trueImgNum >= 3 ) ) {
biffAddf( GAGE, "%s: trueImgNum %u not >= 3", me, trueImgNum );
return NULL;
}
if ( !( sigmaMin >= 0 && sigmaMax > sigmaMin && cutoff > 0 ) ) {
biffAddf( GAGE, "%s: sigmaMin %g, sigmaMax %g, cutoff %g not valid", me,
sigmaMin, sigmaMax, cutoff );
return NULL;
}
oscx->dim = dim;
oscx->sampleNumMax = sampleNumMax;
oscx->trueImgNum = trueImgNum;
oscx->sigmaRange[0] = sigmaMin;
oscx->sigmaRange[1] = sigmaMax;
oscx->cutoff = cutoff;
/* will be set later */
oscx->kssSpec = NULL;
oscx->sampleNum = 0;
oscx->maxIter = 0;
oscx->imgMeasr = nrrdMeasureUnknown;
oscx->allMeasr = nrrdMeasureUnknown;
oscx->convEps = AIR_NAN;
/* allocate internal buffers based on arguments */
kparm[0] = oscx->sigmaRange[1];
kparm[1] = oscx->cutoff;
support = AIR_ROUNDUP( nrrdKernelDiscreteGaussian->support( kparm ) );
oscx->sx = 2*support - 1;
oscx->sy = dim >= 2 ? 2*support - 1 : 1;
oscx->sz = dim >= 3 ? 2*support - 1 : 1;
/*
fprintf( stderr, "%s: max sigma = %g, cutoff %g ==> support=%u, "
"3D vol size=%u x %u x %u\n", me,
sigmaMax, cutoff, support, oscx->sx, oscx->sy, oscx->sz );
*/
oscx->nerr = nrrdNew( );
oscx->ninterp = nrrdNew( );
oscx->ndiff = nrrdNew( );
if ( nrrdMaybeAlloc_va( oscx->nerr, nrrdTypeDouble, 1,
AIR_CAST( size_t, oscx->trueImgNum ) )
|| nrrdMaybeAlloc_va( oscx->ninterp, nrrdTypeDouble, 3,
AIR_CAST( size_t, oscx->sx ),
AIR_CAST( size_t, oscx->sy ),
AIR_CAST( size_t, oscx->sz ) )
|| nrrdMaybeAlloc_va( oscx->ndiff, nrrdTypeDouble, 3,
AIR_CAST( size_t, oscx->sx ),
AIR_CAST( size_t, oscx->sy ),
AIR_CAST( size_t, oscx->sz ) ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't allocate buffers", me );
return NULL;
}
nrrdAxisInfoSet_va( oscx->ninterp, nrrdAxisInfoSpacing,
1.0, 1.0, 1.0 );
nrrdAxisInfoSet_va( oscx->ndiff, nrrdAxisInfoSpacing,
1.0, 1.0, 1.0 );
oscx->rhoRange[0] = _RhoOfSig( oscx->sigmaRange[0] );
oscx->rhoRange[1] = _RhoOfSig( oscx->sigmaRange[1] );
nrrdAxisInfoSet_va( oscx->nerr, nrrdAxisInfoMin,
oscx->rhoRange[0] );
nrrdAxisInfoSet_va( oscx->nerr, nrrdAxisInfoMax,
oscx->rhoRange[1] );
fprintf( stderr, "!%s: sigma [%g, %g] -> rho [%g, %g]\n", me,
oscx->sigmaRange[0], oscx->sigmaRange[1],
oscx->rhoRange[0], oscx->rhoRange[1] );
fprintf( stderr, "!%s: rho %g -- %g\n", me,
oscx->rhoRange[0], oscx->rhoRange[1] );
for ( ii=0; ii<oscx->trueImgNum; ii++ ) {
double rr, ss, rc, eps=1e-13;
rr = AIR_AFFINE( 0, ii, oscx->trueImgNum-1,
oscx->rhoRange[0], oscx->rhoRange[1] );
ss = _SigOfRho( rr );
rc = _RhoOfSig( ss );
/*
fprintf( stderr, "!%s: ( %u ) rho %.17g -> sig %.17g -> rho %.17g ( %.17g )\n",
me, ii, rr, ss, rc, AIR_ABS( rr - rc )/( rr + eps/2 ) );
*/
if ( AIR_ABS( rr - rc )/( rr + eps ) > eps ) {
biffAddf( GAGE, "%s: rho %g -> sig %g -> rho %g has error %g > %g; "
"_SigOfRho( ) and _RhoOfSig( ) not invertible",
me, rr, ss, rc, AIR_ABS( rr - rc )/( rr + eps/2 ), eps );
return NULL;
}
}
oscx->kloc = AIR_CALLOC( oscx->sx, double );
oscx->kern = AIR_CALLOC( oscx->sx, double );
oscx->ktmp1 = AIR_CALLOC( oscx->sx, double );
oscx->ktmp2 = AIR_CALLOC( oscx->sx, double );
if ( !( oscx->kloc && oscx->kern && oscx->ktmp1 && oscx->ktmp2 ) ) {
biffAddf( GAGE, "%s: couldn't allocate kernel buffers", me );
return NULL;
}
for ( ii=0; ii<oscx->sx; ii++ ) {
oscx->kloc[ii] = AIR_CAST( double, ii ) - ( ( oscx->sx + 1 )/2 - 1 );
}
oscx->kone[0] = 1.0;
oscx->gctx = NULL;
oscx->pvlBase = NULL;
oscx->pvlSS = AIR_CALLOC( oscx->sampleNumMax, gagePerVolume * );
oscx->nsampleImg = AIR_CALLOC( oscx->sampleNumMax, Nrrd * );
oscx->sampleSigma = AIR_CALLOC( oscx->sampleNumMax, double );
oscx->sampleRho = AIR_CALLOC( oscx->sampleNumMax, double );
oscx->sampleTmp = AIR_CALLOC( oscx->sampleNumMax, double );
oscx->sampleErrMax = AIR_CALLOC( oscx->sampleNumMax, double );
oscx->step = AIR_CALLOC( oscx->sampleNumMax, double );
if ( !( oscx->pvlSS && oscx->nsampleImg
&& oscx->sampleSigma && oscx->sampleRho
&& oscx->sampleTmp && oscx->sampleErrMax && oscx->step ) ) {
biffAddf( GAGE, "%s: couldn't allocate per-sample arrays", me );
return NULL;
}
for ( ii=0; ii<oscx->sampleNumMax; ii++ ) {
oscx->nsampleImg[ii] = nrrdNew( );
if ( nrrdMaybeAlloc_va( oscx->nsampleImg[ii], nrrdTypeDouble, 3,
AIR_CAST( size_t, oscx->sx ),
AIR_CAST( size_t, oscx->sy ),
AIR_CAST( size_t, oscx->sz ) ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't allocate vol[%u]", me, ii );
return NULL;
}
nrrdAxisInfoSet_va( oscx->nsampleImg[ii], nrrdAxisInfoSpacing,
1.0, 1.0, 1.0 );
}
/* implementation not started
oscx->nsampleHist = nrrdNew( );
*/
return oscx;
}
gageOptimSigContext *
471 gageOptimSigContextNix( gageOptimSigContext *oscx ) {
if ( oscx ) {
unsigned int si;
nrrdKernelSpecNix( oscx->kssSpec );
nrrdNuke( oscx->nerr );
nrrdNuke( oscx->ninterp );
nrrdNuke( oscx->ndiff );
airFree( oscx->kloc );
airFree( oscx->kern );
airFree( oscx->ktmp1 );
airFree( oscx->ktmp2 );
gageContextNix( oscx->gctx );
/* airFree( oscx->pvlSS ); needed? */
for ( si=0; si<oscx->sampleNumMax; si++ ) {
nrrdNuke( oscx->nsampleImg[si] );
}
airFree( oscx->nsampleImg );
airFree( oscx->sampleSigma );
airFree( oscx->sampleRho );
airFree( oscx->sampleTmp );
airFree( oscx->sampleErrMax );
airFree( oscx->step );
/* nrrdNuke( oscx->nsampleHist ); */
airFree( oscx );
}
return NULL;
}
static int
501 _volInterp( Nrrd *ninterp, double rho, gageOptimSigContext *oscx ) {
static const char me[]="_volInterp";
double *interp, scaleIdx, sigma;
const double *answer;
unsigned int xi, yi, zi;
int outside;
/*
debugging = rho > 1.197;
gageParmSet( oscx->gctx, gageParmVerbose, 2*debugging );
*/
sigma = _SigOfRho( rho );
scaleIdx = gageStackWtoI( oscx->gctx, sigma, &outside );
/* Because of limited numerical precision, _SigOfRho( rhoRange[1] )
can end up "outside" stack, which should really be a bug.
However, since the use of gage is pretty straight-forward here,
we're okay with ignoring the "outside" here, and also clamping
the probe below */
answer = gageAnswerPointer( oscx->gctx, oscx->pvlBase, gageSclValue );
interp = AIR_CAST( double *, ninterp->data );
for ( zi=0; zi<oscx->sz; zi++ ) {
for ( yi=0; yi<oscx->sy; yi++ ) {
for ( xi=0; xi<oscx->sx; xi++ ) {
if ( gageStackProbeSpace( oscx->gctx, xi, yi, zi, scaleIdx,
AIR_TRUE /* index space */,
AIR_TRUE /* clamping */ ) ) {
biffAddf( GAGE, "%s: probe error at ( %u, %u, %u, %.17g ): %s ( %d )", me,
xi, yi, zi, scaleIdx,
oscx->gctx->errStr, oscx->gctx->errNum );
return 1;
}
interp[xi + oscx->sx*( yi + oscx->sy*zi )] = answer[0];
}
}
}
/*
gageParmSet( oscx->gctx, gageParmVerbose, 0 );
*/
/*
if ( debugging ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "interp-%04u.nrrd", debugii );
nrrdSave( fname, ninterp, NULL );
}
*/
return 0;
}
static void
550 _kernset( double **kzP, double **kyP, double **kxP,
gageOptimSigContext *oscx,
double rho ) {
NrrdKernel *dg;
double sig, kparm[NRRD_KERNEL_PARMS_NUM],
*kloc, *kern, *ktmp1, *ktmp2;
unsigned int ki, kj, kk, sx;
kern = oscx->kern;
kloc = oscx->kloc;
ktmp1 = oscx->ktmp1;
ktmp2 = oscx->ktmp2;
sx = oscx->sx;
sig = _SigOfRho( rho );
dg = nrrdKernelDiscreteGaussian;
kparm[1] = oscx->cutoff;
if ( sig < GOOD_SIGMA_MAX ) {
/* for small sigma, can evaluate directly into kern */
kparm[0] = sig;
dg->evalN_d( kern, kloc, sx, kparm );
} else {
double timeleft, tdelta;
unsigned int rx;
rx = ( sx + 1 )/2 - 1;
/* we have to iteratively blur */
kparm[0] = GOOD_SIGMA_MAX;
dg->evalN_d( kern, kloc, sx, kparm );
timeleft = sig*sig - GOOD_SIGMA_MAX*GOOD_SIGMA_MAX;
do {
tdelta = AIR_MIN( GOOD_SIGMA_MAX*GOOD_SIGMA_MAX, timeleft );
kparm[0] = sqrt( tdelta );
dg->evalN_d( ktmp1, kloc, sx, kparm );
for ( ki=0; ki<sx; ki++ ) {
double csum = 0.0;
for ( kj=0; kj<sx; kj++ ) {
kk = ki - kj + rx;
if ( kk < sx ) {
csum += kern[kk]*ktmp1[kj];
}
}
ktmp2[ki] = csum;
}
for ( ki=0; ki<sx; ki++ ) {
kern[ki] = ktmp2[ki];
}
timeleft -= tdelta;
} while ( timeleft );
}
*kzP = oscx->dim >= 3 ? kern : oscx->kone;
*kyP = oscx->dim >= 2 ? kern : oscx->kone;
*kxP = kern;
return;
}
/*
** sets one of the sampleImg, to be used as a sample in scale-space interp
*/
static void
608 _sampleSet( gageOptimSigContext *oscx, unsigned int si, double rho ) {
double *vol, *kz, *ky, *kx;
unsigned int ii, xi, yi, zi;
oscx->sampleSigma[si] = _SigOfRho( rho );
oscx->sampleRho[si] = rho;
vol = AIR_CAST( double *, oscx->nsampleImg[si]->data );
_kernset( &kz, &ky, &kx, oscx, rho );
ii = 0;
for ( zi=0; zi<oscx->sz; zi++ ) {
for ( yi=0; yi<oscx->sy; yi++ ) {
for ( xi=0; xi<oscx->sx; xi++ ) {
vol[ii] = kz[zi]*ky[yi]*kx[xi];
ii++;
}
}
}
if ( oscx->gctx ) {
/* the gage stack needs to know new scale pos */
oscx->gctx->stackPos[si] = oscx->sampleSigma[si];
/* HEY: GLK forgets why this is needed,
but remembers it was a tricky bug to find */
gagePointReset( &( oscx->gctx->point ) );
}
return;
}
static int
636 _errSingle( double *retP, gageOptimSigContext *oscx, double rho ) {
static const char me[]="_errSingle";
double *diff, *kx, *ky, *kz;
const double *interp;
unsigned int ii, xi, yi, zi;
if ( _volInterp( oscx->ninterp, rho, oscx ) ) {
biffAddf( GAGE, "%s: trouble at rho %.17g\n", me, rho );
return 1;
}
/*
if ( debugging ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "interp-%04u.nrrd", debugii );
nrrdSave( fname, oscx->ninterp, NULL );
}
*/
interp = AIR_CAST( const double *, oscx->ninterp->data );
diff = AIR_CAST( double *, oscx->ndiff->data );
_kernset( &kz, &ky, &kx, oscx, rho );
ii = 0;
for ( zi=0; zi<oscx->sz; zi++ ) {
for ( yi=0; yi<oscx->sy; yi++ ) {
for ( xi=0; xi<oscx->sx; xi++ ) {
double tru;
tru = kz[zi]*ky[yi]*kx[xi];
diff[ii] = interp[ii] - tru;
if ( debugReconErrArr ) {
unsigned int idx = airArrayLenIncr( debugReconErrArr, 2 );
debugReconErr[idx + 0] = tru;
debugReconErr[idx + 1] = interp[ii];
}
ii++;
}
}
}
nrrdMeasureLine[oscx->imgMeasr]( retP, nrrdTypeDouble,
diff, nrrdTypeDouble,
ii /* HEY: cleverness */,
AIR_NAN, AIR_NAN );
return 0;
}
static int
680 _errTotal( double *retP, gageOptimSigContext *oscx ) {
static const char me[]="_errTotal";
unsigned int ii;
double *err;
err = AIR_CAST( double *, oscx->nerr->data );
for ( ii=0; ii<oscx->trueImgNum; ii++ ) {
/* debugii = ii; */
if ( _errSingle( err + ii, oscx, AIR_AFFINE( 0, ii, oscx->trueImgNum-1,
oscx->rhoRange[0],
oscx->rhoRange[1] ) ) ) {
biffAddf( GAGE, "%s: trouble at ii %u", me, ii );
return 1;
}
}
nrrdMeasureLine[oscx->allMeasr]( retP, nrrdTypeDouble,
err, nrrdTypeDouble,
oscx->trueImgNum,
AIR_NAN, AIR_NAN );
/*
if ( debugging ) {
char fname[AIR_STRLEN_SMALL];
unsigned int ni;
for ( ni=0; ni<oscx->sampleNum; ni++ ) {
sprintf( fname, "sample-%04u.nrrd", ni );
nrrdSave( fname, oscx->nsampleImg[ni], NULL );
}
}
*/
if ( 0 ) {
static unsigned int call=0;
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "err-%04u.nrrd", call );
nrrdSave( fname, oscx->nerr, NULL );
call++;
}
return 0;
}
static int
720 _errTotalLinf( double *retP, gageOptimSigContext *oscx,
unsigned int mmIdx[2], double mmErr[2] ) {
static const char me[]="_errTotalLinf";
unsigned int ii, pi;
double *err, *sem, *rr, rho, sig, pid;
int outside;
err = AIR_CAST( double *, oscx->nerr->data );
sem = oscx->sampleErrMax;
rr = oscx->rhoRange;
for ( pi=0; pi<=oscx->sampleNum-2; pi++ ) {
sem[pi] = 0;
}
/* NOTE: we don't bother with last "true image": it will always be a
low error, and not meaningfully associated with a gap */
for ( ii=0; ii<oscx->trueImgNum-1; ii++ ) {
/* debugii = ii; */
rho = AIR_AFFINE( 0, ii, oscx->trueImgNum-1, rr[0], rr[1] );
if ( _errSingle( err + ii, oscx, rho ) ) {
biffAddf( GAGE, "%s: trouble at ii %u", me, ii );
return 1;
}
sig = _SigOfRho( rho );
pid = gageStackWtoI( oscx->gctx, sig, &outside );
pi = AIR_CAST( unsigned int, pid );
if ( outside || !( pi <= oscx->sampleNum-2 ) ) {
biffAddf( GAGE, "%s: ii %u -> rho %g -> sig %g -> pi %u-> OUTSIDE",
me, ii, rho, sig, pi );
return 1;
}
sem[pi] = AIR_MAX( sem[pi], err[ii] );
}
mmIdx[0] = mmIdx[1] = 0;
mmErr[0] = mmErr[1] = sem[0];
for ( pi=1; pi<=oscx->sampleNum-2; pi++ ) {
if ( sem[pi] < mmErr[0] ) {
mmIdx[0] = pi;
mmErr[0] = sem[pi];
}
if ( sem[pi] > mmErr[1] ) {
mmIdx[1] = pi;
mmErr[1] = sem[pi];
}
}
/* returned error invented, not really Linf, but minimizing this
does the right thing */
*retP = 1000*oscx->sampleNum*( mmErr[1] - mmErr[0] )/( rr[1] - rr[0] );
if ( 0 ) {
static unsigned int call=0;
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "err-%04u.nrrd", call );
nrrdSave( fname, oscx->nerr, NULL );
call++;
}
return 0;
}
/*
** this can be called repeatedly
*/
static int
781 _gageSetup( gageOptimSigContext *oscx ) {
static const char me[]="_gageSetup";
double kparm[NRRD_KERNEL_PARMS_NUM];
int E;
if ( oscx->gctx ) {
gageContextNix( oscx->gctx );
}
oscx->gctx = gageContextNew( );
gageParmSet( oscx->gctx, gageParmVerbose, 0 );
gageParmSet( oscx->gctx, gageParmRenormalize, AIR_FALSE );
gageParmSet( oscx->gctx, gageParmCheckIntegrals, AIR_FALSE );
gageParmSet( oscx->gctx, gageParmOrientationFromSpacing, AIR_TRUE );
gageParmSet( oscx->gctx, gageParmStackUse, AIR_TRUE );
E = 0;
if ( !E ) E |= !( oscx->pvlBase = gagePerVolumeNew( oscx->gctx,
oscx->nsampleImg[0],
gageKindScl ) );
if ( !E ) E |= gageStackPerVolumeNew( oscx->gctx, oscx->pvlSS,
AIR_CAST( const Nrrd*const*,
oscx->nsampleImg ),
oscx->sampleNum, gageKindScl );
if ( !E ) E |= gageStackPerVolumeAttach( oscx->gctx,
oscx->pvlBase, oscx->pvlSS,
oscx->sampleSigma, oscx->sampleNum );
kparm[0] = 1;
if ( !E ) E |= gageKernelSet( oscx->gctx, gageKernel00,
nrrdKernelBox, kparm );
if ( !E ) E |= gageKernelSet( oscx->gctx, gageKernelStack,
oscx->kssSpec->kernel, oscx->kssSpec->parm );
if ( !E ) E |= gageQueryItemOn( oscx->gctx, oscx->pvlBase, gageSclValue );
if ( !E ) E |= gageUpdate( oscx->gctx );
if ( E ) {
biffAddf( GAGE, "%s: problem setting up gage", me );
return 1;
}
return 0;
}
static char *
821 _timefmt( char tstr[AIR_STRLEN_MED], double deltim ) {
if ( deltim < 60 ) {
sprintf( tstr, "%g secs", deltim );
return tstr;
}
deltim /= 60;
if ( deltim < 60 ) {
sprintf( tstr, "%g mins", deltim );
return tstr;
}
deltim /= 60;
if ( deltim < 24 ) {
sprintf( tstr, "%g hours", deltim );
return tstr;
}
deltim /= 24;
if ( deltim < 7 ) {
sprintf( tstr, "%g days", deltim );
return tstr;
}
deltim /= 7;
sprintf( tstr, "%g weeks", deltim );
return tstr;
}
static int
848 _optsigrun( gageOptimSigContext *oscx ) {
static const char me[]="_optsigrun";
char tstr[AIR_STRLEN_MED];
unsigned int iter, pnt;
double lastErr, newErr, rhoeps, oppor, lastPos, backoff, decavg, time0;
int badStep;
/* used to debug failure to measure error meaningfully
{
unsigned int hi, hn=200;
double rr;
for ( hi=0; hi<hn; hi++ ) {
rr = AIR_AFFINE( -20, hi, hn+20, oscx->rhoRange[0], oscx->rhoRange[1] );
_sampleSet( oscx, 1, rr );
_errTotal( oscx );
}
}
*/
time0 = airTime( );
if ( _errTotal( &lastErr, oscx ) ) {
biffAddf( GAGE, "%s: first error measurement", me );
return 1;
}
fprintf( stderr, "%s: ( %s for initial error measr )\n", me,
_timefmt( tstr, airTime( ) - time0 ) );
newErr = AIR_NAN;
decavg = oscx->sampleNum; /* hack */
/* meaningful discrete difference for looking at error gradient is
bounded by the resolution of the sampling we're doing along scale */
rhoeps = 0.1*( oscx->rhoRange[1]-oscx->rhoRange[0] )/oscx->trueImgNum;
oppor = 1.3333;
backoff = 0.25;
/* initialize step for the moving samples: 1 through oscx->sampleNum-2 */
for ( pnt=1; pnt<oscx->sampleNum-1; pnt++ ) {
oscx->step[pnt] = 10;
}
for ( iter=0; iter<oscx->maxIter; iter++ ) {
double limit, err1, grad, delta;
unsigned int tryi;
int zerodelta, esgn;
esgn = 2*AIR_CAST( int, airRandInt( 2 ) ) - 1;
pnt = 1 + ( iter % ( oscx->sampleNum-2 ) );
lastPos = oscx->sampleRho[pnt];
fprintf( stderr, "%s: ***** iter %u; [[ err %g ]] %s\n",
me, iter, lastErr, _timefmt( tstr, airTime( ) - time0 ) );
limit = AIR_MIN( ( oscx->sampleRho[pnt] - oscx->sampleRho[pnt-1] )/3,
( oscx->sampleRho[pnt+1] - oscx->sampleRho[pnt] )/3 );
fprintf( stderr, ". pnt %u: pos %g, step %g\n",
pnt, lastPos, oscx->step[pnt] );
fprintf( stderr, ". limit = min( ( %g-%g )/3, ( %g-%g )/3 ) = %g\n",
oscx->sampleRho[pnt], oscx->sampleRho[pnt-1],
oscx->sampleRho[pnt+1], oscx->sampleRho[pnt], limit );
_sampleSet( oscx, pnt, lastPos + esgn*rhoeps );
if ( _errTotal( &err1, oscx ) ) {
biffAddf( GAGE, "%s: for err1 ( %u -> %.17g )",
me, pnt, lastPos + esgn*rhoeps );
return 1;
}
_sampleSet( oscx, pnt, lastPos );
grad = ( err1 - lastErr )/( esgn*rhoeps );
fprintf( stderr, ". grad = %g\n", grad );
delta = -grad*oscx->step[pnt];
if ( !AIR_EXISTS( delta ) ) {
biffAddf( GAGE, "%s: got non-exist delta %g on iter %u ( pnt %u ) err %g",
me, delta, iter, pnt, lastErr );
return 1;
}
if ( AIR_ABS( delta ) > limit ) {
oscx->step[pnt] *= limit/AIR_ABS( delta );
fprintf( stderr, ". step *= %g/%g -> %g\n",
limit, AIR_ABS( delta ), oscx->step[pnt] );
delta = -grad*oscx->step[pnt];
}
fprintf( stderr, ". delta = %g\n", delta );
tryi = 0;
badStep = AIR_FALSE;
do {
if ( tryi == oscx->maxIter ) {
biffAddf( GAGE, "%s: confusion ( tryi %u ) on iter %u ( pnt %u ) err %g",
me, tryi, iter, pnt, lastErr );
return 1;
}
if ( !delta ) {
fprintf( stderr, "... try %u: delta = 0; nothing to do\n", tryi );
newErr = lastErr;
zerodelta = AIR_TRUE;
} else {
zerodelta = AIR_FALSE;
_sampleSet( oscx, pnt, lastPos + delta );
if ( _errTotal( &newErr, oscx ) ) {
biffAddf( GAGE, "%s: for newErr ( %u -> %.17g )", me,
pnt, lastPos + delta );
return 1;
}
badStep = newErr > lastErr;
fprintf( stderr, "... try %u: pos[%u] %g + %g = %g;\n"
"%s: err %g %s %g\n",
tryi, pnt, lastPos, delta,
oscx->sampleRho[pnt],
badStep ? "*BAD*" : "good",
newErr, newErr > lastErr ? ">" : "<=", lastErr );
if ( badStep ) {
oscx->step[pnt] *= backoff;
if ( oscx->step[pnt] < rhoeps/1000 ) {
/* step got so small its stupid to be moving this point */
fprintf( stderr, "... !! step %g < %g pointlessly small, "
"moving on\n", oscx->step[pnt], rhoeps/1000 );
_sampleSet( oscx, pnt, lastPos );
newErr = lastErr;
badStep = AIR_FALSE;
} else {
delta = -grad*oscx->step[pnt];
}
}
}
tryi++;
} while ( badStep );
if ( !zerodelta ) {
/* don't update decavg if we moved on because slope was EXACTLY zero */
decavg = AIR_AFFINE( 0, 1, oscx->sampleNum,
decavg, ( lastErr - newErr )/lastErr );
oscx->step[pnt] *= oppor;
}
if ( decavg <= oscx->convEps ) {
fprintf( stderr, "%s: converged ( %g <= %g ) after %u iters\n", me,
decavg, oscx->convEps, iter );
break;
} else {
fprintf( stderr, "%s: _____ iter %u done; decavg = %g > eps %g\n", me,
iter, decavg, oscx->convEps );
}
lastErr = newErr;
}
if ( iter == oscx->maxIter && decavg > oscx->convEps ) {
biffAddf( GAGE, "%s: failed to converge ( %g > %g ) after %u iters\n", me,
decavg, oscx->convEps, iter );
return 1;
}
oscx->finalErr = lastErr;
return 0;
}
static int
993 _optsigrunLinf( gageOptimSigContext *oscx ) {
static const char me[]="_optsigrunLinf";
char tstr[AIR_STRLEN_MED];
double *srho, *stmp, time0, lastErr, newErr, decavg,
step, oppor, backoff, ceps, mmErr[2];
unsigned int iter, si, sn, mmIdx[2];
int shrink;
time0 = airTime( );
if ( _errTotalLinf( &lastErr, oscx, mmIdx, mmErr ) ) {
biffAddf( GAGE, "%s: first error measurement", me );
return 1;
}
fprintf( stderr, "%s: ( init ) min %u %g max %u %g\n", me,
mmIdx[0], mmErr[0], mmIdx[1], mmErr[1] );
fprintf( stderr, "%s: ( %s for initial error measr )\n", me,
_timefmt( tstr, airTime( ) - time0 ) );
newErr = AIR_NAN;
/* shorcuts */
sn = oscx->sampleNum;
srho = oscx->sampleRho;
stmp = oscx->sampleTmp;
/* Linf uses a single scalar step istead of oscx->step array */
step = 0.1;
oppor = 1.1;
backoff = 0.5;
/* more demanding for more points */
ceps = oscx->convEps/sn;
decavg = 2*ceps;
for ( iter=0; iter<oscx->maxIter; iter++ ) {
double midp, wlo, whi, glo, ghi, gerr;
unsigned int gap, tryi;
int badStep;
if ( iter % 2 ) {
/* we will grow gap around smallest peak */
gap = mmIdx[0];
gerr = mmErr[0];
shrink = AIR_FALSE;
} else {
/* we will shrink gap around tallest peak */
gap = mmIdx[1];
gerr = mmErr[1];
shrink = AIR_TRUE;
}
midp = ( srho[gap] + srho[gap+1] )/2;
fprintf( stderr, "%s: ---- iter %u ( step %g ): gap [%u]/%g ( %s )\n",
me, iter, step, gap, gerr,
shrink ? "shrinking tallest" : "growing lowest" );
/* save last set of positions to restore after bad step */
for ( si=1; si<sn-1; si++ ) {
stmp[si] = srho[si];
}
tryi = 0;
badStep = AIR_FALSE;
do {
if ( tryi == oscx->maxIter ) {
biffAddf( GAGE, "%s: confusion ( tryi %u ) on iter %u err %g",
me, tryi, iter, lastErr );
return 1;
}
if ( shrink ) {
wlo = AIR_AFFINE( 0, step, 1, srho[gap], midp );
whi = AIR_AFFINE( 0, step, 1, srho[gap+1], midp );
} else {
wlo = AIR_AFFINE( 0, step, -2, srho[gap], midp );
whi = AIR_AFFINE( 0, step, -2, srho[gap+1], midp );
}
glo = srho[gap];
ghi = srho[gap+1];
fprintf( stderr, "%s: rho[%u] %g | %g -- rho[%u] %g | %g\n", me,
gap, srho[gap], wlo, gap+1, srho[gap+1], whi );
for ( si=1; si<sn-1; si++ ) {
_sampleSet( oscx, si, ( si <= gap
? AIR_AFFINE( srho[0], srho[si], glo,
srho[0], wlo )
: AIR_AFFINE( ghi, srho[si], srho[sn-1],
whi, srho[sn-1] ) ) );
}
if ( _errTotalLinf( &newErr,
oscx, mmIdx, mmErr ) ) {
biffAddf( GAGE, "%s: iter %u", me, iter );
return 1;
}
fprintf( stderr, "%s: min %u %g max %u %g\n", me,
mmIdx[0], mmErr[0], mmIdx[1], mmErr[1] );
if ( iter % 3 ) {
badStep = newErr > lastErr;
fprintf( stderr, "... try %u [%u] step %g -> newErr %g %s "
"lastErr %g %s\n",
tryi, gap, step, newErr,
badStep ? ">" : "<=",
lastErr,
badStep ? "*BAD*" : "good" );
if ( badStep ) {
step *= backoff;
for ( si=1; si<sn-1; si++ ) {
srho[si] = stmp[si];
}
}
tryi++;
}
} while ( badStep );
step *= oppor;
decavg = ( decavg + ( lastErr - newErr ) )/2;
if ( AIR_IN_OP( 0, decavg, ceps ) ) {
fprintf( stderr, "%s: converged ( %g <= %g ) after %u iters\n", me,
decavg, ceps, iter );
break;
} else {
fprintf( stderr, "%s: iter %u done; decavg = %g > eps %g\n", me,
iter, decavg, ceps );
}
lastErr = newErr;
} /* for iter */
if ( oscx->maxIter
&& iter == oscx->maxIter
&& decavg > ceps ) {
biffAddf( GAGE, "%s: failed to converge ( %g > %g ) after %u iters\n", me,
decavg, ceps, iter );
return 1;
}
oscx->finalErr = lastErr;
return 0;
}
int
1125 gageOptimSigCalculate( gageOptimSigContext *oscx,
/* output */ double *sigma,
unsigned int sigmaNum,
const NrrdKernelSpec *kssSpec,
int imgMeasr, int allMeasr,
unsigned int maxIter, double convEps ) {
static const char me[]="gageOptimSigCalculate";
unsigned int ii;
if ( !( oscx && sigma && kssSpec ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( sigmaNum > oscx->sampleNumMax ) {
biffAddf( GAGE, "%s: initialized for max %u samples, not %u", me,
oscx->sampleNumMax, sigmaNum );
return 1;
}
/* initialize to uniform samples in rho */
oscx->sampleNum = sigmaNum;
fprintf( stderr, "%s: initializing %u samples ... ", me, oscx->sampleNum );
fflush( stderr );
for ( ii=0; ii<oscx->sampleNum; ii++ ) {
_sampleSet( oscx, ii, AIR_AFFINE( 0, ii, oscx->sampleNum-1,
oscx->rhoRange[0], oscx->rhoRange[1] ) );
}
fprintf( stderr, "done.\n" );
/* copy remaining input parameters */
nrrdKernelSpecNix( oscx->kssSpec );
oscx->kssSpec = nrrdKernelSpecCopy( kssSpec );
oscx->imgMeasr = imgMeasr;
oscx->allMeasr = allMeasr;
oscx->convEps = convEps;
oscx->maxIter = maxIter;
oscx->convEps = convEps;
/* set up gage */
fprintf( stderr, "%s: setting up gage ... \n", me );
if ( _gageSetup( oscx ) ) {
biffAddf( GAGE, "%s: problem setting up gage", me );
return 1;
}
fprintf( stderr, "%s: ... gage setup done.\n", me );
/* run the optimization */
if ( oscx->sampleNum > 2 ) {
int EE;
EE = ( nrrdMeasureLinf == oscx->allMeasr
? _optsigrunLinf( oscx )
: _optsigrun( oscx ) );
if ( EE ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
} else {
fprintf( stderr, "%s: num == 2, no optimization, finding error ... ", me );
fflush( stderr );
if ( _errTotal( &( oscx->finalErr ), oscx ) ) {
biffAddf( GAGE, "%s: for finalErr", me );
return 1;
}
fprintf( stderr, "done.\n" );
}
/* save output */
for ( ii=0; ii<oscx->sampleNum; ii++ ) {
sigma[ii] = oscx->sampleSigma[ii];
}
return 0;
}
int
1200 gageOptimSigErrorPlot( gageOptimSigContext *oscx, Nrrd *nout,
const double *sigma,
unsigned int sigmaNum,
const NrrdKernelSpec *kssSpec,
int imgMeasr ) {
static const char me[]="gageOptimSigErrorPlot";
char doneStr[AIR_STRLEN_SMALL];
double *out;
unsigned int ii;
if ( !( oscx && nout && sigma ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( sigmaNum >= 2 ) ) {
biffAddf( GAGE, "%s: need sigmaNum >= 2 ( not %u )", me, sigmaNum );
return 1;
}
if ( sigmaNum > oscx->sampleNumMax ) {
biffAddf( GAGE, "%s: initialized for max %u samples, not %u", me,
oscx->sampleNumMax, sigmaNum );
return 1;
}
/* copy remaining input parms */
nrrdKernelSpecNix( oscx->kssSpec );
oscx->kssSpec = nrrdKernelSpecCopy( kssSpec );
oscx->sampleNum = sigmaNum;
oscx->maxIter = 0;
oscx->imgMeasr = imgMeasr;
oscx->allMeasr = nrrdMeasureUnknown;
oscx->convEps = AIR_NAN;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, oscx->trueImgNum ) ) ) {
biffMovef( GAGE, NRRD, "%s: trouble allocating output", me );
return 1;
}
out = AIR_CAST( double *, nout->data );
/* set up requested samples */
for ( ii=0; ii<oscx->sampleNum; ii++ ) {
_sampleSet( oscx, ii, _RhoOfSig( sigma[ii] ) );
}
if ( _gageSetup( oscx ) ) {
biffAddf( GAGE, "%s: problem setting up gage", me );
return 1;
}
fprintf( stderr, "%s: plotting ... ", me ); fflush( stderr );
for ( ii=0; ii<oscx->trueImgNum; ii++ ) {
double rho, err;
fprintf( stderr, "%s", airDoneStr( 0, ii, oscx->trueImgNum, doneStr ) );
fflush( stderr );
rho = AIR_AFFINE( 0, ii, oscx->trueImgNum-1,
oscx->rhoRange[0], oscx->rhoRange[1] );
out[0 + 2*ii] = rho;
/* debugii = ii; */
if ( _errSingle( &err, oscx, rho ) ) {
biffAddf( GAGE, "%s: plotting %u", me, ii );
return 1;
}
out[1 + 2*ii] = err;
/*
if ( AIR_IN_CL( 69, ii, 71 ) ) {
fprintf( stderr, "!%s: ----- %u : %g\n", me, ii, err );
}
*/
}
fprintf( stderr, "%s\n", airDoneStr( 0, ii, oscx->trueImgNum, doneStr ) );
/*
if ( 0 ) {
static unsigned int call=0;
char fname[AIR_STRLEN_SMALL];
unsigned int ni;
if ( 0 ) {
sprintf( fname, "err-%04u.nrrd", call );
nrrdSave( fname, oscx->nerr, NULL );
}
if ( debugging ) {
for ( ni=0; ni<oscx->sampleNum; ni++ ) {
sprintf( fname, "sample-%04u.nrrd", ni );
nrrdSave( fname, oscx->nsampleImg[ni], NULL );
}
}
call++;
debugging = ( 2 == call );
}
*/
return 0;
}
int
1294 gageOptimSigErrorPlotSliding( gageOptimSigContext *oscx, Nrrd *nout,
double windowRho,
unsigned int sampleNum,
const NrrdKernelSpec *kssSpec,
int imgMeasr ) {
static const char me[]="gageOptimSigRecondErrorPlotSliding";
char doneStr[AIR_STRLEN_SMALL];
unsigned int ii;
double *out;
char hackKeyStr[]="TEEM_OPTSIG_RECONERR";
if ( !( oscx && nout && kssSpec ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( windowRho <= 0 ) {
biffAddf( GAGE, "%s: need positive windowRho ( not %g )", me, windowRho );
return 1;
}
if ( windowRho > oscx->rhoRange[1] - oscx->rhoRange[0] ) {
biffAddf( GAGE, "%s: window %g > rhorange %g-%g=%g", me,
windowRho, oscx->rhoRange[1], oscx->rhoRange[0],
oscx->rhoRange[1] - oscx->rhoRange[0] );
return 1;
}
if ( nrrdGetenvString( &debugReconErrName, hackKeyStr ) ) {
fprintf( stderr, "%s: %s hack on: will save recon results to %s\n",
me, hackKeyStr, debugReconErrName );
debugReconErrArr = airArrayNew( AIR_CAST( void **, &( debugReconErr ) ), NULL,
sizeof( double ), 1000 );
}
/* copy remaining input parms */
nrrdKernelSpecNix( oscx->kssSpec );
oscx->kssSpec = nrrdKernelSpecCopy( kssSpec );
oscx->sampleNum = 3; /* hacky */
oscx->maxIter = 0;
oscx->imgMeasr = imgMeasr;
oscx->allMeasr = nrrdMeasureUnknown;
oscx->convEps = AIR_NAN;
oscx->sampleSigma[0] = oscx->sigmaRange[0]; /* just for gage setup */
oscx->sampleSigma[1] = oscx->sigmaRange[1]; /* just for gage setup */
oscx->sampleSigma[2] = oscx->sigmaRange[1]+1;
if ( _gageSetup( oscx ) ) {
biffAddf( GAGE, "%s: problem setting up gage", me );
return 1;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, sampleNum ) ) ) {
biffMovef( GAGE, NRRD, "%s: trouble allocating output", me );
return 1;
}
out = AIR_CAST( double *, nout->data );
fprintf( stderr, "%s: plotting ... ", me ); fflush( stderr );
for ( ii=0; ii<sampleNum; ii++ ) {
double rho, rlo, rhi, err;
fprintf( stderr, "%s", airDoneStr( 0, ii, oscx->trueImgNum, doneStr ) );
fflush( stderr );
rho = AIR_AFFINE( 0, ii, sampleNum,
oscx->rhoRange[0], oscx->rhoRange[1] );
rlo = rho - windowRho/2;
rhi = rho + windowRho/2;
if ( rlo < oscx->rhoRange[0]
|| rhi > oscx->rhoRange[1] ) {
if ( debugReconErrArr ) {
/* we have to simulate the updates to debugReconErrArr
that would happen with a call to _errSingle */
airArrayLenIncr( debugReconErrArr, 2*oscx->sz*oscx->sy*oscx->sx );
}
out[0 + 2*ii] = _SigOfRho( rho );
out[1 + 2*ii] = AIR_NAN;
continue;
}
/* required samples will slide along with plotting */
_sampleSet( oscx, 0, rlo );
_sampleSet( oscx, 1, rhi );
if ( _errSingle( &err, oscx, rho ) ) {
biffAddf( GAGE, "%s: plotting/sliding %u", me, ii );
return 1;
}
out[0 + 2*ii] = _SigOfRho( rho );
out[1 + 2*ii] = err;
}
fprintf( stderr, "%s\n", airDoneStr( 0, ii, oscx->trueImgNum, doneStr ) );
if ( debugReconErrArr ) {
Nrrd *nre = nrrdNew( );
nrrdWrap_va( nre, debugReconErr, nrrdTypeDouble, 3,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, oscx->sz*oscx->sy*oscx->sx ),
AIR_CAST( size_t, sampleNum ) );
nrrdSave( debugReconErrName, nre, NULL );
nrrdNix( nre );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
void
28 _gagePrint_off( FILE *file, gageContext *ctx ) {
int i, fd;
unsigned int *off;
fd = 2*ctx->radius;
off = ctx->off;
fprintf( file, "off[]:\n" );
switch( fd ) {
case 2:
fprintf( file, "% 6d % 6d\n", off[6], off[7] );
fprintf( file, " % 6d % 6d\n\n", off[4], off[5] );
fprintf( file, "% 6d % 6d\n", off[2], off[3] );
fprintf( file, " % 6d % 6d\n", off[0], off[1] );
break;
case 4:
for ( i=3; i>=0; i-- ) {
fprintf( file, "% 6d % 6d % 6d % 6d\n",
off[12+16*i], off[13+16*i],
off[14+16*i], off[15+16*i] );
fprintf( file, " % 6d %c% 6d % 6d%c % 6d\n",
off[ 8+16*i], ( i==1||i==2 )?'\\':' ',
off[ 9+16*i], off[10+16*i], ( i==1||i==2 )?'\\':' ',
off[11+16*i] );
fprintf( file, " % 6d %c% 6d % 6d%c % 6d\n",
off[ 4+16*i], ( i==1||i==2 )?'\\':' ',
off[ 5+16*i], off[ 6+16*i], ( i==1||i==2 )?'\\':' ',
off[ 7+16*i] );
fprintf( file, " % 6d % 6d % 6d % 6d\n",
off[ 0+16*i], off[ 1+16*i],
off[ 2+16*i], off[ 3+16*i] );
if ( i ) fprintf( file, "\n" );
}
break;
default:
for ( i=0; i<fd*fd*fd; i++ ) {
fprintf( file, " off[% 3d, % 3d, % 3d] = % 6d\n",
i%fd, ( i/fd )%fd, i/( fd*fd ), off[i] );
}
break;
}
}
#define PRINT_2( NN, C ) \
fw = fw##NN##C; \
fprintf( file, " --" #NN "-->% 15.7f % 15.7f\n", \
( float )fw[0], ( float )fw[1] )
#define PRINT_4( NN, C ) \
fw = fw##NN##C; \
fprintf( file, " --" #NN "-->% 15.7f % 15.7f % 15.7f % 15.7f\n", \
77 ( float )fw[0], ( float )fw[1], ( float )fw[2], ( float )fw[3] )
#define PRINT_N( NN, C ) \
fw = fw##NN##C; \
fprintf( file, " --" #NN "--> \n" ); \
for ( i=0; i<fd; i++ ) \
fprintf( file, " % 5d : % 15.7f\n", i, ( float )fw[i] )
#define PRINTALL( HOW, C ) \
if ( ctx->needK[gageKernel00] ) { HOW( 00, C ); } \
if ( ctx->needK[gageKernel10] ) { HOW( 10, C ); } \
if ( ctx->needK[gageKernel11] ) { HOW( 11, C ); } \
if ( ctx->needK[gageKernel20] ) { HOW( 20, C ); } \
if ( ctx->needK[gageKernel21] ) { HOW( 21, C ); } \
if ( ctx->needK[gageKernel22] ) { HOW( 22, C ); }
void
_gagePrint_fslw( FILE *file, gageContext *ctx ) {
int i, fd;
double *fslx, *fsly, *fslz, *fw,
*fw000, *fw001, *fw002,
*fw100, *fw101, *fw102,
*fw110, *fw111, *fw112,
*fw200, *fw201, *fw202,
*fw210, *fw211, *fw212,
*fw220, *fw221, *fw222;
/* float *p; */
fd = 2*ctx->radius;
fslx = ctx->fsl + fd*0;
fsly = ctx->fsl + fd*1;
fslz = ctx->fsl + fd*2;
fw000 = ctx->fw + 0 + fd*( 0 + 3*gageKernel00 );
fw001 = ctx->fw + 0 + fd*( 1 + 3*gageKernel00 );
fw002 = ctx->fw + 0 + fd*( 2 + 3*gageKernel00 );
fw100 = ctx->fw + 0 + fd*( 0 + 3*gageKernel10 );
fw101 = ctx->fw + 0 + fd*( 1 + 3*gageKernel10 );
fw102 = ctx->fw + 0 + fd*( 2 + 3*gageKernel10 );
fw110 = ctx->fw + 0 + fd*( 0 + 3*gageKernel11 );
fw111 = ctx->fw + 0 + fd*( 1 + 3*gageKernel11 );
fw112 = ctx->fw + 0 + fd*( 2 + 3*gageKernel11 );
fw200 = ctx->fw + 0 + fd*( 0 + 3*gageKernel20 );
fw201 = ctx->fw + 0 + fd*( 1 + 3*gageKernel20 );
fw202 = ctx->fw + 0 + fd*( 2 + 3*gageKernel20 );
fw210 = ctx->fw + 0 + fd*( 0 + 3*gageKernel21 );
fw211 = ctx->fw + 0 + fd*( 1 + 3*gageKernel21 );
fw212 = ctx->fw + 0 + fd*( 2 + 3*gageKernel21 );
fw220 = ctx->fw + 0 + fd*( 0 + 3*gageKernel22 );
fw221 = ctx->fw + 0 + fd*( 1 + 3*gageKernel22 );
fw222 = ctx->fw + 0 + fd*( 2 + 3*gageKernel22 );
fprintf( file, "fsl -> fw: \n" );
switch( fd ) {
case 2:
fprintf( file, "x[]: % 15.7f % 15.7f\n",
( float )fslx[0], ( float )fslx[1] );
PRINTALL( PRINT_2, 0 );
fprintf( file, "y[]: % 15.7f % 15.7f\n",
( float )fsly[0], ( float )fsly[1] );
PRINTALL( PRINT_2, 1 );
fprintf( file, "z[]: % 15.7f % 15.7f\n",
( float )fslz[0], ( float )fslz[1] );
PRINTALL( PRINT_2, 2 );
break;
case 4:
fprintf( file, "x[]: % 15.7f % 15.7f % 15.7f % 15.7f\n",
( float )fslx[0], ( float )fslx[1], ( float )fslx[2], ( float )fslx[3] );
PRINTALL( PRINT_4, 0 );
fprintf( file, "y[]: % 15.7f % 15.7f % 15.7f % 15.7f\n",
( float )fsly[0], ( float )fsly[1], ( float )fsly[2], ( float )fsly[3] );
PRINTALL( PRINT_4, 1 );
fprintf( file, "z[]: % 15.7f % 15.7f % 15.7f % 15.7f\n",
( float )fslz[0], ( float )fslz[1], ( float )fslz[2], ( float )fslz[3] );
PRINTALL( PRINT_4, 2 );
break;
default:
fprintf( file, "x[]:\n" );
for ( i=0; i<fd; i++ )
155 fprintf( file, " % 5d : % 15.7f\n", i, ( float )fslx[i] );
PRINTALL( PRINT_N, 0 );
fprintf( file, "y[]:\n" );
for ( i=0; i<fd; i++ )
fprintf( file, " % 5d : % 15.7f\n", i, ( float )fsly[i] );
PRINTALL( PRINT_N, 1 );
fprintf( file, "z[]:\n" );
for ( i=0; i<fd; i++ )
fprintf( file, " % 5d : % 15.7f\n", i, ( float )fslz[i] );
PRINTALL( PRINT_N, 2 );
break;
}
return;
}
void
gageQueryPrint( FILE *file, const gageKind *kind, gageQuery query ) {
int ii;
fprintf( file, "%s query = ...\n", kind->name );
ii = kind->itemMax+1;
do {
ii--;
if ( GAGE_QUERY_ITEM_TEST( query, ii ) ) {
fprintf( file, " %3d: %s\n", ii, airEnumStr( kind->enm, ii ) );
}
} while ( ii );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
******** gageVolumeCheck( )
**
** checks whether a given volume is valid for the given kind
** and the given parameter settings in the context
**
** Note that ctx is simply passed to _gageShapeSet( ), with no NULL-ity
** test- which is fine- it just means that the check is not specific
** to the parameters that can be set in the gageContext.
*/
int
38 gageVolumeCheck( const gageContext *ctx, const Nrrd *nin,
const gageKind *kind ) {
static const char me[]="gageVolumeCheck";
gageShape shape;
gageShapeReset( &shape );
if ( _gageShapeSet( ctx, &shape, nin, kind->baseDim ) ) {
biffAddf( GAGE, "%s: trouble setting volume as %s kind", me, kind->name );
return 1;
}
return 0;
}
/*
******** gagePerVolumeNew( )
**
** creates a new pervolume of a known kind, but nothing besides the
** answer array is allocated
**
** uses biff primarily because of the error checking in gageVolumeCheck( )
*/
gagePerVolume *
60 gagePerVolumeNew( gageContext *ctx, const Nrrd *nin, const gageKind *kind ) {
static const char me[]="gagePerVolumeNew";
gagePerVolume *pvl;
int ii;
airArray *mop;
if ( !( nin && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer ( %p, %p, or %p )", me,
AIR_VOIDP( ctx ), AIR_CVOIDP( nin ), AIR_CVOIDP( kind ) );
return NULL;
}
/* Craziness: since circa 2003, the test below was to call gageVolumeCheck,
which is now just a wrapper around _gageShapeSet( ), and which never
actually does the important checks of gageKindVolumeCheck ( which in turn
eventually calls gageVolumeCheck ). This means that basic errors were not
being caught, like having the wrong number of samples along axis 0 for
non-scalar kinds. These various functions need to be simplified soon */
if ( gageKindVolumeCheck( kind, nin ) ) {
biffAddf( GAGE, "%s: problem with volume as %s kind", me, kind->name );
return NULL;
}
pvl = AIR_CALLOC( 1, gagePerVolume );
if ( !pvl ) {
biffAddf( GAGE, "%s: couldn't alloc gagePerVolume", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, pvl, airFree, airMopOnError );
pvl->verbose = gageDefVerbose;
pvl->kind = kind;
GAGE_QUERY_RESET( pvl->query );
for ( ii=0; ii<=GAGE_DERIV_MAX; ii++ ) {
ctx->needD[ii] = AIR_FALSE;
}
pvl->nin = nin;
for ( ii=gagePvlFlagUnknown+1; ii<gagePvlFlagLast; ii++ ) {
pvl->flag[ii] = AIR_FALSE;
}
pvl->iv3 = pvl->iv2 = pvl->iv1 = NULL;
pvl->lup = nrrdDLookup[nin->type];
pvl->answer = AIR_CALLOC( gageKindTotalAnswerLength( kind ), double );
airMopAdd( mop, pvl->answer, airFree, airMopOnError );
pvl->directAnswer = AIR_CALLOC( kind->itemMax+1, double* );
airMopAdd( mop, pvl->directAnswer, airFree, airMopOnError );
if ( !( pvl->answer && pvl->directAnswer ) ) {
biffAddf( GAGE, "%s: couldn't alloc answer and directAnswer arrays", me );
airMopError( mop ); return NULL;
}
for ( ii=1; ii<=kind->itemMax; ii++ ) {
pvl->directAnswer[ii] = pvl->answer + gageKindAnswerOffset( kind, ii );
}
pvl->flag[gagePvlFlagVolume] = AIR_TRUE;
if ( kind->pvlDataNew ) {
if ( !( pvl->data = kind->pvlDataNew( kind ) ) ) {
biffAddf( GAGE, "%s: double creating gagePerVolume data", me );
airMopError( mop ); return NULL;
}
} else {
pvl->data = NULL;
}
airMopOkay( mop );
return pvl;
}
/*
** _gagePerVolumeCopy( )
**
** copies a pervolume for use in a copied context, and probably
** should only be called by gageContextCopy( )
*/
gagePerVolume *
132 _gagePerVolumeCopy( gagePerVolume *pvl, unsigned int fd ) {
static const char me[]="gagePerVolumeCopy";
gagePerVolume *nvl;
int ii;
airArray *mop;
nvl = AIR_CALLOC( 1, gagePerVolume );
if ( !nvl ) {
biffAddf( GAGE, "%s: couldn't create new pervolume", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, nvl, airFree, airMopOnError );
/* we should probably restrict ourselves to gage API calls, but given the
constant state of gage construction, this seems much simpler.
Pointers to per-pervolume-allocated arrays are fixed below */
memcpy( nvl, pvl, sizeof( gagePerVolume ) );
nvl->iv3 = AIR_CALLOC( fd*fd*fd*nvl->kind->valLen, double );
nvl->iv2 = AIR_CALLOC( fd*fd*nvl->kind->valLen, double );
nvl->iv1 = AIR_CALLOC( fd*nvl->kind->valLen, double );
airMopAdd( mop, nvl->iv3, airFree, airMopOnError );
airMopAdd( mop, nvl->iv2, airFree, airMopOnError );
airMopAdd( mop, nvl->iv1, airFree, airMopOnError );
nvl->answer = AIR_CALLOC( gageKindTotalAnswerLength( nvl->kind ), double );
airMopAdd( mop, nvl->answer, airFree, airMopOnError );
nvl->directAnswer = AIR_CALLOC( nvl->kind->itemMax+1, double* );
airMopAdd( mop, nvl->directAnswer, airFree, airMopOnError );
if ( !( nvl->iv3 && nvl->iv2 && nvl->iv1
&& nvl->answer && nvl->directAnswer ) ) {
biffAddf( GAGE, "%s: couldn't allocate all caches "
"( fd=%u, valLen=%u, totAnsLen=%u, itemMax=%u )", me,
fd, nvl->kind->valLen, gageKindTotalAnswerLength( nvl->kind ),
nvl->kind->itemMax );
airMopError( mop ); return NULL;
}
for ( ii=1; ii<=pvl->kind->itemMax; ii++ ) {
nvl->directAnswer[ii] = nvl->answer + gageKindAnswerOffset( pvl->kind, ii );
}
if ( pvl->kind->pvlDataCopy ) {
if ( !( nvl->data = pvl->kind->pvlDataCopy( pvl->kind, pvl->data ) ) ) {
biffAddf( GAGE, "%s: double copying gagePerVolume data", me );
airMopError( mop ); return NULL;
}
/* HEY: pvlDataNix takes 2 arguments; so we can't mop nvl->data,
so its a good thing that we created nvl->data last */
} else {
nvl->data = NULL;
}
airMopOkay( mop );
return nvl;
}
/*
******** gagePerVolumeNix( )
**
** frees all dynamically allocated memory assocated with a pervolume
**
** does not use biff
*/
gagePerVolume *
193 gagePerVolumeNix( gagePerVolume *pvl ) {
if ( pvl ) {
if ( pvl->kind->pvlDataNix ) {
pvl->data = pvl->kind->pvlDataNix( pvl->kind, pvl->data );
}
pvl->iv3 = ( double * )airFree( pvl->iv3 );
pvl->iv2 = ( double * )airFree( pvl->iv2 );
pvl->iv1 = ( double * )airFree( pvl->iv1 );
pvl->answer = ( double * )airFree( pvl->answer );
pvl->directAnswer = ( double ** )airFree( pvl->directAnswer );
airFree( pvl );
}
return NULL;
}
/*
******** gageAnswerPointer( )
**
** way of getting a pointer to a specific answer in a pervolume's ans array
**
** Returns NULL if the item is invalid, but there is no other error checking.
** In particular, this does not let you know if the item is actually part
** of the current query.
**
*/
const double *
220 gageAnswerPointer( const gageContext *ctx, const gagePerVolume *pvl, int item ) {
const double *ret;
AIR_UNUSED( ctx );
if ( pvl && !airEnumValCheck( pvl->kind->enm, item ) ) {
ret = pvl->answer + gageKindAnswerOffset( pvl->kind, item );
} else {
ret = NULL;
}
return ret;
}
/* non-const version of the above */
double *
234 _gageAnswerPointer( const gageContext *ctx, gagePerVolume *pvl, int item ) {
double *ret;
AIR_UNUSED( ctx );
if ( pvl && !airEnumValCheck( pvl->kind->enm, item ) ) {
ret = pvl->answer + gageKindAnswerOffset( pvl->kind, item );
} else {
ret = NULL;
}
return ret;
}
unsigned int
247 gageAnswerLength( const gageContext *ctx, const gagePerVolume *pvl, int item ) {
unsigned int ret;
AIR_UNUSED( ctx );
if ( pvl && !airEnumValCheck( pvl->kind->enm, item ) ) {
ret = gageKindAnswerLength( pvl->kind, item );
} else {
ret = 0;
}
return ret;
}
int
260 gageQueryReset( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="gageQueryReset";
AIR_UNUSED( ctx );
if ( !( pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
GAGE_QUERY_RESET( pvl->query );
return 0;
}
/*
******** gageQuerySet( )
**
** sets a query in a pervolume. Does recursive expansion of query
** to cover all prerequisite measures.
**
** Sets: pvl->query
**
** the gageContext is not actually used here, but I'm cautiously
** including it in case its used in the future.
*/
int
287 gageQuerySet( gageContext *ctx, gagePerVolume *pvl, gageQuery query ) {
static const char me[]="gageQuerySet";
gageQuery lastQuery;
int pi, ii;
AIR_UNUSED( ctx );
if ( !( pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
GAGE_QUERY_COPY( pvl->query, query );
if ( pvl->verbose ) {
fprintf( stderr, "%s: original ", me );
gageQueryPrint( stderr, pvl->kind, pvl->query );
}
/* recursive expansion of prerequisites */
do {
GAGE_QUERY_COPY( lastQuery, pvl->query );
ii = pvl->kind->itemMax+1;
do {
ii--;
if ( GAGE_QUERY_ITEM_TEST( pvl->query, ii ) ) {
for ( pi=0; pi<GAGE_ITEM_PREREQ_MAXNUM; pi++ ) {
if ( 0 != pvl->kind->table[ii].prereq[pi] ) {
GAGE_QUERY_ITEM_ON( pvl->query, pvl->kind->table[ii].prereq[pi] );
}
}
}
} while ( ii );
} while ( !GAGE_QUERY_EQUAL( pvl->query, lastQuery ) );
if ( pvl->verbose ) {
fprintf( stderr, "%s: expanded ", me );
gageQueryPrint( stderr, pvl->kind, pvl->query );
}
/* doing this kind of error checking here is not really
the way gage should work-- it should be done at the
time of gageUpdate( )-- but the novelty of pvl->data
encourages putting new smarts at superficial levels
instead of deeper levels */
if ( !pvl->data ) {
for ( ii=1; ii<=pvl->kind->itemMax; ii++ ) {
if ( GAGE_QUERY_ITEM_TEST( pvl->query, ii )
&& pvl->kind->table[ii].needData ) {
biffAddf( GAGE, "%s: item %d ( %s ) needs data, but pvl->data is NULL",
me, ii, airEnumStr( pvl->kind->enm, ii ) );
return 1;
}
}
}
pvl->flag[gagePvlFlagQuery] = AIR_TRUE;
return 0;
}
int
344 gageQueryAdd( gageContext *ctx, gagePerVolume *pvl, gageQuery query ) {
static const char me[]="gageQueryAdd";
if ( !( pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
GAGE_QUERY_ADD( pvl->query, query );
if ( gageQuerySet( ctx, pvl, pvl->query ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
return 0;
}
int
362 gageQueryItemOn( gageContext *ctx, gagePerVolume *pvl, int item ) {
static const char me[]="gageQueryItemOn";
if ( !( pvl ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( pvl->kind->enm, item ) ) {
biffAddf( GAGE, "%s: %d not a valid %s value", me,
item, pvl->kind->enm->name );
return 1;
}
GAGE_QUERY_ITEM_ON( pvl->query, item );
if ( gageQuerySet( ctx, pvl, pvl->query ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
** _gageSclTable
**
** the static array of item information for the scalar kind.
*/
gageItemEntry
_gageSclTable[GAGE_SCL_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, parent index, needData */
{gageSclUnknown, 0, 0, {0}, 0, 0, AIR_FALSE},
{gageSclValue, 1, 0, {0}, 0, 0, AIR_FALSE},
{gageSclGradVec, 3, 1, {0}, 0, 0, AIR_FALSE},
{gageSclGradMag, 1, 1, {gageSclGradVec}, 0, 0, AIR_FALSE},
{gageSclNormal, 3, 1, {gageSclGradVec, gageSclGradMag}, 0, 0, AIR_FALSE},
{gageSclNProj, 9, 1, {gageSclNormal}, 0, 0, AIR_FALSE},
{gageSclNPerp, 9, 1, {gageSclNormal}, 0, 0, AIR_FALSE},
{gageSclHessian, 9, 2, {gageSclHessian}, 0, 0, AIR_FALSE},
{gageSclHessianTen, 7, 2, {gageSclHessian}, 0, 0, AIR_FALSE},
{gageSclLaplacian, 1, 2, {gageSclHessian}, 0, 0, AIR_FALSE},
{gageSclHessFrob, 1, 2, {gageSclHessian}, 0, 0, AIR_FALSE},
{gageSclHessEval, 3, 2, {gageSclHessian}, 0, 0, AIR_FALSE},
{gageSclHessEval0, 1, 2, {gageSclHessEval}, gageSclHessEval, 0, AIR_FALSE},
{gageSclHessEval1, 1, 2, {gageSclHessEval}, gageSclHessEval, 1, AIR_FALSE},
{gageSclHessEval2, 1, 2, {gageSclHessEval}, gageSclHessEval, 2, AIR_FALSE},
{gageSclHessEvec, 9, 2, {gageSclHessian, gageSclHessEval}, 0, 0, AIR_FALSE},
{gageSclHessEvec0, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 0, AIR_FALSE},
{gageSclHessEvec1, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 3, AIR_FALSE},
{gageSclHessEvec2, 3, 2, {gageSclHessEvec}, gageSclHessEvec, 6, AIR_FALSE},
{gageScl2ndDD, 1, 2, {gageSclHessian, gageSclNormal}, 0, 0, AIR_FALSE},
{gageSclGeomTens, 9, 2, {gageSclHessian, gageSclNPerp, gageSclGradMag}, 0, 0, AIR_FALSE},
{gageSclGeomTensTen, 7, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE},
{gageSclK1, 1, 2, {gageSclTotalCurv, gageSclShapeTrace}, 0, 0, AIR_FALSE},
{gageSclK2, 1, 2, {gageSclTotalCurv, gageSclShapeTrace}, 0, 0, AIR_FALSE},
{gageSclTotalCurv, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE},
{gageSclShapeTrace, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE},
{gageSclShapeIndex, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE},
{gageSclMeanCurv, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE},
{gageSclGaussCurv, 1, 2, {gageSclK1, gageSclK2}, 0, 0, AIR_FALSE},
{gageSclCurvDir1, 3, 2, {gageSclGeomTens, gageSclK1, gageSclK2}, 0, 0, AIR_FALSE},
{gageSclCurvDir2, 3, 2, {gageSclGeomTens, gageSclK1, gageSclK2}, 0, 0, AIR_FALSE},
{gageSclFlowlineCurv, 1, 2, {gageSclGeomTens}, 0, 0, AIR_FALSE},
{gageSclMedian, 1, 0, {0}, 0, 0, AIR_FALSE},
{gageSclHessValleyness, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE},
{gageSclHessRidgeness, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE},
{gageSclHessDotPeakness, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE},
{gageSclHessMode, 1, 2, {gageSclHessEval}, 0, 0, AIR_FALSE}
};
const char *
_gageSclStr[] = {
"( unknown gageScl )",
"value",
"gradient vector",
"gradient magnitude",
"normalized gradient",
"normal projector",
"tangent projector",
"Hessian",
"HessianTen",
"Laplacian",
"Frob( Hessian )",
"Hessian eigenvalues",
"Hessian eigenvalue[0]",
"Hessian eigenvalue[1]",
"Hessian eigenvalue[2]",
"Hessian eigenvectors",
"Hessian eigenvector[0]",
"Hessian eigenvector[1]",
"Hessian eigenvector[2]",
"2nd DD along gradient",
"geometry tensor",
"geometry tensor tensor", /* HEY this is really silly, should be fixed for Teem 2.0 */
"kappa1",
"kappa2",
"total curvature",
"shape trace",
"shape index",
"mean curvature",
"Gaussian curvature",
"1st curvature direction",
"2nd curvature direction",
"flowline curvature",
"median",
"Hessian valleyness",
"Hessian ridgeness",
"Hessian peakness",
"Hessian mode"
};
const char *
_gageSclDesc[] = {
"unknown gageScl query",
"reconstructed scalar data value",
"gradient vector, un-normalized",
"gradient magnitude ( length of gradient vector )",
"normalized gradient vector",
"projection into normal",
"projection into tangent ( perp space of normal )",
"3x3 Hessian matrix",
"7-element Hessian tensor",
"Laplacian",
"Frobenius norm of Hessian",
"Hessian's eigenvalues",
"Hessian's 1st eigenvalue",
"Hessian's 2nd eigenvalue",
"Hessian's 3rd eigenvalue",
"Hessian's eigenvectors",
"Hessian's 1st eigenvector",
"Hessian's 2nd eigenvector",
"Hessian's 3rd eigenvector",
"2nd directional derivative along gradient",
"geometry tensor",
"7-element geometry tensor",
"1st principal curvature ( K1 )",
"2nd principal curvature ( K2 )",
"total curvature ( L2 norm of K1, K2 )",
"shape trace = ( K1+K2 )/( total curvature )",
"Koenderink's shape index",
"mean curvature = ( K1+K2 )/2",
"gaussian curvature = K1*K2",
"1st principal curvature direction",
"2nd principal curvature direction",
"curvature of normal streamline",
"median of iv3 cache ( not weighted by any filter ( yet ) )",
"measure of valleyness of Hessian",
"measure of ridgeness of Hessian",
"measure of peakness of Hessian",
"mode of Hessian eigenvalues"
};
int
_gageSclVal[] = {
gageSclUnknown,
gageSclValue,
gageSclGradVec,
gageSclGradMag,
gageSclNormal,
gageSclNProj,
gageSclNPerp,
gageSclHessian,
gageSclHessianTen,
gageSclLaplacian,
gageSclHessFrob,
gageSclHessEval,
gageSclHessEval0,
gageSclHessEval1,
gageSclHessEval2,
gageSclHessEvec,
gageSclHessEvec0,
gageSclHessEvec1,
gageSclHessEvec2,
gageScl2ndDD,
gageSclGeomTens,
gageSclGeomTensTen,
gageSclK1,
gageSclK2,
gageSclTotalCurv,
gageSclShapeTrace,
gageSclShapeIndex,
gageSclMeanCurv,
gageSclGaussCurv,
gageSclCurvDir1,
gageSclCurvDir2,
gageSclFlowlineCurv,
gageSclMedian,
gageSclHessValleyness,
gageSclHessRidgeness,
gageSclHessDotPeakness,
gageSclHessMode
};
#define GS_V gageSclValue
#define GS_GV gageSclGradVec
#define GS_GM gageSclGradMag
#define GS_N gageSclNormal
#define GS_NProj gageSclNProj
#define GS_NP gageSclNPerp
#define GS_H gageSclHessian
#define GS_HT gageSclHessianTen
#define GS_L gageSclLaplacian
#define GS_HF gageSclHessFrob
#define GS_HA gageSclHessEval
#define GS_HA0 gageSclHessEval0
#define GS_HA1 gageSclHessEval1
#define GS_HA2 gageSclHessEval2
#define GS_HE gageSclHessEvec
#define GS_HE0 gageSclHessEvec0
#define GS_HE1 gageSclHessEvec1
#define GS_HE2 gageSclHessEvec2
#define GS_2D gageScl2ndDD
#define GS_GT gageSclGeomTens
#define GS_GTT gageSclGeomTensTen
#define GS_K1 gageSclK1
#define GS_K2 gageSclK2
#define GS_TC gageSclTotalCurv
#define GS_ST gageSclShapeTrace
#define GS_SI gageSclShapeIndex
#define GS_MC gageSclMeanCurv
#define GS_GC gageSclGaussCurv
#define GS_C1 gageSclCurvDir1
#define GS_C2 gageSclCurvDir2
#define GS_FC gageSclFlowlineCurv
#define GS_MD gageSclMedian
#define GS_HV gageSclHessValleyness
#define GS_HR gageSclHessRidgeness
#define GS_PK gageSclHessDotPeakness
#define GS_HM gageSclHessMode
const char *
_gageSclStrEqv[] = {
"v", "val", "value",
"gv", "gvec", "gradvec", "grad vec", "gradient vector",
"gm", "gmag", "gradmag", "grad mag", "gradient magnitude",
"gn", "n", "normal", "gnorm", "normg", "norm", "normgrad",
"norm grad", "normalized gradient",
"nproj", "normal projector",
"np", "nperp", "tangent projector",
"h", "hess", "hessian",
"ht", "hessten", "hessianten",
"l", "lapl", "laplacian",
"hf", "frob( hessian )",
"heval", "hesseval", "hessian eval", "hessian eigenvalues",
"heval0", "hesseval0", "hessian eigenvalue[0]",
"heval1", "hesseval1", "hessian eigenvalue[1]",
"heval2", "hesseval2", "hessian eigenvalue[2]",
"hevec", "hessevec", "hessian evec", "hessian eigenvectors",
"hevec0", "hessevec0", "hessian eigenvector[0]",
"hevec1", "hessevec1", "hessian eigenvector[1]",
"hevec2", "hessevec2", "hessian eigenvector[2]",
"2d", "2dd", "2nddd", "2nd", "2nd dd", "2nd dd along gradient",
"gt", "gten", "geoten", "geomten", "geometry tensor",
"gtenten", "geometry tensor tensor",
"k1", "kap1", "kappa1",
"k2", "kap2", "kappa2",
"total curv", "totalcurv", "total curvature", "tc", "cv", "curvedness",
"st", "shape trace",
"si", "shape index",
"mc", "mcurv", "meancurv", "mean curvature",
"gc", "gcurv", "gausscurv", "gaussian curvature",
"cdir1", "c dir1", "curvdir1", "curv dir1", "curvature direction 1", "1st curvature direction",
"cdir2", "c dir2", "curvdir2", "curv dir2", "curvature direction 2", "2nd curvature direction",
"fc", "flowlinecurv", "flowline curv", "flowline curvature",
"med", "median",
"hvalley", "hessvalley", "hessian valleyness",
"hridge", "hessridge", "hessian ridgeness",
"hdpeak", "hessian peakness",
"hmode", "hessmode", "hessian mode",
""
};
const int
_gageSclValEqv[] = {
GS_V, GS_V, GS_V,
GS_GV, GS_GV, GS_GV, GS_GV, GS_GV,
GS_GM, GS_GM, GS_GM, GS_GM, GS_GM,
GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N, GS_N,
GS_NProj, GS_NProj,
GS_NP, GS_NP, GS_NP,
GS_H, GS_H, GS_H,
GS_HT, GS_HT, GS_HT,
GS_L, GS_L, GS_L,
GS_HF, GS_HF,
GS_HA, GS_HA, GS_HA, GS_HA,
GS_HA0, GS_HA0, GS_HA0,
GS_HA1, GS_HA1, GS_HA1,
GS_HA2, GS_HA2, GS_HA2,
GS_HE, GS_HE, GS_HE, GS_HE,
GS_HE0, GS_HE0, GS_HE0,
GS_HE1, GS_HE1, GS_HE1,
GS_HE2, GS_HE2, GS_HE2,
GS_2D, GS_2D, GS_2D, GS_2D, GS_2D, GS_2D,
GS_GT, GS_GT, GS_GT, GS_GT, GS_GT,
GS_GTT, GS_GTT,
GS_K1, GS_K1, GS_K1,
GS_K2, GS_K2, GS_K2,
GS_TC, GS_TC, GS_TC, GS_TC, GS_TC, GS_TC,
GS_ST, GS_ST,
GS_SI, GS_SI,
GS_MC, GS_MC, GS_MC, GS_MC,
GS_GC, GS_GC, GS_GC, GS_GC,
GS_C1, GS_C1, GS_C1, GS_C1, GS_C1, GS_C1,
GS_C2, GS_C2, GS_C2, GS_C2, GS_C2, GS_C2,
GS_FC, GS_FC, GS_FC, GS_FC,
GS_MD, GS_MD,
GS_HV, GS_HV, GS_HV,
GS_HR, GS_HR, GS_HR,
GS_PK, GS_PK,
GS_HM, GS_HM, GS_HM
};
const airEnum
_gageScl = {
"gageScl",
GAGE_SCL_ITEM_MAX,
_gageSclStr, _gageSclVal,
_gageSclDesc,
_gageSclStrEqv, _gageSclValEqv,
AIR_FALSE
};
325 const airEnum *const
gageScl = &_gageScl;
gageKind
_gageKindScl = {
AIR_FALSE, /* statically allocated */
"scalar",
&_gageScl,
0, /* baseDim */
1, /* valLen */
GAGE_SCL_ITEM_MAX,
_gageSclTable,
_gageSclIv3Print,
_gageSclFilter,
_gageSclAnswer,
NULL, NULL, NULL, NULL,
NULL
};
343 gageKind *const
gageKindScl = &_gageKindScl;
const gageItemPack
_gageItemPackSclValue = {
&_gageKindScl,
{gageSclUnknown,
gageSclValue,
gageSclGradVec,
gageSclGradMag,
gageSclNormal,
gageSclHessian,
gageSclHessEval0,
gageSclHessEval1,
gageSclHessEval2,
gageSclHessEvec0,
gageSclHessEvec1,
gageSclHessEvec2}
};
363 const gageItemPack *const
gageItemPackSclValue = &_gageItemPackSclValue;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/* HEY copied from ten.h */
#define TEN_M2T( t, m ) ( \
( t )[1] = ( m )[0], \
( t )[2] = ( ( m )[1]+( m )[3] )/2.0, \
( t )[3] = ( ( m )[2]+( m )[6] )/2.0, \
32 ( t )[4] = ( m )[4], \
( t )[5] = ( ( m )[5]+( m )[7] )/2.0, \
( t )[6] = ( m )[8] )
#define TEN_T_SCALE( a, s, b ) ( \
( a )[0] = ( b )[0], \
( a )[1] = ( s )*( b )[1], \
( a )[2] = ( s )*( b )[2], \
( a )[3] = ( s )*( b )[3], \
( a )[4] = ( s )*( b )[4], \
( a )[5] = ( s )*( b )[5], \
( a )[6] = ( s )*( b )[6] )
void
_gageSclAnswer( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_gageSclAnswer";
double gmag=0, *hess, *norm, *gvec, *gten, *k1, *k2, curv=0,
sHess[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
double tmpMat[9], tmpVec[3], hevec[9], heval[3];
double len, gp1[3], gp2[3], *nPerp, ncTen[9], nProj[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
double alpha = 0.5;
double beta = 0.5;
double _gamma = 5;
double cc = 1e-6;
#define FD_MEDIAN_MAX 16
int fd, nidx, xi, yi, zi;
double *fw, iv3wght[2*FD_MEDIAN_MAX*FD_MEDIAN_MAX*FD_MEDIAN_MAX],
wghtSum, wght;
/* convenience pointers for work below */
hess = pvl->directAnswer[gageSclHessian];
gvec = pvl->directAnswer[gageSclGradVec];
norm = pvl->directAnswer[gageSclNormal];
nPerp = pvl->directAnswer[gageSclNPerp];
gten = pvl->directAnswer[gageSclGeomTens];
k1 = pvl->directAnswer[gageSclK1];
k2 = pvl->directAnswer[gageSclK2];
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclValue ) ) {
/* done if doV */
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: val = % 15.7f\n", me,
( double )( pvl->directAnswer[gageSclValue][0] ) );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclGradVec ) ) {
/* done if doD1 */
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: gvec = ", me );
ell_3v_print_d( stderr, gvec );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclGradMag ) ) {
/* this is the true value of gradient magnitude */
gmag = pvl->directAnswer[gageSclGradMag][0] = sqrt( ELL_3V_DOT( gvec, gvec ) );
}
/* NB: it would seem that gageParmGradMagMin is completely ignored . . . */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclNormal ) ) {
if ( gmag ) {
ELL_3V_SCALE( norm, 1/gmag, gvec );
/* polishing
len = sqrt( ELL_3V_DOT( norm, norm ) );
ELL_3V_SCALE( norm, 1/len, norm );
*/
} else {
ELL_3V_COPY( norm, gageZeroNormal );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclNProj ) ) {
ELL_3MV_OUTER( pvl->directAnswer[gageSclNProj], norm, norm );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclNPerp ) ) {
/* nPerp = I - outer( norm, norm ) */
/* NB: this sets both nPerp and nProj */
ELL_3MV_OUTER( nProj, norm, norm );
ELL_3M_SCALE( nPerp, -1, nProj );
nPerp[0] += 1;
nPerp[4] += 1;
nPerp[8] += 1;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessian ) ) {
/* done if doD2 */
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: hess = \n", me );
ell_3m_print_d( stderr, hess );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessianTen ) ) {
pvl->directAnswer[gageSclHessianTen][0] = 1.0;
TEN_M2T( pvl->directAnswer[gageSclHessianTen],
pvl->directAnswer[gageSclHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclLaplacian ) ) {
pvl->directAnswer[gageSclLaplacian][0] = hess[0] + hess[4] + hess[8];
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: lapl = %g + %g + %g = %g\n", me,
hess[0], hess[4], hess[8],
pvl->directAnswer[gageSclLaplacian][0] );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessFrob ) ) {
pvl->directAnswer[gageSclHessFrob][0] = ELL_3M_FROB( hess );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessEval ) ) {
if ( ctx->parm.twoDimZeroZ ) {
ell_3m2sub_eigensolve_d( heval, hevec, hess );
} else {
/* HEY: look at the return value for root multiplicity? */
ell_3m_eigensolve_d( heval, hevec, hess, AIR_TRUE );
}
ELL_3V_COPY( pvl->directAnswer[gageSclHessEval], heval );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessEvec ) ) {
ELL_3M_COPY( pvl->directAnswer[gageSclHessEvec], hevec );
}
#if 1
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessRidgeness ) ) {
double A, B, S;
if ( heval[1] >0 || heval[2]>0 ) {
pvl->directAnswer[gageSclHessRidgeness][0] = 0;
}
else if ( AIR_ABS( heval[1] )<1e-10 || AIR_ABS( heval[2] )<1e-10 ) {
pvl->directAnswer[gageSclHessRidgeness][0] = 0;
}
else {
double *ans;
A = AIR_ABS( heval[1] )/AIR_ABS( heval[2] );
B = AIR_ABS( heval[0] )/sqrt( AIR_ABS( heval[1]*heval[2] ) );
S = sqrt( heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2] );
ans = pvl->directAnswer[gageSclHessRidgeness];
ans[0] = ( 1-exp( -A*A/( 2*alpha*alpha ) ) ) *
exp( -B*B/( 2*beta*beta ) ) *
( 1-exp( -S*S/( 2*_gamma*_gamma ) ) ) *
exp( -2*cc*cc/( AIR_ABS( heval[1] )*heval[2]*heval[2] ) );
}
}
#else
/* alternative implementation by GLK, based on directly following
Frangi text. Only significant difference from above is a
discontinuity at heval[0] = -heval[1] */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessRidgeness ) ) {
double ev[4], tmp;
ELL_3V_COPY( ev+1, heval );
if ( AIR_ABS( ev[2] ) > AIR_ABS( ev[3] ) ) { ELL_SWAP2( ev[2], ev[3], tmp ); }
if ( AIR_ABS( ev[1] ) > AIR_ABS( ev[2] ) ) { ELL_SWAP2( ev[1], ev[2], tmp ); }
if ( AIR_ABS( ev[2] ) > AIR_ABS( ev[3] ) ) { ELL_SWAP2( ev[2], ev[3], tmp ); }
if ( ev[2] > 0 || ev[3] > 0 ) {
pvl->directAnswer[gageSclHessRidgeness][0] = 0;
} else {
double a1, a2, a3, RB, RA, SS, fa, fb, fg, aa, bb, gg, frangi;
a1 = AIR_ABS( ev[1] );
a2 = AIR_ABS( ev[2] );
a3 = AIR_ABS( ev[3] );
RB = a1/sqrt( a2*a3 );
RA = a2/a3;
SS = sqrt( a1*a1 + a2*a2 + a3*a3 );
aa = bb = 0.5;
gg = 1;
fa = 1 - exp( -RA*RA/( 2*aa*aa ) );
fb = exp( -RB*RB/( 2*bb*bb ) );
fg = 1 - exp( -SS*SS/( 2*gg*gg ) );
frangi = fa*fb*fg;
if ( !AIR_EXISTS( frangi ) ) { frangi = 0.0; }
pvl->directAnswer[gageSclHessRidgeness][0] = frangi;
}
}
#endif
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessValleyness ) ) {
double A, B, S;
if ( heval[0] <0 || heval[1]<0 ) {
pvl->directAnswer[gageSclHessValleyness][0] = 0;
}
else if ( AIR_ABS( heval[0] )<1e-10 || AIR_ABS( heval[1] )<1e-10 ) {
pvl->directAnswer[gageSclHessValleyness][0] = 0;
}
else {
double *ans;
A = AIR_ABS( heval[1] )/AIR_ABS( heval[0] );
B = AIR_ABS( heval[2] )/sqrt( AIR_ABS( heval[1]*heval[0] ) );
S = sqrt( heval[0]*heval[0] + heval[1]*heval[1] + heval[2]*heval[2] );
ans = pvl->directAnswer[gageSclHessValleyness];
ans[0] = ( 1-exp( -A*A/( 2*alpha*alpha ) ) ) *
exp( -B*B/( 2*beta*beta ) ) *
( 1-exp( -S*S/( 2*_gamma*_gamma ) ) ) *
exp( -2*cc*cc/( AIR_ABS( heval[1] )*heval[0]*heval[0] ) );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessDotPeakness ) ) {
#define OSQT 0.57735026918962576451
double neval[3], hlen, pness,
peak[3] = {-OSQT, -OSQT, -OSQT};
ELL_3V_NORM( neval, heval, hlen );
if ( !hlen ) {
pness = 0;
} else {
pness = ELL_3V_DOT( peak, neval );
pness = AIR_AFFINE( -1, pness, 1, 0, 1 );
pvl->directAnswer[gageSclHessDotPeakness][0] = pness;
}
#undef OSQT
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclHessMode ) ) {
pvl->directAnswer[gageSclHessMode][0] = airMode3_d( heval );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageScl2ndDD ) ) {
ELL_3MV_MUL( tmpVec, hess, norm );
pvl->directAnswer[gageScl2ndDD][0] = ELL_3V_DOT( norm, tmpVec );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclGeomTens ) ) {
if ( gmag > ctx->parm.gradMagCurvMin ) {
/* parm.curvNormalSide applied here to determine the sense of the
normal when doing all curvature calculations */
ELL_3M_SCALE( sHess, -( ctx->parm.curvNormalSide )/gmag, hess );
/* gten = nPerp * sHess * nPerp */
ELL_3M_MUL( tmpMat, sHess, nPerp );
ELL_3M_MUL( gten, nPerp, tmpMat );
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: gten: \n", me );
ell_3m_print_d( stderr, gten );
ELL_3MV_MUL( tmpVec, gten, norm );
len = ELL_3V_LEN( tmpVec );
fprintf( stderr, "%s: should be small: %30.15f\n", me, ( double )len );
ell_3v_perp_d( gp1, norm );
ELL_3MV_MUL( tmpVec, gten, gp1 );
len = ELL_3V_LEN( tmpVec );
fprintf( stderr, "%s: should be bigger: %30.15f\n", me, ( double )len );
ELL_3V_CROSS( gp2, gp1, norm );
ELL_3MV_MUL( tmpVec, gten, gp2 );
len = ELL_3V_LEN( tmpVec );
fprintf( stderr, "%s: should ( also ) be bigger: %30.15f\n",
me, ( double )len );
}
} else {
ELL_3M_ZERO_SET( gten );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclGeomTensTen ) ) {
pvl->directAnswer[gageSclGeomTensTen][0] = 1.0;
TEN_M2T( pvl->directAnswer[gageSclGeomTensTen],
pvl->directAnswer[gageSclGeomTens] );
TEN_T_SCALE( pvl->directAnswer[gageSclGeomTensTen], -1,
pvl->directAnswer[gageSclGeomTensTen] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclTotalCurv ) ) {
curv = pvl->directAnswer[gageSclTotalCurv][0] = ELL_3M_FROB( gten );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclShapeTrace ) ) {
pvl->directAnswer[gageSclShapeTrace][0] = ( curv
? ELL_3M_TRACE( gten )/curv
: 0 );
}
if ( ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclK1 ) ) ||
( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclK2 ) ) ){
double T, N, D;
T = ELL_3M_TRACE( gten );
N = curv;
D = 2*N*N - T*T;
/*
if ( D < -0.0000001 ) {
fprintf( stderr, "%s: %g %g\n", me, T, N );
fprintf( stderr, "%s: !!! D curv determinant % 22.10f < 0.0\n", me, D );
fprintf( stderr, "%s: gten: \n", me );
ell_3m_print_d( stderr, gten );
}
*/
D = AIR_MAX( D, 0 );
D = sqrt( D );
k1[0] = 0.5*( T + D );
k2[0] = 0.5*( T - D );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclMeanCurv ) ) {
pvl->directAnswer[gageSclMeanCurv][0] = ( *k1 + *k2 )/2;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclGaussCurv ) ) {
pvl->directAnswer[gageSclGaussCurv][0] = ( *k1 )*( *k2 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclShapeIndex ) ) {
pvl->directAnswer[gageSclShapeIndex][0] =
-( 2/AIR_PI )*atan2( *k1 + *k2, *k1 - *k2 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclCurvDir1 ) ) {
/* HEY: this only works when K1, K2, 0 are all well mutually distinct,
since these are the eigenvalues of the geometry tensor, and this
code assumes that the eigenspaces are all one-dimensional */
ELL_3M_COPY( tmpMat, gten );
ELL_3M_DIAG_SET( tmpMat, gten[0] - *k1, gten[4]- *k1, gten[8] - *k1 );
ell_3m_1d_nullspace_d( tmpVec, tmpMat );
ELL_3V_COPY( pvl->directAnswer[gageSclCurvDir1], tmpVec );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclCurvDir2 ) ) {
/* HEY: this only works when K1, K2, 0 are all well mutually distinct,
since these are the eigenvalues of the geometry tensor, and this
code assumes that the eigenspaces are all one-dimensional */
ELL_3M_COPY( tmpMat, gten );
ELL_3M_DIAG_SET( tmpMat, gten[0] - *k2, gten[4] - *k2, gten[8] - *k2 );
ell_3m_1d_nullspace_d( tmpVec, tmpMat );
ELL_3V_COPY( pvl->directAnswer[gageSclCurvDir2], tmpVec );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclFlowlineCurv ) ) {
if ( gmag >= ctx->parm.gradMagCurvMin ) {
/* because of the gageSclGeomTens prerequisite, sHess, nPerp, and
nProj are all already set */
/* ncTen = nPerp * sHess * nProj */
ELL_3M_MUL( tmpMat, sHess, nProj );
ELL_3M_MUL( ncTen, nPerp, tmpMat );
} else {
ELL_3M_ZERO_SET( ncTen );
}
/* there used to be a wrong extra sqrt( ) here */
pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB( ncTen );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageSclMedian ) ) {
/* this item is currently a complete oddball in that it does not
benefit from anything done in the "filter" stage, which is in
fact a waste of time if the query consists only of this item */
fd = 2*ctx->radius;
if ( fd > FD_MEDIAN_MAX ) {
fprintf( stderr, "%s: PANIC: current filter diameter = %d "
"> FD_MEDIAN_MAX = %d\n", me, fd, FD_MEDIAN_MAX );
exit( 1 );
}
fw = ctx->fw + fd*3*gageKernel00;
/* HEY: this needs some optimization help */
wghtSum = 0;
nidx = 0;
for ( xi=0; xi<fd; xi++ ) {
for ( yi=0; yi<fd; yi++ ) {
for ( zi=0; zi<fd; zi++ ) {
iv3wght[0 + 2*nidx] = pvl->iv3[nidx];
iv3wght[1 + 2*nidx] = fw[xi + 0*fd]*fw[yi + 1*fd]*fw[zi + 2*fd];
wghtSum += iv3wght[1 + 2*nidx];
nidx++;
}
}
}
qsort( iv3wght, fd*fd*fd, 2*sizeof( double ), nrrdValCompare[nrrdTypeDouble] );
wght = 0;
for ( nidx=0; nidx<fd*fd*fd; nidx++ ) {
wght += iv3wght[1 + 2*nidx];
if ( wght > wghtSum/2 ) {
break;
}
}
pvl->directAnswer[gageSclMedian][0] = iv3wght[0 + 2*nidx];
}
return;
}
#undef TEN_M2T
#undef TEN_T_SCALE
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
** The functions gageScl3PFilter2, gageScl3PFilter4, and
** gageScl3PFilterN weren't really intended to be public, but they
** have become so because they implement the brains of the lowest
** level of dot-products needed to do the convolution-based
** reconstruction, and, to do the the transforms that affect gradient
** and hessian values. These functions are very useful for the
** implementation of other gageKinds, in which non-scalar values are
** filtered per-component.
*/
/*
** Teem trivia: "3P" stands for "3-pack", the kind of kernel set used ( k00,
** k11, and k22 ). 6-pack filtering is still unimplemented. The name
** "3-pack" persists even with the possibility of higher derivatives, and
** what would be called 10-pack filtering ( ability to specify and use all
** 10 kinds of spatial kernels ) is also still unimplemented.
*/
#define X 0
#define Y 1
#define Z 2
void
51 gageScl3PFilter2( gageShape *shape,
double *ivX, double *ivY, double *ivZ,
double *fw0, double *fw1, double *fw2,
double *val, double *gvec, double *hess,
const int *needD ) {
int doV, doD1, doD2;
doV = needD[0];
doD1 = needD[1];
doD2 = needD[2];
/* fw? + 2*?
| |
| +- along which axis ( 0:x, 1:y, 2:z )
|
+ what information ( 0:value, 1:1st deriv, 2:2nd deriv )
ivX: 3D cube cache of original volume values
( its scanlines are along the X axis )
ivY: 2D square cache of intermediate filter results
( its scanlines are along the Y axis )
ivZ: 1D linear cache of intermediate filter results
( it is a scanline along the Z axis )
*/
#define DOT_2( a, b ) ( ( a )[0]*( b )[0] + ( a )[1]*( b )[1] )
#define VL_2( i, axis ) DOT_2( fw0 + ( axis )*2, iv##axis + i*2 )
#define D1_2( i, axis ) DOT_2( fw1 + ( axis )*2, iv##axis + i*2 )
#define D2_2( i, axis ) DOT_2( fw2 + ( axis )*2, iv##axis + i*2 )
/* x0 */
ivY[0] = VL_2( 0, X ); /* interpolate values of 0th scanline along X axis */
ivY[1] = VL_2( 1, X );
ivY[2] = VL_2( 2, X );
ivY[3] = VL_2( 3, X );
/* x0y0 */
ivZ[0] = VL_2( 0, Y );
ivZ[1] = VL_2( 1, Y );
/* x0y0z0 */
if ( doV ) {
*val = VL_2( 0, Z ); /* f */
}
if ( !( doD1 || doD2 ) )
return;
/* x0y0z1 */
if ( doD1 ) {
gvec[2] = D1_2( 0, Z ); /* g_z */
}
if ( doD2 ) {
/* actually, there is no possible way in which it makes sense to
try to measure a second derivative with only two samples, so
all this "if ( doD2 )" code is basically bogus, but we'll keep it
around for generality . . . */
/* x0y0z2 */
hess[8] = D2_2( 0, Z ); /* h_zz */
}
/* x0y1 */
ivZ[0] = D1_2( 0, Y );
ivZ[1] = D1_2( 1, Y );
/* x0y1z0 */
if ( doD1 ) {
gvec[1] = VL_2( 0, Z ); /* g_y */
}
if ( doD2 ) {
/* x0y1z1 */
hess[5] = hess[7] = D1_2( 0, Z ); /* h_yz */
/* x0y2 */
ivZ[0] = D2_2( 0, Y );
ivZ[1] = D2_2( 1, Y );
/* x0y2z0 */
hess[4] = VL_2( 0, Z ); /* h_yy */
}
/* x1 */
ivY[0] = D1_2( 0, X );
ivY[1] = D1_2( 1, X );
ivY[2] = D1_2( 2, X );
ivY[3] = D1_2( 3, X );
/* x1y0 */
ivZ[0] = VL_2( 0, Y );
ivZ[1] = VL_2( 1, Y );
/* x1y0z0 */
if ( doD1 ) {
gvec[0] = VL_2( 0, Z ); /* g_x */
}
ell_3mv_mul_d( gvec, shape->ItoWSubInvTransp, gvec );
if ( !doD2 )
return;
/* x1y0z1 */
hess[2] = hess[6] = D1_2( 0, Z ); /* h_xz */
/* x1y1 */
ivZ[0] = D1_2( 0, Y );
ivZ[1] = D1_2( 1, Y );
/* x1y1z0 */
hess[1] = hess[3] = VL_2( 0, Z ); /* h_xy */
/* x2 */
ivY[0] = D2_2( 0, X );
ivY[1] = D2_2( 1, X );
ivY[2] = D2_2( 2, X );
ivY[3] = D2_2( 3, X );
/* x2y0 */
ivZ[0] = VL_2( 0, Y );
ivZ[1] = VL_2( 1, Y );
/* x2y0z0 */
hess[0] = VL_2( 0, Z ); /* h_xx */
if ( 1 ) {
double matA[9];
ELL_3M_MUL( matA, shape->ItoWSubInvTransp, hess );
ELL_3M_MUL( hess, matA, shape->ItoWSubInv );
}
return;
}
void
170 gageScl3PFilter4( gageShape *shape,
double *ivX, double *ivY, double *ivZ,
double *fw0, double *fw1, double *fw2,
double *val, double *gvec, double *hess,
const int *needD ) {
int doV, doD1, doD2;
doV = needD[0];
doD1 = needD[1];
doD2 = needD[2];
/* fw? + 4*?
| |
| +- along which axis ( 0:x, 1:y, 2:z )
|
+ what information ( 0:value, 1:1st deriv, 2:2nd deriv )
ivX: 3D cube cache of original volume values
( its scanlines are along the X axis )
ivY: 2D square cache of intermediate filter results
( its scanlines are along the Y axis )
ivZ: 1D linear cache of intermediate filter results
( it is a scanline along the Z axis )
*/
#define DOT_4( a, b ) ( ( a )[0]*( b )[0]+( a )[1]*( b )[1]+( a )[2]*( b )[2]+( a )[3]*( b )[3] )
#define VL_4( i, axis ) DOT_4( fw0 + ( axis )*4, iv##axis + i*4 )
#define D1_4( i, axis ) DOT_4( fw1 + ( axis )*4, iv##axis + i*4 )
#define D2_4( i, axis ) DOT_4( fw2 + ( axis )*4, iv##axis + i*4 )
/* x0 */
ivY[ 0] = VL_4( 0, X );
ivY[ 1] = VL_4( 1, X );
ivY[ 2] = VL_4( 2, X );
ivY[ 3] = VL_4( 3, X );
ivY[ 4] = VL_4( 4, X );
ivY[ 5] = VL_4( 5, X );
ivY[ 6] = VL_4( 6, X );
ivY[ 7] = VL_4( 7, X );
ivY[ 8] = VL_4( 8, X );
ivY[ 9] = VL_4( 9, X );
ivY[10] = VL_4( 10, X );
ivY[11] = VL_4( 11, X );
ivY[12] = VL_4( 12, X );
ivY[13] = VL_4( 13, X );
ivY[14] = VL_4( 14, X );
ivY[15] = VL_4( 15, X );
/*
*/
/* x0y0 */
ivZ[ 0] = VL_4( 0, Y );
ivZ[ 1] = VL_4( 1, Y );
ivZ[ 2] = VL_4( 2, Y );
ivZ[ 3] = VL_4( 3, Y );
/* x0y0z0 */
if ( doV ) {
*val = VL_4( 0, Z ); /* f */
}
if ( !( doD1 || doD2 ) )
return;
/* x0y0z1 */
if ( doD1 ) {
gvec[2] = D1_4( 0, Z ); /* g_z */
}
if ( doD2 ) {
/* x0y0z2 */
hess[8] = D2_4( 0, Z ); /* h_zz */
}
/* x0y1 */
ivZ[ 0] = D1_4( 0, Y );
ivZ[ 1] = D1_4( 1, Y );
ivZ[ 2] = D1_4( 2, Y );
ivZ[ 3] = D1_4( 3, Y );
/* x0y1z0 */
if ( doD1 ) {
gvec[1] = VL_4( 0, Z ); /* g_y */
}
if ( doD2 ) {
/* x0y1z1 */
hess[5] = hess[7] = D1_4( 0, Z ); /* h_yz */
/* x0y2 */
ivZ[ 0] = D2_4( 0, Y );
ivZ[ 1] = D2_4( 1, Y );
ivZ[ 2] = D2_4( 2, Y );
ivZ[ 3] = D2_4( 3, Y );
/* x0y2z0 */
hess[4] = VL_4( 0, Z ); /* h_yy */
}
/* x1 */
ivY[ 0] = D1_4( 0, X );
ivY[ 1] = D1_4( 1, X );
ivY[ 2] = D1_4( 2, X );
ivY[ 3] = D1_4( 3, X );
ivY[ 4] = D1_4( 4, X );
ivY[ 5] = D1_4( 5, X );
ivY[ 6] = D1_4( 6, X );
ivY[ 7] = D1_4( 7, X );
ivY[ 8] = D1_4( 8, X );
ivY[ 9] = D1_4( 9, X );
ivY[10] = D1_4( 10, X );
ivY[11] = D1_4( 11, X );
ivY[12] = D1_4( 12, X );
ivY[13] = D1_4( 13, X );
ivY[14] = D1_4( 14, X );
ivY[15] = D1_4( 15, X );
/* x1y0 */
ivZ[ 0] = VL_4( 0, Y );
ivZ[ 1] = VL_4( 1, Y );
ivZ[ 2] = VL_4( 2, Y );
ivZ[ 3] = VL_4( 3, Y );
/* x1y0z0 */
if ( doD1 ) {
gvec[0] = VL_4( 0, Z ); /* g_x */
}
ell_3mv_mul_d( gvec, shape->ItoWSubInvTransp, gvec );
if ( !doD2 )
return;
/* x1y0z1 */
hess[2] = hess[6] = D1_4( 0, Z ); /* h_xz */
/* x1y1 */
ivZ[ 0] = D1_4( 0, Y );
ivZ[ 1] = D1_4( 1, Y );
ivZ[ 2] = D1_4( 2, Y );
ivZ[ 3] = D1_4( 3, Y );
/* x1y1z0 */
hess[1] = hess[3] = VL_4( 0, Z ); /* h_xy */
/* x2 */
ivY[ 0] = D2_4( 0, X );
ivY[ 1] = D2_4( 1, X );
ivY[ 2] = D2_4( 2, X );
ivY[ 3] = D2_4( 3, X );
ivY[ 4] = D2_4( 4, X );
ivY[ 5] = D2_4( 5, X );
ivY[ 6] = D2_4( 6, X );
ivY[ 7] = D2_4( 7, X );
ivY[ 8] = D2_4( 8, X );
ivY[ 9] = D2_4( 9, X );
ivY[10] = D2_4( 10, X );
ivY[11] = D2_4( 11, X );
ivY[12] = D2_4( 12, X );
ivY[13] = D2_4( 13, X );
ivY[14] = D2_4( 14, X );
ivY[15] = D2_4( 15, X );
/* x2y0 */
ivZ[ 0] = VL_4( 0, Y );
ivZ[ 1] = VL_4( 1, Y );
ivZ[ 2] = VL_4( 2, Y );
ivZ[ 3] = VL_4( 3, Y );
/* x2y0z0 */
hess[0] = VL_4( 0, Z ); /* h_xx */
if ( 1 ) {
double matA[9];
ELL_3M_MUL( matA, shape->ItoWSubInvTransp, hess );
ELL_3M_MUL( hess, matA, shape->ItoWSubInv );
}
return;
}
void
335 gageScl3PFilter6( gageShape *shape,
double *ivX, double *ivY, double *ivZ,
double *fw0, double *fw1, double *fw2,
double *val, double *gvec, double *hess,
const int *needD ) {
int i, j;
double T;
int doV, doD1, doD2;
doV = needD[0];
doD1 = needD[1];
doD2 = needD[2];
#define fd 6
#include "scl3pfilterbody.c"
#undef fd
return;
}
void
355 gageScl3PFilter8( gageShape *shape,
double *ivX, double *ivY, double *ivZ,
double *fw0, double *fw1, double *fw2,
double *val, double *gvec, double *hess,
const int *needD ) {
int i, j;
double T;
int doV, doD1, doD2;
doV = needD[0];
doD1 = needD[1];
doD2 = needD[2];
#define fd 8
#include "scl3pfilterbody.c"
#undef fd
return;
}
void
375 gageScl3PFilterN( gageShape *shape, int fd,
double *ivX, double *ivY, double *ivZ,
double *fw0, double *fw1, double *fw2,
double *val, double *gvec, double *hess,
const int *needD ) {
int i, j;
double T;
int doV, doD1, doD2;
doV = needD[0];
doD1 = needD[1];
doD2 = needD[2];
#include "scl3pfilterbody.c"
return;
}
void
393 _gageSclFilter( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_gageSclFilter";
int fd;
double *fw00, *fw11, *fw22;
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4,
gageScl3PFilter6, gageScl3PFilter8};
fd = 2*ctx->radius;
if ( !ctx->parm.k3pack ) {
fprintf( stderr, "!%s: sorry, 6-pack filtering not implemented\n", me );
return;
}
fw00 = ctx->fw + fd*3*gageKernel00;
fw11 = ctx->fw + fd*3*gageKernel11;
fw22 = ctx->fw + fd*3*gageKernel22;
/* perform the filtering */
if ( fd <= 8 ) {
filter[ctx->radius]( ctx->shape, pvl->iv3, pvl->iv2, pvl->iv1,
fw00, fw11, fw22,
pvl->directAnswer[gageSclValue],
pvl->directAnswer[gageSclGradVec],
pvl->directAnswer[gageSclHessian],
pvl->needD );
} else {
gageScl3PFilterN( ctx->shape, fd,
pvl->iv3, pvl->iv2, pvl->iv1,
fw00, fw11, fw22,
pvl->directAnswer[gageSclValue],
pvl->directAnswer[gageSclGradVec],
pvl->directAnswer[gageSclHessian],
pvl->needD );
}
return;
}
#undef X
#undef Y
#undef Z
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
void
28 _gageSclIv3Print ( FILE *file, gageContext *ctx, gagePerVolume *pvl ) {
double *iv3;
int i, fd;
iv3 = pvl->iv3;
fd = 2*ctx->radius;
fprintf( file, "iv3[]:\n" );
switch( fd ) {
case 2:
fprintf( file, "% 10.4f % 10.4f\n", ( float )iv3[6], ( float )iv3[7] );
fprintf( file, " % 10.4f % 10.4f\n\n", ( float )iv3[4], ( float )iv3[5] );
fprintf( file, "% 10.4f % 10.4f\n", ( float )iv3[2], ( float )iv3[3] );
fprintf( file, " % 10.4f % 10.4f\n", ( float )iv3[0], ( float )iv3[1] );
break;
case 4:
for ( i=3; i>=0; i-- ) {
fprintf( file, "% 10.4f % 10.4f % 10.4f % 10.4f\n",
( float )iv3[12+16*i], ( float )iv3[13+16*i],
( float )iv3[14+16*i], ( float )iv3[15+16*i] );
fprintf( file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n",
( float )iv3[ 8+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 9+16*i], ( float )iv3[10+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[11+16*i] );
fprintf( file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n",
( float )iv3[ 4+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 5+16*i], ( float )iv3[ 6+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 7+16*i] );
fprintf( file, " % 10.4f % 10.4f % 10.4f % 10.4f\n",
( float )iv3[ 0+16*i], ( float )iv3[ 1+16*i],
( float )iv3[ 2+16*i], ( float )iv3[ 3+16*i] );
if ( i ) fprintf( file, "\n" );
}
break;
default:
for ( i=0; i<fd*fd*fd; i++ ) {
fprintf( file, " iv3[% 3d, % 3d, % 3d] = % 10.4f\n",
i%fd, ( i/fd )%fd, i/( fd*fd ), ( float )iv3[i] );
}
break;
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
void
28 gageShapeReset( gageShape *shape ) {
/* NOTE this guards against NULL */
if ( shape ) {
/* input */
shape->defaultCenter = gageDefDefaultCenter;
shape->orientationFromSpacing = gageDefOrientationFromSpacing;
/* output; setting this in ways that can be used to test
whether the shape has been set */
shape->center = nrrdCenterUnknown;
shape->fromOrientation = AIR_FALSE;
ELL_3V_SET( shape->size, 0, 0, 0 );
ELL_3V_SET( shape->spacing, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_4M_NAN_SET( shape->ItoW );
ELL_4M_NAN_SET( shape->WtoI );
ELL_3M_NAN_SET( shape->ItoWSubInvTransp );
ELL_3M_NAN_SET( shape->ItoWSubInv );
}
return;
}
static void *
50 _mopShapeReset( void *_shape ) {
gageShape *shape;
shape = AIR_CAST( gageShape *, _shape );
gageShapeReset( shape );
return NULL;
}
gageShape *
59 gageShapeNew( ) {
gageShape *shape;
shape = ( gageShape * )calloc( 1, sizeof( gageShape ) );
if ( shape ) {
gageShapeReset( shape );
}
return shape;
}
gageShape *
70 gageShapeCopy( const gageShape *shp ) {
gageShape *nhp;
if ( ( nhp = gageShapeNew( ) ) ) {
/* no pointers, so this is easy */
memcpy( nhp, shp, sizeof( gageShape ) );
}
return nhp;
}
gageShape *
81 gageShapeNix( gageShape *shape ) {
airFree( shape );
return NULL;
}
static void
88 shapeUnitItoW( const gageShape *shape, double world[3],
const double indx[3], const double volHalfLen[3] ) {
unsigned int i;
if ( nrrdCenterNode == shape->center ) {
for ( i=0; i<=2; i++ ) {
world[i] = NRRD_NODE_POS( -volHalfLen[i], volHalfLen[i],
shape->size[i], indx[i] );
}
} else {
for ( i=0; i<=2; i++ ) {
world[i] = NRRD_CELL_POS( -volHalfLen[i], volHalfLen[i],
shape->size[i], indx[i] );
}
}
}
/*
** _gageShapeSet
**
** we are serving two masters here. If ctx is non-NULL, we are being called
** from within gage, and we are to be lax or strict according to the settings
** of ctx->parm.requireAllSpacings and ctx->parm.requireEqualCenters. If
** ctx is NULL, gageShapeSet was called, in which case we go with lax
** behavior ( nothing "required" )
**
** This function has subsumed the contents of the old gageVolumeCheck,
** and hence has become this weird beast- part error checker and part
** ( gageShape ) initializer. Oh well...
*/
int
119 _gageShapeSet( const gageContext *ctx, gageShape *shape,
const Nrrd *nin, unsigned int baseDim ) {
static const char me[]="_gageShapeSet";
int ai, cx, cy, cz, statCalc[3], status, ofspc;
unsigned int minsize;
const NrrdAxisInfo *ax[3];
double vecA[4], vecB[3], vecC[3], vecD[4], matA[9],
spcCalc[3], vecCalc[3][NRRD_SPACE_DIM_MAX], orig[NRRD_SPACE_DIM_MAX];
airArray *mop;
/*
fprintf( stderr, "!%s: ctx = %p ( %s, %s )\n", me, ctx,
( ctx
? ( ctx->shape->fromOrientation
? "YES from orient"
: "not from orient" )
: "???" ),
( ctx
? ( ctx->parm.orientationFromSpacing
? "YES ofs"
: "not ofs" )
: "???" ) );
*/
/* ------ basic error checking */
mop = airMopNew( );
airMopAdd( mop, shape, _mopShapeReset, airMopOnError );
if ( !( shape && nin ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
if ( nrrdCheck( nin ) ) {
biffMovef( GAGE, NRRD, "%s: basic nrrd validity check failed", me );
airMopError( mop ); return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( GAGE, "%s: need a non-block type nrrd", me );
airMopError( mop ); return 1;
}
if ( !( nin->dim == 3 + baseDim ) ) {
biffAddf( GAGE, "%s: nrrd should be %u-D, not %u-D",
me, 3 + baseDim, nin->dim );
airMopError( mop ); return 1;
}
ax[0] = &( nin->axis[baseDim+0] );
ax[1] = &( nin->axis[baseDim+1] );
ax[2] = &( nin->axis[baseDim+2] );
statCalc[0] = nrrdSpacingCalculate( nin, baseDim + 0,
spcCalc + 0, vecCalc[0] );
statCalc[1] = nrrdSpacingCalculate( nin, baseDim + 1,
spcCalc + 1, vecCalc[1] );
statCalc[2] = nrrdSpacingCalculate( nin, baseDim + 2,
spcCalc + 2, vecCalc[2] );
/* see if nrrdSpacingCalculate ever *failed* */
if ( nrrdSpacingStatusUnknown == statCalc[0]
|| nrrdSpacingStatusUnknown == statCalc[1]
|| nrrdSpacingStatusUnknown == statCalc[2] ) {
biffAddf( GAGE, "%s: nrrdSpacingCalculate trouble on axis %d, %d, or %d",
me, baseDim + 0, baseDim + 1, baseDim + 2 );
airMopError( mop ); return 1;
}
if ( !( statCalc[0] == statCalc[1] && statCalc[1] == statCalc[2] ) ) {
biffAddf( GAGE, "%s: inconsistent spacing information on axes "
"%u ( %s ), %u ( %s ), and %u ( %s )", me,
baseDim + 0, airEnumDesc( nrrdSpacingStatus, statCalc[0] ),
baseDim + 1, airEnumDesc( nrrdSpacingStatus, statCalc[1] ),
baseDim + 2, airEnumDesc( nrrdSpacingStatus, statCalc[2] ) );
airMopError( mop ); return 1;
}
/* this simplifies reasoning in the code that follows */
status = statCalc[0];
/* zero spacing would be problematic */
if ( 0 == spcCalc[0] && 0 == spcCalc[1] && 0 == spcCalc[2] ) {
biffAddf( GAGE, "%s: spacings ( %g, %g, %g ) for axes %d, %d, %d not all "
"non-zero", me, spcCalc[1], spcCalc[1], spcCalc[2],
baseDim+0, baseDim+1, baseDim+2 );
airMopError( mop ); return 1;
}
/* error checking based on status */
if ( nrrdSpacingStatusScalarWithSpace == status ) {
biffAddf( GAGE, "%s: sorry, can't handle per-axis spacing that isn't part "
"of a surrounding world space ( %s )",
me, airEnumStr( nrrdSpacingStatus, status ) );
airMopError( mop ); return 1;
}
/* we no longer allow a nrrd to come in with no spacing info at all */
if ( nrrdSpacingStatusNone == status ) {
biffAddf( GAGE, "%s: sorry, need some spacing info for spatial axes "
"%u, %u, %u", me,
baseDim+0, baseDim+1, baseDim+2 );
airMopError( mop ); return 1;
}
/* actually, there shouldn't be any other options for spacing status
besides these too; this is just being careful */
if ( !( nrrdSpacingStatusDirection == status
|| nrrdSpacingStatusScalarNoSpace == status ) ) {
biffAddf( GAGE, "%s: sorry, can only handle spacing status %d ( %s ) "
"or %d ( %s ), not %d ( %s )", me,
nrrdSpacingStatusDirection,
airEnumStr( nrrdSpacingStatus, nrrdSpacingStatusDirection ),
nrrdSpacingStatusScalarNoSpace,
airEnumStr( nrrdSpacingStatus, nrrdSpacingStatusScalarNoSpace ),
status, airEnumStr( nrrdSpacingStatus, status ) );
airMopError( mop ); return 1;
}
if ( nrrdSpacingStatusDirection == status ) {
shape->fromOrientation = AIR_TRUE;
if ( 3 != nin->spaceDim ) {
biffAddf( GAGE, "%s: orientation space dimension %d != 3",
me, nin->spaceDim );
airMopError( mop ); return 1;
}
} else {
shape->fromOrientation = AIR_FALSE;
}
/* ------ find centering ( set shape->center ) */
/* NOTE: when the volume is being crammed in a bi-unit cube, the centering
will actually affect the positions of the samples. Otherwise,
( having full orientation, or using orientationFromSpacing ), the
centering will only affect the probe-able bounds of the volume, but
the sample positions in space don't depend on centering */
cx = ax[0]->center;
cy = ax[1]->center;
cz = ax[2]->center;
if ( !( cx == cy && cy == cz ) ) {
biffAddf( GAGE,
"%s: axes %d, %d, %d centerings ( %s, %s, %s ) not all equal",
me, baseDim+0, baseDim+1, baseDim+2,
airEnumStr( nrrdCenter, cx ),
airEnumStr( nrrdCenter, cy ),
airEnumStr( nrrdCenter, cz ) );
airMopError( mop ); return 1;
}
/* Hopefully, ctx->parm.defaultCenter == shape->defaultCenter; and this
worry will be moot if ctx->parm.defaultCenter goes away */
shape->center = ( nrrdCenterUnknown != cx
? cx /* cx == cy == cz, by above */
: ( ctx
? ctx->parm.defaultCenter
: shape->defaultCenter ) );
/* ------ find sizes ( set shape->size[0, 1, 2] ) */
shape->size[0] = ax[0]->size;
shape->size[1] = ax[1]->size;
shape->size[2] = ax[2]->size;
minsize = ( nrrdCenterCell == shape->center ? 1 : 2 );
/* this can't be relaxed in the face of having full orientation info,
because even then, you can't have a non-zero probe-able volume if
there's only one sample along a node-centered axis */
if ( !( shape->size[0] >= minsize
&& shape->size[1] >= minsize
&& shape->size[2] >= minsize ) ) {
biffAddf( GAGE, "%s: sizes ( %u, %u, %u ) must all be >= %u "
"( min number of %s-centered samples )", me,
shape->size[0], shape->size[1], shape->size[2],
minsize, airEnumStr( nrrdCenter, shape->center ) );
airMopError( mop ); return 1;
}
/* ------ find spacings[0, 1, 2] and ItoW matrix */
/* Hopefully, ctx->parm.orientationFromSpacing and
shape->orientationFromSpacing don't represent competing interests;
this worry will be moot if ctx->parm.orientationFromSpacing goes away */
ofspc = ( ( ctx && ctx->parm.orientationFromSpacing )
|| shape->orientationFromSpacing );
if ( shape->fromOrientation || ofspc ) {
if ( ofspc ) {
/* need abs( ) in case an axis had negative spacing */
ELL_3V_ABS( shape->spacing, spcCalc );
ELL_3V_SET( vecCalc[0], airSgn( spcCalc[0] ), 0.0, 0.0 );
ELL_3V_SET( vecCalc[1], 0.0, airSgn( spcCalc[1] ), 0.0 );
ELL_3V_SET( vecCalc[2], 0.0, 0.0, airSgn( spcCalc[2] ) );
} else {
ELL_3V_COPY( shape->spacing, spcCalc );
/* vecCalc set by nrrdSpacingCalculate */
}
if ( shape->fromOrientation ) {
/* if the spaceOrigin isn't set, this will be all NaNs */
nrrdSpaceOriginGet( nin, orig );
} else {
/* sorry, if you want to specify an image origin that over-rides the
behavior of centering the volume at ( 0, 0, 0 ), then it has to be
done through the full orientation info. That is, we don't want
to use nrrdOriginCalculate( ) because otherwise the logic gets
too complicated */
ELL_3V_SET( orig, AIR_NAN, AIR_NAN, AIR_NAN );
}
if ( !ELL_3V_EXISTS( orig ) ) {
/* don't have origin, for whatever reason; center volume on ( 0, 0, 0 ) */
ELL_3V_SET( orig, 0.0, 0.0, 0.0 );
ELL_3V_SCALE_INCR( orig, -( shape->size[0] - 1.0 )*shape->spacing[0]/2.0,
vecCalc[0] );
ELL_3V_SCALE_INCR( orig, -( shape->size[1] - 1.0 )*shape->spacing[1]/2.0,
vecCalc[1] );
ELL_3V_SCALE_INCR( orig, -( shape->size[2] - 1.0 )*shape->spacing[2]/2.0,
vecCalc[2] );
}
vecD[3] = 0;
ELL_3V_SCALE( vecD, spcCalc[0], vecCalc[0] );
ELL_4MV_COL0_SET( shape->ItoW, vecD );
ELL_3V_SCALE( vecD, spcCalc[1], vecCalc[1] );
ELL_4MV_COL1_SET( shape->ItoW, vecD );
ELL_3V_SCALE( vecD, spcCalc[2], vecCalc[2] );
ELL_4MV_COL2_SET( shape->ItoW, vecD );
vecD[3] = 1;
ELL_3V_COPY( vecD, orig );
ELL_4MV_COL3_SET( shape->ItoW, vecD );
/*
fprintf( stderr, "%s: %g ( %g, %g, %g )\n", me,
spcCalc[0], vecCalc[0][0], vecCalc[0][1], vecCalc[0][2] );
fprintf( stderr, "%s: %g ( %g, %g, %g )\n", me,
spcCalc[1], vecCalc[1][0], vecCalc[1][1], vecCalc[1][2] );
fprintf( stderr, "%s: %g ( %g, %g, %g )\n", me,
spcCalc[2], vecCalc[2][0], vecCalc[2][1], vecCalc[2][2] );
*/
/*
fprintf( stderr, "%s: ItoW = %g %g %g %g\n", me,
shape->ItoW[ 0], shape->ItoW[ 1], shape->ItoW[ 2], shape->ItoW[ 3] );
fprintf( stderr, "%s: %g %g %g %g\n", me,
shape->ItoW[ 4], shape->ItoW[ 5], shape->ItoW[ 6], shape->ItoW[ 7] );
fprintf( stderr, "%s: %g %g %g %g\n", me,
shape->ItoW[ 8], shape->ItoW[ 9], shape->ItoW[10], shape->ItoW[11] );
fprintf( stderr, "%s: %g %g %g %g\n", me,
shape->ItoW[12], shape->ItoW[13], shape->ItoW[14], shape->ItoW[15] );
*/
} else { /* not ( shape->fromOrientation || ofspc ) */
double maxLen, volHalfLen[3];
size_t num[3];
/* ------ learn lengths for bounding nrrd in bi-unit cube */
ELL_3V_ABS( shape->spacing, spcCalc );
maxLen = 0.0;
for ( ai=0; ai<=2; ai++ ) {
num[ai] = ( nrrdCenterNode == shape->center
? shape->size[ai]-1
: shape->size[ai] );
volHalfLen[ai] = num[ai]*shape->spacing[ai];
maxLen = AIR_MAX( maxLen, volHalfLen[ai] );
}
/* Thu Dec 13 02:45:01 EST 2007
fixed long-standing bug in handling vols without full orientation info:
spacing[ai] was never scaled to account for being crammed into
the bi-unit cube!! */
for ( ai=0; ai<=2; ai++ ) {
volHalfLen[ai] /= maxLen;
shape->spacing[ai] = 2*volHalfLen[ai]/num[ai];
}
ELL_3V_SET( vecC, 0, 0, 0 );
shapeUnitItoW( shape, vecA, vecC, volHalfLen );
ELL_3V_SET( vecC, 1, 0, 0 );
shapeUnitItoW( shape, vecB, vecC, volHalfLen );
ELL_3V_SUB( vecD, vecB, vecA );
vecD[3] = 0;
ELL_4MV_COL0_SET( shape->ItoW, vecD );
ELL_3V_SET( vecC, 0, 1, 0 );
shapeUnitItoW( shape, vecB, vecC, volHalfLen );
ELL_3V_SUB( vecD, vecB, vecA );
vecD[3] = 0;
ELL_4MV_COL1_SET( shape->ItoW, vecD );
ELL_3V_SET( vecC, 0, 0, 1 );
shapeUnitItoW( shape, vecB, vecC, volHalfLen );
ELL_3V_SUB( vecD, vecB, vecA );
vecD[3] = 0;
ELL_4MV_COL2_SET( shape->ItoW, vecD );
vecA[3] = 1;
ELL_4MV_COL3_SET( shape->ItoW, vecA );
}
/* ------ set the rest of the matrices */
ell_4m_inv_d( shape->WtoI, shape->ItoW );
ELL_34M_EXTRACT( matA, shape->ItoW );
ell_3m_inv_d( shape->ItoWSubInv, matA );
ELL_3M_TRANSPOSE( shape->ItoWSubInvTransp, shape->ItoWSubInv );
airMopOkay( mop );
return 0;
}
int
403 gageShapeSet( gageShape *shape, const Nrrd *nin, int baseDim ) {
static const char me[]="gageShapeSet";
if ( _gageShapeSet( NULL, shape, nin, baseDim ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
return 0;
}
/*
** this wasn't being used at all
void
gageShapeUnitWtoI( gageShape *shape, double indx[3], double world[3] ) {
int i;
if ( nrrdCenterNode == shape->center ) {
for ( i=0; i<=2; i++ ) {
indx[i] = NRRD_NODE_IDX( -shape->volHalfLen[i], shape->volHalfLen[i],
shape->size[i], world[i] );
}
} else {
for ( i=0; i<=2; i++ ) {
indx[i] = NRRD_CELL_IDX( -shape->volHalfLen[i], shape->volHalfLen[i],
shape->size[i], world[i] );
}
}
}
*/
void
434 gageShapeWtoI( const gageShape *shape,
double _indx[3], const double _world[3] ) {
/* static const char me[]="gageShapeWtoI"; */
double indx[4], world[4];
/*
fprintf( stderr, "!%s: hello %p %p %p; %p\n", me,
shape, _indx, _world, shape->WtoI );
*/
ELL_3V_COPY( world, _world );
world[3] = 1.0;
ELL_4MV_MUL( indx, shape->WtoI, world );
ELL_3V_SCALE( _indx, 1.0/indx[3], indx );
}
void
450 gageShapeItoW( const gageShape *shape,
double _world[3], const double _indx[3] ) {
double world[4], indx[4];
ELL_3V_COPY( indx, _indx );
indx[3] = 1.0;
ELL_4MV_MUL( world, shape->ItoW, indx );
ELL_3V_SCALE( _world, 1.0/world[3], world );
}
/*
******** gageShapeEqual
**
** shapes not being equal is a biffable error,
** returning 0 signifies this "error"
** returning 1 means no error, they ARE equal
*/
int
468 gageShapeEqual( const gageShape *shape1, const char *_name1,
const gageShape *shape2, const char *_name2 ) {
static const char me[]="gageShapeEqual";
const char *name1, *name2, what[] = "???";
if ( !( shape1 && shape2 ) ) {
biffAddf( GAGE, "%s: can't judge equality w/ NULL pointer", me );
return 0;
}
name1 = _name1 ? _name1 : what;
name2 = _name2 ? _name2 : what;
if ( !( shape1->fromOrientation == shape2->fromOrientation ) ) {
biffAddf( GAGE,
"%s: fromOrientation of %s ( %s ) != %s's ( %s )", me,
name1, shape1->fromOrientation ? "true" : "false",
name2, shape2->fromOrientation ? "true" : "false" );
return 0;
}
if ( !( shape1->size[0] == shape2->size[0] &&
shape1->size[1] == shape2->size[1] &&
shape1->size[2] == shape2->size[2] ) ) {
biffAddf( GAGE,
"%s: dimensions of %s ( %u, %u, %u ) != %s's ( %u, %u, %u )",
me, name1,
shape1->size[0], shape1->size[1], shape1->size[2],
name2,
shape2->size[0], shape2->size[1], shape2->size[2] );
return 0;
}
if ( shape1->fromOrientation ) {
if ( !( ELL_4M_EQUAL( shape1->ItoW, shape2->ItoW ) ) ) {
biffAddf( GAGE, "%s: ItoW matrices of %s and %s not the same", me,
name1, name2 );
return 0;
}
} else {
if ( !( shape1->spacing[0] == shape2->spacing[0] &&
shape1->spacing[1] == shape2->spacing[1] &&
shape1->spacing[2] == shape2->spacing[2] ) ) {
biffAddf( GAGE, "%s: spacings of %s ( %g, %g, %g ) != %s's ( %g, %g, %g )",
me, name1,
shape1->spacing[0], shape1->spacing[1], shape1->spacing[2],
name2,
shape2->spacing[0], shape2->spacing[1], shape2->spacing[2] );
return 0;
}
if ( !( shape1->center == shape2->center ) ) {
biffAddf( GAGE,
"%s: centering of %s ( %s ) != %s's ( %s )", me,
name1, airEnumStr( nrrdCenter, shape1->center ),
name2, airEnumStr( nrrdCenter, shape2->center ) );
return 0;
}
}
return 1;
}
void
527 gageShapeBoundingBox( double min[3], double max[3],
const gageShape *shape ) {
/* static const char me[]="gageShapeBoundingBox"; */
double minIdx[3], maxIdx[3], cornerIdx[8][3], tmp[3];
unsigned int ii;
if ( !( min && max && shape ) ) {
return;
}
if ( nrrdCenterNode == shape->center ) {
ELL_3V_SET( minIdx, 0, 0, 0 );
ELL_3V_SET( maxIdx,
shape->size[0]-1,
shape->size[1]-1,
shape->size[2]-1 );
} else {
ELL_3V_SET( minIdx, -0.5, -0.5, -0.5 );
ELL_3V_SET( maxIdx,
shape->size[0]-0.5,
shape->size[1]-0.5,
shape->size[2]-0.5 );
}
ELL_3V_SET( cornerIdx[0], minIdx[0], minIdx[1], minIdx[2] );
ELL_3V_SET( cornerIdx[1], maxIdx[0], minIdx[1], minIdx[2] );
ELL_3V_SET( cornerIdx[2], minIdx[0], maxIdx[1], minIdx[2] );
ELL_3V_SET( cornerIdx[3], maxIdx[0], maxIdx[1], minIdx[2] );
ELL_3V_SET( cornerIdx[4], minIdx[0], minIdx[1], maxIdx[2] );
ELL_3V_SET( cornerIdx[5], maxIdx[0], minIdx[1], maxIdx[2] );
ELL_3V_SET( cornerIdx[6], minIdx[0], maxIdx[1], maxIdx[2] );
ELL_3V_SET( cornerIdx[7], maxIdx[0], maxIdx[1], maxIdx[2] );
gageShapeItoW( shape, tmp, cornerIdx[0] );
ELL_3V_COPY( min, tmp );
ELL_3V_COPY( max, tmp );
for ( ii=1; ii<8; ii++ ) {
gageShapeItoW( shape, tmp, cornerIdx[ii] );
ELL_3V_MIN( min, min, tmp );
ELL_3V_MAX( max, max, tmp );
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
#define GAGE_CACHE_LEN 1013
unsigned int
30 _gageHash( int x, int y, int z ) {
unsigned int h, g;
unsigned char s[6];
int i;
s[0] = x & 0xff;
s[1] = ( x >> 8 ) & 0xff;
s[2] = y & 0xff;
s[3] = ( y >> 8 ) & 0xff;
s[4] = z & 0xff;
s[5] = ( z >> 8 ) & 0xff;
h = 0;
for ( i=0; i<=5; i++ ) {
h = ( h << 4 ) + s[i];
if ( ( g = h & 0xf0000000 ) ) {
h = h ^ ( g >> 24 );
h = h ^ g;
}
}
return h % GAGE_CACHE_LEN;
}
void
54 _gageCacheProbe( gageContext *ctx, double *grad,
int *cc, double *gc,
int x, int y, int z ) {
int hi;
hi = _gageHash( x, y, z );
if ( ( cc[3*hi + 0] == x ) &&
( cc[3*hi + 1] == y ) &&
( cc[3*hi + 2] == z ) ) {
/* cache hit */
ELL_3V_COPY( grad, gc + 3*hi );
} else {
/* cache miss */
cc[3*hi + 0] = x;
cc[3*hi + 1] = y;
cc[3*hi + 2] = z;
gageProbe( ctx, x, y, z );
ELL_3V_COPY( gc + 3*hi, grad );
}
return ;
}
/*
******** gageStructureTensor( )
**
** Computes volume of structure tensors. Currently, only works on a scalar
** fields ( for multi-scalar fields, just add structure tensors from each
** component scalar ), and only uses the B-spline kernel for differentiation
** and derivative blurring.
**
** Note, if you want to use dsmp > 1, its your responsibility to give
** an appropriate iScale > 1, so that you don't undersample.
*/
int
88 gageStructureTensor( Nrrd *nout, const Nrrd *nin,
int dScale, int iScale, int dsmp ) {
static const char me[]="gageStructureTensor";
NrrdKernelSpec *gk0, *gk1, *ik0;
int E, rad, diam, osx, osy, osz, oxi, oyi, ozi,
_ixi, _iyi, _izi, ixi, iyi, izi, wi, *coordCache;
gageContext *ctx;
gageQuery query;
gagePerVolume *pvl;
airArray *mop;
double *grad, *ixw, *iyw, *izw, wght, sten[6], *gradCache, *out;
double xs, ys, zs, ms;
if ( !( nout && nin ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( 3 == nin->dim && nrrdTypeBlock != nin->type ) ) {
biffAddf( GAGE, "%s: nin isn't a 3D non-block type nrrd", me );
return 1;
}
/*
if ( !( AIR_EXISTS( nin->axis[0].spacing )
&& AIR_EXISTS( nin->axis[1].spacing )
&& AIR_EXISTS( nin->axis[2].spacing ) ) ) {
biffAddf( GAGE, "%s: nin's axis 0, 1, 2 spacings don't all exist", me );
return 1;
}
*/
if ( !( dScale >= 1 && iScale >= 1 && dsmp >= 1 ) ) {
biffAddf( GAGE, "%s: dScale ( %d ), iScale ( %d ), dsmp ( %d ) not all >= 1",
me, dScale, iScale, dsmp );
return 1;
}
mop = airMopNew( );
gk0 = nrrdKernelSpecNew( );
gk1 = nrrdKernelSpecNew( );
ik0 = nrrdKernelSpecNew( );
airMopAdd( mop, gk0, ( airMopper )nrrdKernelSpecNix, airMopAlways );
airMopAdd( mop, gk1, ( airMopper )nrrdKernelSpecNix, airMopAlways );
airMopAdd( mop, ik0, ( airMopper )nrrdKernelSpecNix, airMopAlways );
if ( nrrdKernelSpecParse( gk0, "cubic:1, 0" )
|| nrrdKernelSpecParse( gk1, "cubicd:1, 0" )
|| nrrdKernelSpecParse( ik0, "cubic:1, 0" ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't set up kernels", me );
airMopError( mop ); return 1;
}
/* manually set scale parameters */
gk0->parm[0] = dScale;
gk1->parm[0] = dScale;
ik0->parm[0] = 1.0; /* this is more complicated . . . */
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, gk0->kernel, gk0->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, gk1->kernel, gk1->parm );
if ( !E ) GAGE_QUERY_RESET( query );
if ( !E ) GAGE_QUERY_ITEM_ON( query, gageSclGradVec );
if ( !E ) E |= gageQuerySet( ctx, pvl, query );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
biffAddf( GAGE, "%s: ", me );
airMopError( mop ); return 1;
}
grad = _gageAnswerPointer( ctx, pvl, gageSclGradVec );
xs = nin->axis[0].spacing;
ys = nin->axis[1].spacing;
zs = nin->axis[2].spacing;
xs = AIR_EXISTS( xs ) ? AIR_ABS( xs ) : 1.0;
ys = AIR_EXISTS( ys ) ? AIR_ABS( ys ) : 1.0;
zs = AIR_EXISTS( zs ) ? AIR_ABS( zs ) : 1.0;
ms = airCbrt( xs*ys*zs );
/* ms is geometric mean of {xs, ys, zs} */
xs /= ms;
ys /= ms;
zs /= ms;
fprintf( stderr, "iScale = %d, xs, ys, zs = %g, %g, %g\n",
iScale, xs, ys, xs );
rad = 0;
ik0->parm[0] = iScale/xs;
rad = AIR_MAX( rad, AIR_ROUNDUP( ik0->kernel->support( ik0->parm ) ) );
ik0->parm[0] = iScale/ys;
rad = AIR_MAX( rad, AIR_ROUNDUP( ik0->kernel->support( ik0->parm ) ) );
ik0->parm[0] = iScale/zs;
rad = AIR_MAX( rad, AIR_ROUNDUP( ik0->kernel->support( ik0->parm ) ) );
diam = 2*rad + 1;
ixw = AIR_CALLOC( diam, double );
iyw = AIR_CALLOC( diam, double );
izw = AIR_CALLOC( diam, double );
airMopAdd( mop, ixw, airFree, airMopAlways );
airMopAdd( mop, iyw, airFree, airMopAlways );
airMopAdd( mop, izw, airFree, airMopAlways );
if ( !( ixw && iyw && izw ) ) {
biffAddf( GAGE, "%s: couldn't allocate grad vector or weight buffers", me );
airMopError( mop ); return 1;
}
/* the only reason that it is thread-safe to cache gageProbe results,
without having the cache hang directly off the gageContext, is that
we're doing all the probing for one context in one shot- producing
an entirely volume of structure tensors with one function call */
gradCache = AIR_CALLOC( 3*GAGE_CACHE_LEN, double );
coordCache = AIR_CALLOC( 3*GAGE_CACHE_LEN, int );
airMopAdd( mop, gradCache, airFree, airMopAlways );
airMopAdd( mop, coordCache, airFree, airMopAlways );
if ( !( gradCache && coordCache ) ) {
biffAddf( GAGE, "%s: couldn't allocate caches", me );
airMopError( mop ); return 1;
}
for ( ixi=0; ixi<GAGE_CACHE_LEN; ixi++ ) {
coordCache[3*ixi + 0] = -1;
coordCache[3*ixi + 1] = -1;
coordCache[3*ixi + 2] = -1;
}
for ( wi=-rad; wi<=rad; wi++ ) {
ik0->parm[0] = iScale/xs;
ixw[wi+rad] = ik0->kernel->eval1_d( wi, ik0->parm );
ik0->parm[0] = iScale/ys;
iyw[wi+rad] = ik0->kernel->eval1_d( wi, ik0->parm );
ik0->parm[0] = iScale/zs;
izw[wi+rad] = ik0->kernel->eval1_d( wi, ik0->parm );
fprintf( stderr, "%d --> ( %g, %g, %g ) -> ( %g, %g, %g )\n",
wi, wi/xs, wi/ys, wi/zs, ixw[wi+rad], iyw[wi+rad], izw[wi+rad] );
}
osx = ( nin->axis[0].size )/dsmp;
osy = ( nin->axis[1].size )/dsmp;
osz = ( nin->axis[2].size )/dsmp;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 4,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, osx ),
AIR_CAST( size_t, osy ),
AIR_CAST( size_t, osz ) ) ) {
biffAddf( GAGE, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nout, ( airMopper )nrrdEmpty, airMopOnError );
out = AIR_CAST( double *, nout->data );
for ( ozi=0; ozi<osz; ozi++ ) {
fprintf( stderr, "%s: z = %d/%d\n", me, ozi+1, osz );
for ( oyi=0; oyi<osy; oyi++ ) {
for ( oxi=0; oxi<osx; oxi++ ) {
sten[0] = sten[1] = sten[2] = sten[3] = sten[4] = sten[5] = 0;
for ( _izi=0; _izi<diam; _izi++ ) {
izi = AIR_CLAMP( 0, _izi - rad + ozi*dsmp,
( int )nin->axis[2].size-1 );
if ( !izw[_izi] ) continue;
for ( _iyi=0; _iyi<diam; _iyi++ ) {
iyi = AIR_CLAMP( 0, _iyi - rad + oyi*dsmp,
( int )nin->axis[1].size-1 );
if ( !iyw[_iyi] ) continue;
for ( _ixi=0; _ixi<diam; _ixi++ ) {
ixi = AIR_CLAMP( 0, _ixi - rad + oxi*dsmp,
( int )nin->axis[0].size-1 );
if ( !ixw[_ixi] ) continue;
wght = ixw[_ixi]*iyw[_iyi]*izw[_izi];
_gageCacheProbe( ctx, grad, coordCache, gradCache, ixi, iyi, izi );
sten[0] += wght*grad[0]*grad[0];
sten[1] += wght*grad[0]*grad[1];
sten[2] += wght*grad[0]*grad[2];
sten[3] += wght*grad[1]*grad[1];
sten[4] += wght*grad[1]*grad[2];
sten[5] += wght*grad[2]*grad[2];
}
}
}
out[0] = 1.0;
out[1] = sten[0];
out[2] = sten[1];
out[3] = sten[2];
out[4] = sten[3];
out[5] = sten[4];
out[6] = sten[5];
out += 7;
}
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
double
28 gageStackWtoI( gageContext *ctx, double swrl, int *outside ) {
double si;
if ( ctx && ctx->parm.stackUse && outside ) {
unsigned int sidx;
if ( swrl < ctx->stackPos[0] ) {
/* we'll extrapolate from stackPos[0] and [1] */
sidx = 0;
*outside = AIR_TRUE;
} else if ( swrl > ctx->stackPos[ctx->pvlNum-2] ) {
/* extrapolate from stackPos[ctx->pvlNum-3] and [ctx->pvlNum-2];
gageStackPerVolumeAttach ensures that we there are at least two
blurrings pvls & one base pvl ==> pvlNum >= 3 ==> pvlNum-3 >= 0 */
sidx = ctx->pvlNum-3;
*outside = AIR_TRUE;
} else {
/* HEY: stupid linear search */
for ( sidx=0; sidx<ctx->pvlNum-2; sidx++ ) {
if ( AIR_IN_CL( ctx->stackPos[sidx], swrl, ctx->stackPos[sidx+1] ) ) {
break;
}
}
if ( sidx == ctx->pvlNum-2 ) {
/* search failure */
*outside = AIR_FALSE;
return AIR_NAN;
}
*outside = AIR_FALSE;
}
si = AIR_AFFINE( ctx->stackPos[sidx], swrl, ctx->stackPos[sidx+1],
sidx, sidx+1 );
} else {
si = AIR_NAN;
}
return si;
}
double
66 gageStackItoW( gageContext *ctx, double si, int *outside ) {
unsigned int sidx;
double swrl, sfrac;
if ( ctx && ctx->parm.stackUse && outside ) {
if ( si < 0 ) {
sidx = 0;
*outside = AIR_TRUE;
} else if ( si > ctx->pvlNum-2 ) {
sidx = ctx->pvlNum-3;
*outside = AIR_TRUE;
} else {
sidx = AIR_CAST( unsigned int, si );
*outside = AIR_FALSE;
}
sfrac = si - sidx;
swrl = AIR_AFFINE( 0, sfrac, 1, ctx->stackPos[sidx], ctx->stackPos[sidx+1] );
/*
fprintf( stderr, "!%s: si %g ( %u ) --> %u + %g --> [%g, %g] -> %g\n", me,
si, ctx->pvlNum, sidx, sfrac,
ctx->stackPos[sidx], ctx->stackPos[sidx+1], swrl );
*/
} else {
swrl = AIR_NAN;
}
return swrl;
}
/*
** this is a little messy: the given pvlStack array has to be allocated
** by the caller to hold blNum gagePerVolume pointers, BUT, the values
** of pvlStack[i] shouldn't be set to anything: as with gagePerVolumeNew( ),
** gage allocates the pervolume itself.
*/
int
101 gageStackPerVolumeNew( gageContext *ctx,
gagePerVolume **pvlStack,
const Nrrd *const *nblur, unsigned int blNum,
const gageKind *kind ) {
static const char me[]="gageStackPerVolumeNew";
unsigned int blIdx;
if ( !( ctx && pvlStack && nblur && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !blNum ) {
biffAddf( GAGE, "%s: need non-zero num", me );
return 1;
}
for ( blIdx=0; blIdx<blNum; blIdx++ ) {
if ( !( pvlStack[blIdx] = gagePerVolumeNew( ctx, nblur[blIdx], kind ) ) ) {
biffAddf( GAGE, "%s: on pvl %u of %u", me, blIdx, blNum );
return 1;
}
}
return 0;
}
/*
** the "base" pvl is the LAST pvl, ctx->pvl[pvlNum-1]
*/
int
131 gageStackPerVolumeAttach( gageContext *ctx, gagePerVolume *pvlBase,
gagePerVolume **pvlStack, const double *stackPos,
unsigned int blNum ) {
static const char me[]="gageStackPerVolumeAttach";
unsigned int blIdx;
if ( !( ctx && pvlBase && pvlStack && stackPos ) ) {
biffAddf( GAGE, "%s: got NULL pointer %p %p %p %p", me,
AIR_VOIDP( ctx ), AIR_VOIDP( pvlBase ),
AIR_VOIDP( pvlStack ), AIR_CVOIDP( stackPos ) );
return 1;
}
if ( !( blNum >= 2 ) ) {
/* this constraint is important for the logic of stack reconstruction:
minimum number of node-centered samples is 2, and the number of
pvls has to be at least 3 ( two blurrings + one base pvl ) */
biffAddf( GAGE, "%s: need at least two samples along stack", me );
return 1;
}
if ( ctx->pvlNum ) {
biffAddf( GAGE, "%s: can't have pre-existing volumes ( %u ) "
"prior to stack attachment", me, ctx->pvlNum );
return 1;
}
for ( blIdx=0; blIdx<blNum; blIdx++ ) {
if ( !AIR_EXISTS( stackPos[blIdx] ) ) {
biffAddf( GAGE, "%s: stackPos[%u] = %g doesn't exist", me, blIdx,
stackPos[blIdx] );
return 1;
}
if ( blIdx < blNum-1 ) {
if ( !( stackPos[blIdx] < stackPos[blIdx+1] ) ) {
biffAddf( GAGE, "%s: stackPos[%u] = %g not < stackPos[%u] = %g", me,
blIdx, stackPos[blIdx], blIdx+1, stackPos[blIdx+1] );
return 1;
}
}
}
/* the base volume is LAST, after all the stack samples */
for ( blIdx=0; blIdx<blNum; blIdx++ ) {
if ( gagePerVolumeAttach( ctx, pvlStack[blIdx] ) ) {
biffAddf( GAGE, "%s: on pvl %u of %u", me, blIdx, blNum );
return 1;
}
}
if ( gagePerVolumeAttach( ctx, pvlBase ) ) {
biffAddf( GAGE, "%s: on base pvl", me );
return 1;
}
airFree( ctx->stackPos );
airFree( ctx->stackFsl );
airFree( ctx->stackFw );
ctx->stackPos = AIR_CALLOC( blNum, double );
ctx->stackFsl = AIR_CALLOC( blNum, double );
ctx->stackFw = AIR_CALLOC( blNum, double );
if ( !( ctx->stackPos && ctx->stackFsl && ctx->stackFw ) ) {
biffAddf( GAGE, "%s: couldn't allocate stack buffers ( %p %p %p )", me,
AIR_CAST( void *, ctx->stackPos ),
AIR_CAST( void *, ctx->stackFsl ),
AIR_CAST( void *, ctx->stackFw ) );
return 1;
}
for ( blIdx=0; blIdx<blNum; blIdx++ ) {
ctx->stackPos[blIdx] = stackPos[blIdx];
}
return 0;
}
/*
** _gageStackBaseIv3Fill
**
** after the individual iv3's in the stack have been filled, this does
** the across-stack filtering to fill the iv3 of pvl[pvlNum-1] ( the
** "base" pvl )
*/
int
210 _gageStackBaseIv3Fill( gageContext *ctx ) {
static const char me[]="_gageStackBaseIv3Fill";
unsigned int fd, pvlIdx, cacheIdx, cacheLen, baseIdx, valLen;
fd = 2*ctx->radius;
/* the "base" pvl is the LAST pvl */
baseIdx = ctx->pvlNum - 1;
cacheLen = fd*fd*fd*ctx->pvl[0]->kind->valLen;
if ( ctx->verbose > 2 ) {
fprintf( stderr, "%s: cacheLen = %u\n", me, cacheLen );
}
if ( nrrdKernelHermiteScaleSpaceFlag == ctx->ksp[gageKernelStack]->kernel ) {
unsigned int iii, xi, yi, zi, blurIdx, valIdx, fdd, sz, sy;
double xx, *iv3, *iv30, *iv31, sigma0, sigma1,
val0, val1, drv0, drv1, lapl0, lapl1;
fdd = fd*fd;
/* initialize the output iv3 to all zeros, since we won't be
usefully setting the values on the boundary ( the boundary which
is required in the rest of the stack's iv3s in order to do the
laplacian-based spline recon ), and we can't have any
non-existent values creeping in. We shouldn't need to do any
kind of nrrdBoundaryBleed thing here, because the kernel
weights really should be zero on the boundary. */
iv3 = ctx->pvl[baseIdx]->iv3;
for ( cacheIdx=0; cacheIdx<cacheLen; cacheIdx++ ) {
iv3[cacheIdx] = 0;
}
/* find the interval in the pre-blurred volumes containing the
desired scale location */
for ( pvlIdx=0; pvlIdx<ctx->pvlNum-1; pvlIdx++ ) {
if ( ctx->stackFw[pvlIdx] ) {
/* has to be non-zero somewhere, since _gageLocationSet( )
gives an error if there aren't non-zero stackFw[i] */
break;
}
}
/* so no way that pvlIdx == pvlNum-1 */
if ( pvlIdx == ctx->pvlNum-2 ) {
/* pvlNum-2 is pvl index of last pre-blurred volume */
/* gageStackPerVolumeAttach( ) enforces getting at least two
pre-blurred volumes --> pvlNum >= 3 --> blurIdx >= 0 */
blurIdx = pvlIdx-1;
xx = 1;
} else {
blurIdx = pvlIdx;
/* by design, the hermite non-kernel generates the same values as
the tent kernel ( with scale forced == 1 ), so we can use those
to control the interpolation */
xx = 1 - ctx->stackFw[pvlIdx];
}
iv30 = ctx->pvl[blurIdx]->iv3;
iv31 = ctx->pvl[blurIdx+1]->iv3;
sigma0 = ctx->stackPos[blurIdx];
sigma1 = ctx->stackPos[blurIdx+1];
valLen = ctx->pvl[baseIdx]->kind->valLen;
sy = ctx->shape->size[1];
sz = ctx->shape->size[2];
if ( 1 == sz ) {
if ( 1 == sy ) {
/* ( 1-D data: HEY copy and paste; see 2D case below ) */
for ( valIdx=0; valIdx<valLen; valIdx++ ) {
/* nixed "for zi" and "for yi" loop; zi==yi==1 */
for ( xi=1; xi<fd-1; xi++ ) {
iii = xi + fd*( 1 /* yi */ + fd*( 1 /* zi */ + fd*valIdx ) );
val0 = iv30[iii];
/* can do a 1D instead of 2D discrete laplacian */
lapl0 = ( iv30[iii+1] + iv30[iii-1] - 2*val0 );
val1 = iv31[iii];
lapl1 = ( iv31[iii+1] + iv31[iii-1] - 2*val1 );
drv0 = sigma0*lapl0*( sigma1 - sigma0 );
drv1 = sigma1*lapl1*( sigma1 - sigma0 );
iv3[iii] = val0 + xx*( drv0 + xx*( drv0*( -2 + xx ) + drv1*( -1 + xx )
+ ( val0 - val1 )*( -3 + 2*xx ) ) );
/*
fprintf( stderr, "!%s: ( %u ): val %g %g, lapl %g %g, sigma %g %g, drv %g %g --> iv3[%u] = %g\n", me,
xi, val0, val1, lapl0, lapl1, sigma0, sigma1, drv0, drv1, iii, iv3[iii] );
fprintf( stderr, "!%s: lapl0 %g = %g + %g + %g + %g - 4*%g\n", me, lapl0,
iv30[iii+1] , iv30[iii-1] , iv30[iii+fd] , iv30[iii-fd] , val0 );
fprintf( stderr, "!%s: lapl1 %g = %g + %g + %g + %g - 4*%g\n", me, lapl1,
iv31[iii+1] , iv31[iii-1] , iv31[iii+fd] , iv31[iii-fd] , val1 );
*/
}
for ( zi= 2 ; zi<fd-1; zi++ ) {
for ( yi= 2 ; yi<fd-1; yi++ ) {
for ( xi=1; xi<fd-1; xi++ ) {
iii = xi + fd*( yi + fd*( zi + fd*valIdx ) );
iv3[iii] = iv3[xi + fd*( 1 + fd*( 1 + fd*valIdx ) )];
}
}
}
}
} else {
/* as in gageIv3Fill; we do some special-case-ing for 2-D images;
( HEY copy and paste; see sz > 1 below for explanatory comments ) */
for ( valIdx=0; valIdx<valLen; valIdx++ ) {
/* nixed "for zi" loop; zi==1 */
for ( yi=1; yi<fd-1; yi++ ) {
for ( xi=1; xi<fd-1; xi++ ) {
iii = xi + fd*( yi + fd*( 1 /* zi */ + fd*valIdx ) );
val0 = iv30[iii];
/* can do a 2D instead of 3D discrete laplacian */
lapl0 = ( iv30[iii+1] + iv30[iii-1] +
iv30[iii+fd] + iv30[iii-fd] - 4*val0 );
val1 = iv31[iii];
lapl1 = ( iv31[iii+1] + iv31[iii-1] +
iv31[iii+fd] + iv31[iii-fd] - 4*val1 );
drv0 = sigma0*lapl0*( sigma1 - sigma0 );
drv1 = sigma1*lapl1*( sigma1 - sigma0 );
iv3[iii] = val0 + xx*( drv0 + xx*( drv0*( -2 + xx ) + drv1*( -1 + xx )
+ ( val0 - val1 )*( -3 + 2*xx ) ) );
/*
fprintf( stderr, "!%s: ( %u, %u ): val %g %g, lapl %g %g, sigma %g %g, drv %g %g --> iv3[%u] = %g\n", me,
xi, yi, val0, val1, lapl0, lapl1, sigma0, sigma1, drv0, drv1, iii, iv3[iii] );
fprintf( stderr, "!%s: lapl0 %g = %g + %g + %g + %g - 4*%g\n", me, lapl0,
iv30[iii+1] , iv30[iii-1] , iv30[iii+fd] , iv30[iii-fd] , val0 );
fprintf( stderr, "!%s: lapl1 %g = %g + %g + %g + %g - 4*%g\n", me, lapl1,
iv31[iii+1] , iv31[iii-1] , iv31[iii+fd] , iv31[iii-fd] , val1 );
*/
}
}
for ( zi= 2 ; zi<fd-1; zi++ ) {
for ( yi=1; yi<fd-1; yi++ ) {
for ( xi=1; xi<fd-1; xi++ ) {
iii = xi + fd*( yi + fd*( zi + fd*valIdx ) );
iv3[iii] = iv3[xi + fd*( yi + fd*( 1 + fd*valIdx ) )];
}
}
}
}
}
} else {
/* sz > 1 */
for ( valIdx=0; valIdx<valLen; valIdx++ ) {
for ( zi=1; zi<fd-1; zi++ ) {
for ( yi=1; yi<fd-1; yi++ ) {
for ( xi=1; xi<fd-1; xi++ ) {
/* note that iv3 axis ordering is x, y, z, tuple */
iii = xi + fd*( yi + fd*( zi + fd*valIdx ) );
val0 = iv30[iii];
lapl0 = ( iv30[iii+1] + iv30[iii-1] +
iv30[iii+fd] + iv30[iii-fd] +
iv30[iii+fdd] + iv30[iii-fdd] - 6*val0 );
val1 = iv31[iii];
lapl1 = ( iv31[iii+1] + iv31[iii-1] +
iv31[iii+fd] + iv31[iii-fd] +
iv31[iii+fdd] + iv31[iii-fdd] - 6*val1 );
/* the ( sigma1 - sigma0 ) factor is needed to convert the
derivative with respect to sigma ( sigma*lapl ) into the
derivative with respect to xx ( ranges from 0 to 1 ) */
drv0 = sigma0*lapl0*( sigma1 - sigma0 );
drv1 = sigma1*lapl1*( sigma1 - sigma0 );
/* This inner loop is the bottleneck for some uses of
scale-space; a re-arrangement of the Hermite spline
evaluation ( thanks Mathematica ) does save a little time */
iv3[iii] = val0 + xx*( drv0 + xx*( drv0*( -2 + xx ) + drv1*( -1 + xx )
+ ( val0 - val1 )*( -3 + 2*xx ) ) );
}
}
}
}
}
} else {
/* we're doing simple convolution-based recon on the stack */
/* NOTE we are treating the 4D fd*fd*fd*valLen iv3 as a big 1-D array */
double wght, val;
for ( cacheIdx=0; cacheIdx<cacheLen; cacheIdx++ ) {
val = 0;
for ( pvlIdx=0; pvlIdx<ctx->pvlNum-1; pvlIdx++ ) {
wght = ctx->stackFw[pvlIdx];
val += ( wght
? wght*ctx->pvl[pvlIdx]->iv3[cacheIdx]
: 0 );
}
ctx->pvl[baseIdx]->iv3[cacheIdx] = val;
}
}
return 0;
}
/*
******** gageStackProbe( )
*/
int
395 gageStackProbe( gageContext *ctx,
double xi, double yi, double zi, double stackIdx ) {
static const char me[]="gageStackProbe";
if ( !ctx ) {
return 1;
}
if ( !ctx->parm.stackUse ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: can't probe stack without parm.stackUse", me );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrStackUnused;
return 1;
}
return _gageProbe( ctx, xi, yi, zi, stackIdx );
}
int
415 gageStackProbeSpace( gageContext *ctx,
double xx, double yy, double zz, double ss,
int indexSpace, int clamp ) {
static const char me[]="gageStackProbeSpace";
int ret;
/*
fprintf( stderr, "!%s( %g, %g, %g, %g, %d, %d ): hello\n", me,
xx, yy, zz, ss, indexSpace, clamp );
if ( AIR_ABS( -98.2539 - xx ) < 0.1 && AIR_ABS( yy ) < 0.1 && AIR_ABS( zz ) < 0.1 && AIR_ABS( 495.853 - ss ) ) {
ctx->verbose += 10;
}
*/
if ( !ctx ) {
return 1;
}
if ( !ctx->parm.stackUse ) {
if ( ctx->parm.generateErrStr ) {
sprintf( ctx->errStr, "%s: can't probe stack without parm.stackUse", me );
} else {
strcpy( ctx->errStr, _GAGE_NON_ERR_STR );
}
ctx->errNum = gageErrStackUnused;
return 1;
}
ret = _gageProbeSpace( ctx, xx, yy, zz, ss, indexSpace, clamp );
/* fprintf( stderr, "!%s: returning %d\n", me, ret ); */
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
const char *
_gageSigmaSamplingStr[] = {
"( unknown_sampling )",
"unisig", /* "uniform-sigma", */
"unitau", /* "uniform-tau", */
"optil2" /* "optimal-3d-l2l2" */
};
const char *
_gageSigmaSamplingDesc[] = {
"unknown sampling",
"uniform samples along sigma",
"uniform samples along Lindeberg's tau",
"optimal sampling ( 3D L2 image error and L2 error across scales )"
};
const char *
_gageSigmaSamplingStrEqv[] = {
"uniform-sigma", "unisigma", "unisig",
"uniform-tau", "unitau",
"optimal-3d-l2l2", "optimal-l2l2", "optil2",
""
};
const int
_gageSigmaSamplingValEqv[] = {
gageSigmaSamplingUniformSigma, gageSigmaSamplingUniformSigma,
/* */ gageSigmaSamplingUniformSigma,
gageSigmaSamplingUniformTau, gageSigmaSamplingUniformTau,
gageSigmaSamplingOptimal3DL2L2, gageSigmaSamplingOptimal3DL2L2,
/* */ gageSigmaSamplingOptimal3DL2L2
};
const airEnum
_gageSigmaSampling_enum = {
"sigma sampling strategy",
GAGE_SIGMA_SAMPLING_MAX,
_gageSigmaSamplingStr, NULL,
_gageSigmaSamplingDesc,
_gageSigmaSamplingStrEqv, _gageSigmaSamplingValEqv,
AIR_FALSE
};
69 const airEnum *const
gageSigmaSampling = &_gageSigmaSampling_enum;
void
74 gageStackBlurParmInit( gageStackBlurParm *parm ) {
if ( parm ) {
parm->num = 0;
parm->sigmaRange[0] = AIR_NAN;
parm->sigmaRange[1] = AIR_NAN;
parm->sigmaSampling = gageSigmaSamplingUnknown;
parm->sigma = airFree( parm->sigma );
parm->kspec = nrrdKernelSpecNix( parm->kspec );
/* this will be effectively moot when nrrdKernelDiscreteGaussian is used
with a bit cut-off, and will only help with smaller cut-offs and with
any other kernel, and will be moot for FFT-based blurring */
parm->renormalize = AIR_TRUE;
parm->bspec = nrrdBoundarySpecNix( parm->bspec );
parm->oneDim = AIR_FALSE;
/* the cautious application of the FFT--based blurring justifies enables
it by default */
parm->needSpatialBlur = AIR_FALSE;
parm->verbose = 1; /* HEY: this may be revisited */
parm->dgGoodSigmaMax = nrrdKernelDiscreteGaussianGoodSigmaMax;
}
return;
}
/*
** does not use biff
*/
gageStackBlurParm *
102 gageStackBlurParmNew( ) {
gageStackBlurParm *parm;
parm = AIR_CALLOC( 1, gageStackBlurParm );
gageStackBlurParmInit( parm );
return parm;
}
gageStackBlurParm *
111 gageStackBlurParmNix( gageStackBlurParm *sbp ) {
if ( sbp ) {
airFree( sbp->sigma );
nrrdKernelSpecNix( sbp->kspec );
nrrdBoundarySpecNix( sbp->bspec );
free( sbp );
}
return NULL;
}
/*
** *differ is set to 0 or 1; not useful for sorting
*/
int
126 gageStackBlurParmCompare( const gageStackBlurParm *aa, const char *_nameA,
const gageStackBlurParm *bb, const char *_nameB,
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="gageStackBlurParmCompare",
baseA[]="A", baseB[]="B";
const char *nameA, *nameB;
unsigned int si, warnLen = AIR_STRLEN_LARGE/4;
char stmp[2][AIR_STRLEN_LARGE], subexplain[AIR_STRLEN_LARGE];
if ( !( aa && bb && differ ) ) {
biffAddf( GAGE, "%s: got NULL pointer ( %p %p %p )", me,
AIR_VOIDP( aa ), AIR_VOIDP( bb ), AIR_VOIDP( differ ) );
return 1;
}
nameA = _nameA ? _nameA : baseA;
nameB = _nameB ? _nameB : baseB;
if ( strlen( nameA ) + strlen( nameB ) > warnLen ) {
biffAddf( GAGE, "%s: names ( len %s, %s ) might lead to overflow", me,
airSprintSize_t( stmp[0], strlen( nameA ) ),
airSprintSize_t( stmp[1], strlen( nameB ) ) );
return 1;
}
/*
** HEY: really ambivalent about not doing this check:
** its unusual in Teem to not take an opportunity to do this kind
** of sanity check when its available, but we don't really know the
** circumstances of when this will be called, and if that includes
** some interaction with hest, there may not yet have been the chance
** to complete the sbp.
if ( gageStackBlurParmCheck( aa ) ) {
biffAddf( GAGE, "%s: problem with sbp %s", me, nameA );
return 1;
}
if ( gageStackBlurParmCheck( bb ) ) {
biffAddf( GAGE, "%s: problem with sbp %s", me, nameB );
return 1;
}
*/
#define CHECK( VAR, FMT ) \
if ( aa->VAR != bb->VAR ) { \
if ( explain ) { \
sprintf( explain, "%s->" #VAR "=" #FMT " != %s->" #VAR "=" #FMT, \
nameA, aa->VAR, nameB, bb->VAR ); \
} \
*differ = 1; \
return 0; \
}
CHECK( num, %u );
CHECK( sigmaRange[0], %.17g );
CHECK( sigmaRange[1], %.17g );
CHECK( renormalize, %d );
CHECK( oneDim, %d );
CHECK( needSpatialBlur, %d );
/* This is sketchy: the apparent point of the function is to see if two
sbp's are different. But a big role of the function is to enable
leeching in meet. And for leeching, a difference in verbose is moot */
/* CHECK( verbose, %d ); */
CHECK( dgGoodSigmaMax, %.17g );
#undef CHECK
if ( aa->sigmaSampling != bb->sigmaSampling ) {
if ( explain ) {
sprintf( explain, "%s->sigmaSampling=%s != %s->sigmaSampling=%s",
nameA, airEnumStr( gageSigmaSampling, aa->sigmaSampling ),
nameB, airEnumStr( gageSigmaSampling, bb->sigmaSampling ) );
}
*differ = 1; return 0;
}
for ( si=0; si<aa->num; si++ ) {
if ( aa->sigma[si] != bb->sigma[si] ) {
if ( explain ) {
sprintf( explain, "%s->sigma[%u]=%.17g != %s->sigma[%u]=%.17g",
nameA, si, aa->sigma[si], nameB, si, bb->sigma[si] );
}
*differ = 1; return 0;
}
}
if ( nrrdKernelSpecCompare( aa->kspec, bb->kspec,
differ, subexplain ) ) {
biffMovef( GAGE, NRRD, "%s: trouble comparing kernel specs", me );
return 1;
}
if ( *differ ) {
if ( explain ) {
sprintf( explain, "kernel specs different: %s", subexplain );
}
*differ = 1; return 0;
}
if ( nrrdBoundarySpecCompare( aa->bspec, bb->bspec,
differ, subexplain ) ) {
biffMovef( GAGE, NRRD, "%s: trouble comparing boundary specs", me );
return 1;
}
if ( *differ ) {
if ( explain ) {
sprintf( explain, "boundary specs different: %s", subexplain );
}
222 *differ = 1; return 0;
}
/* no differences so far */
*differ = 0;
return 0;
}
int
gageStackBlurParmCopy( gageStackBlurParm *dst,
const gageStackBlurParm *src ) {
static const char me[]="gageStackBlurParmCopy";
int differ;
char explain[AIR_STRLEN_LARGE];
if ( !( dst && src ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( gageStackBlurParmCheck( src ) ) {
biffAddf( GAGE, "%s: given src parm has problems", me );
return 1;
}
if ( gageStackBlurParmSigmaSet( dst, src->num,
src->sigmaRange[0], src->sigmaRange[1],
src->sigmaSampling )
|| gageStackBlurParmKernelSet( dst, src->kspec )
|| gageStackBlurParmRenormalizeSet( dst, src->renormalize )
|| gageStackBlurParmDgGoodSigmaMaxSet( dst, src->dgGoodSigmaMax )
|| gageStackBlurParmBoundarySpecSet( dst, src->bspec )
|| gageStackBlurParmNeedSpatialBlurSet( dst, src->needSpatialBlur )
|| gageStackBlurParmVerboseSet( dst, src->verbose )
|| gageStackBlurParmOneDimSet( dst, src->oneDim ) ) {
biffAddf( GAGE, "%s: problem setting dst parm", me );
return 1;
}
if ( gageStackBlurParmCompare( dst, "copy", src, "original",
&differ, explain ) ) {
biffAddf( GAGE, "%s: trouble assessing correctness of copy", me );
return 1;
}
262 if ( differ ) {
biffAddf( GAGE, "%s: problem: copy not equal: %s", me, explain );
return 1;
}
return 0;
}
int
gageStackBlurParmSigmaSet( gageStackBlurParm *sbp, unsigned int num,
double sigmaMin, double sigmaMax,
int sigmaSampling ) {
static const char me[]="gageStackBlurParmSigmaSet";
unsigned int ii;
if ( !( sbp ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
airFree( sbp->sigma );
sbp->sigma = NULL;
if ( !( 0 <= sigmaMin ) ) {
biffAddf( GAGE, "%s: need sigmaMin >= 0 ( not %g )", me, sigmaMin );
return 1;
}
if ( !( sigmaMin < sigmaMax ) ) {
biffAddf( GAGE, "%s: need sigmaMax %g > sigmaMin %g",
me, sigmaMax, sigmaMin );
return 1;
}
if ( airEnumValCheck( gageSigmaSampling, sigmaSampling ) ) {
biffAddf( GAGE, "%s: %d is not a valid %s", me,
sigmaSampling, gageSigmaSampling->name );
return 1;
}
if ( !( num >= 2 ) ) {
biffAddf( GAGE, "%s: need # scale samples >= 2 ( not %u )", me, num );
return 1;
}
sbp->sigma = AIR_CALLOC( num, double );
if ( !sbp->sigma ) {
biffAddf( GAGE, "%s: couldn't alloc scale for %u", me, num );
return 1;
}
sbp->num = num;
sbp->sigmaRange[0] = sigmaMin;
sbp->sigmaRange[1] = sigmaMax;
sbp->sigmaSampling = sigmaSampling;
switch ( sigmaSampling ) {
double tau0, tau1, tau;
unsigned int sigmax;
case gageSigmaSamplingUniformSigma:
for ( ii=0; ii<num; ii++ ) {
sbp->sigma[ii] = AIR_AFFINE( 0, ii, num-1, sigmaMin, sigmaMax );
}
break;
case gageSigmaSamplingUniformTau:
tau0 = gageTauOfSig( sigmaMin );
tau1 = gageTauOfSig( sigmaMax );
for ( ii=0; ii<num; ii++ ) {
tau = AIR_AFFINE( 0, ii, num-1, tau0, tau1 );
sbp->sigma[ii] = gageSigOfTau( tau );
}
break;
case gageSigmaSamplingOptimal3DL2L2:
sigmax = AIR_CAST( unsigned int, sigmaMax );
if ( 0 != sigmaMin ) {
biffAddf( GAGE, "%s: sigmaMin %g != 0", me, sigmaMin );
return 1;
}
if ( sigmax != sigmaMax ) {
biffAddf( GAGE, "%s: sigmaMax %g not an integer", me, sigmaMax );
return 1;
}
if ( gageOptimSigSet( sbp->sigma, num, sigmax ) ) {
biffAddf( GAGE, "%s: trouble setting optimal sigmas", me );
return 1;
}
break;
default:
biffAddf( GAGE, "%s: sorry, sigmaSampling %s ( %d ) not implemented", me,
airEnumStr( gageSigmaSampling, sigmaSampling ), sigmaSampling );
return 1;
}
if ( sbp->verbose > 1 ) {
fprintf( stderr, "%s: %u samples in [%g, %g] via %s:\n", me,
num, sigmaMin, sigmaMax,
airEnumStr( gageSigmaSampling, sigmaSampling ) );
for ( ii=0; ii<num; ii++ ) {
if ( ii ) {
fprintf( stderr, "%s: "
"| deltas: %g\t %g\n", me,
sbp->sigma[ii] - sbp->sigma[ii-1],
gageTauOfSig( sbp->sigma[ii] )
- gageTauOfSig( sbp->sigma[ii-1] ) );
}
fprintf( stderr, "%s: sigma[%02u]=%g%s\t tau=%g\n", me, ii,
sbp->sigma[ii], !sbp->sigma[ii] ? " " : "",
360 gageTauOfSig( sbp->sigma[ii] ) );
}
}
return 0;
}
int
gageStackBlurParmScaleSet( gageStackBlurParm *sbp,
unsigned int num,
double smin, double smax,
int uniform, int optimal ) {
static const char me[]="gageStackBlurParmScaleSet";
int sampling;
fprintf( stderr, "\n%s: !!! This function is deprecated; use "
"gageStackBlurParmSigmaSet instead !!!\n\n", me );
if ( uniform && optimal ) {
biffAddf( GAGE, "%s: can't have both uniform and optimal sigma sampling",
me );
return 1;
}
sampling = ( uniform
? gageSigmaSamplingUniformSigma
: ( optimal
? gageSigmaSamplingOptimal3DL2L2
: gageSigmaSamplingUniformTau ) );
387 if ( gageStackBlurParmSigmaSet( sbp, num, smin, smax, sampling ) ) {
biffAddf( GAGE, "%s: trouble", me );
return 1;
}
return 0;
}
int
gageStackBlurParmKernelSet( gageStackBlurParm *sbp,
const NrrdKernelSpec *kspec ) {
static const char me[]="gageStackBlurParmKernelSet";
if ( !( sbp && kspec ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
401 return 1;
}
nrrdKernelSpecNix( sbp->kspec );
sbp->kspec = nrrdKernelSpecCopy( kspec );
return 0;
}
int
gageStackBlurParmRenormalizeSet( gageStackBlurParm *sbp,
int renormalize ) {
static const char me[]="gageStackBlurParmRenormalizeSet";
if ( !sbp ) {
414 biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
sbp->renormalize = renormalize;
return 0;
}
int
gageStackBlurParmBoundarySet( gageStackBlurParm *sbp,
int boundary, double padValue ) {
static const char me[]="gageStackBlurParmBoundarySet";
if ( !sbp ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
nrrdBoundarySpecNix( sbp->bspec );
sbp->bspec = nrrdBoundarySpecNew( );
sbp->bspec->boundary = boundary;
sbp->bspec->padValue = padValue;
434 if ( nrrdBoundarySpecCheck( sbp->bspec ) ) {
biffMovef( GAGE, NRRD, "%s: problem", me );
return 1;
}
return 0;
}
int
gageStackBlurParmBoundarySpecSet( gageStackBlurParm *sbp,
const NrrdBoundarySpec *bspec ) {
static const char me[]="gageStackBlurParmBoundarySet";
if ( !sbp ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
nrrdBoundarySpecNix( sbp->bspec );
sbp->bspec = nrrdBoundarySpecCopy( bspec );
452 if ( nrrdBoundarySpecCheck( sbp->bspec ) ) {
biffMovef( GAGE, NRRD, "%s: problem", me );
return 1;
}
return 0;
}
int
gageStackBlurParmOneDimSet( gageStackBlurParm *sbp, int oneDim ) {
static const char me[]="gageStackBlurParmOneDimSet";
if ( !sbp ) {
464 biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
sbp->oneDim = oneDim;
return 0;
}
int
gageStackBlurParmNeedSpatialBlurSet( gageStackBlurParm *sbp,
int needSpatialBlur ) {
static const char me[]="gageStackBlurParmNeedSpatialBlurSet";
if ( !sbp ) {
477 biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
sbp->needSpatialBlur = needSpatialBlur;
return 0;
}
int
gageStackBlurParmVerboseSet( gageStackBlurParm *sbp, int verbose ) {
static const char me[]="gageStackBlurParmVerboseSet";
if ( !sbp ) {
489 biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
sbp->verbose = verbose;
return 0;
}
int
gageStackBlurParmDgGoodSigmaMaxSet( gageStackBlurParm *sbp,
double dgGoodSigmaMax ) {
static const char me[]="gageStackBlurParmDgGoodSigmaMaxSet";
if ( !sbp ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !dgGoodSigmaMax > 0 ) {
506 biffAddf( GAGE, "%s: given dgGoodSigmaMax %g not > 0", me, dgGoodSigmaMax );
return 1;
}
sbp->dgGoodSigmaMax = dgGoodSigmaMax;
return 0;
}
int
gageStackBlurParmCheck( const gageStackBlurParm *sbp ) {
static const char me[]="gageStackBlurParmCheck";
unsigned int ii;
if ( !sbp ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( sbp->num >= 2 ) ) {
biffAddf( GAGE, "%s: need num >= 2, not %u", me, sbp->num );
return 1;
}
if ( !sbp->sigma ) {
biffAddf( GAGE, "%s: sigma vector not allocated", me );
return 1;
}
if ( !sbp->kspec ) {
biffAddf( GAGE, "%s: blurring kernel not set", me );
return 1;
}
if ( !sbp->bspec ) {
biffAddf( GAGE, "%s: boundary specification not set", me );
return 1;
}
for ( ii=0; ii<sbp->num; ii++ ) {
if ( !AIR_EXISTS( sbp->sigma[ii] ) ) {
biffAddf( GAGE, "%s: sigma[%u] = %g doesn't exist", me, ii,
sbp->sigma[ii] );
return 1;
}
if ( ii ) {
if ( !( sbp->sigma[ii-1] < sbp->sigma[ii] ) ) {
biffAddf( GAGE, "%s: sigma[%u] = %g not < sigma[%u] = %g", me,
ii, sbp->sigma[ii-1], ii+1, sbp->sigma[ii] );
return 1;
}
}
}
/* HEY: no sanity check on kernel because there is no
nrrdKernelSpecCheck( ), but there should be! */
554 if ( nrrdBoundarySpecCheck( sbp->bspec ) ) {
biffMovef( GAGE, NRRD, "%s: problem with boundary", me );
return 1;
}
return 0;
}
int
gageStackBlurParmParse( gageStackBlurParm *sbp,
int extraFlags[256],
char **extraParmsP,
const char *_str ) {
static const char me[]="gageStackBlurParmParse";
char *str, *mnmfS, *stok, *slast=NULL, *parmS, *eps;
int flagSeen[256];
double sigmaMin, sigmaMax, dggsm;
unsigned int sigmaNum, parmNum;
int haveFlags, verbose, verboseGot=AIR_FALSE, dggsmGot=AIR_FALSE,
sampling = AIR_FALSE, samplingGot=AIR_FALSE, E;
airArray *mop, *epsArr;
NrrdKernelSpec *kspec=NULL;
NrrdBoundarySpec *bspec=NULL;
if ( !( sbp && _str ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( !( str = airStrdup( _str ) ) ) {
biffAddf( GAGE, "%s: couldn't copy input", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, str, airFree, airMopAlways );
if ( extraParmsP ) {
/* start with empty string */
epsArr = airArrayNew( AIR_CAST( void **, &eps ), NULL, sizeof( char ), 42 );
airMopAdd( mop, epsArr, ( airMopper )airArrayNuke, airMopAlways );
airArrayLenIncr( epsArr, 1 );
*eps = '\0';
} else {
epsArr = NULL;
}
/* working with assumption that '/' does not appear
in mnmfS <minScl>-<#smp>-<maxScl>[-<flags>] */
if ( ( parmS = strchr( str, '/' ) ) ) {
/* there are in fact parms */
*parmS = '\0';
parmS++;
} else {
parmS = NULL;
}
mnmfS = str;
if ( !( 3 == airStrntok( mnmfS, "-" ) || 4 == airStrntok( mnmfS, "-" ) ) ) {
biffAddf( GAGE, "%s: didn't get 3 or 4 \"-\"-separated tokens in \"%s\"",
me, mnmfS );
airMopError( mop ); return 1;
}
haveFlags = ( 4 == airStrntok( mnmfS, "-" ) );
stok = airStrtok( mnmfS, "-", &slast );
if ( 1 != sscanf( stok, "%lg", &sigmaMin ) ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as max sigma", me, stok );
airMopError( mop ); return 1;
}
stok = airStrtok( NULL, "-", &slast );
if ( 1 != sscanf( stok, "%u", &sigmaNum ) ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as # scale samples", me, stok );
airMopError( mop ); return 1;
}
stok = airStrtok( NULL, "-", &slast );
if ( 1 != sscanf( stok, "%lg", &sigmaMax ) ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as max scale", me, stok );
airMopError( mop ); return 1;
}
memset( flagSeen, 0, sizeof( flagSeen ) );
if ( extraFlags ) {
/* not sizeof( extraFlags ) == sizeof( int* ) */
memset( extraFlags, 0, sizeof( flagSeen ) );
}
if ( haveFlags ) {
char *flags, *ff;
/* look for various things in flags */
flags = airToLower( airStrdup( airStrtok( NULL, "-", &slast ) ) );
airMopAdd( mop, flags, airFree, airMopAlways );
ff = flags;
while ( *ff && '+' != *ff ) {
/* '1': oneDim
'r': turn OFF spatial kernel renormalize
'u': uniform ( in sigma ) sampling
'o': optimized ( 3d l2l2 ) sampling
'p': need spatial blur
*/
if ( strchr( "1ruop", *ff ) ) {
flagSeen[AIR_CAST( unsigned char, *ff )] = AIR_TRUE;
} else {
if ( extraFlags ) {
extraFlags[AIR_CAST( unsigned char, *ff )] = AIR_TRUE;
} else {
biffAddf( GAGE, "%s: got extra flag '%c' but NULL extraFlag",
me, *ff );
airMopError( mop ); return 1;
}
}
ff++;
}
if ( flagSeen['u'] && flagSeen['o'] ) {
biffAddf( GAGE, "%s: can't have both optimal ( 'o' ) and uniform ( 'u' ) "
"flags set in \"%s\"", me, flags );
airMopError( mop ); return 1;
}
if ( ff && '+' == *ff ) {
biffAddf( GAGE, "%s: sorry, can no longer indicate a derivative "
"normalization bias via '+' in \"%s\" in flags \"%s\"; "
"use \"dnbias=\" parm instead", me, ff, flags );
airMopError( mop ); return 1;
}
}
if ( parmS ) {
unsigned int parmIdx;
char *pval, xeq[AIR_STRLEN_SMALL];
parmNum = airStrntok( parmS, "/" );
for ( parmIdx=0; parmIdx<parmNum; parmIdx++ ) {
if ( !parmIdx ) {
stok = airStrtok( parmS, "/", &slast );
} else {
stok = airStrtok( NULL, "/", &slast );
}
if ( strcpy( xeq, "k=" ) && stok == strstr( stok, xeq ) ) {
pval = stok + strlen( xeq );
kspec = nrrdKernelSpecNew( );
airMopAdd( mop, kspec, ( airMopper )nrrdKernelSpecNix, airMopAlways );
if ( nrrdKernelSpecParse( kspec, pval ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't parse \"%s\" as blurring kernel",
me, pval );
airMopError( mop ); return 1;
}
} else if ( strcpy( xeq, "b=" ) && strstr( stok, xeq ) == stok ) {
pval = stok + strlen( xeq );
bspec = nrrdBoundarySpecNew( );
airMopAdd( mop, bspec, ( airMopper )nrrdBoundarySpecNix, airMopAlways );
if ( nrrdBoundarySpecParse( bspec, pval ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't parse \"%s\" as boundary",
me, pval );
airMopError( mop ); return 1;
}
} else if ( strcpy( xeq, "v=" ) && strstr( stok, xeq ) == stok ) {
pval = stok + strlen( xeq );
if ( 1 != sscanf( pval, "%d", &verbose ) ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as verbose int", me, pval );
airMopError( mop ); return 1;
}
verboseGot = AIR_TRUE;
} else if ( strcpy( xeq, "s=" ) && strstr( stok, xeq ) == stok ) {
pval = stok + strlen( xeq );
sampling = airEnumVal( gageSigmaSampling, pval );
if ( gageSigmaSamplingUnknown == sampling ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as %s", me, pval,
gageSigmaSampling->name );
airMopError( mop ); return 1;
}
samplingGot = AIR_TRUE;
} else if ( strcpy( xeq, "dggsm=" ) && strstr( stok, xeq ) == stok ) {
pval = stok + strlen( xeq );
if ( 1 != sscanf( pval, "%lg", &dggsm ) ) {
biffAddf( GAGE, "%s: couldn't parse \"%s\" as dgGoodSigmaMax double",
me, pval );
airMopError( mop ); return 1;
}
dggsmGot = AIR_TRUE;
} else {
/* doesn't match any of the parms we know how to parse */
if ( extraParmsP ) {
airArrayLenIncr( epsArr, AIR_CAST( int, 2 + strlen( stok ) ) );
if ( strlen( eps ) ) {
strcat( eps, "/" );
}
strcat( eps, stok );
} else {
biffAddf( GAGE, "%s: got extra parm \"%s\" but NULL extraParmsP",
me, stok );
airMopError( mop ); return 1;
}
}
}
}
/* have parsed everything, now error checking and making sense */
if ( flagSeen['u'] && flagSeen['o'] ) {
biffAddf( GAGE, "%s: can't use flags 'u' and 'o' at same time", me );
airMopError( mop ); return 1;
}
if ( ( flagSeen['u'] || flagSeen['o'] ) && samplingGot ) {
biffAddf( GAGE, "%s: can't use both 'u', 'o' flags and parms to "
"specify sigma sampling", me );
airMopError( mop ); return 1;
}
if ( !samplingGot ) {
/* have to set sampling from flags */
if ( flagSeen['u'] ) {
sampling = gageSigmaSamplingUniformSigma;
} else if ( flagSeen['o'] ) {
sampling = gageSigmaSamplingOptimal3DL2L2;
} else {
sampling = gageSigmaSamplingUniformTau;
}
}
/* setting sbp fields */
E = 0;
if ( !E ) E |= gageStackBlurParmSigmaSet( sbp, sigmaNum,
sigmaMin, sigmaMax, sampling );
if ( kspec ) {
if ( !E ) E |= gageStackBlurParmKernelSet( sbp, kspec );
}
if ( flagSeen['r'] ) {
if ( !E ) E |= gageStackBlurParmRenormalizeSet( sbp, AIR_FALSE );
}
if ( dggsmGot ) {
if ( !E ) E |= gageStackBlurParmDgGoodSigmaMaxSet( sbp, dggsm );
}
if ( bspec ) {
if ( !E ) E |= gageStackBlurParmBoundarySpecSet( sbp, bspec );
}
if ( flagSeen['p'] ) {
if ( !E ) E |= gageStackBlurParmNeedSpatialBlurSet( sbp, AIR_TRUE );
}
if ( verboseGot ) {
if ( !E ) E |= gageStackBlurParmVerboseSet( sbp, verbose );
}
if ( flagSeen['1'] ) {
if ( !E ) E |= gageStackBlurParmOneDimSet( sbp, AIR_TRUE );
}
/* NOT doing the final check, because if this is being called from
hest, the caller won't have had time to set the default info in
the sbp ( like the default kernel ), so it will probably look
incomplete.
if ( !E ) E |= gageStackBlurParmCheck( sbp ); */
if ( E ) {
biffAddf( GAGE, "%s: problem with blur parm specification", me );
airMopError( mop ); return 1;
}
if ( extraParmsP ) {
if ( airStrlen( eps ) ) {
*extraParmsP = airStrdup( eps );
} else {
797 *extraParmsP = NULL;
}
}
airMopOkay( mop );
return 0;
}
int
gageStackBlurParmSprint( char str[AIR_STRLEN_LARGE],
const gageStackBlurParm *sbp,
int extraFlag[256],
char *extraParm ) {
static const char me[]="gageStackBlurParmSprint";
char *out, stmp[AIR_STRLEN_LARGE];
int needFlags, hef;
unsigned int fi;
if ( !( str && sbp ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
out = str;
sprintf( out, "%.17g-%u-%.17g",
sbp->sigmaRange[0], sbp->num, sbp->sigmaRange[1] );
out += strlen( out );
hef = AIR_FALSE;
if ( extraFlag ) {
for ( fi=0; fi<256; fi++ ) {
hef |= extraFlag[fi];
}
}
needFlags = ( sbp->oneDim
|| sbp->renormalize
|| sbp->needSpatialBlur
|| hef );
if ( needFlags ) {
strcat( out, "-" );
if ( sbp->oneDim ) { strcat( out, "1" ); }
if ( sbp->renormalize ) { strcat( out, "r" ); }
if ( sbp->needSpatialBlur ) { strcat( out, "p" ); }
if ( hef ) {
for ( fi=0; fi<256; fi++ ) {
if ( extraFlag[fi] ) {
sprintf( stmp, "%c", AIR_CAST( char, fi ) );
strcat( out, stmp );
}
}
}
}
if ( sbp->kspec ) {
strcat( out, "/" );
if ( nrrdKernelSpecSprint( stmp, sbp->kspec ) ) {
biffMovef( GAGE, NRRD, "%s: problem with kernel", me );
return 1;
}
strcat( out, "k=" ); strcat( out, stmp );
}
if ( sbp->bspec ) {
strcat( out, "/" );
if ( nrrdBoundarySpecSprint( stmp, sbp->bspec ) ) {
biffMovef( GAGE, NRRD, "%s: problem with boundary", me );
return 1;
}
strcat( out, "b=" ); strcat( out, stmp );
}
if ( !airEnumValCheck( gageSigmaSampling, sbp->sigmaSampling ) ) {
strcat( out, "/s=" );
strcat( out, airEnumStr( gageSigmaSampling, sbp->sigmaSampling ) );
}
if ( sbp->verbose ) {
sprintf( stmp, "/v=%d", sbp->verbose );
strcat( out, stmp );
}
if ( sbp->kspec
&& nrrdKernelDiscreteGaussian == sbp->kspec->kernel
&& nrrdKernelDiscreteGaussianGoodSigmaMax != sbp->dgGoodSigmaMax ) {
sprintf( stmp, "/dggsm=%.17g", sbp->dgGoodSigmaMax );
strcat( out, stmp );
}
if ( extraParm ) {
884 strcat( out, "/" );
strcat( out, extraParm );
}
return 0;
}
int
_gageHestStackBlurParmParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
gageStackBlurParm **sbp;
char me[]="_gageHestStackBlurParmParse", *nerr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
sbp = ( gageStackBlurParm ** )ptr;
if ( !strlen( str ) ) {
/* got an empty string; we trying to emulate an "optional"
command-line option, in a program that likely still has
the older -ssn, -ssr, -kssb, and which may or may not
need any scale-space functionality */
*sbp = NULL;
} else {
*sbp = gageStackBlurParmNew( );
/* NOTE: no way to retrieve extraFlags or extraParms from hest */
if ( gageStackBlurParmParse( *sbp, NULL, NULL, str ) ) {
nerr = biffGetDone( GAGE );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
gageStackBlurParmNix( *sbp );
free( nerr );
return 1;
}
}
return 0;
}
hestCB
_gageHestStackBlurParm = {
sizeof( gageStackBlurParm* ),
"stack blur specification",
925 _gageHestStackBlurParmParse,
( airMopper )gageStackBlurParmNix
};
hestCB *
gageHestStackBlurParm = &_gageHestStackBlurParm;
static int
_checkNrrd( Nrrd *const nblur[], const Nrrd *const ncheck[],
unsigned int blNum, int checking,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="_checkNrrd";
unsigned int blIdx;
for ( blIdx=0; blIdx<blNum; blIdx++ ) {
if ( checking ) {
if ( nrrdCheck( ncheck[blIdx] ) ) {
biffMovef( GAGE, NRRD, "%s: bad ncheck[%u]", me, blIdx );
return 1;
}
} else {
if ( !nblur[blIdx] ) {
biffAddf( GAGE, "%s: NULL blur[%u]", me, blIdx );
return 1;
}
}
}
if ( 3 + kind->baseDim != nin->dim ) {
biffAddf( GAGE, "%s: need nin->dim %u ( not %u ) with baseDim %u", me,
3 + kind->baseDim, nin->dim, kind->baseDim );
return 1;
}
return 0;
}
#define KVP_NUM 9
static const char
_blurKey[KVP_NUM][AIR_STRLEN_LARGE] = {/* 0 */ "gageStackBlur",
/* 1 */ "cksum",
/* 2 */ "scale",
/* 3 */ "kernel",
/* 4 */ "renormalize",
/* 5 */ "boundary",
/* 6 */ "onedim",
/* 7 */ "spatialblurred",
#define KVP_SBLUR_IDX 7
/* 8 */ "dgGoodSigmaMax"
973 #define KVP_DGGSM_IDX 8
/* ( 9 == KVP_NUM, above ) */};
typedef struct {
char val[KVP_NUM][AIR_STRLEN_LARGE];
} blurVal_t;
static blurVal_t *
_blurValAlloc( airArray *mop, gageStackBlurParm *sbp, NrrdKernelSpec *kssb,
const Nrrd *nin, int spatialBlurred ) {
static const char me[]="_blurValAlloc";
blurVal_t *blurVal;
unsigned int blIdx, cksum;
blurVal = AIR_CAST( blurVal_t *, calloc( sbp->num, sizeof( blurVal_t ) ) );
if ( !blurVal ) {
biffAddf( GAGE, "%s: couldn't alloc blurVal for %u", me, sbp->num );
return NULL;
}
cksum = nrrdCRC32( nin, airEndianLittle );
for ( blIdx=0; blIdx<sbp->num; blIdx++ ) {
kssb->parm[0] = sbp->sigma[blIdx];
sprintf( blurVal[blIdx].val[0], "true" );
sprintf( blurVal[blIdx].val[1], "%u", cksum );
sprintf( blurVal[blIdx].val[2], "%.17g", sbp->sigma[blIdx] );
nrrdKernelSpecSprint( blurVal[blIdx].val[3], kssb );
sprintf( blurVal[blIdx].val[4], "%s", sbp->renormalize ? "true" : "false" );
nrrdBoundarySpecSprint( blurVal[blIdx].val[5], sbp->bspec );
sprintf( blurVal[blIdx].val[6], "%s",
sbp->oneDim ? "true" : "false" );
sprintf( blurVal[blIdx].val[7], "%s",
spatialBlurred ? "true" : "false" );
sprintf( blurVal[blIdx].val[8], "%.17g", sbp->dgGoodSigmaMax );
}
airMopAdd( mop, blurVal, airFree, airMopAlways );
return blurVal;
}
1012 /*
** some spot checks suggest that where the PSF of lindeberg-gaussian blurring
** should be significantly non-zero, this is more accurate than the current
** "discrete gauss" kernel, but for small values near zero, the spatial
** blurring is more accurate. This is due to how with limited numerical
** precision, the FFT can produce very low amplitude noise.
*/
static int
_stackBlurDiscreteGaussFFT( Nrrd *const nblur[], gageStackBlurParm *sbp,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="_stackBlurDiscreteGaussFFT";
size_t sizeAll[NRRD_DIM_MAX], *size, ii, xi, yi, zi, nn;
Nrrd *ninC, /* complex version of input, same as input type */
*ninFT, /* FT of input, type double */
*noutFT, /* FT of output, values set manually from ninFT, as double */
*noutCd, /* complex version of output, still as double */
*noutC; /* complex version of output, as input type;
will convert/clamp this to get nblur[i] */
double ( *lup )( const void *, size_t ), ( *ins )( void *, size_t, double ),
*ww[3], tblur, theta, *inFT, *outFT;
unsigned int blIdx, axi, ftaxes[3] = {1, 2, 3};
airArray *mop;
int axmap[NRRD_DIM_MAX];
mop = airMopNew( );
ninC = nrrdNew( );
airMopAdd( mop, ninC, ( airMopper )nrrdNuke, airMopAlways );
ninFT = nrrdNew( );
airMopAdd( mop, ninFT, ( airMopper )nrrdNuke, airMopAlways );
noutFT = nrrdNew( );
airMopAdd( mop, noutFT, ( airMopper )nrrdNuke, airMopAlways );
noutCd = nrrdNew( );
airMopAdd( mop, noutCd, ( airMopper )nrrdNuke, airMopAlways );
noutC = nrrdNew( );
airMopAdd( mop, noutC, ( airMopper )nrrdNuke, airMopAlways );
if ( gageKindScl != kind ) {
biffAddf( GAGE, "%s: sorry, non-scalar kind not yet implemented", me );
/* but it really wouldn't be that hard ... */
airMopError( mop ); return 1;
}
if ( 3 != nin->dim ) {
biffAddf( GAGE, "%s: sanity check fail: nin->dim %u != 3", me, nin->dim );
airMopError( mop ); return 1;
}
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nin->type];
/* unurrdu/fft.c handles real input by doing an axis insert and then
padding, but that is overkill; this is a more direct way, which perhaps
should be migrated to unurrdu/fft.c */
sizeAll[0] = 2;
size = sizeAll + 1;
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( nrrdMaybeAlloc_nva( ninC, nin->type, nin->dim+1, sizeAll ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't allocate complex-valued input", me );
airMopError( mop ); return 1;
}
for ( axi=0; axi<3; axi++ ) {
if ( !( ww[axi] = AIR_CALLOC( size[axi], double ) ) ) {
biffAddf( GAGE, "%s: couldn't allocate axis %u buffer", me, axi );
airMopError( mop ); return 1;
}
airMopAdd( mop, ww[axi], airFree, airMopAlways );
}
nn = size[0]*size[1]*size[2];
for ( ii=0; ii<nn; ii++ ) {
ins( ninC->data, 0 + 2*ii, lup( nin->data, ii ) );
ins( ninC->data, 1 + 2*ii, 0.0 );
}
for ( axi=0; axi<4; axi++ ) {
if ( !axi ) {
axmap[axi] = -1;
} else {
axmap[axi] = axi-1;
}
}
if ( nrrdAxisInfoCopy( ninC, nin, axmap, NRRD_AXIS_INFO_NONE )
|| nrrdBasicInfoCopy( ninC, nin,
( NRRD_BASIC_INFO_DATA_BIT |
NRRD_BASIC_INFO_DIMENSION_BIT |
NRRD_BASIC_INFO_CONTENT_BIT |
NRRD_BASIC_INFO_COMMENTS_BIT |
NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( GAGE, NRRD, "%s: couldn't set complex-valued axinfo", me );
airMopError( mop ); return 1;
}
ninC->axis[0].kind = nrrdKindComplex; /* should use API */
/*
nrrdSave( "ninC.nrrd", ninC, NULL );
*/
/* the copy to noutFT is just to allocate it; the values
there will be re-written over and over in the loop below */
if ( nrrdFFT( ninFT, ninC, ftaxes, 3,
+1 /* forward */,
AIR_TRUE /* rescale */,
nrrdFFTWPlanRigorEstimate /* should generalize! */ )
|| nrrdCopy( noutFT, ninFT ) ) {
biffMovef( GAGE, NRRD, "%s: trouble with initial transforms", me );
airMopError( mop ); return 1;
}
/*
nrrdSave( "ninFT.nrrd", ninFT, NULL );
*/
inFT = AIR_CAST( double *, ninFT->data );
outFT = AIR_CAST( double *, noutFT->data );
for ( blIdx=0; blIdx<sbp->num; blIdx++ ) {
if ( sbp->verbose ) {
fprintf( stderr, "%s: . . . %u/%u ( scale %g, tau %g ) . . . ", me,
blIdx, sbp->num, sbp->sigma[blIdx],
gageTauOfSig( sbp->sigma[blIdx] ) );
fflush( stderr );
}
tblur = sbp->sigma[blIdx]*sbp->sigma[blIdx];
for ( axi=0; axi<3; axi++ ) {
for ( ii=0; ii<size[axi]; ii++ ) {
theta = AIR_AFFINE( 0, ii, size[axi], 0.0, 2*AIR_PI );
/* from eq ( 22 ) of T. Lindeberg "Scale-Space for Discrete
Signals", IEEE PAMI 12( 234-254 ); 1990 */
ww[axi][ii] = exp( tblur*( cos( theta )-1.0 ) );
/*
fprintf( stderr, "!%s: ww[%u][%u] = %g\n", me, axi,
AIR_CAST( unsigned int, ii ), ww[axi][ii] );
*/
}
}
ii=0;
/*
for ( axi=0; axi<3; axi++ ) {
fprintf( stderr, "!%s: size[%u] = %u\n", me, axi,
AIR_CAST( unsigned int, size[axi] ) );
}
*/
for ( zi=0; zi<size[2]; zi++ ) {
for ( yi=0; yi<size[1]; yi++ ) {
for ( xi=0; xi<size[0]; xi++ ) {
double wght;
wght = sbp->oneDim ? 1.0 : ww[1][yi]*ww[2][zi];
wght *= ww[0][xi];
outFT[0 + 2*ii] = wght*inFT[0 + 2*ii];
outFT[1 + 2*ii] = wght*inFT[1 + 2*ii];
/*
fprintf( stderr, "!%s: out[%u] = ( %g, %g ) = %g * ( %g, %g )\n", me,
AIR_CAST( unsigned int, ii ),
outFT[0 + 2*ii], outFT[1 + 2*ii],
wght,
inFT[0 + 2*ii], inFT[1 + 2*ii] );
*/
ii++;
}
}
}
if ( nrrdFFT( noutCd, noutFT, ftaxes, 3,
-1 /* backward */,
AIR_TRUE /* rescale */,
nrrdFFTWPlanRigorEstimate /* should generalize! */ )
|| ( nrrdTypeDouble == nin->type
? nrrdCopy( noutC, noutCd )
: nrrdCastClampRound( noutC, noutCd, nin->type,
AIR_TRUE /* clamp */,
+1 /* roundDir, when needed */ ) )
|| nrrdSlice( nblur[blIdx], noutC, 0, 0 )
|| nrrdContentSet_va( nblur[blIdx], "blur", nin, "" ) ) {
biffMovef( GAGE, NRRD, "%s: trouble with back transform %u", me, blIdx );
airMopError( mop ); return 1;
}
if ( sbp->verbose ) {
fprintf( stderr, "done\n" );
}
/*
if ( 0 ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "noutFT-%03u.nrrd", blIdx );
nrrdSave( fname, noutFT, NULL );
sprintf( fname, "noutCd-%03u.nrrd", blIdx );
nrrdSave( fname, noutCd, NULL );
sprintf( fname, "noutC-%03u.nrrd", blIdx );
nrrdSave( fname, noutC, NULL );
sprintf( fname, "nblur-%03u.nrrd", blIdx );
nrrdSave( fname, nblur[blIdx], NULL );
}
1192 */
}
airMopOkay( mop );
return 0;
}
static int
_stackBlurSpatial( Nrrd *const nblur[], gageStackBlurParm *sbp,
NrrdKernelSpec *kssb,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="_stackBlurSpatial";
NrrdResampleContext *rsmc;
Nrrd *niter;
unsigned int axi, blIdx;
int E, iterative, rsmpType;
double timeStepMax, /* max length of diffusion time allowed per blur,
as determined by sbp->dgGoodSigmaMax */
timeDone, /* amount of diffusion time just applied */
timeLeft; /* amount of diffusion time left to do */
airArray *mop;
mop = airMopNew( );
rsmc = nrrdResampleContextNew( );
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
if ( nrrdKernelDiscreteGaussian == kssb->kernel ) {
iterative = AIR_TRUE;
/* we don't want to lose precision when iterating */
rsmpType = nrrdResample_nt;
/* may be used with iterative diffusion */
niter = nrrdNew( );
airMopAdd( mop, niter, ( airMopper )nrrdNuke, airMopAlways );
} else {
iterative = AIR_FALSE;
rsmpType = nrrdTypeDefault;
niter = NULL;
}
E = 0;
if ( !E ) E |= nrrdResampleDefaultCenterSet( rsmc, nrrdDefaultCenter );
/* the input for the first scale is indeed nin, regardless
of iterative */
if ( !E ) E |= nrrdResampleInputSet( rsmc, nin );
if ( kind->baseDim ) {
unsigned int bai;
for ( bai=0; bai<kind->baseDim; bai++ ) {
if ( !E ) E |= nrrdResampleKernelSet( rsmc, bai, NULL, NULL );
}
}
for ( axi=0; axi<3; axi++ ) {
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, kind->baseDim + axi,
nin->axis[kind->baseDim + axi].size );
if ( !E ) E |= nrrdResampleRangeFullSet( rsmc, kind->baseDim + axi );
}
if ( !E ) E |= nrrdResampleBoundarySpecSet( rsmc, sbp->bspec );
if ( !E ) E |= nrrdResampleTypeOutSet( rsmc, rsmpType );
if ( !E ) E |= nrrdResampleClampSet( rsmc, AIR_TRUE ); /* probably moot */
if ( !E ) E |= nrrdResampleRenormalizeSet( rsmc, sbp->renormalize );
if ( E ) {
biffAddf( GAGE, "%s: trouble setting up resampling", me );
airMopError( mop ); return 1;
}
timeDone = 0;
timeStepMax = ( sbp->dgGoodSigmaMax )*( sbp->dgGoodSigmaMax );
for ( blIdx=0; blIdx<sbp->num; blIdx++ ) {
if ( sbp->verbose ) {
fprintf( stderr, "%s: . . . blurring %u / %u ( scale %g ) . . . ",
me, blIdx, sbp->num, sbp->sigma[blIdx] );
fflush( stderr );
}
if ( iterative ) {
double timeNow = sbp->sigma[blIdx]*sbp->sigma[blIdx];
unsigned int passIdx = 0;
timeLeft = timeNow - timeDone;
if ( sbp->verbose ) {
fprintf( stderr, "\n" );
fprintf( stderr, "%s: scale %g == time %g ( tau %g );\n"
" timeLeft %g = %g - %g\n",
me, sbp->sigma[blIdx], timeNow, gageTauOfTee( timeNow ),
timeLeft, timeNow, timeDone );
if ( timeLeft > timeStepMax ) {
fprintf( stderr, "%s: diffusing for time %g in steps of %g\n", me,
timeLeft, timeStepMax );
}
fflush( stderr );
}
do {
double timeDo;
if ( blIdx || passIdx ) {
/* either we're past the first scale ( blIdx >= 1 ), or
( unlikely ) we're on the first scale but after the first
pass of a multi-pass blurring, so we have to feed the
previous result back in as input.
AND: the way that niter is being used is very sneaky,
and probably too clever: the resampling happens in
multiple passes, among buffers internal to nrrdResample;
so its okay have the output and input nrrds be the same:
they're never used at the same time. */
if ( !E ) E |= nrrdResampleInputSet( rsmc, niter );
}
timeDo = ( timeLeft > timeStepMax
? timeStepMax
: timeLeft );
/* it is the repeated re-setting of this parm[0] which motivated
copying to our own kernel spec, so that the given one in the
gageStackBlurParm can stay untouched */
kssb->parm[0] = sqrt( timeDo );
for ( axi=0; axi<3; axi++ ) {
if ( !sbp->oneDim || !axi ) {
/* we set the blurring kernel on this axis if
we are NOT doing oneDim, or, we are,
but this is axi == 0 */
if ( !E ) E |= nrrdResampleKernelSet( rsmc, kind->baseDim + axi,
kssb->kernel,
kssb->parm );
} else {
/* what to do with oneDom on axi 1, 2 */
/* you might think that we should just do no resampling at all
on this axis, but that would undermine the in==out==niter
trick described above; and produce the mysterious behavior
that the second scale-space volume is all 0.0 */
double boxparm[NRRD_KERNEL_PARMS_NUM] = {1.0};
if ( !E ) E |= nrrdResampleKernelSet( rsmc, kind->baseDim + axi,
nrrdKernelBox, boxparm );
}
}
if ( sbp->verbose ) {
fprintf( stderr, " pass %u ( timeLeft=%g => "
"time=%g, sigma=%g ) ...\n",
passIdx, timeLeft, timeDo, kssb->parm[0] );
}
if ( !E ) E |= nrrdResampleExecute( rsmc, niter );
/* for debugging problem with getting zero output
if ( !E ) {
NrrdRange *nrange;
nrange = nrrdRangeNewSet( niter, AIR_FALSE );
fprintf( stderr, "%s: min/max = %g/%g\n", me,
nrange->min, nrange->max );
if ( !nrange->min || !nrange->max ) {
fprintf( stderr, "%s: what? zero zero\n", me );
biffAddf( GAGE, "%s: no good", me );
airMopError( mop ); return 1;
}
}
*/
timeLeft -= timeDo;
passIdx++;
} while ( !E && timeLeft > 0.0 );
/* at this point we have to replicate the behavior of the
last stage of resampling ( e.g. _nrrdResampleOutputUpdate
in nrrd/resampleContext.c ), since we've gently hijacked
the resampling to access the nrrdResample_t blurring
result ( for further blurring ) */
if ( !E ) E |= nrrdCastClampRound( nblur[blIdx], niter, nin->type,
AIR_TRUE,
nrrdTypeIsIntegral[nin->type] );
if ( !E ) E |= nrrdContentSet_va( nblur[blIdx], "blur", nin, "" );
timeDone = timeNow;
} else { /* do blurring in one shot */
kssb->parm[0] = sbp->sigma[blIdx];
for ( axi=0; axi<( sbp->oneDim ? 1u : 3u ); axi++ ) {
if ( !E ) E |= nrrdResampleKernelSet( rsmc, kind->baseDim + axi,
kssb->kernel,
kssb->parm );
}
if ( !E ) E |= nrrdResampleExecute( rsmc, nblur[blIdx] );
}
if ( E ) {
if ( sbp->verbose ) {
fprintf( stderr, "problem!\n" );
}
biffMovef( GAGE, NRRD, "%s: trouble w/ %u of %u ( scale %g )",
me, blIdx, sbp->num, sbp->sigma[blIdx] );
airMopError( mop ); return 1;
}
if ( sbp->verbose ) {
fprintf( stderr, " done.\n" );
}
} /* for blIdx */
airMopOkay( mop );
return 0;
}
/*
1378 ** little helper function to do pre-blurring of a given nrrd
** of the sort that might be useful for scale-space gage use
**
** nblur has to already be allocated for "blNum" Nrrd*s, AND, they all
** have to point to valid ( possibly empty ) Nrrds, so they can hold the
** results of blurring
*/
int
gageStackBlur( Nrrd *const nblur[], gageStackBlurParm *sbp,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="gageStackBlur";
unsigned int blIdx, kvpIdx;
NrrdKernelSpec *kssb;
blurVal_t *blurVal;
airArray *mop;
int E, fftable, spatialBlurred;
if ( !( nblur && sbp && nin && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( gageStackBlurParmCheck( sbp ) ) {
biffAddf( GAGE, "%s: problem with parms", me );
return 1;
}
if ( _checkNrrd( nblur, NULL, sbp->num, AIR_FALSE, nin, kind ) ) {
biffAddf( GAGE, "%s: problem with input ", me );
return 1;
}
mop = airMopNew( );
kssb = nrrdKernelSpecCopy( sbp->kspec );
airMopAdd( mop, kssb, ( airMopper )nrrdKernelSpecNix, airMopAlways );
/* see if we can use FFT-based implementation */
fftable = ( !sbp->needSpatialBlur
&& nrrdBoundaryWrap == sbp->bspec->boundary
&& nrrdKernelDiscreteGaussian == sbp->kspec->kernel );
if ( fftable && nrrdFFTWEnabled ) {
/* go directly to FFT-based blurring */
if ( _stackBlurDiscreteGaussFFT( nblur, sbp, nin, kind ) ) {
biffAddf( GAGE, "%s: trouble with frequency-space blurring", me );
airMopError( mop ); return 1;
}
spatialBlurred = AIR_FALSE;
} else { /* else either not fft-able, or not it was, but not available;
in either case we have to do spatial blurring */
if ( fftable && !nrrdFFTWEnabled ) {
if ( sbp->verbose ) {
fprintf( stderr, "%s: NOTE: FFT-based blurring applicable but not "
"available in this Teem build ( not built with FFTW )\n", me );
}
} else {
if ( sbp->verbose ) {
char kstr[AIR_STRLEN_LARGE], bstr[AIR_STRLEN_LARGE];
nrrdKernelSpecSprint( kstr, kssb );
nrrdBoundarySpecSprint( bstr, sbp->bspec );
fprintf( stderr, "%s: ( FFT-based blurring not applicable: "
"need spatial blur=%s, boundary=%s, kernel=%s )\n", me,
sbp->needSpatialBlur ? "yes" : "no", bstr, kstr );
}
}
if ( _stackBlurSpatial( nblur, sbp, kssb, nin, kind ) ) {
biffAddf( GAGE, "%s: trouble with spatial-domain blurring", me );
airMopError( mop ); return 1;
}
spatialBlurred = AIR_TRUE;
}
/* add the KVPs to document how these were blurred */
if ( !( blurVal = _blurValAlloc( mop, sbp, kssb, nin, spatialBlurred ) ) ) {
biffAddf( GAGE, "%s: problem getting KVP buffer", me );
airMopError( mop ); return 1;
}
E = 0;
for ( blIdx=0; blIdx<sbp->num; blIdx++ ) {
for ( kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++ ) {
if ( KVP_DGGSM_IDX != kvpIdx ) {
if ( !E ) E |= nrrdKeyValueAdd( nblur[blIdx], _blurKey[kvpIdx],
blurVal[blIdx].val[kvpIdx] );
} else {
/* only need to save dgGoodSigmaMax if it was spatially blurred
with the discrete gaussian kernel */
if ( spatialBlurred
&& nrrdKernelDiscreteGaussian == kssb->kernel ) {
if ( !E ) E |= nrrdKeyValueAdd( nblur[blIdx], _blurKey[kvpIdx],
blurVal[blIdx].val[kvpIdx] );
}
}
}
}
if ( E ) {
biffMovef( GAGE, NRRD, "%s: trouble adding KVPs", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1474
/*
******** gageStackBlurCheck
**
** ( docs )
**
*/
int
gageStackBlurCheck( const Nrrd *const nblur[],
gageStackBlurParm *sbp,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="gageStackBlurCheck";
gageShape *shapeOld, *shapeNew;
blurVal_t *blurVal;
airArray *mop;
unsigned int blIdx, kvpIdx;
NrrdKernelSpec *kssb;
if ( !( nblur && sbp && nin && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
kssb = nrrdKernelSpecCopy( sbp->kspec );
airMopAdd( mop, kssb, ( airMopper )nrrdKernelSpecNix, airMopAlways );
if ( gageStackBlurParmCheck( sbp )
|| _checkNrrd( NULL, nblur, sbp->num, AIR_TRUE, nin, kind )
|| ( !( blurVal = _blurValAlloc( mop, sbp, kssb, nin,
( sbp->needSpatialBlur
? AIR_TRUE
: AIR_FALSE ) ) ) ) ) {
biffAddf( GAGE, "%s: problem", me );
airMopError( mop ); return 1;
}
shapeNew = gageShapeNew( );
airMopAdd( mop, shapeNew, ( airMopper )gageShapeNix, airMopAlways );
if ( gageShapeSet( shapeNew, nin, kind->baseDim ) ) {
biffAddf( GAGE, "%s: trouble setting up reference shape", me );
airMopError( mop ); return 1;
}
shapeOld = gageShapeNew( );
airMopAdd( mop, shapeOld, ( airMopper )gageShapeNix, airMopAlways );
for ( blIdx=0; blIdx<sbp->num; blIdx++ ) {
if ( nin->type != nblur[blIdx]->type ) {
biffAddf( GAGE, "%s: nblur[%u]->type %s != nin type %s\n", me,
blIdx, airEnumStr( nrrdType, nblur[blIdx]->type ),
airEnumStr( nrrdType, nin->type ) );
airMopError( mop ); return 1;
}
/* check to see if nblur[blIdx] is as expected */
if ( gageShapeSet( shapeOld, nblur[blIdx], kind->baseDim )
|| !gageShapeEqual( shapeOld, "nblur", shapeNew, "nin" ) ) {
biffAddf( GAGE, "%s: trouble, or nblur[%u] shape != nin shape",
me, blIdx );
airMopError( mop ); return 1;
}
/* see if the KVPs are there with the required values */
for ( kvpIdx=0; kvpIdx<KVP_NUM; kvpIdx++ ) {
char *tmpval;
tmpval = nrrdKeyValueGet( nblur[blIdx], _blurKey[kvpIdx] );
airMopAdd( mop, tmpval, airFree, airMopAlways );
if ( KVP_DGGSM_IDX != kvpIdx ) {
if ( !tmpval ) {
biffAddf( GAGE, "%s: didn't see key \"%s\" in nblur[%u]", me,
_blurKey[kvpIdx], blIdx );
airMopError( mop ); return 1;
}
if ( KVP_SBLUR_IDX == kvpIdx ) {
/* this KVP is handled differently */
if ( !sbp->needSpatialBlur ) {
/* we don't care if it was frequency-domain or spatial-domain
blurring, so there's no right answer */
continue;
}
/* else we do need spatial domain blurring; so do the check below */
}
if ( strcmp( tmpval, blurVal[blIdx].val[kvpIdx] ) ) {
biffAddf( GAGE, "%s: found key[%s] \"%s\" != wanted \"%s\"", me,
_blurKey[kvpIdx], tmpval, blurVal[blIdx].val[kvpIdx] );
airMopError( mop ); return 1;
}
} else {
/* KVP_DGGSM_IDX == kvpIdx; handled differently since KVP isn't saved
when not needed */
if ( !sbp->needSpatialBlur ) {
/* if you don't care about needing spatial blurring, you
lose the right to care about a difference in dggsm */
continue;
}
if ( tmpval ) {
/* therefore it was spatially blurred, so there's a saved DGGSM,
and we do need spatial blurring, so we compare them */
if ( strcmp( tmpval, blurVal[blIdx].val[kvpIdx] ) ) {
biffAddf( GAGE, "%s: found key[%s] \"%s\" != wanted \"%s\"", me,
_blurKey[kvpIdx], tmpval, blurVal[blIdx].val[kvpIdx] );
/* HEY: a change in the discrete Gaussian cut-off will result
in a recomputation of the blurrings, even with FFT-based
blurring, though cut-off is completely moot then */
airMopError( mop ); return 1;
}
}
continue;
}
1579 }
}
airMopOkay( mop );
return 0;
}
int
gageStackBlurGet( Nrrd *const nblur[], int *recomputedP,
gageStackBlurParm *sbp,
const char *format,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="gageStackBlurGet";
airArray *mop;
int recompute;
unsigned int ii;
if ( !( nblur && sbp && nin && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
for ( ii=0; ii<sbp->num; ii++ ) {
if ( !nblur[ii] ) {
biffAddf( GAGE, "%s: nblur[%u] NULL", me, ii );
return 1;
}
}
if ( gageStackBlurParmCheck( sbp ) ) {
biffAddf( GAGE, "%s: trouble with blur parms", me );
return 1;
}
mop = airMopNew( );
/* set recompute flag */
if ( !airStrlen( format ) ) {
/* no info about files to load, obviously have to recompute */
if ( sbp->verbose ) {
fprintf( stderr, "%s: no file info, must recompute blurrings\n", me );
}
recompute = AIR_TRUE;
} else {
char *fname, *suberr;
int firstExists;
FILE *file;
/* do have info about files to load, but may fail in many ways */
fname = AIR_CALLOC( strlen( format ) + AIR_STRLEN_SMALL, char );
if ( !fname ) {
biffAddf( GAGE, "%s: couldn't allocate fname", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, fname, airFree, airMopAlways );
/* see if we can get the first file ( number 0 ) */
sprintf( fname, format, 0 );
firstExists = !!( file = fopen( fname, "r" ) );
airFclose( file );
if ( !firstExists ) {
if ( sbp->verbose ) {
fprintf( stderr, "%s: no file \"%s\"; will recompute blurrings\n",
me, fname );
}
recompute = AIR_TRUE;
} else if ( nrrdLoadMulti( nblur, sbp->num, format, 0, NULL ) ) {
airMopAdd( mop, suberr = biffGetDone( NRRD ), airFree, airMopAlways );
if ( sbp->verbose ) {
fprintf( stderr, "%s: will recompute blurrings that couldn't be "
"read:\n%s\n", me, suberr );
}
recompute = AIR_TRUE;
} else if ( gageStackBlurCheck( AIR_CAST( const Nrrd*const*, nblur ),
sbp, nin, kind ) ) {
airMopAdd( mop, suberr = biffGetDone( GAGE ), airFree, airMopAlways );
if ( sbp->verbose ) {
fprintf( stderr, "%s: will recompute blurrings ( from \"%s\" ) "
"that don't match:\n%s\n", me, format, suberr );
}
recompute = AIR_TRUE;
} else {
/* else precomputed blurrings could all be read, and did match */
if ( sbp->verbose ) {
fprintf( stderr, "%s: will reuse %u %s pre-blurrings.\n", me,
sbp->num, format );
}
recompute = AIR_FALSE;
}
}
if ( recompute ) {
if ( gageStackBlur( nblur, sbp, nin, kind ) ) {
biffAddf( GAGE, "%s: trouble computing blurrings", me );
airMopError( mop ); return 1;
}
}
if ( recomputedP ) {
*recomputedP = recompute;
}
airMopOkay( mop );
return 0;
}
1679 /*
******** gageStackBlurManage
**
** does the work of gageStackBlurGet and then some:
** allocates the array of Nrrds, allocates an array of doubles for scale,
** and saves output if recomputed
*/
int
gageStackBlurManage( Nrrd ***nblurP, int *recomputedP,
gageStackBlurParm *sbp,
const char *format,
int saveIfComputed, NrrdEncoding *enc,
const Nrrd *nin, const gageKind *kind ) {
static const char me[]="gageStackBlurManage";
Nrrd **nblur;
unsigned int ii;
airArray *mop;
int recomputed;
if ( !( nblurP && sbp && nin && kind ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
nblur = *nblurP = AIR_CALLOC( sbp->num, Nrrd * );
if ( !nblur ) {
biffAddf( GAGE, "%s: couldn't alloc %u Nrrd*s", me, sbp->num );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nblurP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, *nblurP, airFree, airMopOnError );
for ( ii=0; ii<sbp->num; ii++ ) {
nblur[ii] = nrrdNew( );
airMopAdd( mop, nblur[ii], ( airMopper )nrrdNuke, airMopOnError );
}
if ( gageStackBlurGet( nblur, &recomputed, sbp, format, nin, kind ) ) {
biffAddf( GAGE, "%s: trouble getting nblur", me );
airMopError( mop ); return 1;
}
if ( recomputedP ) {
*recomputedP = recomputed;
}
if ( recomputed && format && saveIfComputed ) {
NrrdIoState *nio;
int E;
E = 0;
if ( enc ) {
if ( !enc->available( ) ) {
biffAddf( GAGE, "%s: requested %s encoding which is not "
"available in this build", me, enc->name );
airMopError( mop ); return 1;
}
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
if ( !E ) E |= nrrdIoStateEncodingSet( nio, nrrdEncodingGzip );
} else {
nio = NULL;
}
if ( !E ) E |= nrrdSaveMulti( format, AIR_CAST( const Nrrd *const *,
nblur ),
sbp->num, 0, nio );
if ( E ) {
biffMovef( GAGE, NRRD, "%s: trouble saving blurrings", me );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *aaliasInfo = ( "implements \"Reducing Aliasing Artifacts "
"in Iso-Surfaces of Binary Volumes\", "
"R. T. Whitaker IEEE VolVis 2000. With optimization, "
"could favorably compare to "
"itk::AntiAliasBinaryImageFilter." );
int
32 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err, done[13];
Nrrd *nin, *ndist, *nmask, *nupdate, *nout;
NrrdKernelSpec *k00, *k11, *k22;
int E;
unsigned int sx, sy, sz, xi, yi, zi, si, iter, maxIter;
gageContext *ctx;
gagePerVolume *pvl;
double dt, *dist, *mask, *update, thresh, eps, rmsMin;
const double *valu, *gmag, *mcrv;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1, &k00,
"tent", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kernel", airTypeOther, 1, 1, &k11,
"cubicd:0, 0.5", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kernel", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "dt", "step", airTypeDouble, 1, 1, &dt, "0.17",
"time step size" );
hestOptAdd( &hopt, "th", "val", airTypeDouble, 1, 1, &thresh, "0.0",
"the value to use for thresholding the input "
"volume, to create the binary constraint image." );
hestOptAdd( &hopt, "eps", "val", airTypeDouble, 1, 1, &eps, "0.05",
"width of value bracket around threshold, to constrain the "
"the update from letting to value, originally on either "
"side of the threshold, from getting too close." );
hestOptAdd( &hopt, "rms", "thresh", airTypeDouble, 1, 1, &rmsMin, "0.01",
"RMS change below this terminates updates." );
hestOptAdd( &hopt, "mi", "max", airTypeUInt, 1, 1, &maxIter, "100",
"maximum # iterations" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"fixed volume output" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, aaliasInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
ndist = nrrdNew( );
nmask = nrrdNew( );
nupdate = nrrdNew( );
airMopAdd( mop, ndist, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nmask, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nupdate, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ndist, nin, nrrdTypeDouble )
|| nrrdConvert( nmask, nin, nrrdTypeDouble )
|| nrrdConvert( nupdate, nin, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate buffers:\n%s", me, err );
airMopError( mop ); return 1;
}
dist = AIR_CAST( double *, ndist->data );
mask = AIR_CAST( double *, nmask->data );
update = AIR_CAST( double *, nupdate->data );
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, ndist, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclValue );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclGradMag );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclMeanCurv );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
valu = gageAnswerPointer( ctx, pvl, gageSclValue );
gmag = gageAnswerPointer( ctx, pvl, gageSclGradMag );
mcrv = gageAnswerPointer( ctx, pvl, gageSclMeanCurv );
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
for ( iter=0; iter<maxIter; iter++ ) {
double rms;
unsigned count;
fprintf( stderr, "%s: iter %u: ", me, iter );
fflush( stderr );
for ( zi=0; zi<sz; zi++ ) {
fprintf( stderr, "%s", airDoneStr( 0, zi, sz-1, done ) );
fflush( stderr );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*( yi + sy*zi );
gageProbe( ctx, xi, yi, zi );
update[si] = -dt*( *gmag )*( *mcrv );
}
}
}
rms = 0;
count = 0;
for ( si=0; si<sx*sy*sz; si++ ) {
double newval;
if ( update[si] ) {
/* NOTE: this update behavior is only slightly different than
what's described in Equation 18 of the paper. That update
rule ensures that the value never changes "sign" ( with
respect to the threshold value ), by clamping the values
above or below to 0.0. But worst-case scenario is that two
adjacent samples that were on other side of the threshold
( i.e. 0 ), could then equal the threshold, which would
confuse a marching-cubes type algorithm. So the "eps"
enforces a small value range around the treshold, and keeps
the values on either side of it. */
newval = dist[si] + update[si];
if ( mask[si] > thresh ) {
newval = AIR_MAX( newval, thresh+eps/2 );
} else {
newval = AIR_MIN( newval, thresh-eps/2 );
}
rms += ( dist[si] - newval )*( dist[si] - newval );
dist[si] = newval;
count++;
}
}
fprintf( stderr, "%s", airDoneStr( 0, zi, sz-1, done ) );
rms /= count;
rms = sqrt( rms );
if ( rms < rmsMin ) {
fprintf( stderr, "\n%s: RMS %g below threshold %g\n", me, rms, rmsMin );
break;
} else {
fprintf( stderr, " rms = %g > %g\n", rms, rmsMin );
}
}
if ( iter == maxIter ) {
fprintf( stderr, "%s: hit max iter %u\n", me, maxIter );
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, ndist ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *ctfixInfo = ( "removes circular streaks from CT datasets, "
"assuming you know where they are. nrrd/test/histrad "
"is useful for finding the radius of a streak "
"in a given slice." );
int
31 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err, done[13];
Nrrd *nin, *_nsinfo, *nsinfo, *nout;
NrrdKernelSpec *kern;
int E;
unsigned int sx, sy, sz, xi, yi, zi, si;
gageContext *ctx;
gagePerVolume *pvl;
const double *answer;
double cent[2], r, srad, *sinfo, swidth, astart, angle, astop, corerad;
double ( *ins )( void *v, size_t I, double d );
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "si", "streak info", airTypeOther, 1, 1, &_nsinfo, NULL,
"list of streaks as 2D nrrd; per line: \n "
"<slice> <radius> <width> <start> <stop> \n "
"where start and stop are angles in [0, 4] "
"( instead of [-pi, pi] ) delimiting arc" );
hestOptAdd( &hopt, "k", "kernel", airTypeOther, 1, 1, &kern,
"tent", "kernel to use for median weighting",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "c", "center x, y", airTypeDouble, 2, 2, cent, NULL,
"The center point of tomography, around which streaks are "
"circular arcs or fragments thereof" );
hestOptAdd( &hopt, "r", "core radius", airTypeDouble, 1, 1, &corerad, "0",
"in addition to the per-streak filtering, giving a non-zero "
"radius here turns on filtering of *every* slice within "
"this radius, to handle glitches along central tomography "
"radius" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"fixed volume output" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, ctfixInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 3 == nin->dim ) ) {
fprintf( stderr, "%s: need 3-D input ( not %d-D )\n", me, nin->dim );
airMopError( mop ); return 1;
}
if ( !( 2 == _nsinfo->dim && 5 == _nsinfo->axis[0].size ) ) {
fprintf( stderr, "%s: need a 2-D 5-by-N list of streak locations\n", me );
airMopError( mop ); return 1;
}
nsinfo = nrrdNew( );
airMopAdd( mop, nsinfo, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nsinfo, _nsinfo, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't convert streak info to doubles:\n%s",
me, err );
airMopError( mop ); return 1;
}
sinfo = ( double* )( nsinfo->data );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
#define DIST( x0, y0, x1, y1 ) ( sqrt( ( x0-x1 )*( x0-x1 ) + ( y0-y1 )*( y0-y1 ) ) )
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, kern->kernel, kern->parm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclMedian );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
answer = gageAnswerPointer( ctx, pvl, gageSclMedian );
ins = nrrdDInsert[nout->type];
fprintf( stderr, "%s: processing streaks ... \n", me );
for ( si=0; si<nsinfo->axis[1].size; si++ ) {
fprintf( stderr, "%s: streak %d of %d ",
me, si+1, AIR_CAST( int, nsinfo->axis[1].size ) );
fflush( stderr );
zi = ( int )( sinfo[0 + 5*si] );
if ( !( zi < sz ) ) {
fprintf( stderr, "%s: streak[%d] zi=%d outside valid range [0, %d]\n",
me, si, zi, sz-1 );
airMopError( mop ); return 1;
}
srad = sinfo[1 + 5*si];
swidth = sinfo[2 + 5*si];
astart = sinfo[3 + 5*si];
astop = sinfo[4 + 5*si];
for ( yi=0; yi<sy; yi++ ) {
if ( !( yi%3 ) ) {
fprintf( stderr, "%s", airDoneStr( 0, yi, sy-1, done ) );
fflush( stderr );
}
for ( xi=0; xi<sx; xi++ ) {
r = DIST( xi, yi, cent[0], cent[1] );
if ( !( AIR_ABS( r-srad ) < swidth/2 ) ) {
continue;
}
angle = atan2( yi-cent[1], xi-cent[0] );
angle = AIR_AFFINE( -AIR_PI, angle, AIR_PI, 0, 4 );
if ( !( ( astart < astop && AIR_IN_CL( astart, angle, astop ) )
|| ( astart > astop && ( AIR_IN_CL( astart, angle, 4 )
|| AIR_IN_CL( 0, angle, astop ) ) ) ) ) {
continue;
}
gageProbe( ctx, xi, yi, zi );
ins( nout->data, xi + sx*( yi + sy*zi ), *answer );
}
}
fprintf( stderr, "\n" );
}
if ( corerad > 0 ) {
fprintf( stderr, "%s: processing core ... ", me );
for ( zi=0; zi<sz; zi++ ) {
fprintf( stderr, "%s", airDoneStr( 0, zi, sz-1, done ) );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
r = DIST( xi, yi, cent[0], cent[1] );
if ( !( r < corerad ) ) {
continue;
}
gageProbe( ctx, xi, yi, zi );
ins( nout->data, xi + sx*( yi + sy*zi ), *answer );
}
}
}
fprintf( stderr, "\n" );
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
/* self-contained gage usage example */
int
28 main( int argc, char *argv[] ) {
char *me, *err;
airArray *mop;
Nrrd *nin;
gageContext *ctx;
gagePerVolume *pvl;
const double *grad, *norm;
double kparm[NRRD_KERNEL_PARMS_NUM];
int E;
me = argv[0];
if ( 2 != argc ) {
fprintf( stderr, "usage: %s <nin>\n", me );
return 1;
}
mop = airMopNew( );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nin, argv[1], NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble loading\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( gageKindVolumeCheck( gageKindScl, nin ) ) {
airMopAdd( mop, err=biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get a %s volume:\n%s\n", me,
gageKindScl->name, err );
airMopError( mop ); return 1;
}
kparm[0] = 1.0; /* scale parameter, in units of samples */
kparm[1] = 0.0; /* B */
kparm[2] = 0.5; /* C */
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nin, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, nrrdKernelBCCubic, kparm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, nrrdKernelBCCubicD, kparm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclGradVec );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclNormal );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err=biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up Gage:\n%s\n", me, err );
airMopError( mop ); return 1;
}
grad = gageAnswerPointer( ctx, pvl, gageSclGradVec );
norm = gageAnswerPointer( ctx, pvl, gageSclNormal );
if ( gageProbe( ctx, 1.1, 2.3, 6.8 ) ) {
fprintf( stderr, "%s: trouble:\n( %d ) %s\n", me, ctx->errNum, ctx->errStr );
airMopError( mop ); return 1;
}
printf( "gradient ( %g, %g, %g ) --> norm ( %g, %g, %g )\n",
grad[0], grad[1], grad[2], norm[0], norm[1], norm[2] );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *optsigInfo = ( "Computes tables of optimal sigmas for hermite-spline "
"reconstruction of scale space" );
int
30 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err, *outS;
double sigma[2], convEps, cutoff;
int measr[2], tentRecon;
unsigned int sampleNum[2], dim, measrSampleNum, maxIter, num, ii;
gageOptimSigContext *osctx;
double *scalePos, *out, info[512];
Nrrd *nout;
NrrdKernelSpec *kss;
double *plotPos; unsigned int plotPosNum;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "num", "# samp", airTypeUInt, 2, 2, sampleNum, NULL,
"min and max number of samples to optimize" );
hestOptAdd( &hopt, "dim", "dimension", airTypeUInt, 1, 1, &dim, "3",
"dimension of image to work with" );
hestOptAdd( &hopt, "sig", "min max", airTypeDouble, 2, 2, sigma, NULL,
"sigmas to use for bottom and top sample; normal use "
"should have 0 for bottom scale" );
hestOptAdd( &hopt, "cut", "cut", airTypeDouble, 1, 1, &cutoff, "4",
"at how many sigmas to cut-off discrete gaussian" );
hestOptAdd( &hopt, "maxi", "max", airTypeUInt, 1, 1, &maxIter, "1000",
"maximum # iterations" );
hestOptAdd( &hopt, "N", "# samp", airTypeUInt, 1, 1, &measrSampleNum, "300",
"number of samples in the measurement of error across scales" );
hestOptAdd( &hopt, "eps", "eps", airTypeDouble, 1, 1, &convEps, "0.0001",
"convergence threshold for optimization" );
hestOptAdd( &hopt, "m", "m1 m2", airTypeEnum, 2, 2, measr, "l2 l2",
"how to measure error across image, and across scales",
NULL, nrrdMeasure );
hestOptAdd( &hopt, "p", "s0 s1", airTypeDouble, 1, -1, &plotPos, "nan nan",
"hack: don't do optimization; just plot the recon error given "
"these ( two or more ) samples along scale. OR, hackier hack: "
"if there is only one value given here, do a different "
"plot: of the recon error within a small window ( with the width "
"given here ) in rho, as rho moves through its range",
&plotPosNum );
hestOptAdd( &hopt, "kss", "kern", airTypeOther, 1, 1, &kss, "hermite",
"kernel for gageKernelStack", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "tent", NULL, airTypeInt, 0, 0, &tentRecon, NULL,
"same hack: plot error with tent recon, not hermite" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output array" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, optsigInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
osctx = gageOptimSigContextNew( dim, sampleNum[1],
measrSampleNum,
sigma[0], sigma[1],
cutoff );
if ( !osctx ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopAdd( mop, osctx, AIR_CAST( airMopper, gageOptimSigContextNix ),
airMopAlways );
scalePos = AIR_CALLOC( sampleNum[1], double );
airMopAdd( mop, scalePos, airFree, airMopAlways );
if ( 1 == plotPosNum && AIR_EXISTS( plotPos[0] ) ) {
/* hackity hack: a different kind of plotting requested */
if ( gageOptimSigErrorPlotSliding( osctx, nout,
plotPos[0],
measrSampleNum,
kss, measr[0] ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
} else if ( AIR_EXISTS( plotPos[0] ) && AIR_EXISTS( plotPos[1] ) ) {
/* hack: plotting requested */
if ( gageOptimSigErrorPlot( osctx, nout,
plotPos, plotPosNum,
kss,
measr[0] ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
} else {
/* do sample position optimization */
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, sampleNum[1]+1 ),
AIR_CAST( size_t, sampleNum[1]+1 ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating output:\n%s", me, err );
airMopError( mop ); return 1;
}
out = AIR_CAST( double *, nout->data );
/* hacky way of saving some of the computation information */
/* HEY: this is what KVPs are for */
info[0] = cutoff;
info[1] = measrSampleNum;
info[2] = measr[0];
info[3] = measr[1];
info[4] = convEps;
info[5] = maxIter;
for ( ii=0; ii<sampleNum[1]+1; ii++ ) {
out[ii] = info[ii];
}
for ( num=sampleNum[0]; num<=sampleNum[1]; num++ ) {
printf( "\n%s: ======= optimizing %u/%u samples ( sigma %g--%g ) \n\n",
me, num, sampleNum[1]+1, sigma[0], sigma[1] );
if ( gageOptimSigCalculate( osctx, scalePos, num,
kss, measr[0], measr[1],
maxIter, convEps ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
for ( ii=0; ii<num; ii++ ) {
out[ii + ( sampleNum[1]+1 )*num] = scalePos[ii];
}
out[sampleNum[1] + ( sampleNum[1]+1 )*num] = osctx->finalErr;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *indxInfo = ( "for debugging part of _gageLocationSet" );
int
28 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
double *val, top, min, max;
unsigned ii, valLen, xi, size;
int center;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "val0 val1", airTypeDouble, 1, -1, &val, NULL,
"input values", &valLen, NULL );
hestOptAdd( &hopt, "c, center", "center", airTypeEnum, 1, 1, ¢er, NULL,
"centering ( cell or node )", NULL, nrrdCenter );
hestOptAdd( &hopt, "s", "size", airTypeUInt, 1, 1, &size, NULL,
"number of samples" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, indxInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
fprintf( stderr, "%s: using %s centering\n", me,
airEnumStr( nrrdCenter, center ) );
top = size-1;
if ( nrrdCenterNode == center ) {
min = 0;
max = top;
} else {
min = -0.5;
max = top + 0.5;
}
for ( ii=0; ii<valLen; ii++ ) {
double _xi, xf;
_xi = val[ii];
fprintf( stderr, "%u \t%g \t", ii, _xi );
if ( !( AIR_IN_CL( min, _xi, max ) ) ) {
fprintf( stderr, "OUTSIDE\n" );
} else {
if ( 0 && nrrdCenterCell == center ) {
xi = AIR_CAST( unsigned int, _xi+1 ) - 1;
} else {
xi = AIR_CAST( unsigned int, _xi );
}
xi -= ( xi == max );
xf = _xi - xi;
fprintf( stderr, "%u + %g\n", xi, xf );
}
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
void
27 maxes1( Nrrd *nout, const Nrrd *nin ) {
unsigned int sx, xi, xd;
int xo, ismax;
double val, ( *lup )( const void *, size_t ),
( *ins )( void *, size_t, double );
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nin->type];
sx = AIR_CAST( unsigned int, nin->axis[0].size );
for ( xi=0; xi<sx; xi++ ) {
ismax = AIR_TRUE;
val = lup( nin->data, xi );
for ( xo=-1; xo<=1; xo+=2 ) {
xd = !xi && xo < 0 ? sx-1 : AIR_MOD( xi+xo, sx );
ismax &= lup( nin->data, xd ) < val;
if ( !ismax ) break;
}
ins( nout->data, xi, ismax );
}
return;
}
void
50 maxes2( Nrrd *nout, const Nrrd *nin ) {
unsigned int sx, sy, xi, yi, xd, yd;
int xo, yo, ismax;
double val, ( *lup )( const void *, size_t ),
( *ins )( void *, size_t, double );
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nin->type];
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
ismax = AIR_TRUE;
val = lup( nin->data, xi + sx*yi );
for ( yo=-1; yo<=1; yo++ ) {
yd = !yi && yo < 0 ? sy-1 : AIR_MOD( yi+yo, sy );
for ( xo=-1; xo<=1; xo++ ) {
if ( !xo && !yo ) continue;
xd = !xi && xo < 0 ? sx-1 : AIR_MOD( xi+xo, sx );
ismax &= lup( nin->data, xd + sx*yd ) < val;
}
}
ins( nout->data, xi + sx*yi, ismax );
}
}
return;
}
void
79 maxes3( Nrrd *nout, const Nrrd *nin ) {
unsigned int sx, sy, sz, xi, yi, zi, xd, yd, zd;
int xo, yo, zo, ismax;
double val, ( *lup )( const void *, size_t ),
( *ins )( void *, size_t, double );
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nin->type];
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
sz = AIR_CAST( unsigned int, nin->axis[2].size );
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
ismax = AIR_TRUE;
val = lup( nin->data, xi + sx*( yi + sy*zi ) );
for ( zo=-1; zo<=1; zo++ ) {
zd = !zi && zo < 0 ? sz-1 : AIR_MOD( zi+zo, sz );
for ( yo=-1; yo<=1; yo++ ) {
yd = !yi && yo < 0 ? sy-1 : AIR_MOD( yi+yo, sy );
for ( xo=-1; xo<=1; xo++ ) {
if ( !xo && !yo && !zo ) continue;
xd = !xi && xo < 0 ? sx-1 : AIR_MOD( xi+xo, sx );
ismax &= lup( nin->data, xd + sx*( yd + sy*zd ) ) < val;
}
}
}
ins( nout->data, xi + sx*( yi + sy*zi ), ismax );
}
}
}
return;
}
char *maxesInfo = ( "sets maxima to 1, else 0" );
int
116 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err;
Nrrd *nin, *nout;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL,
"output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, maxesInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !AIR_IN_CL( 1, nin->dim, 3 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: nin->dim %u not 1, 2, or 3", me, nin->dim );
airMopError( mop ); return 1;
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( 1 == nin->dim ) {
maxes1( nout, nin );
} else if ( 2 == nin->dim ) {
maxes2( nout, nin );
} else {
maxes3( nout, nin );
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009, 2008 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *Info = ( "for doing silly conversions" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
double val;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, NULL, "val", airTypeDouble, 1, 1, &val, NULL,
"input value" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, Info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
printf( "%s: tee % 10.7g -> sig % 10.7g tau % 10.7g\n", me, val,
gageSigOfTau( gageTauOfTee( val ) ),
gageTauOfTee( val ) );
printf( "%s: tau % 10.7g -> sig % 10.7g tee % 10.7g\n", me, val,
gageSigOfTau( val ),
gageTeeOfTau( val ) );
printf( "%s: sig % 10.7g -> tee % 10.7g tau % 10.7g\n", me, val,
gageTeeOfTau( gageTauOfSig( val ) ),
gageTauOfSig( val ) );
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *tplotInfo = ( "for plotting tau" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
double dommm[2];
unsigned int num;
char *err;
char *outS;
Nrrd *nout;
double *out, dd;
unsigned int ii;
int tee;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "tee", NULL, airTypeInt, 0, 0, &tee, NULL,
"domain of plot is t, not sigma" );
hestOptAdd( &hopt, "mm", "min max", airTypeDouble, 2, 2, dommm, NULL,
"range of domain to plot" );
hestOptAdd( &hopt, "n", "# samp", airTypeUInt, 1, 1, &num, NULL,
"number of samples" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL,
"output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, tplotInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 1,
AIR_CAST( size_t, num ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
out = AIR_CAST( double *, nout->data );
nout->axis[0].min = dommm[0];
nout->axis[0].max = dommm[1];
nout->axis[0].center = nrrdCenterNode;
for ( ii=0; ii<num; ii++ ) {
dd = AIR_AFFINE( 0, ii, num-1, dommm[0], dommm[1] );
out[ii] = tee ? gageTauOfTee( dd ) : gageTauOfSig( dd );
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../gage.h"
char *vhInfo = ( "deepens valleys. a hack." );
int
28 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err, done[13];
Nrrd *nin, *nblur, *nout;
NrrdKernelSpec *kb0, *kb1, *k00, *k11, *k22;
NrrdResampleContext *rsmc;
int E;
unsigned int sx, sy, sz, xi, yi, zi, ai;
gageContext *ctx;
gagePerVolume *pvl;
const double *gvec, *gmag, *evec0, *eval;
double ( *ins )( void *v, size_t I, double d );
double ( *lup )( const void *v, size_t I );
double dotmax, dotpow, gmmax, evalshift, gmpow, _dotmax, _gmmax, scl, clamp;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "kb0", "kernel", airTypeOther, 1, 1, &kb0,
"guass:3, 5", "kernel to use for pre-process blurring",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "kb1", "kernel", airTypeOther, 1, 1, &kb1,
"cubic:1.5, 1, 0", "kernel to use for pos-process blurring",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1, &k00,
"cubic:1, 0", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kernel", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kernel", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "k00", NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "dotmax", "dot", airTypeDouble, 1, 1, &dotmax, "5",
"max effective value of dot( gvec, evec0 )" );
hestOptAdd( &hopt, "evs", "shift", airTypeDouble, 1, 1, &evalshift, "0",
"negative shift to avoid changing mostly flat regions" );
hestOptAdd( &hopt, "clamp", "clamp", airTypeDouble, 1, 1, &clamp, "nan",
"if it exists, data value can't be forced below this" );
hestOptAdd( &hopt, "dotpow", "pow", airTypeDouble, 1, 1, &dotpow, "1",
"exponent for dot" );
hestOptAdd( &hopt, "gmmax", "dot", airTypeDouble, 1, 1, &gmmax, "2",
"max effective value of gmag" );
hestOptAdd( &hopt, "gmpow", "pow", airTypeDouble, 1, 1, &gmpow, "1",
"exponent for gmag" );
hestOptAdd( &hopt, "scl", "scale", airTypeDouble, 1, 1, &scl, "0.1",
"how much to scale hack to decrease input value" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"fixed volume output" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, vhInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 3 == nin->dim
&& nrrdTypeBlock != nin->type ) ) {
fprintf( stderr, "%s: need a 3-D scalar nrrd ( not %u-D %s )", me,
nin->dim, airEnumStr( nrrdType, nin->type ) );
airMopError( mop ); return 1;
}
nblur = nrrdNew( );
airMopAdd( mop, nblur, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nblur, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: pre-blurring ... ", me );
fflush( stderr );
rsmc = nrrdResampleContextNew( );
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
E = AIR_FALSE;
if ( !E ) E |= nrrdResampleDefaultCenterSet( rsmc, nrrdCenterCell );
if ( !E ) E |= nrrdResampleInputSet( rsmc, nin );
for ( ai=0; ai<3; ai++ ) {
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, kb0->kernel, kb0->parm );
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, nin->axis[ai].size );
if ( !E ) E |= nrrdResampleRangeFullSet( rsmc, ai );
}
if ( !E ) E |= nrrdResampleBoundarySet( rsmc, nrrdBoundaryBleed );
if ( !E ) E |= nrrdResampleTypeOutSet( rsmc, nrrdTypeDefault );
if ( !E ) E |= nrrdResampleRenormalizeSet( rsmc, AIR_TRUE );
if ( !E ) E |= nrrdResampleExecute( rsmc, nblur );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error resampling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "done.\n" );
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
gageParmSet( ctx, gageParmRenormalize, AIR_TRUE );
gageParmSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( ctx, nblur, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, k00->kernel, k00->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel11, k11->kernel, k11->parm );
if ( !E ) E |= gageKernelSet( ctx, gageKernel22, k22->kernel, k22->parm );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclGradVec );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclGradMag );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclHessEvec0 );
if ( !E ) E |= gageQueryItemOn( ctx, pvl, gageSclHessEval );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
gvec = gageAnswerPointer( ctx, pvl, gageSclGradVec );
gmag = gageAnswerPointer( ctx, pvl, gageSclGradMag );
evec0 = gageAnswerPointer( ctx, pvl, gageSclHessEvec0 );
eval = gageAnswerPointer( ctx, pvl, gageSclHessEval );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( !( nout->type == nin->type && nblur->type == nin->type ) ) {
fprintf( stderr, "%s: whoa, types ( %s %s %s ) not all equal\n", me,
airEnumStr( nrrdType, nin->type ),
airEnumStr( nrrdType, nblur->type ),
airEnumStr( nrrdType, nout->type ) );
}
ins = nrrdDInsert[nout->type];
lup = nrrdDLookup[nout->type];
sx = nin->axis[0].size;
sy = nin->axis[1].size;
sz = nin->axis[2].size;
gageProbe( ctx, 0, 0, 0 );
_dotmax = ELL_3V_DOT( gvec, evec0 );
_gmmax = *gmag;
fprintf( stderr, "%s: hacking ", me );
fflush( stderr );
for ( zi=0; zi<sz; zi++ ) {
fprintf( stderr, "%s", airDoneStr( 0, zi, sz-1, done ) );
fflush( stderr );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
size_t si;
double dot, evl, gm, shift, in, out, mode;
gageProbe( ctx, xi, yi, zi );
si = xi + sx*( yi + sy*zi );
dot = ELL_3V_DOT( gvec, evec0 );
_dotmax = AIR_MAX( _dotmax, dot );
dot = AIR_ABS( dot );
dot = 1 - AIR_MIN( dot, dotmax )/dotmax;
dot = pow( dot, dotpow );
evl = AIR_MAX( 0, eval[0] - evalshift );
mode = airMode3_d( eval );
evl *= AIR_AFFINE( -1, mode, 1, 0, 1 );
_gmmax = AIR_MAX( _gmmax, *gmag );
gm = 1 - AIR_MIN( *gmag, gmmax )/gmmax;
gm = pow( gm, gmpow );
shift = scl*gm*evl*dot;
if ( AIR_EXISTS( clamp ) ) {
in = lup( nin->data, si );
out = in - shift;
out = AIR_MAX( out, clamp );
shift = AIR_MAX( 0, in - out );
}
ins( nout->data, si, shift );
}
}
}
fprintf( stderr, "\n" );
fprintf( stderr, "%s: max dot seen: %g\n", me, _dotmax );
fprintf( stderr, "%s: max gm seen: %g\n", me, _gmmax );
fprintf( stderr, "%s: post-blurring ... ", me );
fflush( stderr );
E = AIR_FALSE;
if ( !E ) E |= nrrdResampleInputSet( rsmc, nout );
for ( ai=0; ai<3; ai++ ) {
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, kb1->kernel, kb1->parm );
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, nout->axis[ai].size );
if ( !E ) E |= nrrdResampleRangeFullSet( rsmc, ai );
}
if ( !E ) E |= nrrdResampleExecute( rsmc, nblur );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error resampling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "done.\n" );
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
size_t si;
double in, shift;
si = xi + sx*( yi + sy*zi );
in = lup( nin->data, si );
shift = lup( nblur->data, si );
ins( nout->data, si, in - shift );
}
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
const char *
_gage2VecStr[] = {
"( unknown gage2Vec )",
"vector",
"vector0",
"vector1",
"length",
};
const char *
_gage2VecDesc[] = {
"unknown gage2Vec query",
"component-wise-interpolated vector",
"vector[0]",
"vector[1]",
"length of vector",
};
const int
_gage2VecVal[] = {
gage2VecUnknown,
gage2VecVector,
gage2VecVector0,
gage2VecVector1,
gage2VecLength,
};
#define GV_V gage2VecVector
#define GV_V0 gage2VecVector0
#define GV_V1 gage2VecVector1
#define GV_L gage2VecLength
const char *
_gage2VecStrEqv[] = {
"v", "vector", "vec",
"v0", "vector0", "vec0",
"v1", "vector1", "vec1",
"l", "length", "len",
""
};
const int
_gage2VecValEqv[] = {
GV_V, GV_V, GV_V,
GV_V0, GV_V0, GV_V0,
GV_V1, GV_V1, GV_V1,
GV_L, GV_L, GV_L,
};
const airEnum
_gage2Vec = {
"gage2Vec",
GAGE_2VEC_ITEM_MAX,
_gage2VecStr, _gage2VecVal,
_gage2VecDesc,
_gage2VecStrEqv, _gage2VecValEqv,
AIR_FALSE
};
85 const airEnum *const
gage2Vec = &_gage2Vec;
gageItemEntry
_gage2VecTable[GAGE_2VEC_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, parent index, needData */
{gage2VecUnknown, 0, 0, {0}, 0, 0, AIR_FALSE},
{gage2VecVector, 2, 0, {0}, 0, 0, AIR_FALSE},
{gage2VecVector0, 1, 0, {gage2VecVector}, gage2VecVector, 0, AIR_FALSE},
{gage2VecVector1, 1, 0, {gage2VecVector}, gage2VecVector, 1, AIR_FALSE},
{gage2VecLength, 1, 0, {gage2VecVector}, 0, 0, AIR_FALSE},
};
void
101 _gage2VecFilter( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="_gage2VecFilter";
double *fw00, *fw11, *fw22, *vec;
int fd;
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4,
gageScl3PFilter6, gageScl3PFilter8};
unsigned int valIdx;
fd = 2*ctx->radius;
vec = pvl->directAnswer[gage2VecVector];
if ( !ctx->parm.k3pack ) {
fprintf( stderr, "!%s: sorry, 6pack filtering not implemented\n", me );
return;
}
fw00 = ctx->fw + fd*3*gageKernel00;
fw11 = ctx->fw + fd*3*gageKernel11;
fw22 = ctx->fw + fd*3*gageKernel22;
/* perform the filtering */
if ( fd <= 8 ) {
for ( valIdx=0; valIdx<2; valIdx++ ) {
filter[ctx->radius]( ctx->shape,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
vec + valIdx, NULL, NULL, /* jac + valIdx*2, hes + valIdx*4, */
pvl->needD );
}
} else {
for ( valIdx=0; valIdx<2; valIdx++ ) {
gageScl3PFilterN( ctx->shape, fd,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
vec + valIdx, NULL, NULL, /* jac + valIdx*2, hes + valIdx*4, */
pvl->needD );
}
}
return;
}
void
145 _gage2VecAnswer( gageContext *ctx, gagePerVolume *pvl ) {
/* static const char me[]="_gage2VecAnswer"; */
double *vecAns;
/* int asw; */
AIR_UNUSED( ctx );
vecAns = pvl->directAnswer[gage2VecVector];
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gage2VecVector ) ) {
/* done if doV */
/*
if ( ctx->verbose ) {
fprintf( stderr, "vec = %f %f", vecAns[0], vecAns[1] );
}
*/
}
/* done if doV
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gage2VecVector{0, 1} ) ) {
}
*/
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gage2VecLength ) ) {
pvl->directAnswer[gage2VecLength][0] = ELL_2V_LEN( vecAns );
}
return;
}
void
172 _gage2VecIv3Print ( FILE *file, gageContext *ctx, gagePerVolume *pvl ) {
AIR_UNUSED( ctx );
AIR_UNUSED( pvl );
fprintf( file, "_gage2VecIv3Print( ) not implemented\n" );
}
gageKind
_gageKind2Vec = {
AIR_FALSE, /* statically allocated */
"2vector",
&_gage2Vec,
1, /* baseDim */
2, /* valLen */
GAGE_2VEC_ITEM_MAX,
_gage2VecTable,
_gage2VecIv3Print,
_gage2VecFilter,
_gage2VecAnswer,
NULL, NULL, NULL, NULL,
NULL
};
194 gageKind *const
gageKind2Vec = &_gageKind2Vec;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
int
28 _gagePvlFlagCheck( gageContext *ctx, int pvlFlag ) {
int ret;
unsigned int pvlIdx;
ret = AIR_FALSE;
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
ret |= ctx->pvl[pvlIdx]->flag[pvlFlag];
}
return ret;
}
void
40 _gagePvlFlagDown( gageContext *ctx, int pvlFlag ) {
unsigned int pvlIdx;
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
ctx->pvl[pvlIdx]->flag[pvlFlag] = AIR_FALSE;
}
}
/*
** One could go from all the pvls' queries to the context's needD in
** one shot, but doing it in two steps ( as below ) seems a little clearer,
** and it means that pvl->needD isn't needlessly re-computed for
** pvl's whose query hasn't changed.
*/
/*
** for each pvl: pvl's query --> pvl's needD
*/
void
59 _gagePvlNeedDUpdate( gageContext *ctx ) {
static const char me[]="_gagePvlNeedDUpdate";
gagePerVolume *pvl;
int que, needD[GAGE_DERIV_MAX+1];
unsigned int pvlIdx, di;
if ( ctx->verbose ) fprintf( stderr, "%s: hello\n", me );
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
pvl = ctx->pvl[pvlIdx];
if ( pvl->flag[gagePvlFlagQuery] ) {
GAGE_DV_SET( needD, 0, 0, 0 );
que = pvl->kind->itemMax+1;
do {
que--;
if ( GAGE_QUERY_ITEM_TEST( pvl->query, que ) ) {
needD[pvl->kind->table[que].needDeriv] = 1;
}
} while ( que );
if ( !GAGE_DV_EQUAL( needD, pvl->needD ) ) {
if ( ctx->verbose ) {
fprintf( stderr, "%s: updating pvl[%d]'s needD to ( ", me, pvlIdx );
for ( di=0; di<=GAGE_DERIV_MAX; di++ ) {
fprintf( stderr, "%s%d", di ? ", " : "", needD[di] );
}
fprintf( stderr, "\n" );
}
GAGE_DV_COPY( pvl->needD, needD );
pvl->flag[gagePvlFlagNeedD] = AIR_TRUE;
}
}
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return;
}
/*
** all pvls' needD --> ctx's needD
*/
void
98 _gageNeedDUpdate( gageContext *ctx ) {
static const char me[]="_gageNeedDUpdate";
gagePerVolume *pvl;
int needD[GAGE_DERIV_MAX+1];
unsigned int pvlIdx, di;
if ( ctx->verbose ) fprintf( stderr, "%s: hello\n", me );
GAGE_DV_SET( needD, 0, 0, 0 );
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
pvl = ctx->pvl[pvlIdx];
for ( di=0; di<=GAGE_DERIV_MAX; di++ ) {
needD[di] |= pvl->needD[di];
}
}
if ( !GAGE_DV_EQUAL( needD, ctx->needD ) ) {
if ( ctx->verbose ) {
fprintf( stderr, "%s: updating ctx's needD to ( ", me );
for ( di=0; di<=GAGE_DERIV_MAX; di++ ) {
fprintf( stderr, "%s%d", di ? ", " : "", needD[di] );
}
fprintf( stderr, "\n" );
}
GAGE_DV_COPY( ctx->needD, needD );
ctx->flag[gageCtxFlagNeedD] = AIR_TRUE;
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return;
}
/*
** ctx's needD & k3pack --> needK
*/
static int
132 _gageNeedKUpdate( gageContext *ctx ) {
static const char me[]="_gageNeedKUpdate";
int kernIdx, needK[GAGE_KERNEL_MAX+1], change;
unsigned int di;
if ( ctx->verbose ) fprintf( stderr, "%s: hello\n", me );
for ( kernIdx=gageKernelUnknown+1; kernIdx<gageKernelLast; kernIdx++ ) {
needK[kernIdx] = AIR_FALSE;
}
if ( !ctx->parm.k3pack ) {
biffAddf( GAGE, "%s: sorry, only 3-pack filtering implemented now", me );
return 1;
}
di = 0;
if ( ctx->needD[di] ) {
needK[gageKernel00] = AIR_TRUE;
}
di += 1;
if ( ctx->needD[di] ) {
needK[gageKernel00] = needK[gageKernel11] = AIR_TRUE;
}
di += 1;
if ( ctx->needD[di] ) {
needK[gageKernel00] = needK[gageKernel11] =
needK[gageKernel22] = AIR_TRUE;
}
if ( GAGE_DERIV_MAX != di ) {
biffAddf( GAGE, "%s: sorry, code needs updating for GAGE_DERIV_MAX %u",
me, GAGE_DERIV_MAX );
return 1;
}
change = AIR_FALSE;
for ( kernIdx=gageKernelUnknown+1; kernIdx<gageKernelLast; kernIdx++ ) {
change |= ( needK[kernIdx] != ctx->needK[kernIdx] );
}
if ( change ) {
if ( ctx->verbose ) {
fprintf( stderr, "%s: changing needK to ( ", me );
for ( kernIdx=gageKernelUnknown+1; kernIdx<gageKernelLast; kernIdx++ ) {
fprintf( stderr, "%s%d", kernIdx > gageKernelUnknown+1 ? ", " : "",
needK[kernIdx] );
}
fprintf( stderr, " )\n" );
}
for ( kernIdx=gageKernelUnknown+1; kernIdx<gageKernelLast; kernIdx++ ) {
ctx->needK[kernIdx] = needK[kernIdx];
}
ctx->flag[gageCtxFlagNeedK] = AIR_TRUE;
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return 0;
}
/*
** ctx's ksp[] & needK --> radius
**
*/
int
191 _gageRadiusUpdate( gageContext *ctx ) {
static const char me[]="_gageRadiusUpdate";
unsigned int kernIdx, radius;
double maxRad, rad;
NrrdKernelSpec *ksp;
if ( ctx->verbose ) fprintf( stderr, "%s: hello\n", me );
maxRad = 0;
for ( kernIdx=gageKernelUnknown+1; kernIdx<gageKernelLast; kernIdx++ ) {
if ( ctx->needK[kernIdx] ) {
ksp = ctx->ksp[kernIdx];
if ( !ksp ) {
biffAddf( GAGE, "%s: need kernel %s but it hasn't been set",
me, airEnumStr( gageKernel, kernIdx ) );
return 1;
}
rad = ksp->kernel->support( ksp->parm );
maxRad = AIR_MAX( maxRad, rad );
if ( ctx->verbose ) {
fprintf( stderr, "%s: k[%s]=%s -> rad = %g -> maxRad = %g\n", me,
airEnumStr( gageKernel, kernIdx ), ksp->kernel->name,
rad, maxRad );
}
}
}
radius = AIR_ROUNDUP( maxRad );
/* In case either kernels have tiny supports ( less than 0.5 ), or if
we in fact don't need any kernels, then we need to do this to
ensure that we generate a valid radius */
radius = AIR_MAX( radius, 1 );
if ( ctx->parm.stackUse && ( nrrdKernelHermiteScaleSpaceFlag
== ctx->ksp[gageKernelStack]->kernel ) ) {
if ( ctx->verbose ) {
fprintf( stderr, "%s: hermite on stack: bumping radius %d --> %d\n",
me, radius, radius+1 );
}
radius += 1;
}
if ( radius != ctx->radius ) {
if ( ctx->verbose ) {
fprintf( stderr, "%s: changing radius from %d to %d\n",
me, ctx->radius, radius );
}
ctx->radius = radius;
ctx->flag[gageCtxFlagRadius] = AIR_TRUE;
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return 0;
}
int
243 _gageCacheSizeUpdate( gageContext *ctx ) {
static const char me[]="_gageCacheSizeUpdate";
int fd;
gagePerVolume *pvl;
unsigned int pvlIdx;
if ( ctx->verbose ) fprintf( stderr, "%s: hello ( radius = %d )\n",
me, ctx->radius );
if ( !( ctx->radius > 0 ) ) {
biffAddf( GAGE, "%s: have bad radius %d", me, ctx->radius );
return 1;
}
fd = 2*ctx->radius;
ctx->fsl = ( double * )airFree( ctx->fsl );
ctx->fw = ( double * )airFree( ctx->fw );
ctx->off = ( unsigned int * )airFree( ctx->off );
ctx->fsl = ( double * )calloc( fd*3, sizeof( double ) );
ctx->fw = ( double * )calloc( fd*3*( GAGE_KERNEL_MAX+1 ), sizeof( double ) );
ctx->off = ( unsigned int * )calloc( fd*fd*fd, sizeof( unsigned int ) );
if ( !( ctx->fsl && ctx->fw && ctx->off ) ) {
biffAddf( GAGE, "%s: couldn't allocate filter caches for fd=%d", me, fd );
return 1;
}
/* note that all pvls get caches allocated for the same size, even if
their queries don't involve the largest-size kernels. This is actually
the feature that enabled the stack functionality to be easily added. */
for ( pvlIdx=0; pvlIdx<ctx->pvlNum; pvlIdx++ ) {
pvl = ctx->pvl[pvlIdx];
pvl->iv3 = ( double * )airFree( pvl->iv3 );
pvl->iv2 = ( double * )airFree( pvl->iv2 );
pvl->iv1 = ( double * )airFree( pvl->iv1 );
pvl->iv3 = ( double * )calloc( fd*fd*fd*pvl->kind->valLen, sizeof( double ) );
pvl->iv2 = ( double * )calloc( fd*fd*pvl->kind->valLen, sizeof( double ) );
pvl->iv1 = ( double * )calloc( fd*pvl->kind->valLen, sizeof( double ) );
if ( !( pvl->iv3 && pvl->iv2 && pvl->iv1 ) ) {
biffAddf( GAGE, "%s: couldn't allocate pvl[%d]'s value caches for fd=%d",
me, pvlIdx, fd );
return 1;
}
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return 0;
}
void
289 _gageOffValueUpdate( gageContext *ctx ) {
static const char me[]="_gageOffValueUpdate";
int fd, i, j, k;
unsigned int sx, sy;
if ( ctx->verbose ) fprintf( stderr, "%s: hello\n", me );
sx = ctx->shape->size[0];
sy = ctx->shape->size[1];
fd = 2*ctx->radius;
/* HEY: look into special casing this for small fd */
for ( k=0; k<fd; k++ ) {
for ( j=0; j<fd; j++ ) {
for ( i=0; i<fd; i++ ) {
ctx->off[i + fd*( j + fd*k )] = i + sx*( j + sy*k );
}
}
}
/* no flags to set for further action */
if ( ctx->verbose ) fprintf( stderr, "%s: bye\n", me );
return;
}
/*
******** gageUpdate( )
**
** call just before probing begins.
*/
int
319 gageUpdate( gageContext *ctx ) {
static const char me[]="gageUpdate";
unsigned int pi;
int i, haveQuery;
if ( !( ctx ) ) {
biffAddf( GAGE, "%s: got NULL pointer", me );
return 1;
}
if ( 0 == ctx->pvlNum ) {
biffAddf( GAGE, "%s: context has no attached pervolumes", me );
return 1;
}
haveQuery = AIR_FALSE;
for ( pi=0; pi<ctx->pvlNum; pi++ ) {
haveQuery |= GAGE_QUERY_NONZERO( ctx->pvl[pi]->query );
}
if ( !haveQuery ) {
biffAddf( GAGE, "%s: no query item set in %s", me,
( ctx->pvlNum == 1
? "the pervolume"
: "any of the pervolumes" ) );
return 1;
}
/* HEY: shouldn't there be some more logic/state for this? */
if ( ctx->parm.stackUse ) {
if ( !ctx->ksp[gageKernelStack] ) {
biffAddf( GAGE, "%s: can't do stack without ksp[%s]", me,
airEnumStr( gageKernel, gageKernelStack ) );
return 1;
}
if ( !( 2 <= ctx->pvlNum ) ) {
biffAddf( GAGE, "%s: need at least 2 pervolumes for stack", me );
return 1;
}
for ( pi=1; pi<ctx->pvlNum; pi++ ) {
if ( !( ctx->pvl[0]->kind == ctx->pvl[pi]->kind ) ) {
biffAddf( GAGE, "%s: pvl[%u] kind ( %s ) != pvl[0] kind ( %s )", me,
pi, ctx->pvl[pi]->kind->name, ctx->pvl[0]->kind->name );
return 1;
}
}
}
/* start traversing the whole update graph . . . */
if ( ctx->verbose ) {
fprintf( stderr, "%s: hello ____________________ \n", me );
fprintf( stderr, " context flags:" );
for ( i=gageCtxFlagUnknown+1; i<gageCtxFlagLast; i++ ) {
fprintf( stderr, " %d=%d", i, ctx->flag[i] );
}
fprintf( stderr, "\n" );
fprintf( stderr, " pvl flags:" );
for ( i=gagePvlFlagUnknown+1; i<gagePvlFlagLast; i++ ) {
fprintf( stderr, " %d=%d", i, _gagePvlFlagCheck( ctx, i ) );
}
fprintf( stderr, "\n" );
}
if ( _gagePvlFlagCheck( ctx, gagePvlFlagQuery ) ) {
_gagePvlNeedDUpdate( ctx );
_gagePvlFlagDown( ctx, gagePvlFlagQuery );
}
if ( _gagePvlFlagCheck( ctx, gagePvlFlagNeedD ) ) {
_gageNeedDUpdate( ctx );
_gagePvlFlagDown( ctx, gagePvlFlagNeedD );
}
if ( ctx->flag[gageCtxFlagNeedD] || ctx->flag[gageCtxFlagK3Pack] ) {
if ( _gageNeedKUpdate( ctx ) ) {
biffAddf( GAGE, "%s: trouble", me ); return 1;
}
ctx->flag[gageCtxFlagNeedD] = AIR_FALSE;
ctx->flag[gageCtxFlagK3Pack] = AIR_FALSE;
}
if ( ctx->flag[gageCtxFlagKernel] || ctx->flag[gageCtxFlagNeedK] ) {
if ( _gageRadiusUpdate( ctx ) ) {
biffAddf( GAGE, "%s: trouble", me ); return 1;
}
ctx->flag[gageCtxFlagKernel] = AIR_FALSE;
ctx->flag[gageCtxFlagNeedK] = AIR_FALSE;
}
if ( ctx->flag[gageCtxFlagRadius]
/* HEY HEY HEY: this is a total hack: right now its possible for a
new pvl to have unallocated iv3, iv2, iv1, if it was attached to a
context which had already been probing, as was the case with
_tenRegisterDoit. So, with this hack we reallocate ALL caches
just because a new pervolume was attached . . . */
|| _gagePvlFlagCheck( ctx, gagePvlFlagVolume ) ) {
if ( _gageCacheSizeUpdate( ctx ) ) {
biffAddf( GAGE, "%s: trouble", me ); return 1;
}
}
if ( ctx->flag[gageCtxFlagRadius]
|| ctx->flag[gageCtxFlagShape]
/* see above; following flags that triggered _gageCacheSizeUpdate( ctx ) */
|| _gagePvlFlagCheck( ctx, gagePvlFlagVolume ) ) {
_gageOffValueUpdate( ctx );
ctx->flag[gageCtxFlagShape] = AIR_FALSE;
}
ctx->flag[gageCtxFlagRadius] = AIR_FALSE;
/* chances are, something above has invalidated the state maintained
during successive calls to gageProbe( ) */
gagePointReset( &ctx->point );
for ( pi=0; pi<ctx->pvlNum; pi++ ) {
if ( ctx->pvl[pi]->kind->pvlDataUpdate ) {
if ( ctx->pvl[pi]->kind->pvlDataUpdate( ctx->pvl[pi]->kind,
ctx,
ctx->pvl[pi],
ctx->pvl[pi]->data ) ) {
biffAddf( GAGE, "%s: pvlDataUpdate( pvl[%u] ) failed", me, pi );
return 1;
}
}
}
if ( ctx->verbose > 3 && ctx->stackPos ) {
fprintf( stderr, "%s: pvlNum = %u -> stack of %u [0, %u]\n", me,
ctx->pvlNum, ctx->pvlNum-1, ctx->pvlNum-2 );
for ( pi=0; pi<ctx->pvlNum-1; pi++ ) {
fprintf( stderr, "%s: stackPos[%u] = %g\n", me, pi, ctx->stackPos[pi] );
}
}
if ( ctx->verbose ) fprintf( stderr, "%s: bye ^^^^^^^^^^^^^^^^^^^ \n", me );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
/*
* highly inefficient computation of the imaginary part of complex
* conjugate eigenvalues of a 3x3 non-symmetric matrix
*/
double
32 gage_imaginary_part_eigenvalues( double *M ) {
double A, B, C, scale, frob, m[9], _eval[3];
double beta, _gamma;
int roots;
frob = ELL_3M_FROB( M );
scale = frob > 10 ? 10.0/frob : 1.0;
ELL_3M_SCALE( m, scale, M );
/*
** from gordon with mathematica; these are the coefficients of the
** cubic polynomial in x: det( x*I - M ). The full cubic is
** x^3 + A*x^2 + B*x + C.
*/
A = -m[0] - m[4] - m[8];
B = m[0]*m[4] - m[3]*m[1]
+ m[0]*m[8] - m[6]*m[2]
+ m[4]*m[8] - m[7]*m[5];
C = ( m[6]*m[4] - m[3]*m[7] )*m[2]
+ ( m[0]*m[7] - m[6]*m[1] )*m[5]
+ ( m[3]*m[1] - m[0]*m[4] )*m[8];
roots = ell_cubic( _eval, A, B, C, AIR_TRUE );
if ( roots != ell_cubic_root_single )
return 0.;
/* 2 complex conjuguate eigenvalues */
beta = A + _eval[0];
_gamma = -C/_eval[0];
return sqrt( 4.*_gamma - beta*beta );
}
gageItemEntry
_gageVecTable[GAGE_VEC_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, parent index, needData */
{gageVecUnknown, 0, 0, {0}, 0, 0, AIR_FALSE},
{gageVecVector, 3, 0, {0}, 0, 0, AIR_FALSE},
{gageVecVector0, 1, 0, {gageVecVector}, gageVecVector, 0, AIR_FALSE},
{gageVecVector1, 1, 0, {gageVecVector}, gageVecVector, 1, AIR_FALSE},
{gageVecVector2, 1, 0, {gageVecVector}, gageVecVector, 2, AIR_FALSE},
{gageVecLength, 1, 0, {gageVecVector}, 0, 0, AIR_FALSE},
{gageVecNormalized, 3, 0, {gageVecVector, gageVecLength}, 0, 0, AIR_FALSE},
{gageVecJacobian, 9, 1, {0}, 0, 0, AIR_FALSE},
{gageVecStrain, 9, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecDivergence, 1, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecCurl, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecCurlNorm, 1, 1, {gageVecCurl}, 0, 0, AIR_FALSE},
{gageVecHelicity, 1, 1, {gageVecVector, gageVecCurl}, 0, 0, AIR_FALSE},
{gageVecNormHelicity, 1, 1, {gageVecNormalized, gageVecCurl}, 0, 0, AIR_FALSE},
{gageVecSOmega, 9, 1, {gageVecJacobian, gageVecStrain}, 0, 0, AIR_FALSE},
{gageVecLambda2, 1, 1, {gageVecSOmega}, 0, 0, AIR_FALSE},
{gageVecImaginaryPart, 1, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecHessian, 27, 2, {0}, 0, 0, AIR_FALSE},
{gageVecDivGradient, 3, 1, {gageVecHessian}, 0, 0, AIR_FALSE},
{gageVecCurlGradient, 9, 2, {gageVecHessian}, 0, 0, AIR_FALSE},
{gageVecCurlNormGrad, 3, 2, {gageVecHessian, gageVecCurl}, 0, 0, AIR_FALSE},
{gageVecNCurlNormGrad, 3, 2, {gageVecCurlNormGrad}, 0, 0, AIR_FALSE},
{gageVecHelGradient, 3, 2, {gageVecVector, gageVecJacobian, gageVecCurl, gageVecCurlGradient}, 0, 0, AIR_FALSE},
{gageVecDirHelDeriv, 1, 2, {gageVecNormalized, gageVecHelGradient}, 0, 0, AIR_FALSE},
{gageVecProjHelGradient, 3, 2, {gageVecNormalized, gageVecHelGradient, gageVecDirHelDeriv}, 0, 0, AIR_FALSE},
/* HEY: these should change to sub-items!!! */
{gageVecGradient0, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecGradient1, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecGradient2, 3, 1, {gageVecJacobian}, 0, 0, AIR_FALSE},
{gageVecMultiGrad, 9, 1, {gageVecGradient0, gageVecGradient1, gageVecGradient2}, 0, 0, AIR_FALSE},
{gageVecMGFrob, 1, 1, {gageVecMultiGrad}, 0, 0, AIR_FALSE},
{gageVecMGEval, 3, 1, {gageVecMultiGrad}, 0, 0, AIR_FALSE},
{gageVecMGEvec, 9, 1, {gageVecMultiGrad, gageVecMGEval}, 0, 0, AIR_FALSE}
};
void
102 _gageVecFilter( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_gageVecFilter";
double *fw00, *fw11, *fw22, *vec, *jac, *hes;
int fd;
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4,
gageScl3PFilter6, gageScl3PFilter8};
unsigned int valIdx;
fd = 2*ctx->radius;
vec = pvl->directAnswer[gageVecVector];
jac = pvl->directAnswer[gageVecJacobian];
hes = pvl->directAnswer[gageVecHessian];
if ( !ctx->parm.k3pack ) {
fprintf( stderr, "!%s: sorry, 6pack filtering not implemented\n", me );
return;
}
fw00 = ctx->fw + fd*3*gageKernel00;
fw11 = ctx->fw + fd*3*gageKernel11;
fw22 = ctx->fw + fd*3*gageKernel22;
/* perform the filtering */
if ( fd <= 8 ) {
for ( valIdx=0; valIdx<3; valIdx++ ) {
filter[ctx->radius]( ctx->shape,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
vec + valIdx, jac + valIdx*3, hes + valIdx*9,
pvl->needD );
}
} else {
for ( valIdx=0; valIdx<3; valIdx++ ) {
gageScl3PFilterN( ctx->shape, fd,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
vec + valIdx, jac + valIdx*3, hes + valIdx*9,
pvl->needD );
}
}
return;
}
void
148 _gageVecAnswer( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_gageVecAnswer";
double cmag, tmpMat[9], mgevec[9], mgeval[3];
double asym[9], tran[9], eval[3], tmpVec[3], norm;
double *vecAns, *normAns, *jacAns, *strainAns, *somegaAns,
*curlAns, *hesAns, *curlGradAns,
*helGradAns, *dirHelDirAns, *curlnormgradAns;
/* int asw; */
vecAns = pvl->directAnswer[gageVecVector];
normAns = pvl->directAnswer[gageVecNormalized];
jacAns = pvl->directAnswer[gageVecJacobian];
strainAns = pvl->directAnswer[gageVecStrain];
somegaAns = pvl->directAnswer[gageVecSOmega];
curlAns = pvl->directAnswer[gageVecCurl];
hesAns = pvl->directAnswer[gageVecHessian];
curlGradAns = pvl->directAnswer[gageVecCurlGradient];
curlnormgradAns = pvl->directAnswer[gageVecCurlNormGrad];
helGradAns = pvl->directAnswer[gageVecHelGradient];
dirHelDirAns = pvl->directAnswer[gageVecDirHelDeriv];
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecVector ) ) {
/* done if doV */
if ( ctx->verbose ) {
fprintf( stderr, "vec = " );
ell_3v_print_d( stderr, vecAns );
}
}
/* done if doV
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecVector{0, 1, 2} ) ) {
}
*/
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecLength ) ) {
pvl->directAnswer[gageVecLength][0] = ELL_3V_LEN( vecAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecNormalized ) ) {
if ( pvl->directAnswer[gageVecLength][0] ) {
ELL_3V_SCALE( normAns, 1.0/pvl->directAnswer[gageVecLength][0], vecAns );
} else {
ELL_3V_COPY( normAns, gageZeroNormal );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecJacobian ) ) {
/* done if doD1 */
/*
0:dv_x/dx 1:dv_x/dy 2:dv_x/dz
3:dv_y/dx 4:dv_y/dy 5:dv_y/dz
6:dv_z/dx 7:dv_z/dy 8:dv_z/dz
*/
if ( ctx->verbose ) {
fprintf( stderr, "%s: jac = \n", me );
ell_3m_print_d( stderr, jacAns );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecDivergence ) ) {
pvl->directAnswer[gageVecDivergence][0] = jacAns[0] + jacAns[4] + jacAns[8];
if ( ctx->verbose ) {
fprintf( stderr, "%s: div = %g + %g + %g = %g\n", me,
jacAns[0], jacAns[4], jacAns[8],
pvl->directAnswer[gageVecDivergence][0] );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecCurl ) ) {
ELL_3V_SET( curlAns,
jacAns[7] - jacAns[5],
jacAns[2] - jacAns[6],
jacAns[3] - jacAns[1] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecCurlNorm ) ) {
pvl->directAnswer[gageVecCurlNorm][0] = ELL_3V_LEN( curlAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecHelicity ) ) {
pvl->directAnswer[gageVecHelicity][0] =
ELL_3V_DOT( vecAns, curlAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecNormHelicity ) ) {
cmag = ELL_3V_LEN( curlAns );
pvl->directAnswer[gageVecNormHelicity][0] =
cmag ? ELL_3V_DOT( normAns, curlAns )/cmag : 0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecStrain ) ) {
ELL_3M_TRANSPOSE( tran, jacAns );
/* symmetric part */
ELL_3M_SCALE_ADD2( strainAns, 0.5, jacAns, 0.5, tran );
/* nested these ifs to make dependency explicit to the compiler */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecSOmega ) ) {
/* antisymmetric part */
ELL_3M_SCALE_ADD2( asym, 0.5, jacAns, -0.5, tran );
/* square symmetric part */
ELL_3M_MUL( tmpMat, strainAns, strainAns );
ELL_3M_COPY( somegaAns, tmpMat );
/* square antisymmetric part */
ELL_3M_MUL( tmpMat, asym, asym );
/* sum of both */
ELL_3M_ADD2( somegaAns, somegaAns, tmpMat );
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecLambda2 ) ) {
/* get eigenvalues in sorted order */
/* asw = */ ell_3m_eigenvalues_d( eval, somegaAns, AIR_TRUE );
pvl->directAnswer[gageVecLambda2][0] = eval[1];
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecImaginaryPart ) ) {
pvl->directAnswer[gageVecImaginaryPart][0] =
gage_imaginary_part_eigenvalues( jacAns );
}
/* 2nd order vector derivative continued */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecHessian ) ) {
/* done if doD2 */
/* the ordering is induced by the scalar hessian computation :
0:d2v_x/dxdx 1:d2v_x/dxdy 2:d2v_x/dxdz
3:d2v_x/dydx 4:d2v_x/dydy 5:d2v_x/dydz
6:d2v_x/dzdx 7:d2v_x/dzdy 8:d2v_x/dzdz
9:d2v_y/dxdx [. . .]
[. . .]
24:dv2_z/dzdx 25:d2v_z/dzdy 26:d2v_z/dzdz
*/
if ( ctx->verbose ) {
fprintf( stderr, "%s: hes = \n", me );
ell_3m_print_d( stderr, hesAns ); /* ?? */
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecDivGradient ) ) {
pvl->directAnswer[gageVecDivGradient][0] = hesAns[0] + hesAns[12] + hesAns[24];
pvl->directAnswer[gageVecDivGradient][1] = hesAns[1] + hesAns[13] + hesAns[25];
pvl->directAnswer[gageVecDivGradient][2] = hesAns[2] + hesAns[14] + hesAns[26];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecCurlGradient ) ) {
pvl->directAnswer[gageVecCurlGradient][0] = hesAns[21]-hesAns[15];
pvl->directAnswer[gageVecCurlGradient][1] = hesAns[22]-hesAns[16];
pvl->directAnswer[gageVecCurlGradient][2] = hesAns[23]-hesAns[17];
pvl->directAnswer[gageVecCurlGradient][3] = hesAns[ 6]-hesAns[18];
pvl->directAnswer[gageVecCurlGradient][4] = hesAns[ 7]-hesAns[19];
pvl->directAnswer[gageVecCurlGradient][5] = hesAns[ 8]-hesAns[20];
pvl->directAnswer[gageVecCurlGradient][6] = hesAns[ 9]-hesAns[ 1];
pvl->directAnswer[gageVecCurlGradient][7] = hesAns[10]-hesAns[ 2];
pvl->directAnswer[gageVecCurlGradient][8] = hesAns[11]-hesAns[ 3];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecCurlNormGrad ) ) {
norm = 1./ELL_3V_LEN( curlAns );
tmpVec[0] = hesAns[21] - hesAns[15];
tmpVec[1] = hesAns[ 6] - hesAns[18];
tmpVec[2] = hesAns[ 9] - hesAns[ 3];
pvl->directAnswer[gageVecCurlNormGrad][0] =
norm*ELL_3V_DOT( tmpVec, curlAns );
tmpVec[0] = hesAns[22] - hesAns[16];
tmpVec[1] = hesAns[ 7] - hesAns[19];
tmpVec[2] = hesAns[10] - hesAns[ 4];
pvl->directAnswer[gageVecCurlNormGrad][1]=
norm*ELL_3V_DOT( tmpVec, curlAns );
tmpVec[0] = hesAns[23] - hesAns[17];
tmpVec[1] = hesAns[ 8] - hesAns[20];
tmpVec[2] = hesAns[11] - hesAns[ 5];
pvl->directAnswer[gageVecCurlNormGrad][2]=
norm*ELL_3V_DOT( tmpVec, curlAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecNCurlNormGrad ) ) {
norm = 1./ELL_3V_LEN( curlnormgradAns );
ELL_3V_SCALE( pvl->directAnswer[gageVecNCurlNormGrad],
norm, pvl->directAnswer[gageVecCurlNormGrad] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecHelGradient ) ) {
pvl->directAnswer[gageVecHelGradient][0] =
jacAns[0]*curlAns[0]+
jacAns[3]*curlAns[1]+
jacAns[6]*curlAns[2]+
curlGradAns[0]*vecAns[0]+
curlGradAns[3]*vecAns[1]+
curlGradAns[6]*vecAns[2];
pvl->directAnswer[gageVecHelGradient][1] =
jacAns[1]*curlAns[0]+
jacAns[4]*curlAns[1]+
jacAns[7]*curlAns[2]+
curlGradAns[1]*vecAns[0]+
curlGradAns[4]*vecAns[1]+
curlGradAns[7]*vecAns[2];
pvl->directAnswer[gageVecHelGradient][0] =
jacAns[2]*curlAns[0]+
jacAns[5]*curlAns[1]+
jacAns[8]*curlAns[2]+
curlGradAns[2]*vecAns[0]+
curlGradAns[5]*vecAns[1]+
curlGradAns[8]*vecAns[2];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecDirHelDeriv ) ) {
pvl->directAnswer[gageVecDirHelDeriv][0] =
ELL_3V_DOT( normAns, helGradAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecProjHelGradient ) ) {
pvl->directAnswer[gageVecDirHelDeriv][0] =
helGradAns[0]-dirHelDirAns[0]*normAns[0];
pvl->directAnswer[gageVecDirHelDeriv][1] =
helGradAns[1]-dirHelDirAns[0]*normAns[1];
pvl->directAnswer[gageVecDirHelDeriv][2] =
helGradAns[2]-dirHelDirAns[0]*normAns[2];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecGradient0 ) ) {
ELL_3V_SET( pvl->directAnswer[gageVecGradient0],
jacAns[0],
jacAns[1],
jacAns[2] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecGradient1 ) ) {
ELL_3V_SET( pvl->directAnswer[gageVecGradient1],
jacAns[3],
jacAns[4],
jacAns[5] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecGradient2 ) ) {
ELL_3V_SET( pvl->directAnswer[gageVecGradient2],
jacAns[6],
jacAns[7],
jacAns[8] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecMultiGrad ) ) {
ELL_3M_IDENTITY_SET( pvl->directAnswer[gageVecMultiGrad] );
ELL_3MV_OUTER_INCR( pvl->directAnswer[gageVecMultiGrad],
pvl->directAnswer[gageVecGradient0],
pvl->directAnswer[gageVecGradient0] );
ELL_3MV_OUTER_INCR( pvl->directAnswer[gageVecMultiGrad],
pvl->directAnswer[gageVecGradient1],
pvl->directAnswer[gageVecGradient1] );
ELL_3MV_OUTER_INCR( pvl->directAnswer[gageVecMultiGrad],
pvl->directAnswer[gageVecGradient2],
pvl->directAnswer[gageVecGradient2] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecMGFrob ) ) {
pvl->directAnswer[gageVecMGFrob][0]
= ELL_3M_FROB( pvl->directAnswer[gageVecMultiGrad] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecMGEval ) ) {
ELL_3M_COPY( tmpMat, pvl->directAnswer[gageVecMultiGrad] );
/* HEY: look at the return value for root multiplicity? */
ell_3m_eigensolve_d( mgeval, mgevec, tmpMat, AIR_TRUE );
ELL_3V_COPY( pvl->directAnswer[gageVecMGEval], mgeval );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, gageVecMGEvec ) ) {
ELL_3M_COPY( pvl->directAnswer[gageVecMGEvec], mgevec );
}
return;
}
const char *
_gageVecStr[] = {
"( unknown gageVec )",
"vector",
"vector0",
"vector1",
"vector2",
"length",
"normalized",
"Jacobian",
"strain",
"divergence",
"curl",
"curl norm",
"helicity",
"normalized helicity",
"SOmega",
"lambda2",
"imag",
"vector hessian",
"div gradient",
"curl gradient",
"curl norm gradient",
"normalized curl norm gradient",
"helicity gradient",
"directional helicity derivative",
"projected helicity gradient",
"gradient0",
"gradient1",
"gradient2",
"multigrad",
"frob( multigrad )",
"multigrad eigenvalues",
"multigrad eigenvectors",
};
const char *
_gageVecDesc[] = {
"unknown gageVec query",
"component-wise-interpolated vector",
"vector[0]",
"vector[1]",
"vector[2]",
"length of vector",
"normalized vector",
"3x3 Jacobian",
"rate-of-strain tensor",
"divergence",
"curl",
"curl magnitude",
"helicity: dot( vector, curl )",
"normalized helicity",
"S squared plus Omega squared for vortex characterization",
"lambda2 value of SOmega",
"imaginary part of complex-conjugate eigenvalues of Jacobian",
"3x3x3 second-order vector derivative",
"gradient of divergence",
"3x3 derivative of curl",
"gradient of curl norm",
"normalized gradient of curl norm",
"gradient of helicity",
"directional derivative of helicity along flow",
"projection of the helicity gradient onto plane orthogonal to flow",
"gradient of 1st component of vector",
"gradient of 2nd component of vector",
"gradient of 3rd component of vector",
"multi-gradient: sum of outer products of gradients",
"frob norm of multi-gradient",
"eigenvalues of multi-gradient",
"eigenvectors of multi-gradient"
};
const int
_gageVecVal[] = {
gageVecUnknown,
gageVecVector,
gageVecVector0,
gageVecVector1,
gageVecVector2,
gageVecLength,
gageVecNormalized,
gageVecJacobian,
gageVecStrain,
gageVecDivergence,
gageVecCurl,
gageVecCurlNorm,
gageVecHelicity,
gageVecNormHelicity,
gageVecSOmega,
gageVecLambda2,
gageVecImaginaryPart,
gageVecHessian,
gageVecDivGradient,
gageVecCurlGradient,
gageVecCurlNormGrad,
gageVecNCurlNormGrad,
gageVecHelGradient,
gageVecDirHelDeriv,
gageVecProjHelGradient,
gageVecGradient0,
gageVecGradient1,
gageVecGradient2,
gageVecMultiGrad,
gageVecMGFrob,
gageVecMGEval,
gageVecMGEvec,
};
#define GV_V gageVecVector
#define GV_V0 gageVecVector0
#define GV_V1 gageVecVector1
#define GV_V2 gageVecVector2
#define GV_L gageVecLength
#define GV_N gageVecNormalized
#define GV_J gageVecJacobian
#define GV_S gageVecStrain
#define GV_D gageVecDivergence
#define GV_C gageVecCurl
#define GV_CM gageVecCurlNorm
#define GV_H gageVecHelicity
#define GV_NH gageVecNormHelicity
#define GV_SO gageVecSOmega
#define GV_LB gageVecLambda2
#define GV_IM gageVecImaginaryPart
#define GV_VH gageVecHessian
#define GV_DG gageVecDivGradient
#define GV_CG gageVecCurlGradient
#define GV_CNG gageVecCurlNormGrad
#define GV_NCG gageVecNCurlNormGrad
#define GV_HG gageVecHelGradient
#define GV_DH gageVecDirHelDeriv
#define GV_PH gageVecProjHelGradient
#define GV_G0 gageVecGradient0
#define GV_G1 gageVecGradient1
#define GV_G2 gageVecGradient2
#define GV_MG gageVecMultiGrad
#define GV_MF gageVecMGFrob
#define GV_ML gageVecMGEval
#define GV_MC gageVecMGEvec
const char *
_gageVecStrEqv[] = {
"v", "vector", "vec",
"v0", "vector0", "vec0",
"v1", "vector1", "vec1",
"v2", "vector2", "vec2",
"l", "length", "len",
"n", "normalized", "normalized vector",
"jacobian", "jac", "j",
"strain", "S",
"divergence", "div", "d",
"curl", "c",
"curlnorm", "curl norm", "curl magnitude", "cm",
"h", "hel", "hell", "helicity",
"nh", "nhel", "normhel", "normhell", "normalized helicity",
"SOmega",
"lbda2", "lambda2",
"imag", "imagpart",
"vh", "vhes", "vhessian", "vector hessian",
"dg", "divgrad", "div gradient",
"cg", "curlgrad", "curlg", "curljac", "curl gradient",
"cng", "curl norm gradient",
"ncng", "norm curl norm gradient", "normalized curl norm gradient",
"hg", "helg", "helgrad", "helicity gradient",
"dirhelderiv", "dhd", "ddh", "directional helicity derivative",
"phg", "projhel", "projhelgrad", "projected helicity gradient",
"g0", "grad0", "gradient0",
"g1", "grad1", "gradient1",
"g2", "grad2", "gradient2",
"mg", "multigrad",
"mgfrob", "frob( multigrad )",
"mgeval", "mg eval", "multigrad eigenvalues",
"mgevec", "mg evec", "multigrad eigenvectors",
""
};
const int
_gageVecValEqv[] = {
GV_V, GV_V, GV_V,
GV_V0, GV_V0, GV_V0,
GV_V1, GV_V1, GV_V1,
GV_V2, GV_V2, GV_V2,
GV_L, GV_L, GV_L,
GV_N, GV_N, GV_N,
GV_J, GV_J, GV_J,
GV_S, GV_S,
GV_D, GV_D, GV_D,
GV_C, GV_C,
GV_CM, GV_CM, GV_CM, GV_CM,
GV_H, GV_H, GV_H, GV_H,
GV_NH, GV_NH, GV_NH, GV_NH, GV_NH,
GV_SO,
GV_LB, GV_LB,
GV_IM, GV_IM,
GV_VH, GV_VH, GV_VH, GV_VH,
GV_DG, GV_DG, GV_DG,
GV_CG, GV_CG, GV_CG, GV_CG, GV_CG,
GV_CNG, GV_CNG,
GV_NCG, GV_NCG, GV_NCG,
GV_HG, GV_HG, GV_HG, GV_HG,
GV_DH, GV_DH, GV_DH, GV_DH,
GV_PH, GV_PH, GV_PH, GV_PH,
GV_G0, GV_G0, GV_G0,
GV_G1, GV_G1, GV_G1,
GV_G2, GV_G2, GV_G2,
GV_MG, GV_MG,
GV_MF, GV_MF,
GV_ML, GV_ML, GV_ML,
GV_MC, GV_MC, GV_MC
};
const airEnum
_gageVec = {
"gageVec",
GAGE_VEC_ITEM_MAX,
_gageVecStr, _gageVecVal,
_gageVecDesc,
_gageVecStrEqv, _gageVecValEqv,
AIR_FALSE
};
614 const airEnum *const
gageVec = &_gageVec;
gageKind
_gageKindVec = {
AIR_FALSE, /* statically allocated */
"vector",
&_gageVec,
1, /* baseDim */
3, /* valLen */
GAGE_VEC_ITEM_MAX,
_gageVecTable,
_gageVecIv3Print,
_gageVecFilter,
_gageVecAnswer,
NULL, NULL, NULL, NULL,
NULL
};
632 gageKind *const
gageKindVec = &_gageKindVec;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "gage.h"
#include "privateGage.h"
void
28 _gageVecIv3Print ( FILE *file, gageContext *ctx, gagePerVolume *pvl ) {
AIR_UNUSED( ctx );
AIR_UNUSED( pvl );
fprintf( file, "_gageVecIv3Print( ) not implemented\n" );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hest.h"
#include "privateHest.h"
int hestVerbosity = 0;
int hestRespFileEnable = AIR_FALSE;
unsigned int hestColumns = 79;
int hestElideSingleEnumType = AIR_FALSE;
int hestElideSingleOtherType = AIR_FALSE;
int hestElideSingleOtherDefault = AIR_FALSE;
int hestElideSingleNonExistFloatDefault = AIR_FALSE;
int hestElideMultipleNonExistFloatDefault = AIR_FALSE;
int hestElideSingleEmptyStringDefault = AIR_FALSE;
int hestElideMultipleEmptyStringDefault = AIR_FALSE;
int hestNoArgsIsNoProblem = AIR_FALSE;
int hestGreedySingleString = AIR_TRUE;
int hestCleverPluralizeOtherY = AIR_FALSE;
char hestRespFileFlag = '@';
char hestRespFileComment = '#';
char hestVarParamStopFlag = '-';
char hestMultiFlagSep = ', ';
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hest.h"
#include "privateHest.h"
#include <limits.h>
const int
hestPresent = 42;
hestParm *
32 hestParmNew( ) {
hestParm *parm;
parm = AIR_CALLOC( 1, hestParm );
if ( parm ) {
parm->verbosity = hestVerbosity;
parm->respFileEnable = hestRespFileEnable;
parm->elideSingleEnumType = hestElideSingleEnumType;
parm->elideSingleOtherType = hestElideSingleOtherType;
parm->elideSingleOtherDefault = hestElideSingleOtherDefault;
parm->greedySingleString = hestGreedySingleString;
parm->elideSingleNonExistFloatDefault =
hestElideSingleNonExistFloatDefault;
parm->elideMultipleNonExistFloatDefault =
hestElideMultipleNonExistFloatDefault;
parm->elideSingleEmptyStringDefault =
hestElideSingleEmptyStringDefault;
parm->elideMultipleEmptyStringDefault =
hestElideMultipleEmptyStringDefault;
parm->cleverPluralizeOtherY = hestCleverPluralizeOtherY;
parm->columns = hestColumns;
parm->respFileFlag = hestRespFileFlag;
parm->respFileComment = hestRespFileComment;
parm->varParamStopFlag = hestVarParamStopFlag;
parm->multiFlagSep = hestMultiFlagSep;
}
return parm;
}
hestParm *
62 hestParmFree( hestParm *parm ) {
airFree( parm );
return NULL;
}
void
69 _hestOptInit( hestOpt *opt ) {
opt->flag = opt->name = NULL;
opt->type = opt->min = opt->max = 0;
opt->valueP = NULL;
opt->dflt = opt->info = NULL;
opt->sawP = NULL;
opt->enm = NULL;
opt->CB = NULL;
opt->sawP = NULL;
opt->kind = opt->alloc = 0;
opt->source = hestSourceUnknown;
}
/*
hestOpt *
hestOptNew( void ) {
hestOpt *opt;
opt = AIR_CALLOC( 1, hestOpt );
if ( opt ) {
_hestOptInit( opt );
opt->min = 1;
}
return opt;
}
*/
/*
** as of Sept 2013 this returns information: the index of the
** option just added. Returns UINT_MAX in case of error.
*/
unsigned int
102 hestOptAdd( hestOpt **optP,
const char *flag, const char *name,
int type, int min, int max,
void *valueP, const char *dflt, const char *info, ... ) {
hestOpt *ret = NULL;
int num;
va_list ap;
unsigned int retIdx;
if ( !optP )
return UINT_MAX;
num = *optP ? _hestNumOpts( *optP ) : 0;
if ( !( ret = AIR_CALLOC( num+2, hestOpt ) ) ) {
return UINT_MAX;
}
if ( num )
memcpy( ret, *optP, num*sizeof( hestOpt ) );
retIdx = AIR_UINT( num );
ret[num].flag = airStrdup( flag );
ret[num].name = airStrdup( name );
ret[num].type = type;
ret[num].min = min;
ret[num].max = max;
ret[num].valueP = valueP;
ret[num].dflt = airStrdup( dflt );
ret[num].info = airStrdup( info );
/* initialize the things that may be set below */
ret[num].sawP = NULL;
ret[num].enm = NULL;
ret[num].CB = NULL;
/* seems to be redundant with above _hestOptInit( ) */
ret[num].source = hestSourceUnknown;
/* deal with var args */
if ( 5 == _hestKind( &( ret[num] ) ) ) {
va_start( ap, info );
ret[num].sawP = va_arg( ap, unsigned int* );
va_end( ap );
}
if ( airTypeEnum == type ) {
va_start( ap, info );
va_arg( ap, unsigned int* ); /* skip sawP */
ret[num].enm = va_arg( ap, airEnum* );
va_end( ap );
}
if ( airTypeOther == type ) {
va_start( ap, info );
va_arg( ap, unsigned int* ); /* skip sawP */
va_arg( ap, airEnum* ); /* skip enm */
ret[num].CB = va_arg( ap, hestCB* );
va_end( ap );
}
_hestOptInit( &( ret[num+1] ) );
ret[num+1].min = 1;
if ( *optP )
free( *optP );
*optP = ret;
return retIdx;
}
void
163 _hestOptFree( hestOpt *opt ) {
opt->flag = ( char * )airFree( opt->flag );
opt->name = ( char * )airFree( opt->name );
opt->dflt = ( char * )airFree( opt->dflt );
opt->info = ( char * )airFree( opt->info );
return;
}
hestOpt *
173 hestOptFree( hestOpt *opt ) {
int op, num;
if ( !opt )
return NULL;
num = _hestNumOpts( opt );
if ( opt[num].min ) {
/* we only try to free this if it looks like something we allocated */
for ( op=0; op<num; op++ ) {
_hestOptFree( opt+op );
}
free( opt );
}
return NULL;
}
int
191 hestOptCheck( hestOpt *opt, char **errP ) {
char *err, me[]="hestOptCheck";
hestParm *parm;
int big;
big = _hestErrStrlen( opt, 0, NULL );
if ( !( err = AIR_CALLOC( big, char ) ) ) {
fprintf( stderr, "%s PANIC: couldn't allocate error message "
"buffer ( size %d )\n", me, big );
if ( errP )
*errP = NULL;
return 1;
}
parm = hestParmNew( );
if ( _hestPanic( opt, err, parm ) ) {
/* problems */
if ( errP ) {
/* they did give a pointer address; they'll free it */
*errP = err;
}
else {
/* they didn't give a pointer address; their loss */
free( err );
}
hestParmFree( parm );
return 1;
}
/* else, no problems */
if ( errP )
*errP = NULL;
free( err );
hestParmFree( parm );
return 0;
}
/*
** _hestIdent( )
**
** how to identify an option in error and usage messages
*/
char *
233 _hestIdent( char *ident, hestOpt *opt, const hestParm *parm, int brief ) {
char copy[AIR_STRLEN_HUGE], *sep;
if ( opt->flag && ( sep = strchr( opt->flag, parm->multiFlagSep ) ) ) {
strcpy( copy, opt->flag );
sep = strchr( copy, parm->multiFlagSep );
*sep = '\0';
if ( brief )
sprintf( ident, "-%s%c--%s option", copy, parm->multiFlagSep, sep+1 );
else
sprintf( ident, "-%s option", copy );
}
else {
sprintf( ident, "%s%s%s option",
opt->flag ? "\"-" : "<",
opt->flag ? opt->flag : opt->name,
opt->flag ? "\"" : ">" );
}
return ident;
}
int
255 _hestMax( int max ) {
if ( -1 == max ) {
max = INT_MAX;
}
return max;
}
int
264 _hestKind( const hestOpt *opt ) {
int max;
max = _hestMax( opt->max );
if ( !( ( int )opt->min <= max ) ) { /* HEY scrutinize casts */
/* invalid */
return -1;
}
if ( 0 == opt->min && 0 == max ) {
/* flag */
return 1;
}
if ( 1 == opt->min && 1 == max ) {
/* single fixed parameter */
return 2;
}
if ( 2 <= opt->min && 2 <= max && ( int )opt->min == max ) { /* HEY scrutinize casts */
/* multiple fixed parameters */
return 3;
}
if ( 0 == opt->min && 1 == max ) {
/* single optional parameter */
return 4;
}
/* else multiple variable parameters */
return 5;
}
void
298 _hestPrintArgv( int argc, char **argv ) {
int a;
printf( "argc=%d : ", argc );
for ( a=0; a<argc; a++ ) {
printf( "%s ", argv[a] );
}
printf( "\n" );
}
/*
** _hestWhichFlag( )
**
** given a string in "flag" ( with the hypen prefix ) finds which of
** the flags in the given array of options matches that. Returns
** the index of the matching option, or -1 if there is no match,
** but returns -2 if the flag is the end-of-variable-parameter
** marker ( according to parm->varParamStopFlag )
*/
int
318 _hestWhichFlag( hestOpt *opt, char *flag, const hestParm *parm ) {
char buff[AIR_STRLEN_HUGE], copy[AIR_STRLEN_HUGE], *sep;
int op, numOpts;
numOpts = _hestNumOpts( opt );
if ( parm->verbosity )
printf( "_hestWhichFlag: flag = %s, numOpts = %d\n", flag, numOpts );
for ( op=0; op<numOpts; op++ ) {
if ( parm->verbosity )
printf( "_hestWhichFlag: op = %d\n", op );
if ( !opt[op].flag )
continue;
if ( strchr( opt[op].flag, parm->multiFlagSep ) ) {
strcpy( copy, opt[op].flag );
sep = strchr( copy, parm->multiFlagSep );
*sep = '\0';
/* first try the short version */
sprintf( buff, "-%s", copy );
if ( !strcmp( flag, buff ) )
return op;
/* then try the long version */
sprintf( buff, "--%s", sep+1 );
if ( !strcmp( flag, buff ) )
return op;
}
else {
/* flag has only the short version */
sprintf( buff, "-%s", opt[op].flag );
if ( !strcmp( flag, buff ) )
return op;
}
}
if ( parm->verbosity )
printf( "_hestWhichFlag: numOpts = %d\n", numOpts );
if ( parm->varParamStopFlag ) {
sprintf( buff, "-%c", parm->varParamStopFlag );
if ( parm->verbosity )
printf( "_hestWhichFlag: flag = %s, buff = %s\n", flag, buff );
if ( !strcmp( flag, buff ) )
return -2;
}
if ( parm->verbosity )
printf( "_hestWhichFlag: numOpts = %d\n", numOpts );
return -1;
}
/*
** _hestCase( )
**
** helps figure out logic of interpreting parameters and defaults
** for kind 4 and kind 5 options.
*/
int
372 _hestCase( hestOpt *opt, int *udflt, unsigned int *nprm, int *appr, int op ) {
if ( opt[op].flag && !appr[op] ) {
return 0;
}
else if ( ( 4 == opt[op].kind && udflt[op] ) ||
( 5 == opt[op].kind && !nprm[op] ) ) {
return 1;
}
else {
return 2;
}
}
/*
** _hestExtract( )
**
** takes "pnum" parameters, starting at "base", out of the
** given argv, and puts them into a string WHICH THIS FUNCTION
** ALLOCATES, and also adjusts the argc value given as "*argcP".
*/
char *
394 _hestExtract( int *argcP, char **argv, unsigned int base, unsigned int pnum ) {
unsigned int len, pidx;
char *ret;
if ( !pnum )
return NULL;
len = 0;
for ( pidx=0; pidx<pnum; pidx++ ) {
if ( base+pidx==AIR_UINT( *argcP ) ) {
return NULL;
}
len += AIR_UINT( strlen( argv[base+pidx] ) );
if ( strstr( argv[base+pidx], " " ) ) {
len += 2;
}
}
len += pnum;
ret = AIR_CALLOC( len, char );
strcpy( ret, "" );
for ( pidx=0; pidx<pnum; pidx++ ) {
/* if a single element of argv has spaces in it, someone went
to the trouble of putting it in quotes, and we perpetuate
the favor by quoting it when we concatenate all the argv
elements together, so that airParseStrS will recover it as a
single string again */
if ( strstr( argv[base+pidx], " " ) ) {
strcat( ret, "\"" );
}
/* HEY: if there is a '\"' character in this string, quoted or
not, its going to totally confuse later parsing */
strcat( ret, argv[base+pidx] );
if ( strstr( argv[base+pidx], " " ) ) {
strcat( ret, "\"" );
}
if ( pidx < pnum-1 )
strcat( ret, " " );
}
for ( pidx=base+pnum; pidx<=AIR_UINT( *argcP ); pidx++ ) {
argv[pidx-pnum] = argv[pidx];
}
*argcP -= pnum;
return ret;
}
int
440 _hestNumOpts( const hestOpt *opt ) {
int num = 0;
while ( opt[num].flag || opt[num].name || opt[num].type ) {
num++;
}
return num;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
learned: well duh: when you send arguments to printf( ), they will
be evaluated before printf( ) sees them, so you can't use _hestIdent( )
twice with differen values
*/
#include "hest.h"
#include "privateHest.h"
#include <string.h>
#define ME ( ( parm && parm->verbosity ) ? me : "" )
/*
** _hestArgsInResponseFiles( )
**
** returns the number of args that will be parsed from the response files.
** The role of this function is solely to simplify the task of avoiding
** memory leaks. By knowing exactly how many args we'll get in the response
** file, then hestParse( ) can allocate its local argv[] for exactly as
** long as it needs to be, and we can avoid using an airArray. The drawback
** is that we open and read through the response files twice. Alas.
*/
int
48 _hestArgsInResponseFiles( int *argcP, int *nrfP,
const char **argv, char *err,
const hestParm *parm ) {
FILE *file;
char me[]="_hestArgsInResponseFiles: ", line[AIR_STRLEN_HUGE], *pound;
int ai, len;
*argcP = 0;
*nrfP = 0;
if ( !parm->respFileEnable ) {
/* don't do response files; we're done */
return 0;
}
ai = 0;
while ( argv[ai] ) {
if ( parm->respFileFlag == argv[ai][0] ) {
if ( !( file = fopen( argv[ai]+1, "rb" ) ) ) {
/* can't open the indicated response file for reading */
sprintf( err, "%scouldn't open \"%s\" for reading as response file",
ME, argv[ai]+1 );
*argcP = 0;
*nrfP = 0;
return 1;
}
len = airOneLine( file, line, AIR_STRLEN_HUGE );
while ( len > 0 ) {
if ( ( pound = strchr( line, parm->respFileComment ) ) )
*pound = '\0';
airOneLinify( line );
*argcP += airStrntok( line, AIR_WHITESPACE );
len = airOneLine( file, line, AIR_STRLEN_HUGE );
}
fclose( file );
( *nrfP )++;
}
ai++;
}
return 0;
}
/*
** _hestResponseFiles( )
**
** This function is badly named. Even if there are no response files,
** even if response files are disabled, this is the function that
** copies from the user's argc, argv to our local copy.
*/
int
97 _hestResponseFiles( char **newArgv, const char **oldArgv,
const hestParm *parm, airArray *pmop ) {
char line[AIR_STRLEN_HUGE], *pound;
int len, newArgc, oldArgc, incr, ai;
FILE *file;
newArgc = oldArgc = 0;
while( oldArgv[oldArgc] ) {
if ( parm->verbosity ) {
printf( "!%s:________ newArgc = %d, oldArgc = %d\n",
"dammit", newArgc, oldArgc );
_hestPrintArgv( newArgc, newArgv );
}
if ( !parm->respFileEnable
|| parm->respFileFlag != oldArgv[oldArgc][0] ) {
/* nothing to do with a response file */
newArgv[newArgc] = airStrdup( oldArgv[oldArgc] );
airMopAdd( pmop, newArgv[newArgc], airFree, airMopAlways );
newArgc += 1;
}
else {
/* It is a response file. Error checking on open-ability
should have been done by _hestArgsInResponseFiles( ) */
file = fopen( oldArgv[oldArgc]+1, "rb" );
len = airOneLine( file, line, AIR_STRLEN_HUGE );
while ( len > 0 ) {
if ( parm->verbosity )
printf( "_hestResponseFiles: line: |%s|\n", line );
if ( ( pound = strchr( line, parm->respFileComment ) ) )
*pound = '\0';
if ( parm->verbosity )
printf( "_hestResponseFiles: -0-> line: |%s|\n", line );
airOneLinify( line );
incr = airStrntok( line, AIR_WHITESPACE );
if ( parm->verbosity )
printf( "_hestResponseFiles: -1-> line: |%s|, incr=%d\n",
line, incr );
airParseStrS( newArgv + newArgc, line, AIR_WHITESPACE, incr, AIR_FALSE );
for ( ai=0; ai<incr; ai++ ) {
/* This time, we did allocate memory. We can use airFree and
not airFreeP because these will not be reset before mopping */
airMopAdd( pmop, newArgv[newArgc+ai], airFree, airMopAlways );
}
len = airOneLine( file, line, AIR_STRLEN_HUGE );
newArgc += incr;
}
fclose( file );
}
oldArgc++;
if ( parm->verbosity ) {
_hestPrintArgv( newArgc, newArgv );
printf( "!%s: ^^^^^^^ newArgc = %d, oldArgc = %d\n",
"dammit", newArgc, oldArgc );
}
}
newArgv[newArgc] = NULL;
return 0;
}
/*
** _hestPanic( )
**
** all error checking on the given hest array itself ( not the
** command line to be parsed ). Also, sets the "kind" field of
** the opt struct
*/
int
165 _hestPanic( hestOpt *opt, char *err, const hestParm *parm ) {
char me[]="_hestPanic: ", tbuff[AIR_STRLEN_HUGE], *sep;
int numvar, op, numOpts;
numOpts = _hestNumOpts( opt );
numvar = 0;
for ( op=0; op<numOpts; op++ ) {
opt[op].kind = _hestKind( opt + op );
if ( !( AIR_IN_OP( airTypeUnknown, opt[op].type, airTypeLast ) ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d].type ( %d ) not in valid range [%d, %d]",
ME, op, opt[op].type, airTypeUnknown+1, airTypeLast-1 );
else
fprintf( stderr, "%s: panic 0\n", me );
return 1;
}
if ( !( opt[op].valueP ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d]'s valueP is NULL!", ME, op );
else
fprintf( stderr, "%s: panic 0.5\n", me );
return 1;
}
if ( -1 == opt[op].kind ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d]'s min ( %d ) and max ( %d ) incompatible",
ME, op, opt[op].min, opt[op].max );
else
fprintf( stderr, "%s: panic 1\n", me );
return 1;
}
if ( 5 == opt[op].kind && !( opt[op].sawP ) ) {
if ( err )
sprintf( err, "%s!!!!!! have multiple variable parameters, "
"but sawP is NULL", ME );
else
fprintf( stderr, "%s: panic 2\n", me );
return 1;
}
if ( airTypeEnum == opt[op].type ) {
if ( !( opt[op].enm ) ) {
if ( err ) {
sprintf( err, "%s!!!!!! opt[%d] ( %s ) is type \"enum\", but no "
"airEnum pointer given", ME, op,
opt[op].flag ? opt[op].flag : "?" );
} else {
fprintf( stderr, "%s: panic 3\n", me );
}
return 1;
}
}
if ( airTypeOther == opt[op].type ) {
if ( !( opt[op].CB ) ) {
if ( err ) {
sprintf( err, "%s!!!!!! opt[%d] ( %s ) is type \"other\", but no "
"callbacks given", ME, op,
opt[op].flag ? opt[op].flag : "?" );
} else {
fprintf( stderr, "%s: panic 4\n", me );
}
return 1;
}
if ( !( opt[op].CB->size > 0 ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d]'s \"size\" ( %d ) invalid",
ME, op, ( int )( opt[op].CB->size ) );
else
fprintf( stderr, "%s: panic 5\n", me );
return 1;
}
if ( !( opt[op].type ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d]'s \"type\" is NULL",
ME, op );
else
fprintf( stderr, "%s: panic 6\n", me );
return 1;
}
if ( !( opt[op].CB->parse ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d]'s \"parse\" callback NULL", ME, op );
else
fprintf( stderr, "%s: panic 7\n", me );
return 1;
}
if ( opt[op].CB->destroy && ( sizeof( void* ) != opt[op].CB->size ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d] has a \"destroy\", but size isn't "
"sizeof( void* )", ME, op );
else
fprintf( stderr, "%s: panic 8\n", me );
return 1;
}
}
if ( opt[op].flag ) {
strcpy( tbuff, opt[op].flag );
if ( ( sep = strchr( tbuff, parm->multiFlagSep ) ) ) {
*sep = '\0';
if ( !( strlen( tbuff ) && strlen( sep+1 ) ) ) {
if ( err )
sprintf( err, "%s!!!!!! either short ( \"%s\" ) or long ( \"%s\" ) flag"
" of opt[%d] is zero length", ME, tbuff, sep+1, op );
else
fprintf( stderr, "%s: panic 9\n", me );
return 1;
}
}
else {
if ( !strlen( opt[op].flag ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d].flag is zero length",
ME, op );
else
fprintf( stderr, "%s: panic 10\n", me );
return 1;
}
}
if ( 4 == opt[op].kind ) {
if ( !opt[op].dflt ) {
if ( err )
sprintf( err, "%s!!!!!! flagged single variable parameter must "
"specify a default", ME );
else
fprintf( stderr, "%s: panic 11\n", me );
return 1;
}
if ( !strlen( opt[op].dflt ) ) {
if ( err )
sprintf( err, "%s!!!!!! flagged single variable parameter default "
"must be non-zero length", ME );
else
fprintf( stderr, "%s: panic 12\n", me );
return 1;
}
}
/*
sprintf( tbuff, "-%s", opt[op].flag );
if ( 1 == sscanf( tbuff, "%f", &tmpF ) ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d].flag ( \"%s\" ) is numeric, bad news",
ME, op, opt[op].flag );
return 1;
}
*/
}
if ( 1 == opt[op].kind ) {
if ( !opt[op].flag ) {
if ( err )
sprintf( err, "%s!!!!!! flags must have flags", ME );
else
fprintf( stderr, "%s: panic 13\n", me );
return 1;
}
}
else {
if ( !opt[op].name ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d] isn't a flag: must have \"name\"",
ME, op );
else
fprintf( stderr, "%s: panic 14\n", me );
return 1;
}
}
if ( 4 == opt[op].kind && !opt[op].dflt ) {
if ( err )
sprintf( err, "%s!!!!!! opt[%d] is single variable parameter, but "
"no default set", ME, op );
else
fprintf( stderr, "%s: panic 15\n", me );
return 1;
}
numvar += ( ( int )opt[op].min < _hestMax( opt[op].max ) && ( NULL == opt[op].flag ) ); /* HEY scrutinize casts */
}
if ( numvar > 1 ) {
if ( err )
sprintf( err, "%s!!!!!! can't have %d unflagged min<max opts, only one",
ME, numvar );
else
fprintf( stderr, "%s: panic 16\n", me );
return 1;
}
return 0;
}
int
352 _hestErrStrlen( const hestOpt *opt, int argc, const char **argv ) {
int a, numOpts, ret, other;
ret = 0;
numOpts = _hestNumOpts( opt );
other = AIR_FALSE;
if ( argv ) {
for ( a=0; a<argc; a++ ) {
ret = AIR_MAX( ret, ( int )airStrlen( argv[a] ) );
}
}
for ( a=0; a<numOpts; a++ ) {
ret = AIR_MAX( ret, ( int )airStrlen( opt[a].flag ) );
ret = AIR_MAX( ret, ( int )airStrlen( opt[a].name ) );
other |= opt[a].type == airTypeOther;
}
for ( a=airTypeUnknown+1; a<airTypeLast; a++ ) {
ret = AIR_MAX( ret, ( int )airStrlen( airTypeStr[a] ) );
}
if ( other ) {
/* the callback's error( ) function may sprintf an error message
into a buffer which is size AIR_STRLEN_HUGE */
ret += AIR_STRLEN_HUGE;
}
ret += 4 * 12; /* as many as 4 ints per error message */
ret += 257; /* function name and text of hest's error message */
return ret;
}
/*
** _hestExtractFlagged( )
**
** extracts the parameters associated with all flagged options from the
** given argc and argv, storing them in prms[], recording the number
** of parameters in nprm[], and whether or not the flagged option appeared
** in appr[].
**
** The "saw" information is not set here, since it is better set
** at value parsing time, which happens after defaults are enstated.
*/
int
394 _hestExtractFlagged( char **prms, unsigned int *nprm, int *appr,
int *argcP, char **argv,
hestOpt *opt,
char *err, const hestParm *parm, airArray *pmop ) {
char me[]="_hestExtractFlagged: ", ident1[AIR_STRLEN_HUGE],
ident2[AIR_STRLEN_HUGE];
int a, np, flag, endflag, numOpts, op;
a = 0;
if ( parm->verbosity )
printf( "!%s: *argcP = %d\n", me, *argcP );
while ( a<=*argcP-1 ) {
if ( parm->verbosity )
printf( "!%s: a = %d -> argv[a] = %s\n", me, a, argv[a] );
flag = _hestWhichFlag( opt, argv[a], parm );
if ( parm->verbosity )
printf( "!%s: A: a = %d -> flag = %d\n", me, a, flag );
if ( !( 0 <= flag ) ) {
/* not a flag, move on */
a++;
continue;
}
/* see if we can associate some parameters with the flag */
np = 0;
endflag = 0;
while ( np < _hestMax( opt[flag].max ) &&
a+np+1 <= *argcP-1 &&
-1 == ( endflag = _hestWhichFlag( opt, argv[a+np+1], parm ) ) ) {
np++;
if ( parm->verbosity )
printf( "!%s: np --> %d with endflag = %d\n", me, np, endflag );
}
/* we stopped because we got the max number of parameters, or
because we hit the end of the command line, or
because _hestWhichFlag( ) returned something other than -1,
which means it returned -2, or a valid option index. If
we stopped because of _hestWhichFlag( )'s return value,
endflag has been set to that return value */
if ( parm->verbosity )
printf( "!%s: B: np = %d; endflag = %d\n", me, np, endflag );
if ( np < ( int )opt[flag].min ) { /* HEY scrutinize casts */
/* didn't get minimum number of parameters */
if ( !( a+np+1 <= *argcP-1 ) ) {
sprintf( err, "%shit end of line before getting %d parameter%s "
"for %s ( got %d )",
ME, opt[flag].min, opt[flag].min > 1 ? "s" : "",
_hestIdent( ident1, opt+flag, parm, AIR_TRUE ), np );
}
else {
sprintf( err, "%shit %s before getting %d parameter%s for %s ( got %d )",
ME, _hestIdent( ident1, opt+endflag, parm, AIR_FALSE ),
opt[flag].min, opt[flag].min > 1 ? "s" : "",
_hestIdent( ident2, opt+flag, parm, AIR_FALSE ), np );
}
return 1;
}
nprm[flag] = np;
if ( parm->verbosity ) {
printf( "!%s:________ a=%d, *argcP = %d -> flag = %d\n",
me, a, *argcP, flag );
_hestPrintArgv( *argcP, argv );
}
/* lose the flag argument */
free( _hestExtract( argcP, argv, a, 1 ) );
/* extract the args after the flag */
if ( appr[flag] ) {
airMopSub( pmop, prms[flag], airFree );
prms[flag] = ( char * )airFree( prms[flag] );
}
prms[flag] = _hestExtract( argcP, argv, a, nprm[flag] );
airMopAdd( pmop, prms[flag], airFree, airMopAlways );
appr[flag] = AIR_TRUE;
if ( -2 == endflag ) {
/* we should lose the end-of-variable-parameter marker */
free( _hestExtract( argcP, argv, a, 1 ) );
}
if ( parm->verbosity ) {
_hestPrintArgv( *argcP, argv );
printf( "!%s:^^^^^^^^ *argcP = %d\n", me, *argcP );
printf( "!%s: prms[%d] = %s\n", me, flag,
prms[flag] ? prms[flag] : "( null )" );
}
}
/* make sure that flagged options without default were given */
numOpts = _hestNumOpts( opt );
for ( op=0; op<numOpts; op++ ) {
if ( 1 != opt[op].kind && opt[op].flag && !opt[op].dflt && !appr[op] ) {
sprintf( err, "%sdidn't get required %s",
ME, _hestIdent( ident1, opt+op, parm, AIR_FALSE ) );
return 1;
}
}
return 0;
}
int
492 _hestNextUnflagged( int op, hestOpt *opt, int numOpts ) {
for( ; op<=numOpts-1; op++ ) {
if ( !opt[op].flag )
break;
}
return op;
}
int
502 _hestExtractUnflagged( char **prms, unsigned int *nprm,
int *argcP, char **argv,
hestOpt *opt,
char *err, const hestParm *parm, airArray *pmop ) {
char me[]="_hestExtractUnflagged: ", ident[AIR_STRLEN_HUGE];
int nvp, np, op, unflag1st, unflagVar, numOpts;
numOpts = _hestNumOpts( opt );
unflag1st = _hestNextUnflagged( 0, opt, numOpts );
if ( numOpts == unflag1st ) {
/* no unflagged options; we're done */
return 0;
}
for ( unflagVar = unflag1st;
unflagVar != numOpts;
unflagVar = _hestNextUnflagged( unflagVar+1, opt, numOpts ) ) {
if ( ( int )opt[unflagVar].min < _hestMax( opt[unflagVar].max ) ) /* HEY scrutinize casts */
break;
}
/* now, if there is a variable parameter unflagged opt, unflagVar is its
index in opt[], or else unflagVar is numOpts */
/* grab parameters for all unflagged opts before opt[t] */
for ( op = _hestNextUnflagged( 0, opt, numOpts );
op < unflagVar;
op = _hestNextUnflagged( op+1, opt, numOpts ) ) {
/* printf( "!%s: op = %d; unflagVar = %d\n", me, op, unflagVar ); */
np = opt[op].min; /* min == max */
if ( !( np <= *argcP ) ) {
sprintf( err, "%sdon't have %d parameter%s %s%s%sfor %s",
ME, np, np > 1 ? "s" : "",
argv[0] ? "starting at \"" : "",
argv[0] ? argv[0] : "",
argv[0] ? "\" " : "",
_hestIdent( ident, opt+op, parm, AIR_TRUE ) );
return 1;
}
prms[op] = _hestExtract( argcP, argv, 0, np );
airMopAdd( pmop, prms[op], airFree, airMopAlways );
nprm[op] = np;
}
/*
_hestPrintArgv( *argcP, argv );
*/
/* we skip over the variable parameter unflagged option, subtract from *argcP
the number of parameters in all the opts which follow it, in order to get
the number of parameters in the sole variable parameter option,
store this in nvp */
nvp = *argcP;
for ( op = _hestNextUnflagged( unflagVar+1, opt, numOpts );
op < numOpts;
op = _hestNextUnflagged( op+1, opt, numOpts ) ) {
nvp -= opt[op].min; /* min == max */
}
if ( nvp < 0 ) {
op = _hestNextUnflagged( unflagVar+1, opt, numOpts );
np = opt[op].min;
sprintf( err, "%sdon't have %d parameter%s for %s",
ME, np, np > 1 ? "s" : "",
_hestIdent( ident, opt+op, parm, AIR_FALSE ) );
return 1;
}
/* else we had enough args for all the unflagged options following
the sole variable parameter unflagged option, so snarf them up */
for ( op = _hestNextUnflagged( unflagVar+1, opt, numOpts );
op < numOpts;
op = _hestNextUnflagged( op+1, opt, numOpts ) ) {
np = opt[op].min;
prms[op] = _hestExtract( argcP, argv, nvp, np );
airMopAdd( pmop, prms[op], airFree, airMopAlways );
nprm[op] = np;
}
/* now we grab the parameters of the sole variable parameter unflagged opt,
if it exists ( unflagVar < numOpts ) */
if ( unflagVar < numOpts ) {
/*
printf( "!%s: unflagVar=%d: min, nvp, max = %d %d %d\n", me, unflagVar,
opt[unflagVar].min, nvp, _hestMax( opt[unflagVar].max ) );
*/
/* we'll do error checking for unexpected args later */
nvp = AIR_MIN( nvp, _hestMax( opt[unflagVar].max ) );
if ( nvp < ( int )opt[unflagVar].min ) { /* HEY scrutinize casts */
sprintf( err, "%sdidn't get minimum of %d arg%s for %s ( got %d )",
ME, opt[unflagVar].min,
opt[unflagVar].min > 1 ? "s" : "",
_hestIdent( ident, opt+unflagVar, parm, AIR_TRUE ), nvp );
return 1;
}
if ( nvp ) {
prms[unflagVar] = _hestExtract( argcP, argv, 0, nvp );
airMopAdd( pmop, prms[unflagVar], airFree, airMopAlways );
nprm[unflagVar] = nvp;
}
else {
prms[unflagVar] = NULL;
nprm[unflagVar] = 0;
}
}
return 0;
}
int
606 _hestDefaults( char **prms, int *udflt, unsigned int *nprm, int *appr,
hestOpt *opt,
char *err, const hestParm *parm, airArray *mop ) {
char *tmpS, me[]="_hestDefaults: ", ident[AIR_STRLEN_HUGE];
int op, numOpts;
numOpts = _hestNumOpts( opt );
for ( op=0; op<numOpts; op++ ) {
if ( parm->verbosity )
printf( "%s op=%d/%d: \"%s\" --> kind=%d, nprm=%u, appr=%d\n",
me, op, numOpts-1, prms[op], opt[op].kind,
nprm[op], appr[op] );
switch( opt[op].kind ) {
case 1:
/* -------- ( no-parameter ) boolean flags -------- */
/* default is indeed always ignored for the sake of setting the
option's value, but udflt is used downstream to set
the option's source. The info came from the user if
the flag appears, otherwise it is from the default. */
udflt[op] = !appr[op];
break;
case 2:
case 3:
/* -------- one required parameter -------- */
/* -------- multiple required parameters -------- */
/* we'll used defaults if the flag didn't appear */
udflt[op] = opt[op].flag && !appr[op];
break;
case 4:
/* -------- optional single variables -------- */
/* if the flag appeared ( if there is a flag ) but the paramter didn't,
we'll "invert" the default; if the flag didn't appear ( or if there
isn't a flag ) and the parameter also didn't appear, we'll use the
default. In either case, nprm[op] will be zero, and in both cases,
we need to use the default information. */
udflt[op] = ( 0 == nprm[op] );
/*
fprintf( stderr, "%s nprm[%d] = %u --> udflt[%d] = %d\n", me,
op, nprm[op], op, udflt[op] );
*/
break;
case 5:
/* -------- multiple optional parameters -------- */
/* we'll use the default if the flag didn't appear ( if there is a
flag ) Otherwise, if nprm[op] is zero, then the user is saying,
I want zero parameters */
udflt[op] = opt[op].flag && !appr[op];
break;
}
if ( !udflt[op] )
continue;
prms[op] = airStrdup( opt[op].dflt );
/* fprintf( stderr, "%s: prms[%d] = |%s|\n", me, op, prms[op] ); */
if ( prms[op] ) {
airMopAdd( mop, prms[op], airFree, airMopAlways );
airOneLinify( prms[op] );
tmpS = airStrdup( prms[op] );
nprm[op] = airStrntok( tmpS, " " );
airFree( tmpS );
/* printf( "!%s: nprm[%d] in default = %u\n", me, op, nprm[op] ); */
if ( ( int )opt[op].min < _hestMax( opt[op].max ) ) { /* HEY scrutinize casts */
if ( !( AIR_IN_CL( ( int )opt[op].min, ( int )nprm[op], _hestMax( opt[op].max ) ) /* HEY scrutinize casts */
|| ( airTypeString == opt[op].type
&& parm->elideMultipleEmptyStringDefault ) ) ) {
sprintf( err, "%s# parameters ( in default ) for %s is %d, "
"but need between %d and %d",
ME, _hestIdent( ident, opt+op, parm, AIR_TRUE ), nprm[op],
opt[op].min, _hestMax( opt[op].max ) );
return 1;
}
}
}
}
return 0;
}
/*
** this function moved from air/miscAir; the usage below
** is its only usage in Teem
*/
static int
687 airIStore( void *v, int t, int i ) {
switch( t ) {
case airTypeBool: return ( *( ( int* )v ) = !!i ); break;
case airTypeInt: return ( *( ( int* )v ) = i ); break;
case airTypeUInt: return ( int )( *( ( unsigned int* )v ) = i ); break;
case airTypeLongInt:return ( int )( *( ( long int* )v ) = i ); break;
case airTypeULongInt:return ( int )( *( ( unsigned long int* )v ) = i ); break;
case airTypeSize_t: return ( int )( *( ( size_t* )v ) = i ); break;
case airTypeFloat: return ( int )( *( ( float* )v ) = ( float )( i ) ); break;
case airTypeDouble: return ( int )( *( ( double* )v ) = ( double )( i ) ); break;
case airTypeChar: return ( *( ( char* )v ) = ( char )( i ) ); break;
default: return 0; break;
}
}
/*
** this function moved from air/miscAir; the usage below
** is its only usage in Teem
*/
double
708 airDLoad( void *v, int t ) {
switch( t ) {
case airTypeBool: return AIR_CAST( double, *( ( int* )v ) ); break;
case airTypeInt: return AIR_CAST( double, *( ( int* )v ) ); break;
case airTypeUInt: return AIR_CAST( double, *( ( unsigned int* )v ) ); break;
case airTypeLongInt:return AIR_CAST( double, *( ( long int* )v ) ); break;
case airTypeULongInt:return AIR_CAST( double, *( ( unsigned long int* )v ) ); break;
case airTypeSize_t: return AIR_CAST( double, *( ( size_t* )v ) ); break;
case airTypeFloat: return AIR_CAST( double, *( ( float* )v ) ); break;
case airTypeDouble: return *( ( double* )v ); break;
case airTypeChar: return AIR_CAST( double, *( ( char* )v ) ); break;
default: return 0; break;
}
}
int
726 _hestSetValues( char **prms, int *udflt, unsigned int *nprm, int *appr,
hestOpt *opt,
char *err, const hestParm *parm, airArray *pmop ) {
char ident[AIR_STRLEN_HUGE], me[]="_hestSetValues: ",
cberr[AIR_STRLEN_HUGE], *tok, *last, *prmsCopy;
double tmpD;
int op, type, numOpts, p, ret;
void *vP;
char *cP;
size_t size;
numOpts = _hestNumOpts( opt );
for ( op=0; op<numOpts; op++ ) {
_hestIdent( ident, opt+op, parm, AIR_TRUE );
opt[op].source = udflt[op] ? hestSourceDefault : hestSourceUser;
type = opt[op].type;
size = ( airTypeEnum == type
? ( int )sizeof( int ) /* HEY scrutinize casts */
: ( airTypeOther == type
? ( int )opt[op].CB->size /* HEY scrutinize casts */
: ( int )airTypeSize[type] ) ); /* HEY scrutinize casts */
cP = ( char * )( vP = opt[op].valueP );
if ( parm->verbosity ) {
printf( "%s %d of %d: \"%s\": |%s| --> kind=%d, type=%d, size=%d\n",
me, op, numOpts-1, prms[op], ident, opt[op].kind, type,
( int )size );
}
/* we may over-write these */
opt[op].alloc = 0;
if ( opt[op].sawP ) {
*( opt[op].sawP ) = 0;
}
switch( opt[op].kind ) {
case 1:
/* -------- parameter-less boolean flags -------- */
if ( vP )
*( ( int* )vP ) = appr[op];
break;
case 2:
/* -------- one required parameter -------- */
if ( prms[op] && vP ) {
switch ( type ) {
case airTypeEnum:
if ( 1 != airParseStrE( ( int * )vP, prms[op], " ", 1, opt[op].enm ) ) {
sprintf( err, "%scouldn\'t parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
opt[op].enm->name, ident );
return 1;
}
break;
case airTypeOther:
strcpy( cberr, "" );
ret = opt[op].CB->parse( vP, prms[op], cberr );
if ( ret ) {
if ( strlen( cberr ) ) {
sprintf( err, "%serror parsing \"%s\" as %s for %s:\n%s",
ME, prms[op], opt[op].CB->type, ident, cberr );
} else {
sprintf( err, "%serror parsing \"%s\" as %s for %s: returned %d",
ME, prms[op], opt[op].CB->type, ident, ret );
}
return ret;
}
if ( opt[op].CB->destroy ) {
/* vP is the address of a void*, we manage the void * */
opt[op].alloc = 1;
airMopAdd( pmop, ( void** )vP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( pmop, *( ( void** )vP ), opt[op].CB->destroy, airMopOnError );
}
break;
case airTypeString:
if ( 1 != airParseStrS( ( char ** )vP, prms[op], " ", 1,
parm->greedySingleString ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
airTypeStr[type], ident );
return 1;
}
/* vP is the address of a char* ( a char ** ), but what we
manage with airMop is the char * */
opt[op].alloc = 1;
airMopMem( pmop, vP, airMopOnError );
break;
default:
/* type isn't string or enum, so no last arg to airParseStr[type] */
if ( 1 != airParseStr[type]( vP, prms[op], " ", 1 ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
airTypeStr[type], ident );
return 1;
}
break;
}
}
break;
case 3:
/* -------- multiple required parameters -------- */
if ( prms[op] && vP ) {
switch ( type ) {
case airTypeEnum:
if ( opt[op].min != /* min == max */
airParseStrE( ( int * )vP, prms[op], " ", opt[op].min, opt[op].enm ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %d %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
opt[op].min, opt[op].enm->name,
opt[op].min > 1 ? "s" : "", ident );
return 1;
}
break;
case airTypeOther:
prmsCopy = airStrdup( prms[op] );
for ( p=0; p<( int )opt[op].min; p++ ) { /* HEY scrutinize casts */
tok = airStrtok( !p ? prmsCopy : NULL, " ", &last );
strcpy( cberr, "" );
ret = opt[op].CB->parse( cP + p*size, tok, cberr );
if ( ret ) {
if ( strlen( cberr ) )
sprintf( err, "%serror parsing \"%s\" ( in \"%s\" ) as %s "
"for %s:\n%s",
ME, tok, prms[op], opt[op].CB->type, ident, cberr );
else
sprintf( err, "%serror parsing \"%s\" ( in \"%s\" ) as %s "
"for %s: returned %d",
ME, tok, prms[op], opt[op].CB->type, ident, ret );
free( prmsCopy );
return 1;
}
}
free( prmsCopy );
if ( opt[op].CB->destroy ) {
/* vP is an array of void*s, we manage the individual void*s */
opt[op].alloc = 2;
for ( p=0; p<( int )opt[op].min; p++ ) { /* HEY scrutinize casts */
airMopAdd( pmop, ( ( void** )vP )+p, ( airMopper )airSetNull,
airMopOnError );
airMopAdd( pmop, *( ( ( void** )vP )+p ), opt[op].CB->destroy,
airMopOnError );
}
}
break;
case airTypeString:
if ( opt[op].min != /* min == max */
airParseStr[type]( vP, prms[op], " ", opt[op].min, AIR_FALSE ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %d %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
opt[op].min, airTypeStr[type],
opt[op].min > 1 ? "s" : "", ident );
return 1;
}
/* vP is an array of char*s, ( a char** ), and what we manage
with airMop are the individual vP[p]. */
opt[op].alloc = 2;
for ( p=0; p<( int )opt[op].min; p++ ) { /* HEY scrutinize casts */
airMopMem( pmop, &( ( ( char** )vP )[p] ), airMopOnError );
}
break;
default:
if ( opt[op].min != /* min == max */
airParseStr[type]( vP, prms[op], " ", opt[op].min ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %d %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
opt[op].min, airTypeStr[type],
opt[op].min > 1 ? "s" : "", ident );
return 1;
}
break;
}
}
break;
case 4:
/* -------- optional single variables -------- */
if ( prms[op] && vP ) {
switch ( type ) {
case airTypeEnum:
if ( 1 != airParseStrE( ( int * )vP, prms[op], " ", 1, opt[op].enm ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
opt[op].enm->name, ident );
return 1;
}
break;
case airTypeOther:
/* we're parsing an "other". We will not perform the special
flagged single variable parameter games as done above, so
whether this option is flagged or unflagged, we're going
to treat it like an unflagged single variable parameter option:
if the parameter didn't appear, we'll parse it from the default,
if it did appear, we'll parse it from the command line. Setting
up prms[op] thusly has already been done by _hestDefaults( ) */
strcpy( cberr, "" );
ret = opt[op].CB->parse( vP, prms[op], cberr );
if ( ret ) {
if ( strlen( cberr ) )
sprintf( err, "%serror parsing \"%s\" as %s for %s:\n%s",
ME, prms[op], opt[op].CB->type, ident, cberr );
else
sprintf( err, "%serror parsing \"%s\" as %s for %s: returned %d",
ME, prms[op], opt[op].CB->type, ident, ret );
return 1;
}
if ( opt[op].CB->destroy ) {
/* vP is the address of a void*, we manage the void* */
opt[op].alloc = 1;
airMopAdd( pmop, vP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( pmop, *( ( void** )vP ), opt[op].CB->destroy, airMopOnError );
}
break;
case airTypeString:
if ( 1 != airParseStr[type]( vP, prms[op], " ", 1,
parm->greedySingleString ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
airTypeStr[type], ident );
return 1;
}
opt[op].alloc = 1;
if ( opt[op].flag && 1 == _hestCase( opt, udflt, nprm, appr, op ) ) {
/* we just parsed the default, but now we want to "invert" it */
*( ( char** )vP ) = ( char * )airFree( *( ( char** )vP ) );
opt[op].alloc = 0;
}
/* vP is the address of a char* ( a char** ), and what we
manage with airMop is the char * */
airMopMem( pmop, vP, airMopOnError );
break;
default:
if ( 1 != airParseStr[type]( vP, prms[op], " ", 1 ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
airTypeStr[type], ident );
return 1;
}
opt[op].alloc = 0;
if ( 1 == _hestCase( opt, udflt, nprm, appr, op ) ) {
/* we just parsed the default, but now we want to "invert" it */
tmpD = airDLoad( vP, type );
airIStore( vP, type, tmpD ? 0 : 1 );
}
break;
}
}
break;
case 5:
/* -------- multiple optional parameters -------- */
/* hammerhead problems in this case;
may have been from calloc( 0 ), fixed below */
if ( prms[op] && vP ) {
if ( 1 == _hestCase( opt, udflt, nprm, appr, op ) ) {
*( ( void** )vP ) = NULL;
/* alloc and sawP set above */
} else {
if ( airTypeString == type ) {
/* this is sneakiness: we allocate one more element so that
the resulting char** is, like argv, NULL-terminated */
*( ( void** )vP ) = calloc( nprm[op]+1, size );
} else {
if ( nprm[op] ) {
/* only allocate if there's something to allocate */
*( ( void** )vP ) = calloc( nprm[op], size );
} else {
*( ( void** )vP ) = NULL;
}
}
if ( parm->verbosity ) {
printf( "!%s: nprm[%d] = %u\n", me, op, nprm[op] );
printf( "!%s: new array ( size %u*%u ) is at 0x%p\n", me,
nprm[op], ( unsigned int )size, *( ( void** )vP ) );
}
if ( *( ( void** )vP ) ) {
airMopMem( pmop, vP, airMopOnError );
}
*( opt[op].sawP ) = nprm[op];
/* so far everything we've done is regardless of type */
switch ( type ) {
case airTypeEnum:
opt[op].alloc = 1;
if ( nprm[op] !=
airParseStrE( ( int * )( *( ( void** )vP ) ), prms[op], " ", nprm[op],
opt[op].enm ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %u %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
nprm[op], opt[op].enm->name,
nprm[op] > 1 ? "s" : "", ident );
return 1;
}
break;
case airTypeOther:
cP = ( char * )( *( ( void** )vP ) );
prmsCopy = airStrdup( prms[op] );
opt[op].alloc = ( opt[op].CB->destroy ? 3 : 1 );
for ( p=0; p<( int )nprm[op]; p++ ) { /* HEY scrutinize casts */
tok = airStrtok( !p ? prmsCopy : NULL, " ", &last );
/* hammerhead problems went away when this line
was replaced by the following one:
strcpy( cberr, "" );
*/
cberr[0] = 0;
ret = opt[op].CB->parse( cP + p*size, tok, cberr );
if ( ret ) {
if ( strlen( cberr ) )
sprintf( err, "%serror parsing \"%s\" ( in \"%s\" ) as %s "
"for %s:\n%s",
ME, tok, prms[op], opt[op].CB->type, ident, cberr );
else
sprintf( err, "%serror parsing \"%s\" ( in \"%s\" ) as %s "
"for %s: returned %d",
ME, tok, prms[op], opt[op].CB->type, ident, ret );
free( prmsCopy );
return 1;
}
}
free( prmsCopy );
if ( opt[op].CB->destroy ) {
for ( p=0; p<( int )nprm[op]; p++ ) { /* HEY scrutinize casts */
/* avert your eyes. vP is the address of an array of void*s.
We manage the void*s */
airMopAdd( pmop, ( *( ( void*** )vP ) )+p, ( airMopper )airSetNull,
airMopOnError );
airMopAdd( pmop, *( ( *( ( void*** )vP ) )+p ), opt[op].CB->destroy,
airMopOnError );
}
}
break;
case airTypeString:
opt[op].alloc = 3;
if ( nprm[op] !=
airParseStrS( ( char ** )( *( ( void** )vP ) ), prms[op], " ", nprm[op],
parm->greedySingleString ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %d %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
nprm[op], airTypeStr[type],
nprm[op] > 1 ? "s" : "", ident );
return 1;
}
/* vP is the address of an array of char*s ( a char *** ), and
what we manage with airMop is the individual ( *vP )[p],
as well as vP itself ( above ). */
for ( p=0; p<( int )nprm[op]; p++ ) { /* HEY scrutinize casts */
airMopAdd( pmop, ( *( ( char*** )vP ) )[p], airFree, airMopOnError );
}
/* do the NULL-termination described above */
( *( ( char*** )vP ) )[nprm[op]] = NULL;
break;
default:
opt[op].alloc = 1;
if ( nprm[op] !=
airParseStr[type]( *( ( void** )vP ), prms[op], " ", nprm[op] ) ) {
sprintf( err, "%scouldn't parse %s\"%s\" as %d %s%s for %s",
ME, udflt[op] ? "( default ) " : "", prms[op],
nprm[op], airTypeStr[type],
nprm[op] > 1 ? "s" : "", ident );
return 1;
}
break;
}
}
}
break;
}
}
return 0;
}
/*
******** hestParse( )
**
** documentation?
*/
int
1096 hestParse( hestOpt *opt, int _argc, const char **_argv,
char **_errP, const hestParm *_parm ) {
char me[]="hestParse: ";
char *param, *param_copy;
char **argv, **prms, *err;
int a, argc, argr, *appr, *udflt, nrf, numOpts, big, ret, i;
unsigned int *nprm;
airArray *mop;
hestParm *parm;
size_t start_index, end_index;
numOpts = _hestNumOpts( opt );
/* -------- initialize the mop! */
mop = airMopNew( );
/* -------- either copy given _parm, or allocate one */
if ( _parm ) {
parm = NULL;
}
else {
parm = hestParmNew( );
airMopAdd( mop, parm, ( airMopper )hestParmFree, airMopAlways );
}
/* how to const-correctly use parm or _parm in an expression */
#define PARM ( _parm ? _parm : parm )
/* -------- allocate the err string. To determine its size with total
ridiculous safety we have to find the biggest things which can appear
in the string. */
big = _hestErrStrlen( opt, _argc, _argv );
if ( !( err = AIR_CALLOC( big, char ) ) ) {
fprintf( stderr, "%s PANIC: couldn't allocate error message "
"buffer ( size %d )\n", me, big );
/* exit( 1 ); */
}
if ( _errP ) {
/* if they care about the error string, than it is mopped only
when there _wasn't_ an error */
*_errP = err;
airMopAdd( mop, _errP, ( airMopper )airSetNull, airMopOnOkay );
airMopAdd( mop, err, airFree, airMopOnOkay );
}
else {
/* otherwise, we're making the error string just for our own
convenience, and we'll always clean it up on exit */
airMopAdd( mop, err, airFree, airMopAlways );
}
/* -------- check on validity of the hestOpt array */
if ( _hestPanic( opt, err, PARM ) ) {
airMopError( mop ); return 1;
}
/* -------- Create all the local arrays used to save state during
the processing of all the different options */
nprm = AIR_CALLOC( numOpts, unsigned int );
airMopMem( mop, &nprm, airMopAlways );
appr = AIR_CALLOC( numOpts, int );
airMopMem( mop, &appr, airMopAlways );
udflt = AIR_CALLOC( numOpts, int );
airMopMem( mop, &udflt, airMopAlways );
prms = AIR_CALLOC( numOpts, char * );
airMopMem( mop, &prms, airMopAlways );
for ( a=0; a<numOpts; a++ ) {
prms[a] = NULL;
}
/* -------- find out how big the argv array needs to be, first
by seeing how many args are in the response files, and then adding
on the args from the actual argv ( getting this right the first time
greatly simplifies the problem of eliminating memory leaks ) */
if ( _hestArgsInResponseFiles( &argr, &nrf, _argv, err, PARM ) ) {
airMopError( mop ); return 1;
}
argc = argr + _argc - nrf;
if ( PARM->verbosity ) {
printf( "!%s: nrf = %d; argr = %d; _argc = %d --> argc = %d\n",
me, nrf, argr, _argc, argc );
}
argv = AIR_CALLOC( argc+1, char * );
airMopMem( mop, &argv, airMopAlways );
/* -------- process response files ( if any ) and set the remaining
elements of argv */
if ( PARM->verbosity ) printf( "%s: #### calling hestResponseFiles\n", me );
if ( _hestResponseFiles( argv, _argv, PARM, mop ) ) {
airMopError( mop ); return 1;
}
if ( PARM->verbosity ) printf( "%s: #### hestResponseFiles done!\n", me );
/*
_hestPrintArgv( argc, argv );
*/
/* -------- extract flags and their associated parameters from argv */
if ( PARM->verbosity ) printf( "%s: #### calling hestExtractFlagged\n", me );
if ( _hestExtractFlagged( prms, nprm, appr,
&argc, argv,
opt,
err, PARM, mop ) ) {
airMopError( mop ); return 1;
}
if ( PARM->verbosity ) printf( "%s: #### hestExtractFlagged done!\n", me );
/*
_hestPrintArgv( argc, argv );
*/
/* -------- extract args for unflagged options */
if ( PARM->verbosity ) printf( "%s: #### calling hestExtractUnflagged\n", me );
if ( _hestExtractUnflagged( prms, nprm,
&argc, argv,
opt,
err, PARM, mop ) ) {
airMopError( mop ); return 1;
}
if ( PARM->verbosity ) printf( "%s: #### hestExtractUnflagged done!\n", me );
/* currently, any left over arguments indicate error */
if ( argc ) {
sprintf( err, "%sunexpected arg%s: \"%s\"", ME,
( '-' == argv[0][0]
? " ( or unrecognized flag )"
: "" ), argv[0] );
airMopError( mop ); return 1;
}
/* -------- learn defaults */
if ( PARM->verbosity ) printf( "%s: #### calling hestDefaults\n", me );
if ( _hestDefaults( prms, udflt, nprm, appr,
opt,
err, PARM, mop ) ) {
airMopError( mop ); return 1;
}
if ( PARM->verbosity ) printf( "%s: #### hestDefaults done!\n", me );
/* remove quotes from strings
if greedy wasn't turned on for strings, then we have no hope
of capturing filenames with spaces. */
if ( PARM->greedySingleString ) {
for ( i=0; i<numOpts; i++ ) {
param = prms[i];
param_copy = NULL;
if ( param && strstr( param, " " ) ) {
start_index = 0;
end_index = strlen( param )-1;
if ( param[start_index] == '\"' )
start_index++;
if ( param[end_index] == '\"' )
end_index--;
param_copy = AIR_CALLOC( end_index-start_index+2, char );
strncpy( param_copy, ¶m[start_index], end_index-start_index+1 );
param_copy[end_index-start_index+1] = '\0';
strcpy( param, param_copy );
free( param_copy );
}
}
}
/* -------- now, the actual parsing of values */
/* hammerhead problems in _hestSetValues */
if ( PARM->verbosity ) printf( "%s: #### calling hestSetValues\n", me );
ret = _hestSetValues( prms, udflt, nprm, appr,
opt,
err, PARM, mop );
if ( ret ) {
airMopError( mop ); return ret;
}
if ( PARM->verbosity ) printf( "%s: #### hestSetValues done!\n", me );
#undef PARM
airMopOkay( mop );
return 0;
}
/*
******** hestParseFree( )
**
** free( )s whatever was allocated by hestParse( )
**
** returns NULL only to facilitate use with the airMop functions.
** You should probably just ignore this quirk.
*/
void *
1281 hestParseFree( hestOpt *opt ) {
int op, i, numOpts;
unsigned int ui;
void **vP;
void ***vAP;
char **str;
char ***strP;
numOpts = _hestNumOpts( opt );
for ( op=0; op<numOpts; op++ ) {
/*
printf( "!hestParseFree: op = %d/%d -> kind = %d; type = %d; alloc = %d\n",
op, numOpts-1, opt[op].kind, opt[op].type, opt[op].alloc );
*/
vP = ( void ** )opt[op].valueP;
vAP = ( void *** )opt[op].valueP;
str = ( char ** )opt[op].valueP;
strP = ( char *** )opt[op].valueP;
switch ( opt[op].alloc ) {
case 0:
/* nothing was allocated */
break;
case 1:
if ( airTypeOther != opt[op].type ) {
*vP = airFree( *vP );
}
else {
/* alloc is one either because we parsed one thing, and we have a
destroy callback, or, because we parsed a dynamically-created array
of things, and we don't have a destroy callback */
if ( opt[op].CB->destroy ) {
*vP = opt[op].CB->destroy( *vP );
}
else {
*vP = airFree( *vP );
}
}
break;
case 2:
if ( airTypeString == opt[op].type ) {
for ( i=0; i<( int )opt[op].min; i++ ) { /* HEY scrutinize casts */
str[i] = ( char * )airFree( str[i] );
}
}
else {
for ( i=0; i<( int )opt[op].min; i++ ) { /* HEY scrutinize casts */
vP[i] = opt[op].CB->destroy( vP[i] );
}
}
break;
case 3:
if ( airTypeString == opt[op].type ) {
for ( ui=0; ui<*( opt[op].sawP ); ui++ ) {
( *strP )[ui] = ( char * )airFree( ( *strP )[ui] );
}
*strP = ( char ** )airFree( *strP );
}
else {
for ( ui=0; ui<*( opt[op].sawP ); ui++ ) {
( *vAP )[ui] = opt[op].CB->destroy( ( *vAP )[ui] );
}
*vAP = ( void ** )airFree( *vAP );
}
break;
}
}
return NULL;
}
/*
******** hestParseOrDie( )
**
** dumb little function which encapsulate a common usage of hest:
** first, make sure hestOpt is valid with hestOptCheck( ). Then,
** if argc is 0 ( and !parm->noArgsIsNoProblem ): maybe show
** info, usage, and glossary, all according to given flags, then exit( 1 )
** if parsing failed: show error message, and maybe usage and glossary,
** again according to boolean flags, then exit( 1 )
** if parsing succeeded: return
*/
void
1362 hestParseOrDie( hestOpt *opt, int argc, const char **argv,
hestParm *parm,
const char *me, const char *info,
int doInfo, int doUsage, int doGlossary ) {
int E;
int argcBad;
char *errS;
if ( opt ) {
if ( hestOptCheck( opt, &errS ) ) {
fprintf( stderr, "ERROR in hest usage:\n%s\n", errS ); free( errS );
exit( 1 );
}
E = 0;
/* argc is good if its non-zero, or ( else ) being zero is ok */
argcBad = !( argc || ( parm && parm->noArgsIsNoProblem ) );
if ( argcBad ||
( E = hestParse( opt, argc, argv, &errS, parm ) ) ) {
if ( E ) {
if ( argv[0] && !strcmp( argv[0], "--version" ) ) {
/* print version info and bail */
char vbuff[AIR_STRLEN_LARGE];
airTeemVersionSprint( vbuff );
printf( "%s\n", vbuff );
hestParmFree( parm );
hestOptFree( opt );
exit( 0 );
} else if ( argv[0] && !strcmp( argv[0], "--help" ) ) {
/* actually, not an error, they were asking for help */
E = 0;
} else {
fprintf( stderr, "ERROR: %s\n", errS );
}
free( errS );
}
if ( !E ) {
/* no error, just !argc */
if ( doInfo && info ) hestInfo( stdout, me?me:"", info, parm );
}
if ( doUsage ) hestUsage( E ? stderr : stdout, opt, me?me:"", parm );
if ( doGlossary ) hestGlossary( E ? stderr : stdout, opt, parm );
hestParmFree( parm );
hestOptFree( opt );
exit( 1 );
}
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
int
26 runexp( int *year, int N, int M ) {
int i, m;
memset( year, 0, N*sizeof( int ) );
for ( m=0; m<M; m++ ) {
year[airRandInt( N )]++;
}
for ( i=0; i<N; i++ ) {
if ( year[i] > 1 ) {
return 1;
}
}
return 0;
}
char *info = ( "simulates M people in a room finding out if two or more "
"of them share a birthday. For fun, can vary the number "
"of days in the year." );
int
46 main( int argc, const char *argv[] ) {
airArray *mop;
hestOpt *hopt=NULL;
int i, N, M, P, yes, *year;
unsigned int E;
const char *me;
double crct;
me = argv[0];
mop = airMopNew( );
hestOptAdd( &hopt, "N", "days", airTypeInt, 1, 1, &N, "365",
"# of days in year" );
/* E != P */
hestOptAdd( &hopt, "E", "exps", airTypeInt, 1, 1, &P, "100000",
"number of experiments after which to print out newly "
"computed probability" );
hestOptAdd( &hopt, NULL, "people", airTypeInt, 1, 1, &M, NULL,
"# of people in room" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( N > 1 && M > 0 && P > 1 ) ) {
fprintf( stderr, "%s: need both M, P all > 1, M > 0\n", me );
airMopError( mop ); exit( 1 );
}
if ( !( year = ( int* )calloc( N, sizeof( int ) ) ) ) {
fprintf( stderr, "%s: couldn't calloc( %d, sizeof( int ) )\n", me, N );
airMopError( mop ); exit( 1 );
}
airMopMem( mop, year, airMopAlways );
crct = 1;
for ( i=0; i<M; i++ ) {
crct *= ( double )( N-i )/N;
}
crct = 1-crct;
yes = 0;
E = 1;
airSrandMT( ( int )airTime( ) );
while ( E ) {
yes += runexp( year, N, M );
if ( !( E % P ) ) {
printf( "P = %10d/%10d = %22.20f =?= %22.20f\n",
yes, E, ( double )yes/E, crct );
}
E++;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
/*
** this example has been taken out of the build because GLK was sure
** Fri May 13 00:51:11 CDT 2011 how to handle these annoying warnings:
../hest/test/ex0.c: In function 'main':
../hest/test/ex0.c:32: warning: missing initializer
../hest/test/ex0.c:32: warning: ( near initialization for 'opt[0].sawP' )
../hest/test/ex0.c:34: warning: missing initializer
../hest/test/ex0.c:34: warning: ( near initialization for 'opt[1].sawP' )
*/
int
39 main( int argc, char **argv ) {
static int res[2], v, numIn;
static char **in, *out;
hestOpt opt[] = {
{"res", "sx sy", airTypeInt, 2, 2, res, NULL,
"image resolution"},
{"v", "level", airTypeInt, 1, 1, &v, "0",
"verbosity level"},
{"out", "file", airTypeString, 1, 1, &out, "output.ppm",
"PPM image output"},
{NULL, "input", airTypeString, 1, -1, &in, NULL,
"input image file( s )", &numIn},
{NULL, NULL, 0}
};
char *err = NULL;
if ( hestOptCheck( opt, &err ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
exit( 1 );
}
printf( "hestOpt array looks fine.\n" );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
/*
** this example has been taken out of the build because GLK was sure
** Fri May 13 00:51:11 CDT 2011 how to handle these annoying warnings:
../hest/test/ex1.c: In function 'main':
../hest/test/ex1.c:34: warning: missing initializer
../hest/test/ex1.c:34: warning: ( near initialization for 'opt[0].sawP' )
../hest/test/ex1.c:36: warning: missing initializer
../hest/test/ex1.c:36: warning: ( near initialization for 'opt[1].sawP' )
*/
int
39 main( int argc, char **argv ) {
static int res[2], v, numIn;
static char **in, *out;
static int *mm, mmm;
int n;
hestOpt opt[] = {
{"res", "sx sy", airTypeInt, 2, 2, res, NULL,
"image resolution"},
{"v", "level", airTypeInt, 0, 1, &v, NULL /* "0" */,
"verbosity level"},
{"VV", "level", airTypeInt, 0, 5, &mm, "33 22 11",
"gonzo level", &mmm},
{"out", "file", airTypeString, 1, 1, &out, "output.ppm",
"PPM image output"},
{NULL, "input", airTypeString, 1, -1, &in, NULL,
"input image file( s )", &numIn},
{NULL, NULL, 0}
};
hestParm *parm;
char *err = NULL, info[] =
"This program does nothing in particular, though it does attempt "
"to pose as some sort of command-line image processing program. "
"Any implied functionality is purely coincidental, especially since "
"this software was written by a sleep-deprived grad student.";
parm = hestParmNew( );
parm->respFileEnable = AIR_TRUE;
if ( 1 == argc ) {
/* didn't get anything at all on command line */
hestInfo( stderr, argv[0], info, parm );
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
parm = hestParmFree( parm );
exit( 1 );
}
/* else we got something, see if we can parse it */
if ( hestParse( opt, argc-1, argv+1, &err, parm ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
parm = hestParmFree( parm );
exit( 1 );
}
printf( "( err = %s )\n", err );
printf( "res = %d %d\n", res[0], res[1] );
printf( " v = %d\n", v );
printf( "out = \"%s\"\n", out );
printf( " mm = %d ints:", mmm );
for ( n=0; n<=mmm-1; n++ ) {
printf( " %d", mm[n] );
}
printf( "\n" );
printf( " in = %d files:", numIn );
for ( n=0; n<=numIn-1; n++ ) {
printf( " \"%s\"", in[n] );
}
printf( "\n" );
hestParseFree( opt );
parm = hestParmFree( parm );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
int
27 main( int argc, const char **argv ) {
int res[2], v, numIn;
char **in, *out;
int n;
hestOpt *opt = NULL;
hestParm *parm;
char *err = NULL, info[] =
"This program does nothing in particular, though it does attempt "
"to pose as some sort of command-line image processing program. "
"As usual, any implied functionality is purely coincidental, "
"especially since this is the output of a unicyclist.";
parm = hestParmNew( );
parm->respFileEnable = AIR_TRUE;
opt = NULL;
hestOptAdd( &opt, "res", "sx sy", airTypeInt, 2, 2, res, NULL,
"image resolution" );
hestOptAdd( &opt, "v", "level", airTypeInt, 0, 1, &v, "0",
"verbosity level" );
hestOptAdd( &opt, "out", "file", airTypeString, 1, 1, &out, "output.ppm",
"PPM image output" );
hestOptAdd( &opt, NULL, "input", airTypeString, 1, -1, &in, NULL,
"input image file( s )", &numIn );
if ( 1 == argc ) {
/* didn't get anything at all on command line */
/* print program information ... */
hestInfo( stderr, argv[0], info, parm );
/* ... and usage information ... */
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
/* ... and avoid memory leaks */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
exit( 1 );
}
/* else we got something, see if we can parse it */
if ( hestParse( opt, argc-1, argv+1, &err, parm ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
/* print usage information ... */
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
/* ... and then avoid memory leaks */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
exit( 1 );
}
printf( "( err = %s )\n", err );
printf( "res = %d %d\n", res[0], res[1] );
printf( " v = %d\n", v );
printf( "out = \"%s\"\n", out );
printf( " in = %d files:", numIn );
for ( n=0; n<=numIn-1; n++ ) {
printf( " \"%s\"", in[n] );
}
printf( "\n" );
/* free the memory allocated by parsing ... */
hestParseFree( opt );
/* ... and the other stuff */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
int
27 main( int argc, const char **argv ) {
int res[2], v, numIn;
char **in, *out, *blah[3], *option = NULL;
int n, *ints, numN;
hestOpt *opt = NULL;
hestParm *parm;
char *err = NULL, info[] =
"This program does nothing in particular, though it does attempt "
"to pose as some sort of command-line image processing program. "
"As usual, any implied functionality is purely coincidental, "
"especially since this is the output of a unicyclist.";
parm = hestParmNew( );
parm->respFileEnable = AIR_TRUE;
parm->verbosity = 3;
opt = NULL;
hestOptAdd( &opt, "v, verbose", "level", airTypeInt, 0, 1, &v, "0",
"verbosity level" );
hestOptAdd( &opt, "out", "file", airTypeString, 1, 1, &out, "output.ppm",
"PPM image output" );
hestOptAdd( &opt, "blah", "input", airTypeString, 3, 3, blah, "a b c",
"input image file( s )" );
hestOptAdd( &opt, "option", "opt", airTypeString, 0, 1, &option, "default",
"this is just a test" );
hestOptAdd( &opt, NULL, "input", airTypeString, 1, -1, &in, NULL,
"input image file( s )", &numIn );
hestOptAdd( &opt, "ints", "N", airTypeInt, 1, -1, &ints, "10 20 30",
"a list of integers", &numN );
hestOptAdd( &opt, "res", "sx sy", airTypeInt, 2, 2, res, NULL,
"image resolution" );
printf( "what 0\n" );
if ( 1 == argc ) {
/* didn't get anything at all on command line */
/* print program information ... */
hestInfo( stderr, argv[0], info, parm );
/* ... and usage information ... */
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
/* ... and avoid memory leaks */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
exit( 1 );
}
printf( "what 1\n" );
/* else we got something, see if we can parse it */
if ( hestParse( opt, argc-1, argv+1, &err, parm ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
/* print usage information ... */
hestUsage( stderr, opt, argv[0], parm );
hestGlossary( stderr, opt, parm );
/* ... and then avoid memory leaks */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
printf( " ---- in = %lx\n", ( unsigned long )in );
printf( " ---- blah[0] = %lx\n", ( unsigned long )( blah[0] ) );
printf( " ---- option = %lx\n", ( unsigned long )option );
exit( 1 );
}
printf( "what 2\n" );
printf( "( err = %s )\n", err ? err : "( null )" );
printf( " v = %d\n", v );
printf( "out = \"%s\"\n", out ? out : "( null )" );
printf( "blah = \"%s\" \"%s\" \"%s\"\n", blah[0], blah[1], blah[2] );
printf( "option = \"%s\"\n", option ? option : "( null )" );
printf( "res = %d %d\n", res[0], res[1] );
/*
printf( " ---- in = %lx\n", ( unsigned long )in );
printf( " in = %d files:", numIn );
for ( n=0; n<=numIn-1; n++ ) {
printf( " \"%s\"", in[n] ? in[n] : "( null )" );
}
printf( "\n" );
*/
printf( " ints = %d ints:", numN );
for ( n=0; n<=numN-1; n++ ) {
printf( " %d", ints[n] );
}
printf( "\n" );
printf( "what 3\n" );
/* free the memory allocated by parsing ... */
hestParseFree( opt );
/* ... and the other stuff */
opt = hestOptFree( opt );
parm = hestParmFree( parm );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
int
27 parse( void *_ptr, char *str, char *err ) {
double *ptr;
int ret;
ptr = _ptr;
ret = sscanf( str, "%lf, %lf", ptr + 0, ptr + 1 );
if ( 2 != ret ) {
sprintf( err, "parsed %d ( not 2 ) doubles", ret );
return 1;
}
return 0;
}
hestCB cbinfo = {
2*sizeof( double ),
"location",
parse,
NULL
};
int
48 main( int argc, const char **argv ) {
double single[2], triple[6], maybe[2], *many;
int howMany, i, N;
hestOpt *opt = NULL;
char *err = NULL;
hestOptAdd( &opt, "A", "x, y", airTypeOther, 1, 1, single,
"30, 50", "testing A", NULL, NULL, &cbinfo );
hestOptAdd( &opt, "B", "x1, y1 x2, y2 x3, y3", airTypeOther, 3, 3, triple,
"1, 2 3, 4 5, 6", "testing B", NULL, NULL, &cbinfo );
hestOptAdd( &opt, "C", "mx, my", airTypeOther, 0, 1, maybe,
"-0.1, -0.2", "testing C. The utility of this can be better "
"demonstrated in the following manner:\n "
"- wash the dishes\n "
"- put the dishes in the cupboard\n "
"- watch football on TV\n "
"- remember to walk the dog",
NULL, NULL, &cbinfo );
hestOptAdd( &opt, "D", "nx, ny", airTypeOther, 1, -1, &many,
"8, 8 7, 7", "testing D", &howMany, NULL, &cbinfo );
hestOptAdd( &opt, "int", "N", airTypeInt, 1, 1, &N,
NULL, "an integer" );
if ( hestParse( opt, argc-1, argv+1, &err, NULL ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
hestUsage( stderr, opt, argv[0], NULL );
hestGlossary( stderr, opt, NULL );
exit( 1 );
}
printf( "single: ( %g, %g )\n", single[0], single[1] );
printf( "triple: ( %g, %g ) ( %g, %g ) ( %g, %g )\n", triple[0], triple[1],
triple[2], triple[3], triple[4], triple[5] );
printf( "maybe: ( %g, %g )\n", maybe[0], maybe[1] );
printf( "many( %d ):", howMany );
for ( i=0; i<=howMany-1; i++ ) {
printf( " ( %g, %g )", many[0 + 2*i], many[1 + 2*i] );
}
printf( "\n" );
hestParseFree( opt );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
int
27 parse( void *_ptr, char *str, char *err ) {
char **ptrP;
ptrP = _ptr;
*ptrP = airStrdup( str );
if ( !( *ptrP ) ) {
sprintf( err, "couldn't strdup( ) str" );
return 1;
}
airToUpper( *ptrP );
return 0;
}
hestCB cbinfo = {
sizeof( char* ),
"token",
parse,
airFree
};
int
48 main( int argc, const char **argv ) {
char *single, *triple[3], *maybe, **many;
int howMany, i, N;
hestOpt *opt = NULL;
char *err = NULL;
unsigned int copi, opi, opn;
hestOptAdd( &opt, "A", "token", airTypeOther, 1, 1, &single,
"alpha", "testing A", NULL, NULL, &cbinfo );
hestOptAdd( &opt, "B", "tok1 tok2 tok3", airTypeOther, 3, 3, triple,
"beta psi rho", "testing B", NULL, NULL, &cbinfo );
copi =
hestOptAdd( &opt, "C", "mtok", airTypeOther, 0, 1, &maybe,
"gamma", "testing C", NULL, NULL, &cbinfo );
hestOptAdd( &opt, "D", "tok", airTypeOther, 1, -1, &many,
"kappa omega", "testing D", &howMany, NULL, &cbinfo );
opn =
hestOptAdd( &opt, "int", "N", airTypeInt, 1, 1, &N,
NULL, "an integer" );
opn++;
if ( hestParse( opt, argc-1, argv+1, &err, NULL ) ) {
fprintf( stderr, "ERROR: %s\n", err ); free( err );
hestUsage( stderr, opt, argv[0], NULL );
hestGlossary( stderr, opt, NULL );
exit( 1 );
}
printf( "single: %s\n", single );
printf( "triple: %s %s %s\n", triple[0], triple[1], triple[2] );
printf( "maybe: %s\n", maybe );
printf( "many( %d ):", howMany );
for ( i=0; i<=howMany-1; i++ ) {
printf( " %s", many[i] );
}
printf( "\n" );
printf( "source( %s ) = %d\n\n", opt[copi].flag, opt[copi].source );
for ( opi=0; opi<opn; opi++ ) {
printf( "source( opt[%u] ) = %d\n", opi, opt[opi].source );
}
hestParseFree( opt );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
char *me;
extern void
29 _airCloPrintStr( FILE *f, int indent, int already, int width, char *_str );
int
32 main( ) {
char line[1025];
strcpy( line, "bingo \t\t bob \t boasts \n bumperstickers \n " );
airOneLinify( line );
printf( "|%s|\n", line );
strcpy( line, " \t \n " );
airOneLinify( line );
printf( "|%s|\n", line );
strcpy( line, "Director, writor, and editor John Sayles goes happily "
"against the grain with this tale, set in a fictional Latin "
"American country and shot almost entirely in Spanish and Indian "
"dialects. The story follows a well-to-do physician who has trained "
"young doctors to work in the countryside among local Mayan Indians. "
"He now wants to find each of the 'ambassadors of health, ' but as "
"the film unfolds, he comes to realize that a civil war is engulfing "
"his country and the Indians are practically ensalved. The\tMen\t"
"with\tGuns ( Los\tHombres\tArmados ) have forever left their mark, "
"too, on "
"his students. Sayles based his idea for the film, shot in Mexico, "
"on the 36-year long civil war in Guatemala, which began in 1960." );
printf( "airStrlen( line ) = %d\n", ( int )airStrlen( line ) );
fprintf( stdout, "This was found on my desk: " );
_hestPrintStr( stdout, 10, strlen( "This was found on my desk: " ),
80, line, AIR_FALSE );
fprintf( stdout, "This was found on my desk: " );
_hestPrintStr( stdout, 10, strlen( "This was found on my desk: " ),
79, line, AIR_FALSE );
fprintf( stdout, "This was found on my desk: " );
_hestPrintStr( stdout, 10, strlen( "This was found on my desk: " ),
78, line, AIR_FALSE );
fprintf( stdout, "This was found on my desk: " );
_hestPrintStr( stdout, 10, strlen( "This was found on my desk: " ),
77, line, AIR_FALSE );
fprintf( stdout, "This was found on my desk: " );
_hestPrintStr( stdout, 10, strlen( "This was found on my desk: " ),
76, line, AIR_FALSE );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../hest.h"
char *tmplInfo = ( "shows minimal usage of hest with mops" );
int
28 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
int size[3];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "s", "sx sy sz", airTypeInt, 3, 3, size, "128 128 128",
"dimensions of output volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, tmplInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hest.h"
#include "privateHest.h"
/*
** don't ask
*/
void
31 _hestSetBuff( char *B, const hestOpt *O, const hestParm *P,
int showshort, int showlong ) {
char copy[AIR_STRLEN_HUGE], *sep;
int max; unsigned int len;
max = _hestMax( O->max );
if ( O->flag ) {
strcpy( copy, O->flag );
if ( ( sep = strchr( copy, P->multiFlagSep ) ) ) {
*sep = 0;
if ( showshort ) {
strcat( B, "-" );
strcat( B, copy );
}
if ( showlong ) {
if ( showshort ) {
len = AIR_UINT( strlen( B ) );
B[len] = P->multiFlagSep;
B[len+1] = '\0';
}
strcat( B, "--" );
strcat( B, sep+1 );
}
} else {
strcat( B, "-" );
strcat( B, O->flag );
}
if ( O->min || max ) {
strcat( B, "\t" );
}
}
if ( !O->min && max ) {
strcat( B, "[" );
}
if ( O->min || max ) {
strcat( B, "<" );
strcat( B, O->name );
if ( ( int )( O->min ) < max && max > 1 ) { /* HEY scrutinize casts */
strcat( B, "\t..." );
}
strcat( B, ">" );
}
if ( !O->min && max ) {
strcat( B, "]" );
}
}
/* early version of _hestSetBuff( ) function */
#define SETBUFF( B, O ) \
strcat( B, O.flag ? "-" : "" ), \
strcat( B, O.flag ? O.flag : "" ), \
strcat( B, O.flag && ( O.min || _hestMax( O.max ) ) ? "\t" : "" ), \
strcat( B, !O.min && _hestMax( O.max ) ? "[" : "" ), \
strcat( B, O.min || _hestMax( O.max ) ? "<" : "" ), \
strcat( B, O.min || _hestMax( O.max ) ? O.name : "" ), \
strcat( B, ( O.min < _hestMax( O.max ) && ( _hestMax( O.max ) > 1 ) ) ? " ...": "" ), \
87 strcat( B, O.min || _hestMax( O.max ) ? ">" : "" ), \
strcat( B, !O.min && _hestMax( O.max ) ? "]" : "" );
/*
** _hestPrintStr( )
**
** not a useful function. Do not use.
*/
void
_hestPrintStr( FILE *f, unsigned int indent, unsigned int already,
unsigned int width, const char *_str, int bslash ) {
char *str, *ws, *last;
int newed=AIR_FALSE; unsigned int wrd, nwrd, ii, pos;
str = airStrdup( _str );
nwrd = airStrntok( str, " " );
pos = already;
for ( wrd=0; wrd<nwrd; wrd++ ) {
/* we used airStrtok( ) to delimit words on spaces ... */
ws = airStrtok( !wrd ? str : NULL, " ", &last );
/* ... but then convert tabs to spaces */
airStrtrans( ws, '\t', ' ' );
if ( pos + 1 + AIR_UINT( strlen( ws ) ) <= width - !!bslash ) {
/* if this word would still fit on the current line */
if ( wrd && !newed ) fprintf( f, " " );
fprintf( f, "%s", ws );
pos += 1 + AIR_UINT( strlen( ws ) );
newed = AIR_FALSE;
} else {
/* else we start a new line and print the indent */
if ( bslash ) {
fprintf( f, " \\" );
}
fprintf( f, "\n" );
for ( ii=0; ii<indent; ii++ ) {
fprintf( f, " " );
}
fprintf( f, "%s", ws );
pos = indent + AIR_UINT( strlen( ws ) );
}
/* if the last character of the word was a newline, then indent */
if ( '\n' == ws[strlen( ws )-1] ) {
for ( ii=0; ii<indent; ii++ ) {
fprintf( f, " " );
}
pos = indent;
newed = AIR_TRUE;
} else {
newed = AIR_FALSE;
}
}
fprintf( f, "\n" );
free( str );
}
/*
******** hestMinNumArgs
**
** The idea is that this helps quickly determine if the options given
** on the command line are insufficient, in order to produce general
** usage information instead of some specific parse error.
**
** Because hest is strictly agnostic with respect to how many command-line
** arguments actually constitute the command itself ( "rmdir": one argument,
** "cvs checkout": two arguments ), it only concerns itself with the
** command-line arguments following the command.
**
** Thus, hestMinMinArgs( ) returns the minimum number of command-line
** arguments ( following the command ) that could be valid. If your
** command is only one argument ( like "rmdir" ), then you might use
** the true argc passed by the OS to main( ) as such:
**
** if ( argc-1 < hestMinNumArgs( opt ) ) {
** ... usage ...
** }
**
** But if your command is two arguments ( like "cvs checkout" ):
**
** if ( argc-2 < hestMinNumArgs( opt ) ) {
** ... usage ...
** }
**
** HOWEVER! don't forget the response files can complicate all this:
** in one argument a response file can provide information for any
** number of arguments, and the argc itself is kind of meaningless.
** The code examples above only really apply when
** hparm->respFileEnable is false. For example, in unrrdu ( private.h )
174 ** we find:
**
** if ( ( hparm->respFileEnable && !argc ) ||
** ( !hparm->respFileEnable && argc < hestMinNumArgs( opt ) ) ) {
** ... usage ...
** }
**
*/
int
hestMinNumArgs( hestOpt *opt ) {
hestParm *parm;
int i, count, numOpts;
parm = hestParmNew( );
if ( _hestPanic( opt, NULL, parm ) ) {
hestParmFree( parm );
return _hestMax( -1 );
}
count = 0;
numOpts = _hestNumOpts( opt );
for ( i=0; i<numOpts; i++ ) {
if ( !opt[i].dflt ) {
count += opt[i].min;
if ( !( 0 == opt[i].min && 0 == opt[i].max ) ) {
198 count += !!opt[i].flag;
}
}
}
hestParmFree( parm );
return count;
}
void
hestInfo( FILE *file, const char *argv0, const char *info,
const hestParm *_parm ) {
hestParm *parm;
parm = _parm ? NULL : hestParmNew( );
/* how to const-correctly use parm or _parm in an expression */
#define PARM ( _parm ? _parm : parm )
if ( info ) {
if ( argv0 ) {
fprintf( file, "\n%s: ", argv0 );
_hestPrintStr( file, 0, AIR_UINT( strlen( argv0 ) ) + 2,
PARM->columns, info, AIR_FALSE );
} else {
221 fprintf( file, "ERROR: hestInfo got NULL argv0\n" );
}
}
if ( parm ) {
hestParmFree( parm );
}
}
void
hestUsage( FILE *f, hestOpt *opt, const char *argv0,
const hestParm *_parm ) {
int i, numOpts;
char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE];
hestParm *parm;
parm = _parm ? NULL : hestParmNew( );
if ( _hestPanic( opt, NULL, PARM ) ) {
/* we can't continue; the opt array is botched */
if ( parm ) {
hestParmFree( parm );
}
return;
}
numOpts = _hestNumOpts( opt );
fprintf( f, "\n" );
strcpy( buff, "Usage: " );
strcat( buff, argv0 ? argv0 : "" );
if ( PARM->respFileEnable ) {
sprintf( tmpS, " [%cfile\t...]", PARM->respFileFlag );
strcat( buff, tmpS );
}
for ( i=0; i<numOpts; i++ ) {
strcat( buff, " " );
if ( 1 == opt[i].kind || ( opt[i].flag && opt[i].dflt ) )
strcat( buff, "[" );
_hestSetBuff( buff, opt + i, PARM, AIR_TRUE, AIR_TRUE );
if ( 1 == opt[i].kind || ( opt[i].flag && opt[i].dflt ) )
strcat( buff, "]" );
}
263 _hestPrintStr( f, AIR_UINT( strlen( "Usage: " ) ), 0,
PARM->columns, buff, AIR_TRUE );
if ( parm ) {
hestParmFree( parm );
}
return;
}
void
hestGlossary( FILE *f, hestOpt *opt, const hestParm *_parm ) {
int i, j, maxlen, numOpts; unsigned int len;
char buff[2*AIR_STRLEN_HUGE], tmpS[AIR_STRLEN_HUGE];
hestParm *parm;
parm = _parm ? NULL : hestParmNew( );
if ( _hestPanic( opt, NULL, PARM ) ) {
/* we can't continue; the opt array is botched */
if ( parm ) {
hestParmFree( parm );
}
return;
}
numOpts = _hestNumOpts( opt );
maxlen = 0;
if ( numOpts ) {
fprintf( f, "\n" );
}
for ( i=0; i<numOpts; i++ ) {
strcpy( buff, "" );
_hestSetBuff( buff, opt + i, PARM, AIR_TRUE, AIR_FALSE );
maxlen = AIR_MAX( ( int )strlen( buff ), maxlen );
}
if ( PARM->respFileEnable ) {
sprintf( buff, "%cfile ...", PARM->respFileFlag );
len = AIR_UINT( strlen( buff ) );
for ( j=len; j<maxlen; j++ ) {
fprintf( f, " " );
}
fprintf( f, "%s = ", buff );
strcpy( buff, "response file( s ) containing command-line arguments" );
_hestPrintStr( f, maxlen + 3, maxlen + 3, PARM->columns, buff, AIR_FALSE );
}
for ( i=0; i<numOpts; i++ ) {
strcpy( buff, "" );
_hestSetBuff( buff, opt + i, PARM, AIR_TRUE, AIR_FALSE );
airOneLinify( buff );
len = AIR_UINT( strlen( buff ) );
for ( j=len; j<maxlen; j++ ) {
fprintf( f, " " );
}
fprintf( f, "%s", buff );
strcpy( buff, "" );
#if 1
if ( opt[i].flag && strchr( opt[i].flag, PARM->multiFlagSep ) ) {
/* there is a long-form flag as well as short */
_hestSetBuff( buff, opt + i, PARM, AIR_FALSE, AIR_TRUE );
strcat( buff, " = " );
fprintf( f, " , " );
} else {
/* there is only a short-form flag */
fprintf( f, " = " );
}
#else
fprintf( f, " = " );
#endif
if ( opt[i].info ) {
strcat( buff, opt[i].info );
}
if ( ( opt[i].min || _hestMax( opt[i].max ) )
&& ( !( 2 == opt[i].kind
&& airTypeEnum == opt[i].type
&& PARM->elideSingleEnumType ) )
&& ( !( 2 == opt[i].kind
&& airTypeOther == opt[i].type
&& PARM->elideSingleOtherType ) )
) {
/* if there are newlines in the info, then we want to clarify the
type by printing it on its own line */
if ( opt[i].info && strchr( opt[i].info, '\n' ) ) {
strcat( buff, "\n " );
}
else {
strcat( buff, " " );
}
strcat( buff, "( " );
if ( opt[i].min == 0 && _hestMax( opt[i].max ) == 1 ) {
strcat( buff, "optional\t" );
}
else {
if ( ( int )opt[i].min == _hestMax( opt[i].max ) && _hestMax( opt[i].max ) > 1 ) { /* HEY scrutinize casts */
sprintf( tmpS, "%d\t", _hestMax( opt[i].max ) );
strcat( buff, tmpS );
}
else if ( ( int )opt[i].min < _hestMax( opt[i].max ) ) { /* HEY scrutinize casts */
if ( -1 == opt[i].max ) {
sprintf( tmpS, "%d\tor\tmore\t", opt[i].min );
}
else {
sprintf( tmpS, "%d..%d\t", opt[i].min, _hestMax( opt[i].max ) );
}
strcat( buff, tmpS );
}
}
sprintf( tmpS, "%s%s",
( airTypeEnum == opt[i].type
? opt[i].enm->name
: ( airTypeOther == opt[i].type
? opt[i].CB->type
: airTypeStr[opt[i].type] ) ),
( _hestMax( opt[i].max ) > 1
? ( airTypeOther == opt[i].type
&& 'y' == opt[i].CB->type[airStrlen( opt[i].CB->type )-1]
&& PARM->cleverPluralizeOtherY
? "\bies"
: "s" )
: "" ) );
strcat( buff, tmpS );
strcat( buff, " )" );
}
/*
fprintf( stderr, "!%s: PARM->elideSingleOtherDefault = %d\n",
"hestGlossary", PARM->elideSingleOtherDefault );
*/
if ( opt[i].dflt
&& ( opt[i].min || _hestMax( opt[i].max ) )
&& ( !( 2 == opt[i].kind
&& ( airTypeFloat == opt[i].type || airTypeDouble == opt[i].type )
&& !AIR_EXISTS( airAtod( opt[i].dflt ) )
&& PARM->elideSingleNonExistFloatDefault ) )
&& ( !( ( 3 == opt[i].kind || 5 == opt[i].kind )
&& ( airTypeFloat == opt[i].type || airTypeDouble == opt[i].type )
&& !AIR_EXISTS( airAtod( opt[i].dflt ) )
&& PARM->elideMultipleNonExistFloatDefault ) )
&& ( !( 2 == opt[i].kind
&& airTypeOther == opt[i].type
&& PARM->elideSingleOtherDefault ) )
&& ( !( 2 == opt[i].kind
&& airTypeString == opt[i].type
&& PARM->elideSingleEmptyStringDefault
&& 0 == airStrlen( opt[i].dflt ) ) )
&& ( !( ( 3 == opt[i].kind || 5 == opt[i].kind )
&& airTypeString == opt[i].type
&& PARM->elideMultipleEmptyStringDefault
&& 0 == airStrlen( opt[i].dflt ) ) )
) {
/* if there are newlines in the info, then we want to clarify the
default by printing it on its own line */
if ( opt[i].info && strchr( opt[i].info, '\n' ) ) {
strcat( buff, "\n " );
}
else {
strcat( buff, "; " );
}
strcat( buff, "default:\t" );
strcpy( tmpS, opt[i].dflt );
airStrtrans( tmpS, ' ', '\t' );
strcat( buff, "\"" );
strcat( buff, tmpS );
strcat( buff, "\"" );
}
_hestPrintStr( f, maxlen + 3, maxlen + 3, PARM->columns, buff, AIR_FALSE );
}
if ( parm ) {
hestParmFree( parm );
}
return;
}
#undef PARM
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
** dehex: simple stand-alone hex decoder
**
** Compile with:
** cc -o dehex dehex.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
void
42 dehexUsage( char *me ) {
/* 0 1 2 ( 2/3 ) */
fprintf( stderr, "usage: %s <in> [<out>]\n", me );
fprintf( stderr, " <in>: file to read hex data from\n" );
fprintf( stderr, "<out>: file to write raw data to; "
"uses stdout by default\n" );
fprintf( stderr, " \"-\" can be used to refer to stdin/stdout\n" );
exit( 1 );
}
void
53 dehexFclose( FILE *file ) {
if ( !( stdin == file || stdout == file ) ) {
fclose( file );
}
}
int
dehexTable[128] = {
/* 0 1 2 3 4 5 6 7 8 9 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, /* 0 */
-1, -1, -1, -1, -2, -2, -2, -2, -2, -2, /* 10 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 20 */
-2, -2, -1, -2, -2, -2, -2, -2, -2, -2, /* 30 */
-2, -2, -2, -2, -2, -2, -2, -2, 0, 1, /* 40 */
2, 3, 4, 5, 6, 7, 8, 9, -2, -2, /* 50 */
-2, -2, -2, -2, -2, 10, 11, 12, 13, 14, /* 60 */
15, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 70 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 80 */
-2, -2, -2, -2, -2, -2, -2, 10, 11, 12, /* 90 */
13, 14, 15, -2, -2, -2, -2, -2, -2, -2, /* 100 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 110 */
-2, -2, -2, -2, -2, -2, -2, -2 /* 120 */
};
int
79 main( int argc, char *argv[] ) {
char *me, *inS, *outS;
FILE *fin, *fout;
int car=0, byte, nibble, even;
me = argv[0];
if ( !( 2 == argc || 3 == argc ) )
dehexUsage( me );
inS = argv[1];
if ( !strcmp( "-", inS ) ) {
fin = stdin;
} else {
fin = fopen( inS, "r" );
if ( !fin ) {
fprintf( stderr, "\n%s: couldn't fopen( \"%s\", \"rb\" ): %s\n\n",
me, inS, strerror( errno ) );
dehexUsage( me );
}
}
if ( 2 == argc ) {
fout = stdout;
} else {
outS = argv[2];
if ( !strcmp( "-", outS ) ) {
fout = stdout;
#ifdef _WIN32
_setmode( _fileno( fout ), _O_BINARY );
#endif
} else {
fout = fopen( outS, "w" );
if ( !fout ) {
fprintf( stderr, "\n%s: couldn't fopen( \"%s\", \"w\" ): %s\n\n",
me, outS, strerror( errno ) );
dehexUsage( me );
}
}
}
byte = 0;
even = 1;
for ( car=fgetc( fin ); EOF != car; car=fgetc( fin ) ) {
nibble = dehexTable[car & 127];
if ( -2 == nibble ) {
/* its an invalid character */
break;
}
if ( -1 == nibble ) {
/* its white space */
continue;
}
if ( even ) {
byte = nibble << 4;
} else {
byte += nibble;
if ( EOF == fputc( byte, fout ) ) {
fprintf( stderr, "%s: error writing!!!\n", me );
exit( 1 );
}
}
even = 1 - even;
}
if ( EOF != car ) {
fprintf( stderr, "\n%s: got invalid character '%c'\n\n", me, car );
dehexUsage( me );
}
dehexFclose( fin );
dehexFclose( fout );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
** enhex: simple stand-alone hex encoder
**
** Compile with:
** cc -o enhex enhex.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#include <fcntl.h>
#endif
int
enhexColumns = 70; /* number of characters per line */
void
45 enhexUsage( char *me ) {
/* 0 1 2 ( 2/3 ) */
fprintf( stderr, "usage: %s <in> [<out>]\n", me );
fprintf( stderr, " <in>: file to read raw data from\n" );
fprintf( stderr, "<out>: file to write hex data to; "
"uses stdout by default\n" );
fprintf( stderr, " \"-\" can be used to refer to stdin/stdout\n" );
exit( 1 );
}
void
56 enhexFclose( FILE *file ) {
if ( !( stdin == file || stdout == file ) ) {
fclose( file );
}
}
int
enhexTable[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
int
71 main( int argc, char *argv[] ) {
char *me, *inS, *outS;
FILE *fin, *fout;
int car=0, col;
me = argv[0];
if ( !( 2 == argc || 3 == argc ) )
enhexUsage( me );
inS = argv[1];
if ( !strcmp( "-", inS ) ) {
fin = stdin;
#ifdef _WIN32
_setmode( _fileno( fin ), _O_BINARY );
#endif
} else {
fin = fopen( inS, "rb" );
if ( !fin ) {
fprintf( stderr, "\n%s: couldn't fopen( \"%s\", \"rb\" ): %s\n\n",
me, inS, strerror( errno ) );
enhexUsage( me );
}
}
if ( 2 == argc ) {
fout = stdout;
} else {
outS = argv[2];
if ( !strcmp( "-", outS ) ) {
fout = stdout;
} else {
fout = fopen( outS, "w" );
if ( !fout ) {
fprintf( stderr, "\n%s: couldn't fopen( \"%s\", \"w\" ): %s\n\n",
me, outS, strerror( errno ) );
enhexUsage( me );
}
}
}
col = 0;
car = fgetc( fin );
while ( EOF != car ) {
if ( col > enhexColumns ) {
fprintf( fout, "\n" );
col = 0;
}
fprintf( fout, "%c%c", enhexTable[car>>4], enhexTable[car&15] );
col += 2;
car = fgetc( fin );
}
if ( 2 != col ) {
fprintf( fout, "\n" );
}
enhexFclose( fin );
enhexFclose( fout );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hoover.h"
const int
hooverPresent = 42;
const char *
hooverBiffKey = "hoover";
int
hooverDefVolCentering = nrrdCenterNode;
int
hooverDefImgCentering = nrrdCenterCell;
const char *
_hooverErrStr[HOOVER_ERR_MAX+1] = {
"( unknown_err )",
"Initialization",
"RenderBegin",
"ThreadCreate",
"ThreadBegin",
"RayBegin",
"Sample",
"RayEnd",
"ThreadEnd",
"ThreadJoin",
"RenderEnd"
};
const airEnum
_hooverErr = {
"error",
HOOVER_ERR_MAX,
_hooverErrStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
60 const airEnum *const
hooverErr = &_hooverErr;
/*
hooverErrNone,
hooverErrInit,
hooverErrRenderBegin,
hooverErrThreadCreate,
hooverErrThreadBegin,
hooverErrRayBegin,
hooverErrSample,
hooverErrRayEnd,
hooverErrThreadEnd,
hooverErrThreadJoin,
hooverErrRenderEnd,
hooverErrLast
*/
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hoover.h"
hooverContext *
27 hooverContextNew( ) {
hooverContext *ctx;
ctx = ( hooverContext * )calloc( 1, sizeof( hooverContext ) );
if ( ctx ) {
ctx->cam = limnCameraNew( );
ELL_3V_SET( ctx->volSize, 0, 0, 0 );
ELL_3V_SET( ctx->volSpacing, AIR_NAN, AIR_NAN, AIR_NAN );
ctx->volCentering = hooverDefVolCentering;
ctx->shape = NULL;
ctx->imgSize[0] = ctx->imgSize[1] = 0;
ctx->imgCentering = hooverDefImgCentering;
ctx->user = NULL;
ctx->numThreads = 1;
ctx->workIdx = 0;
ctx->workMutex = NULL;
ctx->renderBegin = hooverStubRenderBegin;
ctx->threadBegin = hooverStubThreadBegin;
ctx->rayBegin = hooverStubRayBegin;
ctx->sample = hooverStubSample;
ctx->rayEnd = hooverStubRayEnd;
ctx->threadEnd = hooverStubThreadEnd;
ctx->renderEnd = hooverStubRenderEnd;
}
return( ctx );
}
int
55 hooverContextCheck( hooverContext *ctx ) {
static const char me[]="hooverContextCheck";
int sxe, sye, sze, minSize, centr;
if ( !ctx ) {
biffAddf( HOOVER, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdCenter, ctx->imgCentering ) ) {
biffAddf( HOOVER, "%s: pixel centering ( %d ) invalid",
me, ctx->imgCentering );
return 1;
}
centr = ( ctx->shape ? ctx->shape->center : ctx->volCentering );
if ( airEnumValCheck( nrrdCenter, centr ) ) {
biffAddf( HOOVER, "%s: voxel centering ( %d ) invalid", me, centr );
return 1;
}
if ( limnCameraAspectSet( ctx->cam,
ctx->imgSize[0], ctx->imgSize[1], ctx->imgCentering )
|| limnCameraUpdate( ctx->cam ) ) {
biffMovef( HOOVER, LIMN, "%s: trouble setting up camera", me );
return 1;
}
if ( ctx->shape ) {
if ( !ELL_4M_EXISTS( ctx->shape->ItoW ) ) {
biffAddf( HOOVER, "%s: given shape doesn't seem to be set", me );
return 1;
}
} else {
minSize = ( nrrdCenterCell == centr ? 1 : 2 );
if ( !( ctx->volSize[0] >= minSize
&& ctx->volSize[1] >= minSize
&& ctx->volSize[2] >= minSize ) ) {
biffAddf( HOOVER, "%s: volume dimensions ( %dx%dx%d ) too small", me,
ctx->volSize[0], ctx->volSize[1], ctx->volSize[2] );
return 1;
}
sxe = AIR_EXISTS( ctx->volSpacing[0] );
sye = AIR_EXISTS( ctx->volSpacing[1] );
sze = AIR_EXISTS( ctx->volSpacing[2] );
if ( !sxe && !sye && !sze ) {
/* none of the incoming spacings existed, we'll go out on a limb
and assume unit spacing */
ctx->volSpacing[0] = nrrdDefaultSpacing;
ctx->volSpacing[1] = ctx->volSpacing[2] = ctx->volSpacing[0];
fprintf( stderr, "%s: WARNING: assuming spacing %g for all axes\n",
me, ctx->volSpacing[0] );
/* HEY : nrrdDefaultSpacing need not be the same as gageParm's
defaultSpacing, but we don't know anything about gage here,
so what else can we do? */
} else if ( sxe && sye && sze ) {
/* all existed */
if ( !( ctx->volSpacing[0] > 0.0
&& ctx->volSpacing[1] > 0.0
&& ctx->volSpacing[2] > 0.0 ) ) {
biffAddf( HOOVER, "%s: volume spacing ( %gx%gx%g ) invalid", me,
ctx->volSpacing[0], ctx->volSpacing[1], ctx->volSpacing[2] );
return 1;
}
} else {
/* some existed, some didn't */
biffAddf( HOOVER, "%s: spacings %g, %g, %g don't all exist or not", me,
ctx->volSpacing[0], ctx->volSpacing[1], ctx->volSpacing[2] );
return 1;
}
}
if ( !( ctx->imgSize[0] > 0 && ctx->imgSize[1] > 0 ) ) {
biffAddf( HOOVER, "%s: image dimensions ( %dx%d ) invalid", me,
ctx->imgSize[0], ctx->imgSize[1] );
return 1;
}
if ( !( ctx->numThreads >= 1 ) ) {
biffAddf( HOOVER, "%s: number threads ( %d ) invalid", me, ctx->numThreads );
return 1;
}
if ( !( ctx->numThreads <= HOOVER_THREAD_MAX ) ) {
biffAddf( HOOVER, "%s: sorry, number threads ( %d ) > max ( %d )", me,
ctx->numThreads, HOOVER_THREAD_MAX );
return 1;
}
if ( !ctx->renderBegin ) {
biffAddf( HOOVER, "%s: need a non-NULL begin rendering callback", me );
return 1;
}
if ( !ctx->rayBegin ) {
biffAddf( HOOVER, "%s: need a non-NULL begin ray callback", me );
return 1;
}
if ( !ctx->threadBegin ) {
biffAddf( HOOVER, "%s: need a non-NULL begin thread callback", me );
return 1;
}
if ( !ctx->sample ) {
biffAddf( HOOVER, "%s: need a non-NULL sampler callback function", me );
return 1;
}
if ( !ctx->rayEnd ) {
biffAddf( HOOVER, "%s: need a non-NULL end ray callback", me );
return 1;
}
if ( !ctx->threadEnd ) {
biffAddf( HOOVER, "%s: need a non-NULL end thread callback", me );
return 1;
}
if ( !ctx->renderEnd ) {
biffAddf( HOOVER, "%s: need a non-NULL end render callback", me );
return 1;
}
return 0;
}
void
169 hooverContextNix( hooverContext *ctx ) {
if ( ctx ) {
limnCameraNix( ctx->cam );
/* workMutex is cleaned up at end of render */
free( ctx );
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hoover.h"
/*
** learned: if you're going to "simplify" code which computes some
** floating point value within a loop using AFFINE( ) on the loop
** control variable, by simply incrementing that value with the
** correct amount iteration, BE SURE THAT THE INCREMENTING IS DONE in
** every possible control path of the loop ( wasn't incrementing ray
** sample position if first sample wasn't inside the volume )
*/
/*
** _hooverLearnLengths( )
**
** This is where we enforce the constraint that the volume always fit
** inside a cube with edge length 2, centered at the origin.
**
** volHLen[i] is the HALF the length of the volume along axis i
**
** NOTE: none of this comes into play if we have ctx->shape
*/
void
46 _hooverLearnLengths( double volHLen[3], double voxLen[3], hooverContext *ctx ) {
double maxLen;
int numSamples[3], numElements[3];
ELL_3V_COPY( numSamples, ctx->volSize );
if ( nrrdCenterNode == ctx->volCentering ) {
numElements[0] = numSamples[0]-1;
numElements[1] = numSamples[1]-1;
numElements[2] = numSamples[2]-1;
} else {
numElements[0] = numSamples[0];
numElements[1] = numSamples[1];
numElements[2] = numSamples[2];
}
volHLen[0] = numElements[0]*ctx->volSpacing[0];
volHLen[1] = numElements[1]*ctx->volSpacing[1];
volHLen[2] = numElements[2]*ctx->volSpacing[2];
maxLen = AIR_MAX( volHLen[0], volHLen[1] );
maxLen = AIR_MAX( volHLen[2], maxLen );
volHLen[0] /= maxLen;
volHLen[1] /= maxLen;
volHLen[2] /= maxLen;
voxLen[0] = 2*volHLen[0]/numElements[0];
voxLen[1] = 2*volHLen[1]/numElements[1];
voxLen[2] = 2*volHLen[2]/numElements[2];
}
/*
** _hooverExtraContext struct
**
** Like hooverContext, this is READ-ONLY information which is not specific
** to any thread.
** Unlike hooverContext, it is solely for the benefit of the calculations
** done in _hooverThreadBody.
**
** No one outside hoover should need to know about this.
*/
typedef struct {
double volHLen[3], /* length of x, y, z edges of volume bounding box */
voxLen[3], /* length of x, y, z edges of voxels */
uBase, uCap, /* uMin and uMax as seen on the near cutting plane */
vBase, vCap, /* analogous to uBase and uCap */
rayZero[3]; /* location of near plane, line of sight interxion */
} _hooverExtraContext;
_hooverExtraContext *
92 _hooverExtraContextNew( hooverContext *ctx ) {
_hooverExtraContext *ec;
ec = ( _hooverExtraContext * )calloc( 1, sizeof( _hooverExtraContext ) );
if ( ec ) {
if ( ctx->shape ) {
ELL_3V_NAN_SET( ec->volHLen );
ELL_3V_NAN_SET( ec->voxLen );
} else {
_hooverLearnLengths( ec->volHLen, ec->voxLen, ctx );
}
ELL_3V_SCALE_ADD2( ec->rayZero,
1.0, ctx->cam->from,
ctx->cam->vspNeer, ctx->cam->N );
}
return ec;
}
_hooverExtraContext *
111 _hooverExtraContextNix( _hooverExtraContext *ec ) {
if ( ec ) {
free( ec );
}
return NULL;
}
/*
** _hooverThreadArg struct
**
** A pointer to this is passed to _hooverThreadBody. It contains all the
** information which is not thread-specific, and all the thread-specific
** information known at the level of hooverRender.
**
** For simplicity sake, a pointer to a struct of this type is also
** returned from _hooverThreadBody, so this is where we store an
** error-signaling return value ( errCode ), and what function had
** trouble ( whichErr ).
*/
typedef struct {
/* ----------------------- input */
hooverContext *ctx;
_hooverExtraContext *ec;
void *render;
int whichThread;
/* ----------------------- output */
int whichErr;
int errCode;
} _hooverThreadArg;
void *
143 _hooverThreadBody( void *_arg ) {
_hooverThreadArg *arg;
void *thread;
int ret, /* to catch return values from callbacks */
sampleI, /* which sample we're on */
inside, /* we're inside the volume */
vI, uI; /* integral coords in image */
double tmp,
mm, /* lowest position in index space, for all axes */
Mx, My, Mz, /* highest position in index space on each axis */
u, v, /* floating-point coords in image */
uvScale, /* how to scale ( u, v ) to go from image to
near plane, according to ortho or perspective */
lx, ly, lz, /* half edge-lengths of volume */
rayLen=0, /* length of segment formed by ray line intersecting
the near and far clipping planes */
rayT, /* current position along ray ( world-space ) */
rayDirW[3], /* unit-length ray direction ( world-space ) */
rayDirI[3], /* rayDirW transformed into index space;
not unit length, but a unit change in
world space along rayDirW translates to
this change in index space along rayDirI */
rayPosW[3], /* current ray location ( world-space ) */
rayPosI[3], /* current ray location ( index-space ) */
rayStartW[3], /* ray start on near plane ( world-space ) */
rayStartI[3], /* ray start on near plane ( index-space ) */
rayStep, /* distance between samples ( world-space ) */
vOff[3], uOff[3]; /* offsets in arg->ec->wU and arg->ec->wV
directions towards start of ray */
arg = ( _hooverThreadArg * )_arg;
if ( ( ret = ( arg->ctx->threadBegin )( &thread,
arg->render,
arg->ctx->user,
arg->whichThread ) ) ) {
arg->errCode = ret;
arg->whichErr = hooverErrThreadBegin;
return arg;
}
if ( arg->ctx->shape ) {
lx = ly = lz = AIR_NAN;
if ( nrrdCenterNode == arg->ctx->shape->center ) {
mm = 0;
Mx = arg->ctx->shape->size[0]-1;
My = arg->ctx->shape->size[1]-1;
Mz = arg->ctx->shape->size[2]-1;
} else {
mm = -0.5;
Mx = arg->ctx->shape->size[0]-0.5;
My = arg->ctx->shape->size[1]-0.5;
Mz = arg->ctx->shape->size[2]-0.5;
}
} else {
lx = arg->ec->volHLen[0];
ly = arg->ec->volHLen[1];
lz = arg->ec->volHLen[2];
if ( nrrdCenterNode == arg->ctx->volCentering ) {
mm = 0;
Mx = arg->ctx->volSize[0]-1;
My = arg->ctx->volSize[1]-1;
Mz = arg->ctx->volSize[2]-1;
} else {
mm = -0.5;
Mx = arg->ctx->volSize[0]-0.5;
My = arg->ctx->volSize[1]-0.5;
Mz = arg->ctx->volSize[2]-0.5;
}
}
if ( arg->ctx->cam->orthographic ) {
ELL_3V_COPY( rayDirW, arg->ctx->cam->N );
if ( arg->ctx->shape ) {
double zeroW[3], zeroI[3];
ELL_3V_SET( zeroW, 0, 0, 0 );
gageShapeWtoI( arg->ctx->shape, zeroI, zeroW );
gageShapeWtoI( arg->ctx->shape, rayDirI, rayDirW );
ELL_3V_SUB( rayDirI, rayDirI, zeroI );
} else {
rayDirI[0] = AIR_DELTA( -lx, rayDirW[0], lx, mm, Mx );
rayDirI[1] = AIR_DELTA( -ly, rayDirW[1], ly, mm, My );
rayDirI[2] = AIR_DELTA( -lz, rayDirW[2], lz, mm, Mz );
}
rayLen = arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer;
uvScale = 1.0;
} else {
uvScale = arg->ctx->cam->vspNeer/arg->ctx->cam->vspDist;
}
while ( 1 ) {
/* the work assignment is simply the next scanline to be rendered:
the result of all this is setting vI */
if ( arg->ctx->workMutex ) {
airThreadMutexLock( arg->ctx->workMutex );
}
vI = arg->ctx->workIdx;
if ( arg->ctx->workIdx < arg->ctx->imgSize[1] ) {
arg->ctx->workIdx += 1;
}
if ( arg->ctx->workMutex ) {
airThreadMutexUnlock( arg->ctx->workMutex );
}
if ( vI == arg->ctx->imgSize[1] ) {
/* we're done! */
break;
}
if ( nrrdCenterCell == arg->ctx->imgCentering ) {
v = uvScale*AIR_AFFINE( -0.5, vI, arg->ctx->imgSize[1]-0.5,
arg->ctx->cam->vRange[0],
arg->ctx->cam->vRange[1] );
} else {
v = uvScale*AIR_AFFINE( 0.0, vI, arg->ctx->imgSize[1]-1.0,
arg->ctx->cam->vRange[0],
arg->ctx->cam->vRange[1] );
}
ELL_3V_SCALE( vOff, v, arg->ctx->cam->V );
for ( uI=0; uI<arg->ctx->imgSize[0]; uI++ ) {
if ( nrrdCenterCell == arg->ctx->imgCentering ) {
u = uvScale*AIR_AFFINE( -0.5, uI, arg->ctx->imgSize[0]-0.5,
arg->ctx->cam->uRange[0],
arg->ctx->cam->uRange[1] );
} else {
u = uvScale*AIR_AFFINE( 0.0, uI, arg->ctx->imgSize[0]-1.0,
arg->ctx->cam->uRange[0],
arg->ctx->cam->uRange[1] );
}
ELL_3V_SCALE( uOff, u, arg->ctx->cam->U );
ELL_3V_ADD3( rayStartW, uOff, vOff, arg->ec->rayZero );
if ( arg->ctx->shape ) {
gageShapeWtoI( arg->ctx->shape, rayStartI, rayStartW );
} else {
rayStartI[0] = AIR_AFFINE( -lx, rayStartW[0], lx, mm, Mx );
rayStartI[1] = AIR_AFFINE( -ly, rayStartW[1], ly, mm, My );
rayStartI[2] = AIR_AFFINE( -lz, rayStartW[2], lz, mm, Mz );
}
if ( !arg->ctx->cam->orthographic ) {
ELL_3V_SUB( rayDirW, rayStartW, arg->ctx->cam->from );
ELL_3V_NORM( rayDirW, rayDirW, tmp );
if ( arg->ctx->shape ) {
double zeroW[3], zeroI[3];
ELL_3V_SET( zeroW, 0, 0, 0 );
gageShapeWtoI( arg->ctx->shape, zeroI, zeroW );
gageShapeWtoI( arg->ctx->shape, rayDirI, rayDirW );
ELL_3V_SUB( rayDirI, rayDirI, zeroI );
} else {
rayDirI[0] = AIR_DELTA( -lx, rayDirW[0], lx, mm, Mx );
rayDirI[1] = AIR_DELTA( -ly, rayDirW[1], ly, mm, My );
rayDirI[2] = AIR_DELTA( -lz, rayDirW[2], lz, mm, Mz );
}
rayLen = ( ( arg->ctx->cam->vspFaar - arg->ctx->cam->vspNeer )/
ELL_3V_DOT( rayDirW, arg->ctx->cam->N ) );
}
if ( ( ret = ( arg->ctx->rayBegin )( thread,
arg->render,
arg->ctx->user,
uI, vI, rayLen,
rayStartW, rayStartI,
rayDirW, rayDirI ) ) ) {
arg->errCode = ret;
arg->whichErr = hooverErrRayBegin;
return arg;
}
sampleI = 0;
rayT = 0;
while ( 1 ) {
ELL_3V_SCALE_ADD2( rayPosW, 1.0, rayStartW, rayT, rayDirW );
if ( arg->ctx->shape ) {
gageShapeWtoI( arg->ctx->shape, rayPosI, rayPosW );
} else {
ELL_3V_SCALE_ADD2( rayPosI, 1.0, rayStartI, rayT, rayDirI );
}
inside = ( AIR_IN_CL( mm, rayPosI[0], Mx ) &&
AIR_IN_CL( mm, rayPosI[1], My ) &&
AIR_IN_CL( mm, rayPosI[2], Mz ) );
rayStep = ( arg->ctx->sample )( thread,
arg->render,
arg->ctx->user,
sampleI, rayT,
inside,
rayPosW, rayPosI );
if ( !AIR_EXISTS( rayStep ) ) {
/* sampling failed */
arg->errCode = 0;
arg->whichErr = hooverErrSample;
return arg;
}
if ( !rayStep ) {
/* ray decided to finish itself */
break;
}
/* else we moved to a new location along the ray */
rayT += rayStep;
if ( !AIR_IN_CL( 0, rayT, rayLen ) ) {
/* ray stepped outside near-far clipping region, its done. */
break;
}
sampleI++;
}
if ( ( ret = ( arg->ctx->rayEnd )( thread,
arg->render,
arg->ctx->user ) ) ) {
arg->errCode = ret;
arg->whichErr = hooverErrRayEnd;
return arg;
}
} /* end this scanline */
} /* end while( 1 ) assignment of scanlines */
if ( ( ret = ( arg->ctx->threadEnd )( thread,
arg->render,
arg->ctx->user ) ) ) {
arg->errCode = ret;
arg->whichErr = hooverErrThreadEnd;
return arg;
}
/* returning NULL actually indicates that there was NOT an error */
return NULL;
}
typedef union {
_hooverThreadArg **h;
void **v;
} _htpu;
/*
******** hooverRender( )
**
** because of the biff usage( ), only one thread can call hooverRender( ),
** and no promises if the threads themselves call biff...
*/
int
377 hooverRender( hooverContext *ctx, int *errCodeP, int *errThreadP ) {
static const char me[]="hooverRender";
_hooverExtraContext *ec;
_hooverThreadArg args[HOOVER_THREAD_MAX];
_hooverThreadArg *errArg;
airThread *thread[HOOVER_THREAD_MAX];
_htpu u;
void *render;
int ret;
airArray *mop;
unsigned int threadIdx;
if ( !( errCodeP && errThreadP ) ) {
biffAddf( HOOVER, "%s: got NULL int return pointer", me );
return hooverErrInit;
}
/* this calls limnCameraUpdate( ) */
if ( hooverContextCheck( ctx ) ) {
biffAddf( HOOVER, "%s: problem detected in given context", me );
*errCodeP = 0;
*errThreadP = 0;
return hooverErrInit;
}
if ( !( ec = _hooverExtraContextNew( ctx ) ) ) {
biffAddf( HOOVER, "%s: problem creating thread context", me );
*errCodeP = 0;
*errThreadP = 0;
return hooverErrInit;
}
mop = airMopNew( );
airMopAdd( mop, ec, ( airMopper )_hooverExtraContextNix, airMopAlways );
if ( ( ret = ( ctx->renderBegin )( &render, ctx->user ) ) ) {
*errCodeP = ret;
*errCodeP = 0;
*errThreadP = 0;
airMopError( mop );
return hooverErrRenderBegin;
}
for ( threadIdx=0; threadIdx<ctx->numThreads; threadIdx++ ) {
args[threadIdx].ctx = ctx;
args[threadIdx].ec = ec;
args[threadIdx].render = render;
args[threadIdx].whichThread = threadIdx;
args[threadIdx].whichErr = hooverErrNone;
args[threadIdx].errCode = 0;
thread[threadIdx] = airThreadNew( );
}
ctx->workIdx = 0;
if ( 1 < ctx->numThreads ) {
ctx->workMutex = airThreadMutexNew( );
} else {
ctx->workMutex = NULL;
}
/* ( done ): call airThreadStart( ) once per thread, passing the
address of a distinct ( and appropriately intialized )
_hooverThreadArg to each. If return of airThreadStart( ) is
non-zero, put its return in *errCodeP, the number of the
problematic in *errThreadP, and return hooverErrThreadCreate.
Then call airThreadJoin( ) on all the threads, passing &errArg as
"retval". On non-zero return, set *errCodeP and *errThreadP,
and return hooverErrThreadJoin. If return of airThreadJoin( ) is
zero, but the errArg is non-NULL, then assume that this errArg
is actually just the passed _hooverThreadArg returned to us, and
from this copy errArg->errCode into *errCodeP, and return
errArg->whichErr */
if ( 1 < ctx->numThreads && !airThreadCapable ) {
fprintf( stderr, "%s: WARNING: not multi-threaded; will do %d "
"\"threads\" serially !!!\n", me, ctx->numThreads );
}
for ( threadIdx=0; threadIdx<ctx->numThreads; threadIdx++ ) {
if ( ( ret = airThreadStart( thread[threadIdx], _hooverThreadBody,
( void * ) &args[threadIdx] ) ) ) {
*errCodeP = ret;
*errThreadP = threadIdx;
airMopError( mop );
return hooverErrThreadCreate;
}
}
for ( threadIdx=0; threadIdx<ctx->numThreads; threadIdx++ ) {
u.h = &errArg;
if ( ( ret = airThreadJoin( thread[threadIdx], u.v ) ) ) {
*errCodeP = ret;
*errThreadP = threadIdx;
airMopError( mop );
return hooverErrThreadJoin;
}
if ( errArg != NULL ) {
*errCodeP = errArg->errCode;
*errThreadP = threadIdx;
return errArg->whichErr;
}
thread[threadIdx] = airThreadNix( thread[threadIdx] );
}
if ( 1 < ctx->numThreads ) {
ctx->workMutex = airThreadMutexNix( ctx->workMutex );
}
if ( ( ret = ( ctx->renderEnd )( render, ctx->user ) ) ) {
*errCodeP = ret;
*errThreadP = -1;
return hooverErrRenderEnd;
}
render = NULL;
airMopOkay( mop );
*errCodeP = 0;
*errThreadP = 0;
return hooverErrNone;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "hoover.h"
int
27 hooverStubRenderBegin( void **rendInfoP, void *userInfo ) {
AIR_UNUSED( userInfo );
*rendInfoP = NULL;
return 0;
}
int
35 hooverStubThreadBegin( void **threadInfoP, void *rendInfo, void *userInfo,
int whichThread ) {
AIR_UNUSED( rendInfo );
AIR_UNUSED( userInfo );
AIR_UNUSED( whichThread );
*threadInfoP = NULL;
return 0;
}
int
46 hooverStubRayBegin( void *threadInfo, void *renderInfo, void *userInfo,
int uIndex,
int vIndex,
double rayLen,
double rayStartWorld[3],
double rayStartIndex[3],
double rayDirWorld[3],
double rayDirIndex[3] ) {
AIR_UNUSED( threadInfo );
AIR_UNUSED( renderInfo );
AIR_UNUSED( userInfo );
AIR_UNUSED( uIndex );
AIR_UNUSED( vIndex );
AIR_UNUSED( rayLen );
AIR_UNUSED( rayStartWorld );
AIR_UNUSED( rayStartIndex );
AIR_UNUSED( rayDirWorld );
AIR_UNUSED( rayDirIndex );
/*
char me[]="hooverStubRayBegin";
fprintf( stderr, "%s: ray( %d, %d ), len=%g\n"
" start=( %g, %g, %g )\n"
" dir=( %g, %g, %g )\n",
me, uIndex, vIndex, rayLen,
rayStartWorld[0], rayStartWorld[1], rayStartWorld[2],
rayDirWorld[0], rayDirWorld[1], rayDirWorld[2] );
*/
return 0;
}
double
79 hooverStubSample( void *threadInfo, void *renderInfo, void *userInfo,
int num, double rayT,
int inside,
double samplePosWorld[3],
double samplePosIndex[3] ) {
AIR_UNUSED( threadInfo );
AIR_UNUSED( renderInfo );
AIR_UNUSED( userInfo );
AIR_UNUSED( num );
AIR_UNUSED( rayT );
AIR_UNUSED( inside );
AIR_UNUSED( samplePosWorld );
AIR_UNUSED( samplePosIndex );
/*
char me[]="hooverStubSample";
fprintf( stderr, "%s: sample( %g, %g, %g )\n", me,
samplePosWorld[0], samplePosWorld[1], samplePosWorld[2] );
*/
/* we want the stub renderer to actually finish */
return 1.0;
}
int
105 hooverStubRayEnd( void *threadInfo, void *rendInfo, void *userInfo ) {
AIR_UNUSED( threadInfo );
AIR_UNUSED( rendInfo );
AIR_UNUSED( userInfo );
return 0;
}
int
115 hooverStubThreadEnd( void *threadInfo, void *rendInfo, void *userInfo ) {
AIR_UNUSED( threadInfo );
AIR_UNUSED( rendInfo );
AIR_UNUSED( userInfo );
return 0;
}
int
125 hooverStubRenderEnd( void *rendInfo, void *userInfo ) {
AIR_UNUSED( rendInfo );
AIR_UNUSED( userInfo );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
******** limnCameraUpdate( )
**
** sets in cam: W2V, V2W, U, V, N, vspNeer, vspFaar, vspDist
** and, if fov and aspect are set, this also sets uRange and vRange
**
** This does use biff to describe problems with camera settings
*/
int
36 limnCameraUpdate( limnCamera *cam ) {
static const char me[] = "limnCameraUpdate";
double len, bb[4], uu[4], vv[4], nn[4], TT[16], RR[16];
if ( !cam ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
ELL_4V_SET( uu, 0, 0, 0, 0 );
ELL_4V_SET( vv, 0, 0, 0, 0 );
ELL_4V_SET( nn, 0, 0, 0, 0 );
ELL_4V_SET( bb, 0, 0, 0, 1 );
ELL_3V_SUB( nn, cam->at, cam->from );
len = ELL_3V_LEN( nn );
if ( !len ) {
biffAddf( LIMN, "%s: cam->at ( %g, %g, %g ) == cam->from", me,
cam->at[0], cam->at[1], cam->at[2] );
return 1;
}
if ( cam->atRelative ) {
/* ctx->cam->{neer, dist} are "at" relative */
cam->vspNeer = cam->neer + len;
cam->vspFaar = cam->faar + len;
cam->vspDist = cam->dist + len;
}
else {
/* ctx->cam->{neer, dist} are eye relative */
cam->vspNeer = cam->neer;
cam->vspFaar = cam->faar;
cam->vspDist = cam->dist;
}
if ( !( cam->vspNeer > 0 && cam->vspDist > 0 && cam->vspFaar > 0 ) ) {
biffAddf( LIMN, "%s: eye-relative near ( %g ), dist ( %g ), or far ( %g ) <= 0",
me, cam->vspNeer, cam->vspDist, cam->vspFaar );
return 1;
}
if ( !( cam->vspNeer <= cam->vspFaar ) ) {
biffAddf( LIMN, "%s: eye-relative near ( %g ) further than far ( %g )",
me, cam->vspNeer, cam->vspFaar );
return 1 ;
}
if ( AIR_EXISTS( cam->fov ) ) {
if ( !( AIR_IN_OP( 0.0, cam->fov, 180.0 ) ) ) {
biffAddf( LIMN, "%s: cam->fov ( %g ) not in valid range between 0 and 180",
me, cam->fov );
return 1 ;
}
if ( !AIR_EXISTS( cam->aspect ) ) {
biffAddf( LIMN, "%s: cam->fov set, but cam->aspect isn't", me );
return 1;
}
/* "fov" is half vertical angle */
cam->vRange[0] = -tan( cam->fov*AIR_PI/360 )*( cam->vspDist );
cam->vRange[1] = -cam->vRange[0];
cam->uRange[0] = cam->vRange[0]*( cam->aspect );
cam->uRange[1] = -cam->uRange[0];
}
/* else cam->fov isn't set, but we're not going to complain if
uRange and vRange aren't both set ... */
ELL_3V_SCALE( nn, 1.0/len, nn );
ELL_3V_CROSS( uu, nn, cam->up );
len = ELL_3V_LEN( uu );
if ( !len ) {
biffAddf( LIMN, "%s: cam->up is co-linear with view direction", me );
return 1 ;
}
ELL_3V_SCALE( uu, 1.0/len, uu );
if ( cam->rightHanded ) {
ELL_3V_CROSS( vv, nn, uu );
}
else {
ELL_3V_CROSS( vv, uu, nn );
}
ELL_4V_COPY( cam->U, uu );
ELL_4V_COPY( cam->V, vv );
ELL_4V_COPY( cam->N, nn );
ELL_4M_TRANSLATE_SET( TT, -cam->from[0], -cam->from[1], -cam->from[2] );
ELL_4M_ROWS_SET( RR, uu, vv, nn, bb );
ELL_4M_MUL( cam->W2V, RR, TT );
ell_4m_inv_d( cam->V2W, cam->W2V );
return 0;
}
/*
******** limnCameraAspectSet
**
** simply sets the "aspect" field of the cam. Note that calling this
** does *not* automatically mean that the uRange and vRange in the camera
** will be set according to the "fov"- the "fov" has to actually be set
** ( be non-NaN ) for that to happen. This allows dumber functions to
** call this whenever they have the information required to do so, even
** if the "aspect" is not going to be needed for a given camera use
*/
int
135 limnCameraAspectSet( limnCamera *cam, unsigned int horz, unsigned int vert,
int centering ) {
static const char me[] = "limnCameraAspectSet";
if ( !cam ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( horz > 0 && vert > 0 ) ) {
biffAddf( LIMN, "%s: bad image dimensions %ux%u", me, horz, vert );
return 1;
}
if ( airEnumValCheck( nrrdCenter, centering ) ) {
biffAddf( LIMN, "%s: centering %d not valid", me, centering );
return 1;
}
if ( nrrdCenterCell == centering ) {
cam->aspect = ( ( double )horz )/vert;
} else {
cam->aspect = ( ( double )( horz-1 ) )/( vert-1 );
}
return 0;
}
/*
******** limnCameraPathMake
**
** uses limnSplines to do camera paths based on key-frames
**
** output: cameras at all "numFrames" frames are set in the
** PRE-ALLOCATED array of output cameras, "cam".
**
** input:
** keycam: array of keyframe cameras
** time: times associated with the key frames
** ---> both of these arrays are length "numKeys" <---
** trackWhat: takes values from the limnCameraPathTrack* enum
** quatType: spline to control camera orientations. This is needed for
** tracking at or from, but not needed for limnCameraPathTrackBoth.
** This is the only limnSplineTypeSpec* argument that can be NULL.
** posType: spline to control whichever of from, at, and up are needed for
** the given style of tracking.
** distType: spline to control neer, faar, dist: positions of near clipping,
** far clipping, and image plane, as well as the
** distance between from and at ( which is used if not doing
** limnCameraPathTrackBoth )
** viewType: spline to control fov ( and aspect, if you're crazy )
**
** NOTE: The "atRelative", "orthographic", and "rightHanded" fields
** are copied from keycam[0] into all output cam[i], but you still need
** to correctly set them for all keycam[i] for limnCameraUpdate to work
** as expected. Also, for the sake of simplicity, this function only works
** with fov and aspect, instead of {u, v}Range, and hence both "fov" and
** "aspect" need to set in *all* the keycams, even if neither of them
** ever changes!
*/
int
194 limnCameraPathMake( limnCamera *cam, int numFrames,
limnCamera *keycam, double *time, int numKeys,
int trackWhat,
limnSplineTypeSpec *quatType,
limnSplineTypeSpec *posType,
limnSplineTypeSpec *distType,
limnSplineTypeSpec *viewType ) {
static const char me[]="limnCameraPathMake";
char which[AIR_STRLEN_MED];
airArray *mop;
Nrrd *nquat, *nfrom, *natpt, *nupvc, *ndist, *nfova, *ntime, *nsample;
double fratVec[3], *quat, *from, *atpt, *upvc, *dist, *fova,
W2V[9], N[3], fratDist;
limnSpline *timeSpline, *quatSpline, *fromSpline, *atptSpline, *upvcSpline,
*distSpline, *fovaSpline;
limnSplineTypeSpec *timeType;
int ii, E;
if ( !( cam && keycam && time && posType && distType && viewType ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_OP( limnCameraPathTrackUnknown, trackWhat,
limnCameraPathTrackLast ) ) ) {
biffAddf( LIMN, "%s: trackWhat %d not in valid range [%d, %d]", me,
trackWhat, limnCameraPathTrackUnknown+1,
limnCameraPathTrackLast-1 );
return 1;
}
if ( limnCameraPathTrackBoth != trackWhat && !quatType ) {
biffAddf( LIMN, "%s: need the quaternion limnSplineTypeSpec if not "
"doing trackBoth", me );
return 1;
}
/* create and allocate nrrds. For the time being, we're allocating
more different nrrds, and filling their contents, than we need
to-- nquat is not needed if we're doing limnCameraPathTrackBoth,
for example. However, we do make an effort to only do the spline
evaluation on the things we actually need to know. */
mop = airMopNew( );
airMopAdd( mop, nquat = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nfrom = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, natpt = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nupvc = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ndist = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nfova = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntime = nrrdNew( ), ( airMopper )nrrdNix, airMopAlways );
if ( nrrdWrap_va( ntime, time, nrrdTypeDouble, 1,
AIR_CAST( size_t, numKeys ) ) ) {
biffMovef( LIMN, NRRD, "%s: trouble wrapping time values", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nsample = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
timeType = limnSplineTypeSpecNew( limnSplineTypeTimeWarp );
airMopAdd( mop, timeType, ( airMopper )limnSplineTypeSpecNix, airMopAlways );
if ( nrrdMaybeAlloc_va( nquat, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ), AIR_CAST( size_t, numKeys ) )
|| nrrdMaybeAlloc_va( nfrom, nrrdTypeDouble, 2,
AIR_CAST( size_t, 3 ), AIR_CAST( size_t, numKeys ) )
|| nrrdMaybeAlloc_va( natpt, nrrdTypeDouble, 2,
AIR_CAST( size_t, 3 ), AIR_CAST( size_t, numKeys ) )
|| nrrdMaybeAlloc_va( nupvc, nrrdTypeDouble, 2,
AIR_CAST( size_t, 3 ), AIR_CAST( size_t, numKeys ) )
|| nrrdMaybeAlloc_va( ndist, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ), AIR_CAST( size_t, numKeys ) )
|| nrrdMaybeAlloc_va( nfova, nrrdTypeDouble, 2,
AIR_CAST( size_t, 2 ), AIR_CAST( size_t, numKeys ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate buffer nrrds", me );
airMopError( mop ); return 1;
}
quat = ( double* )( nquat->data );
from = ( double* )( nfrom->data );
atpt = ( double* )( natpt->data );
upvc = ( double* )( nupvc->data );
dist = ( double* )( ndist->data );
fova = ( double* )( nfova->data );
/* check cameras, and put camera information into nrrds */
for ( ii=0; ii<numKeys; ii++ ) {
if ( limnCameraUpdate( keycam + ii ) ) {
biffAddf( LIMN, "%s: trouble with camera at keyframe %d\n", me, ii );
airMopError( mop ); return 1;
}
if ( !( AIR_EXISTS( keycam[ii].fov ) && AIR_EXISTS( keycam[ii].aspect ) ) ) {
biffAddf( LIMN, "%s: fov, aspect not both defined on keyframe %d",
me, ii );
airMopError( mop ); return 1;
}
ell_4m_to_q_d( quat + 4*ii, keycam[ii].W2V );
if ( ii ) {
if ( 0 > ELL_4V_DOT( quat + 4*ii, quat + 4*( ii-1 ) ) ) {
ELL_4V_SCALE( quat + 4*ii, -1, quat + 4*ii );
}
}
ELL_3V_COPY( from + 3*ii, keycam[ii].from );
ELL_3V_COPY( atpt + 3*ii, keycam[ii].at );
ELL_3V_COPY( upvc + 3*ii, keycam[ii].up );
ELL_3V_SUB( fratVec, keycam[ii].from, keycam[ii].at );
fratDist = ELL_3V_LEN( fratVec );
ELL_4V_SET( dist + 4*ii, fratDist,
keycam[ii].neer, keycam[ii].dist, keycam[ii].faar );
ELL_2V_SET( fova + 2*ii, keycam[ii].fov, keycam[ii].aspect );
}
/* create splines from nrrds */
if ( !( ( strcpy( which, "quaternion" ), quatSpline =
limnSplineCleverNew( nquat, limnSplineInfoQuaternion, quatType ) )
&& ( strcpy( which, "from point" ), fromSpline =
limnSplineCleverNew( nfrom, limnSplineInfo3Vector, posType ) )
&& ( strcpy( which, "at point" ), atptSpline =
limnSplineCleverNew( natpt, limnSplineInfo3Vector, posType ) )
&& ( strcpy( which, "up vector" ), upvcSpline =
limnSplineCleverNew( nupvc, limnSplineInfo3Vector, posType ) )
&& ( strcpy( which, "plane distances" ), distSpline =
limnSplineCleverNew( ndist, limnSplineInfo4Vector, distType ) )
&& ( strcpy( which, "field-of-view" ), fovaSpline =
limnSplineCleverNew( nfova, limnSplineInfo2Vector, viewType ) )
&& ( strcpy( which, "time warp" ), timeSpline =
limnSplineCleverNew( ntime, limnSplineInfoScalar, timeType ) ) ) ) {
biffAddf( LIMN, "%s: trouble creating %s spline", me, which );
airMopError( mop ); return 1;
}
airMopAdd( mop, quatSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, fromSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, atptSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, upvcSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, distSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, fovaSpline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, timeSpline, ( airMopper )limnSplineNix, airMopAlways );
/* evaluate splines */
E = AIR_FALSE;
if ( !E ) E |= limnSplineSample( nsample, timeSpline,
limnSplineMinT( timeSpline ), numFrames,
limnSplineMaxT( timeSpline ) );
quat = NULL;
from = NULL;
atpt = NULL;
upvc = NULL;
switch( trackWhat ) {
case limnCameraPathTrackAt:
if ( !E ) E |= limnSplineNrrdEvaluate( natpt, atptSpline, nsample );
if ( !E ) atpt = ( double* )( natpt->data );
if ( !E ) E |= limnSplineNrrdEvaluate( nquat, quatSpline, nsample );
if ( !E ) quat = ( double* )( nquat->data );
break;
case limnCameraPathTrackFrom:
if ( !E ) E |= limnSplineNrrdEvaluate( nfrom, fromSpline, nsample );
if ( !E ) from = ( double* )( nfrom->data );
if ( !E ) E |= limnSplineNrrdEvaluate( nquat, quatSpline, nsample );
if ( !E ) quat = ( double* )( nquat->data );
break;
case limnCameraPathTrackBoth:
if ( !E ) E |= limnSplineNrrdEvaluate( nfrom, fromSpline, nsample );
if ( !E ) from = ( double* )( nfrom->data );
if ( !E ) E |= limnSplineNrrdEvaluate( natpt, atptSpline, nsample );
if ( !E ) atpt = ( double* )( natpt->data );
if ( !E ) E |= limnSplineNrrdEvaluate( nupvc, upvcSpline, nsample );
if ( !E ) upvc = ( double* )( nupvc->data );
break;
}
dist = NULL;
if ( !E ) E |= limnSplineNrrdEvaluate( ndist, distSpline, nsample );
if ( !E ) dist = ( double* )( ndist->data );
fova = NULL;
if ( !E ) E |= limnSplineNrrdEvaluate( nfova, fovaSpline, nsample );
if ( !E ) fova = ( double* )( nfova->data );
if ( E ) {
biffAddf( LIMN, "%s: trouble evaluating splines", me );
airMopError( mop ); return 1;
}
/* copy information from nrrds back into cameras */
for ( ii=0; ii<numFrames; ii++ ) {
cam[ii].atRelative = keycam[0].atRelative;
cam[ii].orthographic = keycam[0].orthographic;
cam[ii].rightHanded = keycam[0].rightHanded;
if ( limnCameraPathTrackBoth == trackWhat ) {
ELL_3V_COPY( cam[ii].from, from + 3*ii );
ELL_3V_COPY( cam[ii].at, atpt + 3*ii );
ELL_3V_COPY( cam[ii].up, upvc + 3*ii );
} else {
fratDist = ( dist + 4*ii )[0];
ell_q_to_3m_d( W2V, quat + 4*ii );
ELL_3MV_ROW1_GET( cam[ii].up, W2V );
if ( cam[ii].rightHanded ) {
ELL_3V_SCALE( cam[ii].up, -1, cam[ii].up );
}
ELL_3MV_ROW2_GET( N, W2V );
if ( limnCameraPathTrackFrom == trackWhat ) {
ELL_3V_COPY( cam[ii].from, from + 3*ii );
ELL_3V_SCALE_ADD2( cam[ii].at, 1.0, cam[ii].from, fratDist, N );
} else {
ELL_3V_COPY( cam[ii].at, atpt + 3*ii );
ELL_3V_SCALE_ADD2( cam[ii].from, 1.0, cam[ii].at, -fratDist, N );
}
}
cam[ii].neer = ( dist + 4*ii )[1];
cam[ii].dist = ( dist + 4*ii )[2];
cam[ii].faar = ( dist + 4*ii )[3];
cam[ii].fov = ( fova + 2*ii )[0];
cam[ii].aspect = ( fova + 2*ii )[1];
if ( limnCameraUpdate( cam + ii ) ) {
biffAddf( LIMN, "%s: trouble with output camera %d\n", me, ii );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
const int
limnPresent = 42;
const char *
limnBiffKey = "limn";
int
limnDefCameraAtRelative = AIR_FALSE;
int
limnDefCameraOrthographic = AIR_FALSE;
int
limnDefCameraRightHanded = AIR_TRUE;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
const char *
_limnSpaceStr[LIMN_SPACE_MAX+1] = {
"( unknown space )",
"world",
"view",
"screen",
"device"
};
const airEnum
_limnSpace = {
"limn space",
LIMN_SPACE_MAX,
_limnSpaceStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
44 const airEnum *const
limnSpace = &_limnSpace;
/* ------------------------------------------------------------ */
const char *
_limnPolyDataInfoStr[LIMN_POLY_DATA_INFO_MAX+1] = {
"( unknown info )",
"rgba",
"norm",
"tex2",
"tang"
};
const airEnum
_limnPolyDataInfo = {
"limn polydata info",
LIMN_POLY_DATA_INFO_MAX,
_limnPolyDataInfoStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
67 const airEnum *const
limnPolyDataInfo = &_limnPolyDataInfo;
/* ------------------------------------------------------------ */
const char *
_limnCameraPathTrackStr[] = {
"( unknown limnCameraPathTrack )",
"from",
"at",
"both"
};
const char *
_limnCameraPathTrackDesc[] = {
"unknown limnCameraPathTrack",
"track through eye points, quaternions for camera orientation",
"track through look-at points, quaternions for camera orientation",
"track eye point, look-at point, and up vector with separate splines"
};
const char *
_limnCameraPathTrackStrEqv[] = {
"from", "fr",
"at", "look-at", "lookat",
"both",
""
};
const int
_limnCameraPathTrackValEqv[] = {
limnCameraPathTrackFrom, limnCameraPathTrackFrom,
limnCameraPathTrackAt, limnCameraPathTrackAt, limnCameraPathTrackAt,
limnCameraPathTrackBoth
};
const airEnum
_limnCameraPathTrack = {
"limnCameraPathTrack",
LIMN_CAMERA_PATH_TRACK_MAX,
_limnCameraPathTrackStr, NULL,
_limnCameraPathTrackDesc,
_limnCameraPathTrackStrEqv, _limnCameraPathTrackValEqv,
AIR_FALSE
};
112 const airEnum *const
limnCameraPathTrack = &_limnCameraPathTrack;
/* ------------------------------------------------------------ */
const char *
_limnPrimitiveStr[] = {
"( unknown limnPrimitive )",
"noop",
"triangles",
"tristrip",
"trifan",
"quads",
"linestrip",
"lines"
};
const char *
_limnPrimitiveDesc[] = {
"unknown limnPrimitive",
"no-op",
"triangle soup",
"triangle strip",
"triangle fan",
"quad soup",
"line strip",
"lines"
};
const char *
_limnPrimitiveStrEqv[] = {
"noop",
"triangles",
"tristrip",
"trifan",
"quads",
"linestrip",
"lines",
""
};
const int
_limnPrimitiveValEqv[] = {
limnPrimitiveNoop,
limnPrimitiveTriangles,
limnPrimitiveTriangleStrip,
limnPrimitiveTriangleFan,
limnPrimitiveQuads,
limnPrimitiveLineStrip,
limnPrimitiveLines
};
const airEnum
_limnPrimitive = {
"limnPrimitive",
LIMN_PRIMITIVE_MAX,
_limnPrimitiveStr, NULL,
_limnPrimitiveDesc,
_limnPrimitiveStrEqv, _limnPrimitiveValEqv,
AIR_FALSE
};
173 const airEnum *const
limnPrimitive = &_limnPrimitive;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
27 limnEnvMapFill( Nrrd *map, limnEnvMapCB cb, int qnMethod, void *data ) {
static const char me[]="limnEnvMapFill";
unsigned int sx, sy, qn;
float vec[3], *mapData;
if ( !( map && cb ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_OP( limnQNUnknown, qnMethod, limnQNLast ) ) {
biffAddf( LIMN, "%s: QN method %d invalid", me, qnMethod );
return 1;
}
switch( qnMethod ) {
case limnQN16checker:
case limnQN16octa:
sx = sy = 256;
break;
case limnQN14checker:
case limnQN14octa:
sx = sy = 128;
break;
case limnQN12checker:
case limnQN12octa:
sx = sy = 64;
break;
case limnQN10checker:
case limnQN10octa:
sx = sy = 32;
break;
case limnQN8checker:
case limnQN8octa:
sx = sy = 16;
break;
case limnQN15octa:
sx = 128;
sy = 256;
break;
case limnQN13octa:
sx = 64;
sy = 128;
break;
case limnQN11octa:
sx = 32;
sy = 64;
break;
case limnQN9octa:
sx = 16;
sy = 32;
break;
default:
biffAddf( LIMN, "%s: sorry, QN method %d not implemented", me, qnMethod );
return 1;
}
if ( nrrdMaybeAlloc_va( map, nrrdTypeFloat, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't alloc output", me );
return 1;
}
mapData = ( float * )map->data;
for ( qn=0; qn<sx*sy; qn++ ) {
limnQNtoV_f[qnMethod]( vec, qn );
cb( mapData + 3*qn, vec, data );
}
return 0;
}
void
98 limnLightDiffuseCB( float rgb[3], float vec[3], void *_lit ) {
float dot, r, g, b, norm;
limnLight *lit;
int i;
lit = ( limnLight * )_lit;
ELL_3V_NORM_TT( vec, float, vec, norm );
r = lit->amb[0];
g = lit->amb[1];
b = lit->amb[2];
for ( i=0; i<LIMN_LIGHT_NUM; i++ ) {
if ( !lit->on[i] )
continue;
dot = ELL_3V_DOT( vec, lit->dir[i] );
dot = AIR_MAX( 0, dot );
r += dot*lit->col[i][0];
g += dot*lit->col[i][1];
b += dot*lit->col[i][2];
}
/* not really our job to be doing clamping here ... */
rgb[0] = r;
rgb[1] = g;
rgb[2] = b;
}
int
124 limnEnvMapCheck( Nrrd *envMap ) {
static const char me[]="limnEnvMapCheck";
if ( nrrdCheck( envMap ) ) {
biffMovef( LIMN, NRRD, "%s: basic nrrd validity check failed", me );
return 1;
}
if ( !( nrrdTypeFloat == envMap->type ) ) {
biffAddf( LIMN, "%s: type should be %s, not %s", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, envMap->type ) );
return 1;
}
if ( !( 3 == envMap->dim ) ) {
biffAddf( LIMN, "%s: dimension should be 3, not %d", me, envMap->dim );
return 1;
}
if ( !( 3 == envMap->axis[0].size
&& 256 == envMap->axis[1].size
&& 256 == envMap->axis[2].size ) ) {
char stmp[3][AIR_STRLEN_SMALL];
biffAddf( LIMN, "%s: dimension should be 3x256x256, not "
"%s x %s x %s", me,
airSprintSize_t( stmp[0], envMap->axis[0].size ),
airSprintSize_t( stmp[1], envMap->axis[1].size ),
airSprintSize_t( stmp[2], envMap->axis[2].size ) );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
******** limnHestCameraOptAdd( )
**
** calls hestOptAdd a bunch of times to set up command-line options
** useful for specifying a limnCamera. The flags used are as follows:
** fr: cam->from
** at: cam->at
** up: cam->up
** rh: cam->rightHanded
** or: cam->orthographic
** dn: cam->neer
** di: cam->dist
** df: cam->faar
** ar: cam->atRelative
** ur: cam->uRange
** vr: cam->vRange
** fv: cam->fov
*/
void
45 limnHestCameraOptAdd( hestOpt **hoptP, limnCamera *cam,
const char *frDef, const char *atDef, const char *upDef,
const char *dnDef, const char *diDef, const char *dfDef,
const char *urDef, const char *vrDef, const char *fvDef ) {
hestOpt *hopt;
hopt = *hoptP;
hestOptAdd( &hopt, "fr", "eye pos", airTypeDouble, 3, 3, cam->from,
frDef, "camera eye point" );
hestOptAdd( &hopt, "at", "at pos", airTypeDouble, 3, 3, cam->at,
atDef, "camera look-at point" );
hestOptAdd( &hopt, "up", "up dir", airTypeDouble, 3, 3, cam->up,
upDef, "camera pseudo-up vector" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestOptAdd( &hopt, "or", NULL, airTypeInt, 0, 0, &( cam->orthographic ), NULL,
"orthogonal ( not perspective ) projection" );
hestOptAdd( &hopt, "dn", "near", airTypeDouble, 1, 1, &( cam->neer ),
dnDef, "distance to near clipping plane" );
hestOptAdd( &hopt, "di", "image", airTypeDouble, 1, 1, &( cam->dist ),
diDef, "distance to image plane" );
hestOptAdd( &hopt, "df", "far", airTypeDouble, 1, 1, &( cam->faar ),
dfDef, "distance to far clipping plane" );
hestOptAdd( &hopt, "ar", NULL, airTypeInt, 0, 0, &( cam->atRelative ), NULL,
"near, image, and far plane distances are relative to "
"the *at* point, instead of the eye point" );
hestOptAdd( &hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
urDef, "range in U direction of image plane" );
hestOptAdd( &hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
vrDef, "range in V direction of image plane" );
hestOptAdd( &hopt, "fv", "field of view", airTypeDouble, 1, 1, &( cam->fov ),
fvDef, "angle ( in degrees ) vertically subtended by view window" );
*hoptP = hopt;
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
28 limnObjectDescribe( FILE *file, const limnObject *obj ) {
limnFace *face; unsigned int si, fii;
limnEdge *edge; unsigned int eii;
limnVertex *vert; unsigned int vii;
limnPart *part; unsigned int partIdx;
limnLook *look;
fprintf( file, "parts: %d\n", obj->partNum );
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
part = obj->part[partIdx];
fprintf( file, "part %d | verts: %d ========\n", partIdx, part->vertIdxNum );
for ( vii=0; vii<part->vertIdxNum; vii++ ) {
vert = obj->vert + part->vertIdx[vii];
fprintf( file, "part %d | %d( %d ): w=( %g, %g, %g )\n",
partIdx, vii, part->vertIdx[vii],
vert->world[0], vert->world[1], vert->world[2] );
/* vert->view[0], vert->view[1], vert->view[2] ); */
/* vert->screen[0], vert->screen[1], vert->screen[2] ); */
}
fprintf( file, "part %d | edges: %d ========\n", partIdx, part->edgeIdxNum );
for ( eii=0; eii<part->edgeIdxNum; eii++ ) {
edge = obj->edge + part->edgeIdx[eii];
fprintf( file, "part %d==%d | %d( %d ): "
"vert( %d, %d ), face( %d, %d )\n",
partIdx, edge->partIdx, eii, part->edgeIdx[eii],
edge->vertIdx[0], edge->vertIdx[1],
edge->faceIdx[0], edge->faceIdx[1] );
}
fprintf( file, "part %d | faces: %d ========\n", partIdx, part->faceIdxNum );
for ( fii=0; fii<part->faceIdxNum; fii++ ) {
face = obj->face + part->faceIdx[fii];
fprintf( file, "part %d==%d | %d( %d ): [",
partIdx, face->partIdx, fii, part->faceIdx[fii] );
for ( si=0; si<face->sideNum; si++ ) {
fprintf( file, "%d", face->vertIdx[si] );
if ( si < face->sideNum-1 ) {
fprintf( file, ", " );
}
}
fprintf( file, "]; wn = ( %g, %g, %g ) |%g|", face->worldNormal[0],
face->worldNormal[1], face->worldNormal[2],
ELL_3V_LEN( face->worldNormal ) );
look = obj->look + face->lookIdx;
fprintf( file, "; RGB=( %g, %g, %g )",
look->rgba[0], look->rgba[1], look->rgba[2] );
fprintf( file, "\n" );
}
}
return 0;
}
int
81 limnObjectWriteOFF( FILE *file, const limnObject *obj ) {
static const char me[]="limnObjectWriteOFF";
unsigned int si;
limnVertex *vert; unsigned int vii;
limnFace *face; unsigned int fii;
limnPart *part; unsigned int partIdx;
if ( !( obj && file ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
fprintf( file, "OFF # created by Teem/limn\n" );
fprintf( file, "%d %d %d\n", obj->vertNum, obj->faceNum, obj->edgeNum );
/* write vertices */
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
fprintf( file, "### LIMN BEGIN PART %d\n", partIdx );
part = obj->part[partIdx];
for ( vii=0; vii<part->vertIdxNum; vii++ ) {
vert = obj->vert + part->vertIdx[vii];
fprintf( file, "%g %g %g",
vert->world[0]/vert->world[3],
vert->world[1]/vert->world[3],
vert->world[2]/vert->world[3] );
/* verts no longer have a lookIdx
if ( vert->lookIdx ) {
fprintf( file, " %g %g %g",
obj->look[vert->lookIdx].rgba[0],
obj->look[vert->lookIdx].rgba[1],
obj->look[vert->lookIdx].rgba[2] );
}
*/
fprintf( file, "\n" );
}
}
/* write faces */
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
fprintf( file, "### LIMN BEGIN PART %d\n", partIdx );
part = obj->part[partIdx];
for ( fii=0; fii<part->faceIdxNum; fii++ ) {
face = obj->face + part->faceIdx[fii];
fprintf( file, "%d", face->sideNum );
for ( si=0; si<face->sideNum; si++ ) {
fprintf( file, " %d", face->vertIdx[si] );
}
if ( face->lookIdx ) {
fprintf( file, " %g %g %g",
obj->look[face->lookIdx].rgba[0],
obj->look[face->lookIdx].rgba[1],
obj->look[face->lookIdx].rgba[2] );
}
fprintf( file, "\n" );
}
}
return 0;
}
int
141 limnPolyDataWriteIV( FILE *file, const limnPolyData *pld ) {
static const char me[]="limnPolyDataWriteIV";
unsigned int primIdx, xyzwIdx, rgbaIdx, normIdx, bitFlag,
baseVertIdx;
int haveStrips, haveTris, haveElse;
double xyz[3], norm[3], len;
if ( !( file && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
haveStrips = haveTris = haveElse = AIR_FALSE;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
int isTri, isStrip, isElse;
isTri = limnPrimitiveTriangles == pld->type[primIdx];
isStrip = limnPrimitiveTriangleStrip == pld->type[primIdx];
isElse = !( isTri || isStrip );
haveTris |= isTri;
haveStrips |= isStrip;
haveElse |= isElse;
if ( isElse ) {
biffAddf( LIMN,
"%s: sorry, can only have %s or %s prims ( prim[%u] is %s )",
me, airEnumStr( limnPrimitive, limnPrimitiveTriangles ),
airEnumStr( limnPrimitive, limnPrimitiveTriangleStrip ),
primIdx, airEnumStr( limnPrimitive, pld->type[primIdx] ) );
return 1;
}
}
if ( haveStrips && 1 != pld->primNum ) {
biffAddf( LIMN, "%s: sorry, can only have a single triangle strip", me );
return 1;
}
fprintf( file, "#Inventor V2.0 ascii\n" );
fprintf( file, "# written by Teem/limn\n\n" );
fprintf( file, "Separator {\n" );
fprintf( file, " Coordinate3 {\n" );
fprintf( file, " point [\n" );
if ( haveStrips ) {
unsigned int vii;
for ( vii=0; vii<pld->icnt[0]; vii++ ) {
xyzwIdx = ( pld->indx )[vii];
ELL_34V_HOMOG( xyz, pld->xyzw + 4*xyzwIdx );
fprintf( file, " %g %g %g%s\n",
xyz[0], xyz[1], xyz[2],
vii < pld->icnt[0]-1 ? ", " : "" );
}
} else {
for ( xyzwIdx=0; xyzwIdx<pld->xyzwNum; xyzwIdx++ ) {
ELL_34V_HOMOG( xyz, pld->xyzw + 4*xyzwIdx );
fprintf( file, " %g %g %g%s\n",
xyz[0], xyz[1], xyz[2],
xyzwIdx < pld->xyzwNum-1 ? ", " : "" );
}
}
fprintf( file, " ]\n" );
fprintf( file, " }\n" );
bitFlag = limnPolyDataInfoBitFlag( pld );
if ( bitFlag & ( 1 << limnPolyDataInfoNorm ) ) {
fprintf( file, " NormalBinding { value PER_VERTEX_INDEXED }\n" );
fprintf( file, " Normal {\n" );
fprintf( file, " vector [\n" );
if ( haveStrips ) {
unsigned int vii;
for ( vii=0; vii<pld->icnt[0]; vii++ ) {
normIdx = ( pld->indx )[vii];
ELL_3V_SET( norm,
pld->norm[0 + 3*normIdx],
pld->norm[1 + 3*normIdx],
pld->norm[2 + 3*normIdx] );
ELL_3V_NORM( norm, norm, len );
fprintf( file, " %g %g %g%s\n", norm[0], norm[1], norm[2],
vii < pld->icnt[0]-1 ? ", " : "" );
}
} else {
for ( normIdx=0; normIdx<pld->normNum; normIdx++ ) {
fprintf( file, " %g %g %g%s\n",
pld->norm[0 + 3*normIdx],
pld->norm[1 + 3*normIdx],
pld->norm[2 + 3*normIdx],
normIdx < pld->normNum-1 ? ", " : "" );
}
}
fprintf( file, " ]\n" );
fprintf( file, " }\n" );
}
if ( !haveStrips ) {
if ( bitFlag & ( 1 << limnPolyDataInfoRGBA ) ) {
fprintf( file, " MaterialBinding { value PER_VERTEX_INDEXED }\n" );
fprintf( file, " Material {\n" );
fprintf( file, " diffuseColor [\n" );
for ( rgbaIdx=0; rgbaIdx<pld->rgbaNum; rgbaIdx++ ) {
fprintf( file, " %g %g %g%s\n",
pld->rgba[0 + 4*rgbaIdx]/255.0,
pld->rgba[1 + 4*rgbaIdx]/255.0,
pld->rgba[2 + 4*rgbaIdx]/255.0,
rgbaIdx < pld->rgbaNum-1 ? ", " : "" );
}
fprintf( file, " ]\n" );
fprintf( file, " }\n" );
}
}
if ( haveStrips ) {
fprintf( file, " TriangleStripSet {\n" );
fprintf( file, " numVertices %u\n", pld->icnt[0] );
fprintf( file, " }\n" );
} else {
fprintf( file, " IndexedFaceSet {\n" );
fprintf( file, " coordIndex [\n" );
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triIdx, triNum, *indx3;
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
indx3 = pld->indx + baseVertIdx + 3*triIdx;
fprintf( file, " %u, %u, %u, -1%s\n",
indx3[0], indx3[1], indx3[2],
triIdx < triNum-1 ? ", " : "" );
}
baseVertIdx += 3*triNum;
}
fprintf( file, " ]\n" );
fprintf( file, " }\n" );
}
fprintf( file, "}\n" );
return 0;
}
int
276 limnObjectReadOFF( limnObject *obj, FILE *file ) {
static const char me[]="limnObjectReadOFF";
double vert[6];
char line[AIR_STRLEN_LARGE]; /* HEY: bad Gordon */
int lineCount, lookIdx, partIdx, idxTmp, faceNum, faceGot, got;
unsigned int vertGot, vertNum;
unsigned int ibuff[1024]; /* HEY: bad Gordon */
float fbuff[1024]; /* HEY: bad bad Gordon */
float lastRGB[3]={-1, -1, -1}; int lastLook;
unsigned int lret;
int *vertBase;
airArray *vertBaseArr, *mop;
airPtrPtrUnion appu;
if ( !( obj && file ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
vertBase = NULL;
appu.i = &vertBase;
vertBaseArr = airArrayNew( appu.v, NULL, sizeof( int ), 128 );
mop = airMopNew( );
airMopAdd( mop, vertBaseArr, ( airMopper )airArrayNuke, airMopAlways );
got = 0;
lineCount = 0;
do {
if ( !airOneLine( file, line, AIR_STRLEN_LARGE ) ) {
biffAddf( LIMN, "%s: hit EOF before getting #vert #face #edge line", me );
airMopError( mop ); return 1;
}
lineCount++;
got = airParseStrUI( ibuff, line, AIR_WHITESPACE, 3 );
} while ( 3 != got );
vertNum = ibuff[0];
faceNum = ibuff[1];
/* read all vertex information */
lastLook = -1;
partIdx = limnObjectPartAdd( obj );
vertGot = 0;
airArrayLenIncr( vertBaseArr, 1 );
vertBase[partIdx] = vertGot;
while ( vertGot < vertNum ) {
do {
lret = airOneLine( file, line, AIR_STRLEN_LARGE );
lineCount++;
} while ( 1 == lret );
if ( !lret ) {
biffAddf( LIMN,
"%s: ( near line %d ) hit EOF trying to read vert %d ( of %d )",
me, lineCount, vertGot, vertNum );
airMopError( mop ); return 1;
}
if ( 1 == sscanf( line, "### LIMN BEGIN PART %d", &idxTmp ) ) {
if ( idxTmp != 0 ) {
partIdx = limnObjectPartAdd( obj );
if ( idxTmp != partIdx ) {
biffAddf( LIMN, "%s: got signal to start part %d, not %d",
me, idxTmp, partIdx );
airMopError( mop ); return 1;
}
airArrayLenIncr( vertBaseArr, 1 );
vertBase[partIdx] = vertGot;
}
continue;
}
if ( 3 != airParseStrD( vert, line, AIR_WHITESPACE, 3 ) ) {
biffAddf( LIMN, "%s: couldn't parse 3 doubles from \"%s\" "
"for vert %d ( of %d )",
me, line, vertGot, vertNum );
airMopError( mop ); return 1;
}
if ( 6 == airParseStrD( vert, line, AIR_WHITESPACE, 6 ) ) {
/* we could also parse an RGB color */
if ( -1 == lastLook || !ELL_3V_EQUAL( lastRGB, vert+3 ) ) {
lookIdx = limnObjectLookAdd( obj );
ELL_4V_SET( obj->look[lookIdx].rgba,
AIR_CAST( float, vert[3] ),
AIR_CAST( float, vert[4] ),
AIR_CAST( float, vert[5] ),
1 );
lastLook = lookIdx;
ELL_3V_COPY_TT( lastRGB, float, vert+3 );
} else {
lookIdx = lastLook;
}
} else {
lookIdx = 0;
}
/*
fprintf( stderr, "line %d: vertGot = %d; lookIdx = %d; partIdx = %d\n",
lineCount, vertGot, lookIdx, partIdx );
*/
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, vert[0] ),
AIR_CAST( float, vert[1] ),
AIR_CAST( float, vert[2] ) );
vertGot++;
}
/* read face information */
partIdx = 0;
faceGot = 0;
while ( faceGot < faceNum ) {
do {
lret = airOneLine( file, line, AIR_STRLEN_LARGE );
lineCount++;
} while ( 1 == lret );
if ( !lret ) {
biffAddf( LIMN,
"%s: ( near line %d ) hit EOF trying to read face %d ( of %d )",
me, lineCount, faceGot, faceNum );
airMopError( mop ); return 1;
}
if ( 1 == sscanf( line, "### LIMN BEGIN PART %d", &idxTmp ) ) {
if ( idxTmp != 0 ) {
partIdx += 1;
if ( idxTmp != partIdx ) {
biffAddf( LIMN, "%s: ( near line %d ) got signal to start "
"part %d, not %d",
me, lineCount, idxTmp, partIdx );
airMopError( mop ); return 1;
}
}
continue;
}
if ( '#' == line[0] ) {
/* its some other kind of comment line */
continue;
}
if ( 1 != sscanf( line, "%u", &vertNum ) ) {
biffAddf( LIMN, "%s: ( near line %d ) can't get first int "
"( #verts ) from \"%s\" for face %d ( of %d )",
me, lineCount, line, faceGot, faceNum );
airMopError( mop ); return 1;
}
if ( vertNum+1 != airParseStrUI( ibuff, line, AIR_WHITESPACE, vertNum+1 ) ) {
biffAddf( LIMN, "%s: ( near line %d ) couldn't parse %d ints from \"%s\" "
"for face %d ( of %d )",
me, lineCount, vertNum+1, line, faceGot, faceNum );
airMopError( mop ); return 1;
}
if ( vertNum+1+3 == airParseStrF( fbuff, line,
AIR_WHITESPACE, vertNum+1+3 ) ) {
/* could also parse color */
if ( -1 == lastLook || !ELL_3V_EQUAL( lastRGB, fbuff+vertNum+1 ) ) {
lookIdx = limnObjectLookAdd( obj );
ELL_4V_SET( obj->look[lookIdx].rgba, fbuff[vertNum+1+0],
fbuff[vertNum+1+1], fbuff[vertNum+1+2], 1 );
lastLook = lookIdx;
ELL_3V_COPY( lastRGB, fbuff+vertNum+1 );
} else {
lookIdx = lastLook;
}
} else {
lookIdx = 0;
}
/*
fprintf( stderr, "line %d: faceGot = %d; lookIdx = %d; partIdx = %d\n",
lineCount, faceGot, lookIdx, partIdx );
*/
limnObjectFaceAdd( obj, partIdx, lookIdx, vertNum, ibuff+1 );
faceGot++;
}
airMopOkay( mop );
return 0;
}
/*
http://www.npr.org/templates/story/story.php?storyId=4531695
*/
#define LMPD_MAGIC "LIMN0001"
#define DEMARK_STR "====== "
#define DEMARK_CHAR '='
#define NUM_STR "num:"
#define INFO_STR "info:"
#define TYPE_STR "type:"
#define ICNT_STR "icnt:"
#define INDX_STR "indx:"
#define XYZW_STR "xyzw:"
int
460 limnPolyDataWriteLMPD( FILE *file, const limnPolyData *pld ) {
static const char me[]="limnPolyDataWriteLMPD";
char infoS[AIR_STRLEN_MED];
unsigned int primIdx, infoNum, flag, bit;
Nrrd *nrrd;
airArray *mop;
if ( !( file && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
if ( limnPrimitiveNoop == pld->type[primIdx] ) {
biffAddf( LIMN, "%s: sorry, can't save with prim[%u] type %s", me,
primIdx, airEnumStr( limnPrimitive, pld->type[primIdx] ) );
return 1;
}
}
mop = airMopNew( );
fprintf( file, "%s\n", LMPD_MAGIC );
fprintf( file, "%s%s %u %u %u\n", DEMARK_STR, NUM_STR,
pld->xyzwNum, pld->indxNum, pld->primNum );
flag = limnPolyDataInfoBitFlag( pld );
infoNum = 0;
bit = 0;
strcpy( infoS, "" );
while ( flag ) {
if ( flag & 1 ) {
infoNum += 1;
strcat( infoS, airEnumStr( limnPolyDataInfo, bit ) );
strcat( infoS, "\n" );
}
flag /= 2;
bit += 1;
}
fprintf( file, "%s%s %u\n%s", DEMARK_STR, INFO_STR, infoNum, infoS );
fprintf( file, "%s%s\n", DEMARK_STR, TYPE_STR );
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
fprintf( file, "%s\n", airEnumStr( limnPrimitive, pld->type[primIdx] ) );
}
fprintf( file, "%s%s\n", DEMARK_STR, ICNT_STR );
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
fprintf( file, "%u\n", pld->icnt[primIdx] );
}
nrrd = nrrdNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNix, airMopAlways ); /* nix, not nuke */
fprintf( file, "%s%s\n", DEMARK_STR, INDX_STR );
if ( nrrdWrap_va( nrrd, pld->indx, nrrdTypeUInt, 1, pld->indxNum )
|| nrrdWrite( file, nrrd, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: problem saving indx array", me );
airMopError( mop ); return 1;
}
fflush( file );
fprintf( file, "\n" );
fprintf( file, "%s%s\n", DEMARK_STR, XYZW_STR );
if ( nrrdWrap_va( nrrd, pld->xyzw, nrrdTypeFloat, 2, 4, pld->xyzwNum )
|| nrrdWrite( file, nrrd, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: problem saving xyzw array", me );
airMopError( mop ); return 1;
}
fflush( file );
fprintf( file, "\n" );
if ( infoNum ) {
flag = limnPolyDataInfoBitFlag( pld );
bit = 0;
while ( flag ) {
if ( flag & 1 ) {
int E;
fprintf( file, "%s%s %s\n", DEMARK_STR, INFO_STR,
airEnumStr( limnPolyDataInfo, bit ) );
switch ( bit ) {
case limnPolyDataInfoRGBA:
E = nrrdWrap_va( nrrd, pld->rgba, nrrdTypeUChar, 2, 4, pld->rgbaNum );
break;
case limnPolyDataInfoNorm:
E = nrrdWrap_va( nrrd, pld->norm, nrrdTypeFloat, 2, 3, pld->normNum );
break;
case limnPolyDataInfoTex2:
E = nrrdWrap_va( nrrd, pld->tex2, nrrdTypeFloat, 2, 2, pld->tex2Num );
break;
case limnPolyDataInfoTang:
E = nrrdWrap_va( nrrd, pld->tang, nrrdTypeFloat, 2, 3, pld->tangNum );
break;
default:
biffAddf( LIMN, "%s: info %d ( %s ) not handled", me, bit,
airEnumStr( limnPolyDataInfo, bit ) );
airMopError( mop ); return 1;
break;
}
if ( E || nrrdWrite( file, nrrd, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: problem saving %s info",
me, airEnumStr( limnPolyDataInfo, bit ) );
airMopError( mop ); return 1;
}
fflush( file );
fprintf( file, "\n" );
}
flag /= 2;
bit += 1;
}
}
airMopOkay( mop );
return 0;
}
/*
******** limnPolyDataReadLMPD
**
** reads a limnPolyData from an LMPD file
**
** HEY: this was written in a hurry, is pretty hacky, and so it
** needs some serious clean-up
*/
int
583 limnPolyDataReadLMPD( limnPolyData *pld, FILE *file ) {
static const char me[]="limnPolyDatReadLMPD";
char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED], *tmp;
unsigned int vertNum, indxNum, primNum, primIdx, lineLen,
infoNum, infoIdx, info, flag;
Nrrd *nrrd;
airArray *mop;
int hackhack, tmpChar;
if ( !( pld && file ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
sprintf( name, "magic" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, LMPD_MAGIC ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, LMPD_MAGIC );
return 1;
}
sprintf( name, "nums" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strncmp( line, DEMARK_STR NUM_STR, strlen( DEMARK_STR NUM_STR ) ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" didn't start w/ expected \"%s\"",
me, name, line, NUM_STR );
return 1;
}
tmp = line + strlen( DEMARK_STR NUM_STR );
if ( 3 != sscanf( tmp, " %u %u %u", &vertNum, &indxNum, &primNum ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" as 3 uints on %s line",
me, tmp, name );
return 1;
}
sprintf( name, "info" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strncmp( line, DEMARK_STR INFO_STR, strlen( DEMARK_STR INFO_STR ) ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" didn't start w/ expected \"%s\"",
me, name, line, DEMARK_STR INFO_STR );
return 1;
}
tmp = line + strlen( DEMARK_STR INFO_STR );
if ( 1 != sscanf( tmp, " %u", &infoNum ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" as 1 uints on %s line",
me, tmp, name );
return 1;
}
flag = 0;
for ( infoIdx=0; infoIdx<infoNum; infoIdx++ ) {
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line %u/%u",
me, name, infoIdx, infoNum );
return 1;
}
info = airEnumVal( limnPolyDataInfo, line );
if ( !info ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" %s line %u/%u",
me, line, name, infoIdx, infoNum );
return 1;
}
flag |= ( 1 << info );
}
/* finally, allocate the polydata */
if ( limnPolyDataAlloc( pld, flag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate polydata", me );
return 1;
}
/* actually, caller owns pld, so we don't register it with mop */
sprintf( name, "type" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, DEMARK_STR TYPE_STR ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, DEMARK_STR TYPE_STR );
return 1;
}
for ( primIdx=0; primIdx<primNum; primIdx++ ) {
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line %u/%u",
me, name, primIdx, primNum );
return 1;
}
pld->type[primIdx] = airEnumVal( limnPrimitive, line );
if ( !( pld->type[primIdx] ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" %s line %u/%u",
me, line, name, primIdx, primNum );
return 1;
}
}
sprintf( name, "icnt" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, DEMARK_STR ICNT_STR ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, DEMARK_STR ICNT_STR );
return 1;
}
for ( primIdx=0; primIdx<primNum; primIdx++ ) {
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line %u/%u",
me, name, primIdx, primNum );
return 1;
}
if ( 1 != sscanf( line, "%u", &( pld->icnt[primIdx] ) ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" %s line %u/%u",
me, line, name, primIdx, primNum );
return 1;
}
}
sprintf( name, "indx" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, DEMARK_STR INDX_STR ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, DEMARK_STR ICNT_STR );
return 1;
}
/* NOW its finally time to create the mop */
mop = airMopNew( );
nrrd = nrrdNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNuke, airMopAlways );
/* HEY HEY HEY HOLY CRAP!
** why the hell isn't the verbosity level a field in NrrdIoState ?!?!
** THIS NEES TO BE FIXED ( in nrrd ) ASAP!
*/
hackhack = nrrdStateVerboseIO;
nrrdStateVerboseIO = 0;
if ( nrrdRead( nrrd, file, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: trouble reading %s data", me, name );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeUInt == nrrd->type
&& 1 == nrrd->dim
&& indxNum == nrrd->axis[0].size ) ) {
biffAddf( LIMN, "%s: didn't get 1-D %s-type %u-sample array "
"( got %u-D %s-type %u-by-? array )", me,
airEnumStr( nrrdType, nrrdTypeUInt ),
AIR_CAST( unsigned int, indxNum ),
nrrd->dim,
airEnumStr( nrrdType, nrrd->type ),
AIR_CAST( unsigned int, nrrd->axis[0].size ) );
airMopError( mop ); return 1;
}
/* now copy the data */
memcpy( pld->indx, nrrd->data, nrrdElementSize( nrrd )*nrrdElementNumber( nrrd ) );
do {
tmpChar = getc( file );
if ( EOF == tmpChar ) {
biffAddf( LIMN, "%s: hit EOF seeking to begin next line", me );
airMopError( mop ); return 1;
}
} while ( DEMARK_CHAR != tmpChar );
ungetc( tmpChar, file );
sprintf( name, "xyzw" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, DEMARK_STR XYZW_STR ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, DEMARK_STR XYZW_STR );
return 1;
}
if ( nrrdRead( nrrd, file, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: trouble reading %s data", me, name );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeFloat == nrrd->type
&& 2 == nrrd->dim
&& 4 == nrrd->axis[0].size
&& vertNum == nrrd->axis[1].size ) ) {
biffAddf( LIMN, "%s: didn't get 2-D %s-type 4-by-%u array "
"( got %u-D %s-type %u-by-%u array )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
AIR_CAST( unsigned int, vertNum ),
nrrd->dim,
airEnumStr( nrrdType, nrrd->type ),
AIR_CAST( unsigned int, nrrd->axis[0].size ),
AIR_CAST( unsigned int, nrrd->axis[1].size ) );
airMopError( mop ); return 1;
}
/* now copy the data */
memcpy( pld->xyzw, nrrd->data, nrrdElementSize( nrrd )*nrrdElementNumber( nrrd ) );
if ( infoNum ) {
int wantType;
unsigned int wantSize;
void *data;
for ( infoIdx=0; infoIdx<infoNum; infoIdx++ ) {
do {
tmpChar = getc( file );
if ( EOF == tmpChar ) {
biffAddf( LIMN, "%s: hit EOF seeking to begin next line", me );
airMopError( mop ); return 1;
}
} while ( DEMARK_CHAR != tmpChar );
ungetc( tmpChar, file );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( LIMN, "%s: didn't get %s line %u/%u", me,
INFO_STR, infoIdx, infoNum );
return 1;
}
if ( strncmp( line, DEMARK_STR INFO_STR, strlen( DEMARK_STR INFO_STR ) ) ) {
biffAddf( LIMN, "%s: %s line \"%s\" not expected \"%s\"",
me, INFO_STR, line, DEMARK_STR INFO_STR );
return 1;
}
tmp = line + strlen( DEMARK_STR INFO_STR ) + strlen( " " );
info = airEnumVal( limnPolyDataInfo, tmp );
if ( !info ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" as %s in %s line \"%s\"",
me, tmp, limnPolyDataInfo->name, INFO_STR, line );
return 1;
}
if ( nrrdRead( nrrd, file, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: trouble reading %s %s data",
me, INFO_STR, tmp );
airMopError( mop ); return 1;
}
switch ( info ) {
case limnPolyDataInfoRGBA:
wantType = nrrdTypeUChar;
wantSize = 4;
data = pld->rgba;
break;
case limnPolyDataInfoNorm:
wantType = nrrdTypeFloat;
wantSize = 3;
data = pld->norm;
break;
case limnPolyDataInfoTex2:
wantType = nrrdTypeFloat;
wantSize = 2;
data = pld->tex2;
break;
case limnPolyDataInfoTang:
wantType = nrrdTypeFloat;
wantSize = 3;
data = pld->tang;
break;
default:
biffAddf( LIMN, "%s: info %d ( %s ) not handled", me, info,
airEnumStr( limnPolyDataInfo, info ) );
airMopError( mop ); return 1;
break;
}
if ( !( wantType == nrrd->type
&& 2 == nrrd->dim
&& wantSize == nrrd->axis[0].size
&& vertNum == nrrd->axis[1].size ) ) {
biffAddf( LIMN, "%s: didn't get 2-D %s-type %u-by-%u array "
"( got %u-D %s-type %u-by-%u-by-? array )", me,
airEnumStr( nrrdType, wantType ),
wantSize, AIR_CAST( unsigned int, vertNum ),
nrrd->dim,
airEnumStr( nrrdType, nrrd->type ),
AIR_CAST( unsigned int, nrrd->axis[0].size ),
AIR_CAST( unsigned int, nrrd->axis[1].size ) );
airMopError( mop ); return 1;
}
/* now copy the data */
memcpy( data, nrrd->data, nrrdElementSize( nrrd )*nrrdElementNumber( nrrd ) );
}
}
airMopOkay( mop );
nrrdStateVerboseIO = hackhack;
return 0;
}
int
888 _limnHestPolyDataLMPDParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[] = "_limnHestPolyDataLMPDParse";
char *nerr;
limnPolyData **lpldP;
airArray *mop;
FILE *file;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
lpldP = ( limnPolyData ** )ptr;
if ( !strlen( str ) ) {
/* got empty filename; user didn't really want data, that's okay*/
*lpldP = NULL;
return 0;
}
mop = airMopNew( );
if ( !( file = airFopen( str, stdin, "rb" ) ) ) {
sprintf( err, "%s: couldn't fopen( \"%s\", \"rb\" ): %s",
me, str, strerror( errno ) );
biffAdd( LIMN, err ); airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
*lpldP = limnPolyDataNew( );
airMopAdd( mop, *lpldP, ( airMopper )limnPolyDataNix, airMopOnError );
if ( limnPolyDataReadLMPD( *lpldP, file ) ) {
airMopAdd( mop, nerr = biffGetDone( LIMN ), airFree, airMopOnError );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
hestCB
_limnHestPolyDataLMPD = {
sizeof( limnPolyData * ),
"polydata",
_limnHestPolyDataLMPDParse,
( airMopper )limnPolyDataNix
};
hestCB *
limnHestPolyDataLMPD = &_limnHestPolyDataLMPD;
int
938 _limnHestPolyDataOFFParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[] = "_limnHestPolyDataOFFParse";
char *nerr;
limnPolyData **lpldP;
airArray *mop;
FILE *file;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
lpldP = ( limnPolyData ** )ptr;
if ( !strlen( str ) ) {
/* got empty filename; user didn't really want data, that's okay*/
*lpldP = NULL;
return 0;
}
mop = airMopNew( );
if ( !( file = airFopen( str, stdin, "rb" ) ) ) {
sprintf( err, "%s: couldn't fopen( \"%s\", \"rb\" ): %s",
me, str, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
*lpldP = limnPolyDataNew( );
airMopAdd( mop, *lpldP, ( airMopper )limnPolyDataNix, airMopOnError );
if ( limnPolyDataReadOFF( *lpldP, file ) ) {
airMopAdd( mop, nerr = biffGetDone( LIMN ), airFree, airMopOnError );
strncpy( err, nerr, AIR_STRLEN_HUGE-1 );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
hestCB
_limnHestPolyDataOFF = {
sizeof( limnPolyData * ),
"polydata",
_limnHestPolyDataOFFParse,
( airMopper )limnPolyDataNix
};
hestCB *
limnHestPolyDataOFF = &_limnHestPolyDataOFF;
int
988 limnPolyDataWriteVTK( FILE *file, const limnPolyData *pld ) {
static const char me[]="limnPolyDataWriteVTK";
unsigned int pntIdx, prmIdx, *indx, idxNum;
int linesOnly;
if ( !( file && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
fprintf( file, "# vtk DataFile Version 2.0\n" );
fprintf( file, "limnPolyData\n" );
fprintf( file, "ASCII\n" );
fprintf( file, "DATASET POLYDATA\n" );
fprintf( file, "POINTS %u float\n", pld->xyzwNum );
for ( pntIdx=0; pntIdx<pld->xyzwNum; pntIdx++ ) {
float xyz[3];
ELL_34V_HOMOG( xyz, pld->xyzw + 4*pntIdx );
fprintf( file, "%f %f %f\n", xyz[0], xyz[1], xyz[2] );
}
fprintf( file, "\n" );
/* first check if its only lines... */
linesOnly = AIR_TRUE;
for ( prmIdx=0; prmIdx<pld->primNum; prmIdx++ ) {
linesOnly &= ( limnPrimitiveLineStrip == pld->type[prmIdx] );
}
if ( linesOnly ) {
fprintf( file, "LINES %u %u\n", pld->primNum, pld->primNum + pld->indxNum );
indx = pld->indx;
for ( prmIdx=0; prmIdx<pld->primNum; prmIdx++ ) {
unsigned int ii;
idxNum = pld->icnt[prmIdx];
fprintf( file, "%u", idxNum );
for ( ii=0; ii<idxNum; ii++ ) {
fprintf( file, " %u", indx[ii] );
}
fprintf( file, "\n" );
indx += idxNum;
}
} else {
indx = pld->indx;
for ( prmIdx=0; prmIdx<pld->primNum; prmIdx++ ) {
unsigned int triNum, triIdx;
idxNum = pld->icnt[prmIdx];
switch ( pld->type[prmIdx] ) {
case limnPrimitiveTriangleFan:
biffAddf( LIMN, "%s: %s prims ( prim[%u] ) not supported in VTK?",
me, airEnumStr( limnPrimitive, pld->type[prmIdx] ), prmIdx );
return 1;
break;
case limnPrimitiveQuads:
case limnPrimitiveTriangleStrip:
biffAddf( LIMN, "%s: sorry, saving %s prims ( prim[%u] ) not implemented",
me, airEnumStr( limnPrimitive, pld->type[prmIdx] ), prmIdx );
return 1;
break;
case limnPrimitiveLineStrip:
biffAddf( LIMN, "%s: confusion", me );
return 1;
break;
case limnPrimitiveTriangles:
triNum = idxNum/3;
fprintf( file, "POLYGONS %u %u\n", triNum, triNum + idxNum );
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
fprintf( file, "3 %u %u %u\n",
indx[0 + 3*triIdx], indx[1 + 3*triIdx], indx[2 + 3*triIdx] );
}
break;
default:
biffAddf( LIMN, "%s: sorry, type %s ( prim %u ) not handled here", me,
airEnumStr( limnPrimitive, pld->type[prmIdx] ), prmIdx );
return 1;
break;
}
fprintf( file, "\n" );
indx += idxNum;
}
}
return 0;
}
/*
******** limnPolyDataReadOFF
**
** HEY: this has to be re-written with different allocation strategies
** if it is to support anything other than a single limnPrimitiveTriangles
*/
int
1078 limnPolyDataReadOFF( limnPolyData *pld, FILE *file ) {
static const char me[]="limnPolyDataReadOFF";
char line[AIR_STRLEN_LARGE]; /* HEY: bad Gordon */
unsigned int num[3], xyzwNum, xyzwGot,
faceNum, faceGot, lineCount, got, lret;
if ( !( pld && file ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
/* HEY: this just snarfs lines until it parses 3 uints,
disregarding whether the "OFF" magic was actually there! */
got = 0;
lineCount = 0;
do {
if ( !airOneLine( file, line, AIR_STRLEN_LARGE ) ) {
biffAddf( LIMN, "%s: hit EOF before getting #vert #face #edge line", me );
return 1;
}
lineCount++;
got = airParseStrUI( num, line, AIR_WHITESPACE, 3 );
} while ( 3 != got );
xyzwNum = num[0];
faceNum = num[1];
/* allocate */
if ( limnPolyDataAlloc( pld, 0 /* no extra info */,
xyzwNum, 3*faceNum, 1 ) ) {
biffAddf( LIMN, "%s: couldn't allocate", me );
return 1;
}
/* read all vertex information */
xyzwGot = 0;
while ( xyzwGot < xyzwNum ) {
float *xyzw;
do {
lret = airOneLine( file, line, AIR_STRLEN_LARGE );
lineCount++;
} while ( 1 == lret );
if ( !lret ) {
biffAddf( LIMN,
"%s: ( near line %d ) hit EOF trying to read vert %d ( of %d )",
me, lineCount, xyzwGot, xyzwNum );
return 1;
}
xyzw = pld->xyzw + 4*xyzwGot;
if ( 3 != airParseStrF( xyzw, line, AIR_WHITESPACE, 3 ) ) {
biffAddf( LIMN, "%s: couldn't parse 3 floats from \"%s\" "
"for vert %d ( of %d )",
me, line, xyzwGot, xyzwNum );
return 1;
}
xyzw[3] = 1.0;
xyzwGot++;
}
/* read face information */
faceGot = 0;
while ( faceGot < faceNum ) {
unsigned int *indx, indxSingle[4], indxNum;
do {
lret = airOneLine( file, line, AIR_STRLEN_LARGE );
lineCount++;
} while ( 1 == lret );
if ( !lret ) {
biffAddf( LIMN,
"%s: ( near line %d ) hit EOF trying to read face %d ( of %d )",
me, lineCount, faceGot, faceNum );
return 1;
}
if ( '#' == line[0] ) {
/* its some kind of comment, either LIMN BEGIN PART or otherwise */
continue;
}
if ( 1 != sscanf( line, "%u", &indxNum ) ) {
biffAddf( LIMN, "%s: ( near line %d ) can't get first uint "
"( #verts ) from \"%s\" for face %d ( of %d )",
me, lineCount, line, faceGot, faceNum );
return 1;
}
if ( 3 != indxNum ) {
biffAddf( LIMN, "%s: sorry, can only handle triangles ( not %u verts )",
me, indxNum );
return 1;
}
if ( indxNum+1 != airParseStrUI( indxSingle, line,
AIR_WHITESPACE, indxNum+1 ) ) {
biffAddf( LIMN, "%s: ( near line %d ) couldn't parse %d uints from \"%s\" "
"for face %d ( of %d )",
me, lineCount, indxNum+1, line, faceGot, faceNum );
return 1;
}
indx = pld->indx + 3*faceGot;
ELL_3V_SET( indx, indxSingle[1], indxSingle[2], indxSingle[3] );
/* for now ignoring the color information */
faceGot++;
}
/* set remaining info */
pld->type[0] = limnPrimitiveTriangles;
pld->icnt[0] = 3*faceNum;
return 0;
}
int
1186 limnPolyDataSave( const char *_fname, const limnPolyData *lpld ) {
static const char me[]="limnPolyDataSave";
char *fname;
FILE *file;
airArray *mop;
int ret;
if ( !( _fname && lpld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( !( file = airFopen( _fname, stdout, "wb" ) ) ) {
biffAddf( LIMN, "%s: couldn't fopen( \"%s\", \"wb\" ): %s",
me, _fname, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
fname = airToLower( airStrdup( _fname ) );
airMopAdd( mop, fname, ( airMopper )airFree, airMopAlways );
if ( airEndsWith( fname, ".vtk" ) ) {
ret = limnPolyDataWriteVTK( file, lpld );
} else if ( airEndsWith( fname, ".iv" ) ) {
ret = limnPolyDataWriteIV( file, lpld );
} else {
if ( strcmp( _fname, "-" ) && !airEndsWith( fname, ".lmpd" ) ) {
fprintf( stderr, "%s: WARNING: unknown or no suffix on \"%s\"; "
"using LMPD format", me, _fname );
}
ret = limnPolyDataWriteLMPD( file, lpld );
}
if ( ret ) {
biffAddf( LIMN, "%s: trouble", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
******** limnLightSet( )
**
** turns on a light
**
*/
void
35 limnLightSet( limnLight *lit, int which, int vsp,
float r, float g, float b,
float x, float y, float z ) {
if ( lit && AIR_IN_CL( 0, which, LIMN_LIGHT_NUM-1 ) ) {
lit->on[which] = 1;
lit->vsp[which] = vsp;
ELL_4V_SET( lit->col[which], r, g, b, 1.0 );
ELL_4V_SET( lit->_dir[which], x, y, z, 0.0 );
}
}
/*
******** limnLightAmbientSet( )
**
** sets the ambient light color
*/
void
53 limnLightAmbientSet( limnLight *lit, float r, float g, float b ) {
if ( lit ) {
ELL_4V_SET( lit->amb, r, g, b, 1.0 );
}
}
/*
******** limnLightUpdate( )
**
** copies information from the _dir vectors to the dir vectors. This
** needs to be called even if there are no viewspace lights, so that
** the dir vectors are set and normalized. If there are no viewspace
** lights, "cam" can actually be passed as NULL, but don't get carried
** away...
**
** returns 1 if there was a problem in the camera, otherwise 0.
*/
int
72 limnLightUpdate( limnLight *lit, limnCamera *cam ) {
static const char me[]="limnLightUpdate";
double dir[3], _dir[3], uvn[9]={0, 0, 0, 0, 0, 0, 0, 0, 0}, norm;
int i;
if ( cam ) {
if ( limnCameraUpdate( cam ) ) {
biffAddf( LIMN, "%s: trouble in camera", me );
return 1;
}
ELL_34M_EXTRACT( uvn, cam->V2W );
}
for ( i=0; i<LIMN_LIGHT_NUM; i++ ) {
ELL_3V_COPY( _dir, lit->_dir[i] );
if ( cam && lit->vsp[i] ) {
ELL_3MV_MUL( dir, uvn, _dir );
} else {
ELL_3V_COPY( dir, _dir );
}
ELL_3V_NORM( dir, dir, norm );
ELL_4V_SET_TT( lit->dir[i], float, dir[0], dir[1], dir[2], 0.0 );
}
return 0;
}
/*
******** limnLightSwitch
**
** can toggle a light on/off
**
** returns 1 on error, 0 if okay
*/
void
105 limnLightSwitch( limnLight *lit, int which, int on ) {
if ( lit && AIR_IN_CL( 0, which, LIMN_LIGHT_NUM-1 ) ) {
lit->on[which] = on;
}
}
void
113 limnLightReset( limnLight *lit ) {
int i;
if ( lit ) {
ELL_4V_SET( lit->amb, 0, 0, 0, 1 );
for ( i=0; i<LIMN_LIGHT_NUM; i++ ) {
ELL_4V_SET( lit->_dir[i], 0, 0, 0, 0 );
ELL_4V_SET( lit->dir[i], 0, 0, 0, 0 );
ELL_4V_SET( lit->col[i], 0, 0, 0, 1 );
lit->on[i] = AIR_FALSE;
lit->vsp[i] = AIR_FALSE;
}
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
******** limnpuCmdList[]
**
** NULL-terminated array of unrrduCmd pointers, as ordered by
** LIMN_MAP macro
*/
unrrduCmd *
limnpuCmdList[] = {
LIMN_MAP( LIMN_LIST )
NULL
};
/*
******** limnpuUsage
**
** prints out a little banner, and a listing of all available commands
** with their one-line descriptions
*/
void
45 limnpuUsage( char *me, hestParm *hparm ) {
int i, maxlen, len, c;
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE];
maxlen = 0;
for ( i=0; limnpuCmdList[i]; i++ ) {
maxlen = AIR_MAX( maxlen, ( int )strlen( limnpuCmdList[i]->name ) );
}
sprintf( buff, "--- LimnPolyData Hacking ---" );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stderr, fmt, buff );
for ( i=0; limnpuCmdList[i]; i++ ) {
len = strlen( limnpuCmdList[i]->name );
strcpy( buff, "" );
for ( c=len; c<maxlen; c++ )
strcat( buff, " " );
strcat( buff, me );
strcat( buff, " " );
strcat( buff, limnpuCmdList[i]->name );
strcat( buff, " ... " );
len = strlen( buff );
fprintf( stderr, "%s", buff );
_hestPrintStr( stderr, len, len, hparm->columns,
limnpuCmdList[i]->info, AIR_FALSE );
}
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#define INFO "Information about this program and its use"
int
29 limnpu_aboutMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_MED];
char par1[] = "\t\t\t\t"
"\"lpu\" is a complete hack.\n";
char par2[] = "\t\t\t\t"
"Users are encouraged to take cover.\n";
AIR_UNUSED( argc );
AIR_UNUSED( argv );
AIR_UNUSED( me );
fprintf( stderr, "\n" );
sprintf( buff, "--- lpu: LimnPolyData command-line interface ---" );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stderr, fmt, buff );
airTeemVersionSprint( buff );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stderr, fmt, buff );
fprintf( stderr, "\n" );
_hestPrintStr( stderr, 1, 0, 78, par1, AIR_FALSE );
_hestPrintStr( stderr, 1, 0, 78, par2, AIR_FALSE );
return 0;
}
unrrduCmd limnpu_aboutCmd = { "about", INFO, limnpu_aboutMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Find connected components ( CCs )"
static const char *myinfo =
( INFO
", and then sorts primitive according to area." );
int
33 limnpu_ccfindMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pld;
Nrrd *nmeas;
char *out;
hestOptAdd( &hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output polydata filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nmeas = nrrdNew( );
airMopAdd( mop, nmeas, ( airMopper )nrrdNuke, airMopAlways );
if ( limnPolyDataCCFind( pld )
|| limnPolyDataPrimitiveArea( nmeas, pld )
|| limnPolyDataPrimitiveSort( pld, nmeas )
|| limnPolyDataSave( out, pld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_ccfindCmd = { "ccfind", INFO, limnpu_ccfindMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Measures something about each primitive"
static const char *myinfo =
( INFO
". Actually all it can measure is area at this point..." );
int
33 limnpu_measMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pld;
Nrrd *nout;
char *out;
hestOptAdd( &hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output nrrd filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( limnPolyDataPrimitiveArea( nout, pld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
if ( nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_measCmd = { "meas", INFO, limnpu_measMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Select some subset of primitives"
static const char *myinfo =
( INFO
". Can either specify a range, or a list, or not, until implemented." );
int
33 limnpu_pselMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pldIn, *pldOut;
unsigned int prange[2], pi;
Nrrd *nsel;
double *sel;
char *out;
size_t size[NRRD_DIM_MAX];
hestOptAdd( &hopt, "r", "range", airTypeUInt, 2, 2, prange, NULL,
"range of indices of primitives to select" );
hestOptAdd( &hopt, NULL, "input", airTypeOther, 1, 1, &pldIn, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output polydata filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( prange[0] <= pldIn->primNum-1 &&
prange[1] <= pldIn->primNum-1 ) ) {
fprintf( stderr, "%s: prange[0] %u or [1] %u outside range [0, %u]", me,
prange[0], prange[1], pldIn->primNum-1 );
airMopError( mop );
return 1;
}
if ( !( prange[0] <= prange[1] ) ) {
fprintf( stderr, "%s: need prange[0] %u <= [1] %u", me,
prange[0], prange[1] );
airMopError( mop );
return 1;
}
nsel = nrrdNew( );
airMopAdd( mop, nsel, ( airMopper )nrrdNuke, airMopAlways );
size[0] = pldIn->primNum;
if ( nrrdMaybeAlloc_nva( nsel, nrrdTypeDouble, 1, size ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating buffer:%s", me, err );
airMopError( mop );
return 1;
}
sel = AIR_CAST( double *, nsel->data );
for ( pi=prange[0]; pi<=prange[1]; pi++ ) {
sel[pi] = 1;
}
pldOut = limnPolyDataNew( );
airMopAdd( mop, pldOut, ( airMopper )limnPolyDataNix, airMopAlways );
if ( limnPolyDataPrimitiveSelect( pldOut, pldIn, nsel )
|| limnPolyDataSave( out, pldOut ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_pselCmd = { "psel", INFO, limnpu_pselMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Rasterize polydata"
static const char *myinfo =
( INFO
". " );
int
33 limnpu_rastMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pld;
double min[3], max[3];
Nrrd *nout;
char *out;
int type;
size_t size[NRRD_DIM_MAX];
hestOptAdd( &hopt, "min", "min", airTypeDouble, 3, 3, min, NULL,
"bottom corner" );
hestOptAdd( &hopt, "max", "max", airTypeDouble, 3, 3, max, NULL,
"top corner" );
hestOptAdd( &hopt, "s", "size", airTypeSize_t, 3, 3, size, NULL,
"number of samples along each axis" );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &type, "uchar",
"type of output nrrd",
NULL, nrrdType );
hestOptAdd( &hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output nrrd filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( limnPolyDataRasterize( nout, pld, min, max, size, type ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
if ( nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_rastCmd = { "rast", INFO, limnpu_rastMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Sorts primitives according to given values"
static const char *myinfo =
( INFO
". " );
int
33 limnpu_sortMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pld;
Nrrd *nin;
char *out;
hestOptAdd( &hopt, NULL, "input lpld", airTypeOther, 1, 1, &pld, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "input nrrd", airTypeOther, 1, 1, &nin, NULL,
"input nrrd filename",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output lpld filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( limnPolyDataPrimitiveSort( pld, nin ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
if ( limnPolyDataSave( out, pld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_sortCmd = { "sort", INFO, limnpu_sortMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
#include "privateLimn.h"
#define INFO "Extract the vertex array"
static const char *myinfo =
( INFO
". " );
int
33 limnpu_vertsMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *hopt = NULL;
char *err, *perr;
airArray *mop;
int pret;
limnPolyData *pld;
Nrrd *nout;
char *out;
hestOptAdd( &hopt, NULL, "input", airTypeOther, 1, 1, &pld, NULL,
"input polydata filename",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, NULL, "output", airTypeString, 1, 1, &out, NULL,
"output nrrd filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( myinfo );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNix, airMopAlways ); /* NOT nrrdNuke */
if ( nrrdWrap_va( nout, pld->xyzw, nrrdTypeFloat, 2, 4, pld->xyzwNum )
|| nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:%s", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
unrrduCmd limnpu_vertsCmd = { "verts", INFO, limnpu_vertsMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
limnLight *
28 limnLightNew( void ) {
limnLight *lit;
lit = ( limnLight * )calloc( 1, sizeof( limnLight ) );
return lit;
}
limnLight *
36 limnLightNix( limnLight *lit ) {
if ( lit ) {
free( lit );
}
return NULL;
}
void
45 limnCameraInit( limnCamera *cam ) {
if ( cam ) {
cam->atRelative = limnDefCameraAtRelative;
cam->orthographic = limnDefCameraOrthographic;
cam->rightHanded = limnDefCameraRightHanded;
cam->uRange[0] = cam->uRange[1] = AIR_NAN;
cam->vRange[0] = cam->vRange[1] = AIR_NAN;
cam->fov = AIR_NAN;
cam->aspect = AIR_NAN;
}
return;
}
limnCamera *
60 limnCameraNew( void ) {
limnCamera *cam;
cam = ( limnCamera * )calloc( 1, sizeof( limnCamera ) );
if ( cam ) {
limnCameraInit( cam );
}
return cam;
}
limnCamera *
71 limnCameraNix( limnCamera *cam ) {
if ( cam ) {
free( cam );
}
return NULL;
}
void
80 _limnOptsPSDefaults( limnOptsPS *ps ) {
ps->lineWidth[limnEdgeTypeUnknown] = AIR_NAN;
ps->lineWidth[limnEdgeTypeBackFacet] = 0.0;
ps->lineWidth[limnEdgeTypeBackCrease] = 0.0;
ps->lineWidth[limnEdgeTypeContour] = 2.0;
ps->lineWidth[limnEdgeTypeFrontCrease] = 1.0;
ps->lineWidth[limnEdgeTypeFrontFacet] = 0.0;
ps->lineWidth[limnEdgeTypeBorder] = 1.0;
ps->lineWidth[limnEdgeTypeLone] = 1.0;
ps->creaseAngle = 46;
ps->showpage = AIR_FALSE;
ps->wireFrame = AIR_FALSE;
ps->noBackground = AIR_FALSE;
ELL_3V_SET( ps->bg, 1, 1, 1 );
ELL_3V_SET( ps->edgeColor, 0, 0, 0 );
}
limnWindow *
99 limnWindowNew( int device ) {
limnWindow *win;
win = ( limnWindow * )calloc( 1, sizeof( limnWindow ) );
if ( win ) {
win->device = device;
switch( device ) {
case limnDevicePS:
win->yFlip = 1;
_limnOptsPSDefaults( &( win->ps ) );
break;
}
win->scale = 72;
win->file = NULL;
}
return win;
}
limnWindow *
118 limnWindowNix( limnWindow *win ) {
if ( win ) {
free( win );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/* restricted to this file now since here is the only place its needed */
typedef union {
limnVertex **vert;
limnEdge **edge;
limnFace **face;
limnLook **look;
limnPart ***partp;
void **v;
} limnPtrPtrUnion;
int
38 limnObjectLookAdd( limnObject *obj ) {
int lookIdx;
limnLook *look;
lookIdx = airArrayLenIncr( obj->lookArr, 1 );
look = &( obj->look[lookIdx] );
ELL_4V_SET( look->rgba, 1, 1, 1, 1 );
ELL_3V_SET( look->kads, 0.0, 1.0, 0.0 );
look->spow = 50;
return lookIdx;
}
limnObject *
52 limnObjectNew( int incr, int doEdges ) {
limnObject *obj;
limnPtrPtrUnion lppu;
obj = AIR_CALLOC( 1, limnObject );
obj->vert = NULL;
obj->edge = NULL;
obj->face = NULL;
obj->faceSort = NULL;
obj->part = NULL;
obj->partPool = NULL;
obj->look = NULL;
/* create all various airArrays */
obj->vertArr = airArrayNew( ( lppu.vert = &( obj->vert ), lppu.v ),
&( obj->vertNum ),
sizeof( limnVertex ), incr );
obj->edgeArr = airArrayNew( ( lppu.edge = &( obj->edge ), lppu.v ),
&( obj->edgeNum ),
sizeof( limnEdge ), incr );
obj->faceArr = airArrayNew( ( lppu.face = &( obj->face ), lppu.v ),
&( obj->faceNum ),
sizeof( limnFace ), incr );
obj->partArr = airArrayNew( ( lppu.partp = &( obj->part ), lppu.v ),
&( obj->partNum ),
sizeof( limnPart* ), incr );
obj->partPoolArr = airArrayNew( ( lppu.partp = &( obj->partPool ), lppu.v ),
&( obj->partPoolNum ),
sizeof( limnPart* ), incr );
obj->lookArr = airArrayNew( ( lppu.look = &( obj->look ), lppu.v ),
&( obj->lookNum ),
sizeof( limnLook ), incr );
/* create ( default ) look 0 */
limnObjectLookAdd( obj );
obj->vertSpace = limnSpaceUnknown;
obj->setVertexRGBAFromLook = AIR_FALSE;
obj->doEdges = doEdges;
obj->incr = incr;
return obj;
}
limnPart *
97 _limnObjectPartNew( int incr ) {
limnPart *part;
airPtrPtrUnion appu;
part = AIR_CALLOC( 1, limnPart );
if ( part ) {
part->vertIdx = NULL;
part->edgeIdx = NULL;
part->faceIdx = NULL;
part->vertIdxArr = airArrayNew( ( appu.ui = &( part->vertIdx ), appu.v ),
&( part->vertIdxNum ),
sizeof( int ), incr );
part->edgeIdxArr = airArrayNew( ( appu.ui = &( part->edgeIdx ), appu.v ),
&( part->edgeIdxNum ),
sizeof( int ), incr );
part->faceIdxArr = airArrayNew( ( appu.ui = &( part->faceIdx ), appu.v ),
&( part->faceIdxNum ),
sizeof( int ), incr );
}
return part;
}
limnPart *
120 _limnObjectPartNix( limnPart *part ) {
if ( part ) {
airArrayNuke( part->vertIdxArr );
airArrayNuke( part->edgeIdxArr );
airArrayNuke( part->faceIdxArr );
airFree( part );
}
return NULL;
}
void
132 _limnObjectFaceEmpty( limnFace *face ) {
if ( face ) {
airFree( face->vertIdx );
airFree( face->edgeIdx );
}
return;
}
limnObject *
142 limnObjectNix( limnObject *obj ) {
unsigned int partIdx, faceIdx;
if ( obj ) {
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
_limnObjectPartNix( obj->part[partIdx] );
}
airArrayNuke( obj->partArr );
for ( partIdx=0; partIdx<obj->partPoolNum; partIdx++ ) {
_limnObjectPartNix( obj->partPool[partIdx] );
}
airArrayNuke( obj->partPoolArr );
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
_limnObjectFaceEmpty( obj->face + faceIdx );
}
airArrayNuke( obj->faceArr );
airArrayNuke( obj->vertArr );
airArrayNuke( obj->edgeArr );
airFree( obj->faceSort );
airArrayNuke( obj->lookArr );
airFree( obj );
}
return NULL;
}
void
168 limnObjectEmpty( limnObject *obj ) {
unsigned int partIdx, faceIdx;
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
_limnObjectPartNix( obj->part[partIdx] );
}
airArrayLenSet( obj->partArr, 0 );
for ( partIdx=0; partIdx<obj->partPoolNum; partIdx++ ) {
_limnObjectPartNix( obj->partPool[partIdx] );
}
airArrayLenSet( obj->partPoolArr, 0 );
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
_limnObjectFaceEmpty( obj->face + faceIdx );
}
airArrayLenSet( obj->faceArr, 0 );
airArrayLenSet( obj->vertArr, 0 );
airArrayLenSet( obj->edgeArr, 0 );
airFree( obj->faceSort );
/* leaves ( default ) look 0 */
airArrayLenSet( obj->lookArr, 1 );
/* don't touch state flags */
return;
}
/*
******** limnObjectPreSet
**
** an attempt at pre-allocating everything that will be needed in a
** limnObject, so that there will be no calloc/memcpy overhead associated
** with growing any of the airArrays inside
*/
int
202 limnObjectPreSet( limnObject *obj, unsigned int partNum,
unsigned int lookNum, unsigned int vertPerPart,
unsigned int edgePerPart, unsigned int facePerPart ) {
limnPart *part;
unsigned int partIdx;
limnObjectEmpty( obj );
airArrayLenPreSet( obj->vertArr, partNum*vertPerPart );
airArrayLenPreSet( obj->edgeArr, partNum*edgePerPart );
airArrayLenPreSet( obj->faceArr, partNum*facePerPart );
airArrayLenPreSet( obj->lookArr, lookNum );
airArrayLenPreSet( obj->partArr, partNum );
airArrayLenSet( obj->partPoolArr, partNum );
for ( partIdx=0; partIdx<partNum; partIdx++ ) {
part = obj->partPool[partIdx] = _limnObjectPartNew( obj->incr );
airArrayLenPreSet( part->vertIdxArr, vertPerPart );
airArrayLenPreSet( part->edgeIdxArr, edgePerPart );
airArrayLenPreSet( part->faceIdxArr, facePerPart );
}
return 0;
}
int
227 limnObjectPartAdd( limnObject *obj ) {
unsigned int partIdx;
limnPart *part;
partIdx = airArrayLenIncr( obj->partArr, 1 );
if ( obj->partPoolNum > 0 ) {
part = obj->part[partIdx] = obj->partPool[obj->partPoolNum - 1];
airArrayLenIncr( obj->partPoolArr, -1 );
airArrayLenSet( part->vertIdxArr, 0 );
airArrayLenSet( part->edgeIdxArr, 0 );
airArrayLenSet( part->faceIdxArr, 0 );
} else {
/* there are no available parts in the pool */
part = obj->part[partIdx] = _limnObjectPartNew( obj->incr );
}
part->lookIdx = 0;
part->depth = AIR_NAN;
return partIdx;
}
int
248 limnObjectVertexNumPreSet( limnObject *obj, unsigned int partIdx,
unsigned int vertNum ) {
limnPart *part;
part = obj->part[partIdx];
airArrayLenPreSet( obj->vertArr, vertNum );
airArrayLenPreSet( part->vertIdxArr, vertNum );
return 0;
}
int
259 limnObjectVertexAdd( limnObject *obj, unsigned int partIdx,
float x, float y, float z ) {
limnPart *part;
limnVertex *vert;
int vertIdx, vertIdxIdx;
part = obj->part[partIdx];
vertIdx = airArrayLenIncr( obj->vertArr, 1 );
vert = obj->vert + vertIdx;
vertIdxIdx = airArrayLenIncr( part->vertIdxArr, 1 );
part->vertIdx[vertIdxIdx] = vertIdx;
ELL_4V_SET( vert->world, x, y, z, 1 );
ELL_4V_SET( vert->coord, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
/* HEY: this is kind of lame: this information is set in
a rather sneaky way, and the setVertexRGBAFromLook is
pretty clearly a hack */
if ( obj->setVertexRGBAFromLook ) {
ELL_4V_COPY( vert->rgba, obj->look[part->lookIdx].rgba );
} else {
ELL_4V_SET( vert->rgba, 1, 1, 1, 1 );
}
/* ELL_3V_SET( vert->view, AIR_NAN, AIR_NAN, AIR_NAN ); */
/* ELL_3V_SET( vert->screen, AIR_NAN, AIR_NAN, AIR_NAN ); */
ELL_3V_SET( vert->worldNormal, AIR_NAN, AIR_NAN, AIR_NAN );
return vertIdx;
}
int
288 limnObjectEdgeAdd( limnObject *obj, unsigned int partIdx,
unsigned int lookIdx, unsigned int faceIdx,
unsigned int vertIdx0, unsigned int vertIdx1 ) {
int tmp, edgeIdx=-42;
unsigned int edgeIdxIdx;
limnEdge *edge=NULL;
limnPart *part;
part = obj->part[partIdx];
if ( vertIdx0 > vertIdx1 ) {
ELL_SWAP2( vertIdx0, vertIdx1, tmp );
}
/* do a linear search through this part's existing edges */
for ( edgeIdxIdx=0; edgeIdxIdx<part->edgeIdxNum; edgeIdxIdx++ ) {
edgeIdx = part->edgeIdx[edgeIdxIdx];
edge = obj->edge + edgeIdx;
if ( edge->vertIdx[0] == vertIdx0
&& edge->vertIdx[1] == vertIdx1 ) {
break;
}
}
if ( edgeIdxIdx == part->edgeIdxNum ) {
/* edge not found, add it */
edgeIdx = airArrayLenIncr( obj->edgeArr, 1 );
edge = obj->edge + edgeIdx;
edgeIdxIdx = airArrayLenIncr( part->edgeIdxArr, 1 );
part->edgeIdx[edgeIdxIdx] = edgeIdx;
edge->vertIdx[0] = vertIdx0;
edge->vertIdx[1] = vertIdx1;
edge->faceIdx[0] = faceIdx;
edge->faceIdx[1] = -1;
edge->lookIdx = lookIdx;
edge->partIdx = partIdx;
edge->type = limnEdgeTypeUnknown;
edge->once = AIR_FALSE;
} else {
/* edge already exists; "edge", "edgeIdx", and "edgeIdxIdx" are all set */
edge->faceIdx[1] = faceIdx;
}
return edgeIdx;
}
int
333 limnObjectFaceNumPreSet( limnObject *obj, unsigned int partIdx,
unsigned int faceNum ) {
limnPart *part;
part = obj->part[partIdx];
airArrayLenPreSet( obj->faceArr, faceNum );
airArrayLenPreSet( part->faceIdxArr, faceNum );
return 0;
}
int
344 limnObjectFaceAdd( limnObject *obj, unsigned int partIdx,
unsigned int lookIdx, unsigned int sideNum,
unsigned int *vertIdx ) {
limnFace *face;
limnPart *part;
unsigned int faceIdx, faceIdxIdx, sideIdx;
part = obj->part[partIdx];
faceIdx = airArrayLenIncr( obj->faceArr, 1 );
face = obj->face + faceIdx;
faceIdxIdx = airArrayLenIncr( part->faceIdxArr, 1 );
part->faceIdx[faceIdxIdx] = faceIdx;
face->vertIdx = AIR_CALLOC( sideNum, unsigned int );
face->sideNum = sideNum;
if ( obj->doEdges ) {
face->edgeIdx = AIR_CALLOC( sideNum, unsigned int );
}
for ( sideIdx=0; sideIdx<sideNum; sideIdx++ ) {
face->vertIdx[sideIdx] = vertIdx[sideIdx];
if ( obj->doEdges ) {
face->edgeIdx[sideIdx] =
limnObjectEdgeAdd( obj, partIdx, 0, faceIdx,
vertIdx[sideIdx],
vertIdx[AIR_MOD( ( int )sideIdx+1, ( int )sideNum )] );
}
}
ELL_3V_SET( face->worldNormal, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( face->screenNormal, AIR_NAN, AIR_NAN, AIR_NAN );
/* HEY: its potentially confusing that obj->setVertexRGBAFromLook only
has an effect with whole parts, and not individual faces */
face->lookIdx = lookIdx;
face->partIdx = partIdx;
face->visible = AIR_FALSE;
face->depth = AIR_NAN;
return faceIdx;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
Copyright ( C ) 2011 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
limnPolyData *
29 limnPolyDataNew( void ) {
limnPolyData *pld;
pld = AIR_CALLOC( 1, limnPolyData );
if ( pld ) {
pld->xyzw = NULL;
pld->xyzwNum = 0;
pld->rgba = NULL;
pld->rgbaNum = 0;
pld->norm = NULL;
pld->normNum = 0;
pld->tex2 = NULL;
pld->tex2Num = 0;
pld->tang = NULL;
pld->tangNum = 0;
pld->indx = NULL;
pld->indxNum = 0;
pld->primNum = 0;
pld->type = NULL;
pld->icnt = NULL;
}
return pld;
}
limnPolyData *
54 limnPolyDataNix( limnPolyData *pld ) {
if ( pld ) {
airFree( pld->xyzw );
airFree( pld->rgba );
airFree( pld->norm );
airFree( pld->tex2 );
airFree( pld->tang );
airFree( pld->indx );
airFree( pld->type );
airFree( pld->icnt );
}
airFree( pld );
return NULL;
}
/*
** doesn't set pld->xyzwNum, only the per-attribute xxxNum variables
*/
int
74 _limnPolyDataInfoAlloc( limnPolyData *pld, unsigned int infoBitFlag,
unsigned int vertNum ) {
static const char me[]="_limnPolyDataInfoAlloc";
if ( vertNum != pld->rgbaNum
&& ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) ) {
pld->rgba = ( unsigned char * )airFree( pld->rgba );
if ( vertNum ) {
pld->rgba = AIR_CALLOC( vertNum*4, unsigned char );
if ( !pld->rgba ) {
biffAddf( LIMN, "%s: couldn't allocate %u rgba", me, vertNum );
return 1;
}
}
pld->rgbaNum = vertNum;
}
if ( vertNum != pld->normNum
&& ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) ) {
pld->norm = ( float * )airFree( pld->norm );
if ( vertNum ) {
pld->norm = AIR_CALLOC( vertNum*4, float );
if ( !pld->norm ) {
biffAddf( LIMN, "%s: couldn't allocate %u norm", me, vertNum );
return 1;
}
}
pld->normNum = vertNum;
}
if ( vertNum != pld->tex2Num
&& ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) ) {
pld->tex2 = ( float * )airFree( pld->tex2 );
if ( vertNum ) {
pld->tex2 = AIR_CALLOC( vertNum*2, float );
if ( !pld->tex2 ) {
biffAddf( LIMN, "%s: couldn't allocate %u tex2", me, vertNum );
return 1;
}
}
pld->tex2Num = vertNum;
}
if ( vertNum != pld->tangNum
&& ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) ) {
pld->tang = ( float * )airFree( pld->tang );
if ( vertNum ) {
pld->tang = AIR_CALLOC( vertNum*3, float );
if ( !pld->tang ) {
biffAddf( LIMN, "%s: couldn't allocate %u tang", me, vertNum );
return 1;
}
}
pld->tangNum = vertNum;
}
return 0;
}
unsigned int
134 limnPolyDataInfoBitFlag( const limnPolyData *pld ) {
unsigned int ret;
ret = 0;
if ( pld ) {
if ( pld->rgba && pld->rgbaNum == pld->xyzwNum ) {
ret |= ( 1 << limnPolyDataInfoRGBA );
}
if ( pld->norm && pld->normNum == pld->xyzwNum ) {
ret |= ( 1 << limnPolyDataInfoNorm );
}
if ( pld->tex2 && pld->tex2Num == pld->xyzwNum ) {
ret |= ( 1 << limnPolyDataInfoTex2 );
}
if ( pld->tang && pld->tangNum == pld->xyzwNum ) {
ret |= ( 1 << limnPolyDataInfoTang );
}
}
return ret;
}
int
156 limnPolyDataAlloc( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int vertNum,
unsigned int indxNum,
unsigned int primNum ) {
static const char me[]="limnPolyDataAlloc";
if ( !pld ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( vertNum != pld->xyzwNum ) {
pld->xyzw = ( float * )airFree( pld->xyzw );
if ( vertNum ) {
pld->xyzw = AIR_CALLOC( vertNum*4, float );
if ( !pld->xyzw ) {
biffAddf( LIMN, "%s: couldn't allocate %u xyzw", me, vertNum );
return 1;
}
}
pld->xyzwNum = vertNum;
}
if ( _limnPolyDataInfoAlloc( pld, infoBitFlag, vertNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate info", me );
return 1;
}
if ( indxNum != pld->indxNum ) {
pld->indx = ( unsigned int * )airFree( pld->indx );
if ( indxNum ) {
pld->indx = AIR_CALLOC( indxNum, unsigned int );
if ( !pld->indx ) {
biffAddf( LIMN, "%s: couldn't allocate %u indices", me, indxNum );
return 1;
}
}
pld->indxNum = indxNum;
}
if ( primNum != pld->primNum ) {
pld->type = ( unsigned char * )airFree( pld->type );
pld->icnt = ( unsigned int * )airFree( pld->icnt );
if ( primNum ) {
pld->type = AIR_CALLOC( primNum, unsigned char );
pld->icnt = AIR_CALLOC( primNum, unsigned int );
if ( !( pld->type && pld->icnt ) ) {
biffAddf( LIMN, "%s: couldn't allocate %u primitives", me, primNum );
return 1;
}
}
pld->primNum = primNum;
}
return 0;
}
size_t
210 limnPolyDataSize( const limnPolyData *pld ) {
size_t ret = 0;
if ( pld ) {
ret += pld->xyzwNum*sizeof( float )*4;
if ( pld->rgba ) {
ret += pld->rgbaNum*sizeof( unsigned char )*4;
}
if ( pld->norm ) {
ret += pld->normNum*sizeof( float )*3;
}
if ( pld->tex2 ) {
ret += pld->tex2Num*sizeof( float )*2;
}
if ( pld->tang ) {
ret += pld->tangNum*sizeof( float )*3;
}
ret += pld->indxNum*sizeof( unsigned int );
ret += pld->primNum*sizeof( signed char );
ret += pld->primNum*sizeof( unsigned int );
}
return ret;
}
int
235 limnPolyDataCopy( limnPolyData *pldB, const limnPolyData *pldA ) {
static const char me[]="limnPolyDataCopy";
if ( !( pldB && pldA ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( limnPolyDataAlloc( pldB, limnPolyDataInfoBitFlag( pldA ),
pldA->xyzwNum, pldA->indxNum, pldA->primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
memcpy( pldB->xyzw, pldA->xyzw, pldA->xyzwNum*sizeof( float )*4 );
if ( pldA->rgba ) {
memcpy( pldB->rgba, pldA->rgba, pldA->rgbaNum*sizeof( unsigned char )*4 );
}
if ( pldA->norm ) {
memcpy( pldB->norm, pldA->norm, pldA->normNum*sizeof( float )*3 );
}
if ( pldA->tex2 ) {
memcpy( pldB->tex2, pldA->tex2, pldA->tex2Num*sizeof( float )*2 );
}
if ( pldA->tang ) {
memcpy( pldB->tang, pldA->tang, pldA->tangNum*sizeof( float )*3 );
}
memcpy( pldB->indx, pldA->indx, pldA->indxNum*sizeof( unsigned int ) );
memcpy( pldB->type, pldA->type, pldA->primNum*sizeof( signed char ) );
memcpy( pldB->icnt, pldA->icnt, pldA->primNum*sizeof( unsigned int ) );
return 0;
}
int
267 limnPolyDataCopyN( limnPolyData *pldB, const limnPolyData *pldA,
unsigned int num ) {
static const char me[]="limnPolyDataCopyN";
unsigned int ii, jj, size;
if ( !( pldB && pldA ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( limnPolyDataAlloc( pldB, limnPolyDataInfoBitFlag( pldA ),
num*pldA->xyzwNum,
num*pldA->indxNum,
num*pldA->primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
for ( ii=0; ii<num; ii++ ) {
/* fprintf( stderr, "!%s: ii = %u/%u\n", me, ii, num ); */
size = pldA->xyzwNum*4;
/*
char *_beg = ( char * )( pldB->xyzw + ii*size );
char *_end = _beg + size - 1;
fprintf( stderr, "!%s: memcpy( %p+%u=%p, %u ) --> [%p, %p] inside: %d %d\n", me,
pldB->xyzw, ii*size, pldB->xyzw + ii*size, size,
_beg, _end, AIR_IN_CL( _xyzwBeg, _beg, _xyzwEnd ),
AIR_IN_CL( _xyzwBeg, _end, _xyzwEnd ) );
*/
memcpy( pldB->xyzw + ii*size, pldA->xyzw, size*sizeof( float ) );
for ( jj=0; jj<pldA->indxNum; jj++ ) {
( pldB->indx + ii*pldA->indxNum )[jj] = pldA->indx[jj] + ii*pldA->xyzwNum;
}
size = pldA->primNum;
memcpy( pldB->type + ii*size, pldA->type, size*sizeof( unsigned char ) );
memcpy( pldB->icnt + ii*size, pldA->icnt, size*sizeof( unsigned int ) );
if ( pldA->rgba ) {
size = pldA->rgbaNum*4;
memcpy( pldB->rgba + ii*size, pldA->rgba, size*sizeof( unsigned char ) );
}
if ( pldA->norm ) {
size = pldA->normNum*3;
memcpy( pldB->norm + ii*size, pldA->norm, size*sizeof( float ) );
}
if ( pldA->tex2 ) {
size = pldA->tex2Num*2;
memcpy( pldB->tex2 + ii*size, pldA->tex2, size*sizeof( float ) );
}
if ( pldA->tang ) {
size = pldA->tangNum*3;
memcpy( pldB->tang + ii*size, pldA->tang, size*sizeof( float ) );
}
}
return 0;
}
/*
******** limnPolyDataTransform_f, limnPolyDataTransform_d
**
** transforms a surface ( both positions, and normals ( if set ) )
** by given homogenous transform
*/
void
328 limnPolyDataTransform_f( limnPolyData *pld,
const float homat[16] ) {
double hovec[4], mat[9], inv[9], norm[3], tang[3], nmat[9], tlen;
unsigned int vertIdx;
if ( pld && homat ) {
ELL_34M_EXTRACT( mat, homat );
if ( pld->norm ) {
ell_3m_inv_d( inv, mat );
ELL_3M_TRANSPOSE( nmat, inv );
} else {
ELL_3M_IDENTITY_SET( nmat ); /* hush unused value compiler warnings */
}
for ( vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++ ) {
ELL_4MV_MUL( hovec, homat, pld->xyzw + 4*vertIdx );
ELL_4V_COPY_TT( pld->xyzw + 4*vertIdx, float, hovec );
if ( pld->norm ) {
ELL_3MV_MUL( norm, nmat, pld->norm + 3*vertIdx );
ELL_3V_NORM( norm, norm, tlen );
ELL_3V_COPY_TT( pld->norm + 3*vertIdx, float, norm );
}
if ( pld->tang ) { /* HEY: not tested */
ELL_3MV_MUL( tang, mat, pld->tang + 3*vertIdx );
ELL_3V_NORM( tang, tang, tlen );
ELL_3V_COPY_TT( pld->tang + 3*vertIdx, float, tang );
}
}
}
return;
}
/* !!! COPY AND PASTE !!! ( except for double homat[16] ) */
void
361 limnPolyDataTransform_d( limnPolyData *pld, const double homat[16] ) {
double hovec[4], mat[9], inv[9], norm[3], nmat[9];
unsigned int vertIdx;
if ( pld && homat ) {
if ( pld->norm ) {
ELL_34M_EXTRACT( mat, homat );
ell_3m_inv_d( inv, mat );
ELL_3M_TRANSPOSE( nmat, inv );
} else {
ELL_3M_IDENTITY_SET( nmat ); /* hush unused value compiler warnings */
}
for ( vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++ ) {
ELL_4MV_MUL( hovec, homat, pld->xyzw + 4*vertIdx );
ELL_4V_COPY_TT( pld->xyzw + 4*vertIdx, float, hovec );
if ( pld->norm ) {
ELL_3MV_MUL( norm, nmat, pld->norm + 3*vertIdx );
ELL_3V_COPY_TT( pld->norm + 3*vertIdx, float, norm );
}
}
}
return;
}
unsigned int
386 limnPolyDataPolygonNumber( const limnPolyData *pld ) {
unsigned int ret, primIdx;
ret = 0;
if ( pld ) {
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
switch( pld->type[primIdx] ) {
case limnPrimitiveNoop:
/* no increment */
break;
case limnPrimitiveTriangles:
ret += pld->icnt[primIdx]/3;
break;
case limnPrimitiveTriangleStrip:
case limnPrimitiveTriangleFan:
ret += pld->icnt[primIdx] - 2;
break;
case limnPrimitiveQuads:
ret += pld->icnt[primIdx]/4;
break;
}
}
}
return ret;
}
static int
413 limnPolyDataVertexNormals_( limnPolyData *pld, int nonorient ) {
static const char me[]="limnPolyDataVertexNormals_";
unsigned int infoBitFlag, primIdx, triIdx, normIdx, baseVertIdx;
double len, *matrix=NULL;
airArray *mop;
infoBitFlag = limnPolyDataInfoBitFlag( pld );
if ( limnPolyDataAlloc( pld,
infoBitFlag | ( 1 << limnPolyDataInfoNorm ),
pld->xyzwNum,
pld->indxNum,
pld->primNum ) ) {
biffAddf( LIMN, "%s: couldn't alloc polydata normals", me );
return 1;
}
mop = airMopNew( );
if ( nonorient ) { /* we will accumulate outer products */
matrix = ( double * ) malloc( sizeof( double )*9*pld->xyzwNum );
if ( matrix==NULL ) {
biffAddf( LIMN, "%s: couldn't allocate matrix buffer", me );
return 1;
}
airMopAdd( mop, matrix, airFree, airMopAlways );
for ( normIdx=0; normIdx<pld->normNum; normIdx++ ) {
ELL_3M_ZERO_SET( matrix + 9*normIdx );
}
} else {
for ( normIdx=0; normIdx<pld->normNum; normIdx++ ) {
ELL_3V_SET( pld->norm + 3*normIdx, 0, 0, 0 );
}
}
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triNum, indxLine[3]={0, 0, 0}, ii;
float pos[3][3], edgeA[3], edgeB[3], norm[3];
switch ( pld->type[primIdx] ) {
case limnPrimitiveTriangles:
triNum = pld->icnt[primIdx]/3;
break;
case limnPrimitiveTriangleStrip:
case limnPrimitiveTriangleFan:
triNum = pld->icnt[primIdx]-2;
break;
case limnPrimitiveNoop:
triNum = 0;
break;
default:
biffAddf( LIMN, "%s: came across unsupported limnPrimitive \"%s\"", me,
airEnumStr( limnPrimitive, pld->type[primIdx] ) );
airMopError( mop );
return 1;
}
if ( limnPrimitiveNoop != pld->type[primIdx] ) {
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
switch ( pld->type[primIdx] ) {
case limnPrimitiveTriangles:
ELL_3V_COPY( indxLine, pld->indx + baseVertIdx + 3*triIdx );
break;
case limnPrimitiveTriangleStrip:
if ( triIdx%2==0 ) {
ELL_3V_COPY( indxLine, pld->indx+baseVertIdx+triIdx );
} else {
ELL_3V_SET( indxLine, pld->indx[baseVertIdx+triIdx+1],
pld->indx[baseVertIdx+triIdx],
pld->indx[baseVertIdx+triIdx+2] );
}
break;
case limnPrimitiveTriangleFan:
ELL_3V_SET( indxLine, pld->indx[baseVertIdx],
pld->indx[baseVertIdx+triIdx+1],
pld->indx[baseVertIdx+triIdx+2] );
break;
}
for ( ii=0; ii<3; ii++ ) {
ELL_34V_HOMOG( pos[ii], pld->xyzw + 4*indxLine[ii] );
}
ELL_3V_SUB( edgeA, pos[1], pos[0] );
ELL_3V_SUB( edgeB, pos[2], pos[0] );
ELL_3V_CROSS( norm, edgeA, edgeB );
/* Adding cross products without any normalization is
* equivalent to weighting by triangle area, as proposed
* ( among others ) by G. Taubin ( "Estimating the tensor of
* curvature of a surface from a polyhedral approximation",
* ICCV 1995 ). This is efficient, avoids trouble with
* degenerate triangles and gives reasonable results in
* practice. */
if ( nonorient ) {
double outer[9];
ELL_3MV_OUTER( outer, norm, norm );
len = ELL_3V_LEN( norm );
/* re-scale so that we don't weight by square of area */
for ( ii=0; ii<3; ii++ ) {
ELL_3M_SCALE_INCR( matrix+9*indxLine[ii], 1.0/len, outer );
}
} else {
for ( ii=0; ii<3; ii++ ) {
ELL_3V_INCR( pld->norm + 3*indxLine[ii], norm );
}
}
}
}
baseVertIdx += pld->icnt[primIdx];
}
if ( nonorient ) {
double eval[3], evec[9];
for ( normIdx=0; normIdx<pld->normNum; normIdx++ ) {
ell_3m_eigensolve_d( eval, evec, matrix+9*normIdx, AIR_TRUE );
ELL_3V_COPY_TT( pld->norm + 3*normIdx, float, evec );
}
} else {
for ( normIdx=0; normIdx<pld->normNum; normIdx++ ) {
ELL_3V_NORM_TT( pld->norm + 3*normIdx, float, pld->norm + 3*normIdx, len );
}
}
airMopOkay( mop );
return 0;
}
int
538 limnPolyDataVertexNormals( limnPolyData *pld ) {
return limnPolyDataVertexNormals_( pld, 0 );
}
/* counterpart for non-orientable surfaces */
int
544 limnPolyDataVertexNormalsNO( limnPolyData *pld ) {
return limnPolyDataVertexNormals_( pld, 1 );
}
unsigned int
549 limnPolyDataPrimitiveTypes( const limnPolyData *pld ) {
unsigned int ret, primIdx;
ret = 0;
if ( pld ) {
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
ret |= ( 1 << pld->type[primIdx] );
}
}
return ret;
}
int
562 limnPolyDataPrimitiveVertexNumber( Nrrd *nout, limnPolyData *pld ) {
static const char me[]="limnPolyDataPrimitiveVertexNumber";
unsigned int *vnum, pidx;
if ( !( nout && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeUInt, 1,
AIR_CAST( size_t, pld->primNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
vnum = AIR_CAST( unsigned int *, nout->data );
for ( pidx=0; pidx<pld->primNum; pidx++ ) {
vnum[pidx] = pld->icnt[pidx];
}
return 0;
}
int
585 limnPolyDataPrimitiveArea( Nrrd *nout, limnPolyData *pld ) {
static const char me[]="limnPolyDataPrimitiveArea";
unsigned int primIdx, baseVertIdx;
unsigned int triNum, triIdx, *indx, ii;
float vert[3][3], edgeA[3], edgeB[3], cross[3];
double *area;
if ( !( nout && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 1,
AIR_CAST( size_t, pld->primNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
area = AIR_CAST( double *, nout->data );
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
area[primIdx] = 0;
switch ( pld->type[primIdx] ) {
case limnPrimitiveNoop:
area[primIdx] = 0.0;
break;
case limnPrimitiveTriangles:
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
indx = pld->indx + baseVertIdx + 3*triIdx;
for ( ii=0; ii<3; ii++ ) {
ELL_34V_HOMOG( vert[ii], pld->xyzw + 4*indx[ii] );
}
ELL_3V_SUB( edgeA, vert[1], vert[0] );
ELL_3V_SUB( edgeB, vert[2], vert[0] );
ELL_3V_CROSS( cross, edgeA, edgeB );
area[primIdx] += ELL_3V_LEN( cross )/2;
}
break;
case limnPrimitiveTriangleStrip:
case limnPrimitiveTriangleFan:
case limnPrimitiveQuads:
biffAddf( LIMN,
"%s: sorry, haven't implemented area( prim[%u]=%s ) yet", me,
primIdx, airEnumStr( limnPrimitive, pld->type[primIdx] ) );
return 1;
break;
case limnPrimitiveLineStrip:
/* lines have no area */
break;
}
baseVertIdx += pld->icnt[primIdx];
}
return 0;
}
/*
** I may regret making this only be axis-aligned ...
*/
int
645 limnPolyDataRasterize( Nrrd *nout, limnPolyData *pld,
double min[3], double max[3],
size_t size[3], int type ) {
static const char me[]="limnPolyDataRasterize";
size_t xi, yi, zi;
unsigned int vertIdx;
double ( *ins )( void *, size_t, double );
if ( !( nout && pld && min && max && size ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( LIMN, "%s: got invalid %s %d", me, nrrdType->name, type );
return 1;
}
if ( nrrdTypeBlock == type ) {
biffAddf( LIMN, "%s: can't use output type %s", me,
airEnumStr( nrrdType, type ) );
return 1;
}
if ( !( min[0] < max[0] &&
min[1] < max[1] &&
min[2] < max[2] ) ) {
biffAddf( LIMN, "%s min ( %g, %g, %g ) not < max ( %g, %g, %g )", me,
min[0], min[1], min[2], max[0], max[1], max[2] );
return 1;
}
if ( nrrdMaybeAlloc_nva( nout, type, 3, size ) ) {
biffMovef( LIMN, NRRD, "%s: trouble allocating output", me );
return 1;
}
ins = nrrdDInsert[type];
for ( vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++ ) {
double xyz[3];
ELL_34V_HOMOG( xyz, pld->xyzw + 4*vertIdx );
if ( !( AIR_IN_OP( min[0], xyz[0], max[0] ) &&
AIR_IN_OP( min[1], xyz[1], max[1] ) &&
AIR_IN_OP( min[2], xyz[2], max[2] ) ) ) {
continue;
}
xi = airIndex( min[0], xyz[0], max[0], size[0] );
yi = airIndex( min[1], xyz[1], max[1], size[1] );
zi = airIndex( min[2], xyz[2], max[2], size[2] );
ins( nout->data, xi + size[0]*( yi + size[1]*zi ), 1.0 );
}
nrrdAxisInfoSet_nva( nout, nrrdAxisInfoMin, min );
nrrdAxisInfoSet_nva( nout, nrrdAxisInfoMax, max );
return 0;
}
void
702 limnPolyDataColorSet( limnPolyData *pld,
unsigned char RR, unsigned char GG,
unsigned char BB, unsigned char AA ) {
unsigned int vertIdx;
if ( pld && ( ( 1 << limnPolyDataInfoRGBA ) & limnPolyDataInfoBitFlag( pld ) ) ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, RR, GG, BB, AA );
}
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
Copyright ( C ) 2011 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
29 limnPolyDataSpiralTubeWrap( limnPolyData *pldOut, const limnPolyData *pldIn,
unsigned int infoBitFlag, Nrrd *nvertmap,
unsigned int tubeFacet, unsigned int endFacet,
double radius ) {
static const char me[]="limnPolyDataSpiralTubeWrap";
double *cost, *sint;
unsigned int tubeVertNum = 0, tubeIndxNum = 0, primIdx, pi, *vertmap;
unsigned int inVertTotalIdx = 0, outVertTotalIdx = 0, outIndxIdx = 0;
int color;
airArray *mop;
if ( !( pldOut && pldIn ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( ( 1 << limnPrimitiveLineStrip ) != limnPolyDataPrimitiveTypes( pldIn ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveLineStrip ) );
return 1;
}
for ( primIdx=0; primIdx<pldIn->primNum; primIdx++ ) {
unsigned int tvni, tini;
if ( endFacet ) {
tvni = tubeFacet*( 2*endFacet + pldIn->icnt[primIdx] ) + 1;
tini = 2*tubeFacet*( 2*endFacet + pldIn->icnt[primIdx] + 1 ) -2;
} else {
tvni = tubeFacet*( 2 + pldIn->icnt[primIdx] );
tini = 2*tubeFacet*( 1 + pldIn->icnt[primIdx] );
}
tubeVertNum += tvni;
tubeIndxNum += tini;
}
if ( limnPolyDataAlloc( pldOut,
/* sorry have to have normals, even if they weren't
asked for, because currently they're used as part
of vertex position calc */
( infoBitFlag | ( 1 << limnPolyDataInfoNorm ) ),
tubeVertNum, tubeIndxNum, pldIn->primNum ) ) {
biffAddf( LIMN, "%s: trouble allocating output", me );
return 1;
}
if ( nvertmap ) {
if ( nrrdMaybeAlloc_va( nvertmap, nrrdTypeUInt, 1,
AIR_CAST( size_t, tubeVertNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: trouble allocating vert map", me );
return 1;
}
vertmap = AIR_CAST( unsigned int *, nvertmap->data );
} else {
vertmap = NULL;
}
color = ( ( infoBitFlag & ( 1 << limnPolyDataInfoRGBA ) )
&& ( limnPolyDataInfoBitFlag( pldIn ) & ( 1 << limnPolyDataInfoRGBA ) ) );
mop = airMopNew( );
cost = AIR_CAST( double *, calloc( tubeFacet, sizeof( double ) ) );
sint = AIR_CAST( double *, calloc( tubeFacet, sizeof( double ) ) );
airMopAdd( mop, cost, airFree, airMopAlways );
airMopAdd( mop, sint, airFree, airMopAlways );
if ( !( cost && sint ) ) {
biffAddf( LIMN, "%s: couldn't allocate lookup tables", me );
airMopError( mop ); return 1;
}
for ( pi=0; pi<tubeFacet; pi++ ) {
double angle;
angle = AIR_AFFINE( 0, pi, tubeFacet, 0, 2*AIR_PI );
cost[pi] = cos( angle );
sint[pi] = sin( angle );
}
for ( primIdx=0; primIdx<pldIn->primNum; primIdx++ ) {
unsigned int inVertIdx;
pldOut->type[primIdx] = limnPrimitiveTriangleStrip;
if ( endFacet ) {
pldOut->icnt[primIdx] =
2*tubeFacet*( 2*endFacet + pldIn->icnt[primIdx] + 1 ) - 2;
} else {
pldOut->icnt[primIdx] =
2*tubeFacet*( 1 + pldIn->icnt[primIdx] );
}
for ( inVertIdx=0;
inVertIdx<pldIn->icnt[primIdx];
inVertIdx++ ) {
unsigned int forwIdx, backIdx, tubeEndIdx;
double tang[3], tmp, scl, step, perp[3], pimp[3];
/* inVrt = pldIn->vert + pldIn->indx[inVertTotalIdx]; */
if ( 0 == inVertIdx ) {
forwIdx = inVertTotalIdx+1;
backIdx = inVertTotalIdx;
scl = 1;
} else if ( pldIn->icnt[primIdx]-1 == inVertIdx ) {
forwIdx = inVertTotalIdx;
backIdx = inVertTotalIdx-1;
scl = 1;
} else {
forwIdx = inVertTotalIdx+1;
backIdx = inVertTotalIdx-1;
scl = 0.5;
}
if ( 1 == pldIn->icnt[primIdx] ) {
ELL_3V_SET( tang, 0, 0, 1 ); /* completely arbitrary, as it must be */
step = 0;
} else {
ELL_3V_SUB( tang,
pldIn->xyzw + 4*forwIdx,
pldIn->xyzw + 4*backIdx );
ELL_3V_NORM( tang, tang, step );
step *= scl;
}
if ( 0 == inVertIdx || 1 == pldIn->icnt[primIdx] ) {
ell_3v_perp_d( perp, tang );
} else {
/* transport last perp forwards */
double dot;
dot = ELL_3V_DOT( perp, tang );
ELL_3V_SCALE_ADD2( perp, 1.0, perp, -dot, tang );
}
ELL_3V_NORM( perp, perp, tmp );
ELL_3V_CROSS( pimp, perp, tang );
/* ( perp, pimp, tang ) is a left-handed frame, on purpose */
/* limnVrt *outVrt; */
/* -------------------------------------- BEGIN initial endcap */
if ( 0 == inVertIdx ) {
unsigned int startIdx, ei;
startIdx = outVertTotalIdx;
if ( endFacet ) {
for ( ei=0; ei<endFacet; ei++ ) {
for ( pi=0; pi<tubeFacet; pi++ ) {
double costh, sinth, cosph, sinph, phi, theta;
phi = ( AIR_AFFINE( 0, ei, endFacet, 0, AIR_PI/2 )
+ AIR_AFFINE( 0, pi, tubeFacet,
0, AIR_PI/2 )/endFacet );
theta = AIR_AFFINE( 0, pi, tubeFacet, 0.0, 2*AIR_PI );
cosph = cos( phi );
sinph = sin( phi );
costh = cos( theta );
sinth = sin( theta );
ELL_3V_SCALE_ADD3_TT( pldOut->norm + 3*outVertTotalIdx, float,
-cosph, tang,
costh*sinph, perp,
sinth*sinph, pimp );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
-step/2, tang,
radius,
pldOut->norm + 3*outVertTotalIdx );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
if ( color ) {
ELL_4V_COPY( pldOut->rgba + 4*outVertTotalIdx,
pldIn->rgba + 4*inVertTotalIdx );
}
outVertTotalIdx++;
}
}
for ( pi=1; pi<tubeFacet; pi++ ) {
pldOut->indx[outIndxIdx++] = startIdx;
pldOut->indx[outIndxIdx++] = startIdx + pi;
}
for ( ei=0; ei<endFacet; ei++ ) {
/* at the highest ei we're actually linking with the first
row of vertices at the start of the tube */
for ( pi=0; pi<tubeFacet; pi++ ) {
pldOut->indx[outIndxIdx++] = ( startIdx + pi
+ ( ei + 0 )*tubeFacet );
pldOut->indx[outIndxIdx++] = ( startIdx + pi
+ ( ei + 1 )*tubeFacet );
}
}
} else {
/* no endcap, open tube */
for ( pi=0; pi<tubeFacet; pi++ ) {
double costh, sinth, theta;
theta = AIR_AFFINE( 0, pi, tubeFacet, 0.0, 2*AIR_PI );
costh = cos( theta );
sinth = sin( theta );
ELL_3V_SCALE_ADD2_TT( pldOut->norm + 3*outVertTotalIdx, float,
costh, perp,
sinth, pimp );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
-step/2 + step/( 2*tubeFacet ), tang,
radius, pldOut->norm + 3*outVertTotalIdx );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
if ( color ) {
ELL_4V_COPY( pldOut->rgba + 4*outVertTotalIdx,
pldIn->rgba + 4*inVertTotalIdx );
}
outVertTotalIdx++;
}
for ( pi=0; pi<tubeFacet; pi++ ) {
pldOut->indx[outIndxIdx++] = ( startIdx + pi + 0*tubeFacet );
pldOut->indx[outIndxIdx++] = ( startIdx + pi + 1*tubeFacet );
}
}
} /* if ( 0 == inVertIdx ) */
/* -------------------------------------- END initial endcap */
for ( pi=0; pi<tubeFacet; pi++ ) {
double shift, cosa, sina;
shift = AIR_AFFINE( -0.5, pi, tubeFacet-0.5, -step/2, step/2 );
cosa = cost[pi];
sina = sint[pi];
/* outVrt = pldOut->vert + outVertTotalIdx; */
ELL_3V_SCALE_ADD2_TT( pldOut->norm + 3*outVertTotalIdx, float,
cosa, perp, sina, pimp );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
radius,
pldOut->norm + 3*outVertTotalIdx,
shift, tang );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
pldOut->indx[outIndxIdx++] = outVertTotalIdx;
pldOut->indx[outIndxIdx++] = outVertTotalIdx + tubeFacet;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
if ( color ) {
ELL_4V_COPY( pldOut->rgba + 4*outVertTotalIdx,
pldIn->rgba + 4*inVertTotalIdx );
}
outVertTotalIdx++;
}
tubeEndIdx = outVertTotalIdx;
/* -------------------------------------- BEGIN final endcap */
if ( inVertIdx == pldIn->icnt[primIdx]-1 ) {
unsigned int ei;
if ( endFacet ) {
for ( ei=0; ei<endFacet; ei++ ) {
for ( pi=0; pi<tubeFacet; pi++ ) {
double costh, sinth, cosph, sinph, phi, theta;
phi = ( AIR_AFFINE( 0, ei, endFacet, AIR_PI/2, AIR_PI )
+ AIR_AFFINE( 0, pi, tubeFacet,
0, AIR_PI/2 )/endFacet );
theta = AIR_AFFINE( 0, pi, tubeFacet, 0.0, 2*AIR_PI );
cosph = cos( phi );
sinph = sin( phi );
costh = cos( theta );
sinth = sin( theta );
/* outVrt = pldOut->vert + outVertTotalIdx; */
ELL_3V_SCALE_ADD3_TT( pldOut->norm + 3*outVertTotalIdx, float,
-cosph, tang,
costh*sinph, perp,
sinth*sinph, pimp );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
step/2, tang,
radius,
pldOut->norm + 3*outVertTotalIdx );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
if ( color ) {
ELL_4V_COPY( pldOut->rgba + 4*outVertTotalIdx,
pldIn->rgba + 4*inVertTotalIdx );
}
outVertTotalIdx++;
}
}
/* outVrt = pldOut->vert + outVertTotalIdx; */
ELL_3V_COPY_TT( pldOut->norm + 3*outVertTotalIdx, float, tang );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
step/2, tang,
radius,
pldOut->norm + 3*outVertTotalIdx );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
outVertTotalIdx++;
for ( ei=0; ei<endFacet-1; ei++ ) {
for ( pi=0; pi<tubeFacet; pi++ ) {
pldOut->indx[outIndxIdx++] = ( tubeEndIdx + pi
+ ( ei + 0 )*tubeFacet );
pldOut->indx[outIndxIdx++] = ( tubeEndIdx + pi
+ ( ei + 1 )*tubeFacet );
}
}
for ( pi=0; pi<tubeFacet; pi++ ) {
pldOut->indx[outIndxIdx++] = ( tubeEndIdx + pi
+ ( endFacet - 1 )*tubeFacet );
pldOut->indx[outIndxIdx++] = ( tubeEndIdx
+ ( endFacet - 0 )*tubeFacet );
}
} else {
/* no endcap, open tube */
for ( pi=0; pi<tubeFacet; pi++ ) {
double costh, sinth, theta;
theta = AIR_AFFINE( 0, pi, tubeFacet, 0.0, 2*AIR_PI );
costh = cos( theta );
sinth = sin( theta );
ELL_3V_SCALE_ADD2_TT( pldOut->norm + 3*outVertTotalIdx, float,
costh, perp,
sinth, pimp );
ELL_3V_SCALE_ADD3_TT( pldOut->xyzw + 4*outVertTotalIdx, float,
1, pldIn->xyzw + 4*inVertTotalIdx,
step/2 - step/( 2*tubeFacet ), tang,
radius, pldOut->norm + 3*outVertTotalIdx );
( pldOut->xyzw + 4*outVertTotalIdx )[3] = 1.0;
if ( vertmap ) {
vertmap[outVertTotalIdx] = inVertTotalIdx;
}
if ( color ) {
ELL_4V_COPY( pldOut->rgba + 4*outVertTotalIdx,
pldIn->rgba + 4*inVertTotalIdx );
}
outVertTotalIdx++;
}
}
} /* if ( inVertIdx == pldIn->icnt[primIdx]-1 ) */
/* -------------------------------------- END final endcap */
inVertTotalIdx++;
}
}
airMopOkay( mop );
return 0;
}
/* Straightforward implementation of Laplacian+HC mesh smoothing, as
* described by Vollmer et al., Improved Laplacian Smoothing of Noisy
* Surface Meshes, Eurographics/CGF 18( 3 ), 1999
*
* pld is the polydata you want smoothed
* neighbors / idx is from limnPolyDataNeighborArrayComp
* alpha / beta are parameters of the smoothing ( try a=0, b=0.5 )
* iter is the number of iterations you want to run ( try iter=10 )
*
* Returns -1 and leaves a message on biff upon error.
*/
int
371 limnPolyDataSmoothHC( limnPolyData *pld, int *neighbors, int *idx,
double alpha, double beta, int iter ) {
static const char me[]="limnPolyDataSmoothHC";
float *orig, *in, *out, *b;
unsigned int v;
int i, nb;
airArray *mop;
mop=airMopNew( );
if ( pld==NULL || neighbors==NULL || idx==NULL ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
airMopError( mop ); return -1;
}
if ( alpha<0 || alpha>1 || beta<0 || beta>1 ) {
biffAddf( LIMN, "%s: alpha/beta outside parameter range [0, 1]", me );
airMopError( mop ); return -1;
}
orig = in = pld->xyzw;
out = ( float* ) malloc( sizeof( float )*4*pld->xyzwNum );
if ( out==NULL ) {
biffAddf( LIMN, "%s: couldn't allocate output buffer", me );
airMopError( mop ); return -1;
}
airMopAdd( mop, out, airFree, airMopOnError );
b = ( float* ) malloc( sizeof( float )*4*pld->xyzwNum );
if ( b==NULL ) {
biffAddf( LIMN, "%s: couldn't allocate buffer b", me );
airMopError( mop ); return -1;
}
airMopAdd( mop, b, airFree, airMopAlways );
for ( i=0; i<iter; i++ ) {
/* Laplacian smoothing / compute bs */
for ( v=0; v<pld->xyzwNum; v++ ) {
int p=4*v;
if ( idx[v]==idx[v+1] ) {
ELL_4V_COPY( out+p, in+p );
} else {
ELL_4V_SET( out+p, 0, 0, 0, 0 );
for ( nb=idx[v]; nb<idx[v+1]; nb++ ) {
ELL_4V_INCR( out+p, in+4*neighbors[nb] );
}
ELL_4V_SCALE_TT( out+p, float, 1.0/( idx[v+1]-idx[v] ), out+p );
}
ELL_4V_SET_TT( b+p, float, out[p]-( alpha*orig[p]+( 1-alpha )*in[p] ),
out[p+1]-( alpha*orig[p+1]+( 1-alpha )*in[p+1] ),
out[p+2]-( alpha*orig[p+2]+( 1-alpha )*in[p+2] ),
out[p+3]-( alpha*orig[p+3]+( 1-alpha )*in[p+3] ) );
}
/* HC correction step */
for ( v=0; v<pld->xyzwNum; v++ ) {
int p=4*v;
if ( idx[v]<idx[v+1] ) {
float avgb[4]={0, 0, 0, 0};
for ( nb=idx[v]; nb<idx[v+1]; nb++ ) {
ELL_4V_INCR( avgb, b+4*neighbors[nb] );
}
ELL_4V_SCALE_TT( avgb, float, 1.0/( idx[v+1]-idx[v] ), avgb );
ELL_4V_LERP_TT( avgb, float, beta, b+p, avgb );
ELL_4V_SUB( out+p, out+p, avgb );
}
}
if ( i==0 && iter>1 ) {
in = out;
out = ( float* ) malloc( sizeof( float )*4*pld->xyzwNum );
} else { /* swap */
float *tmp = in; in = out; out = tmp;
}
}
if ( iter>1 )
airFree( out );
airFree( pld->xyzw );
pld->xyzw = in;
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
Copyright ( C ) 2012, 2011, 2010, 2009, 2008 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
** determines intersection of elements of srcA and srcB.
** assumes:
** - there are no repeats in either list
** - dstC is allocated for at least as long as the longer of srcA and srcB
*/
static unsigned int
35 flipListIntx( unsigned int *dstC,
const unsigned int *_srcA, const unsigned int *_srcB ) {
const unsigned int *srcA, *srcB;
unsigned int numA, numB, numC, idxA, idxB;
numA = _srcA[0];
srcA = _srcA + 1;
numB = _srcB[0];
srcB = _srcB + 1;
numC = 0;
for ( idxA=0; idxA<numA; idxA++ ) {
for ( idxB=0; idxB<numB; idxB++ ) {
if ( srcA[idxA] == srcB[idxB] ) {
dstC[numC++] = srcA[idxA];
}
}
}
return numC;
}
/*
** given triangle identified by triIdx,
** set neighGot[] and neighInfo[][]
** neighbors are index 0, 1, 2;
** neighbor ii is on edge between vert ii and ( ii+1 )%3
** neighGot[ii] is non-zero iff there was such a neighbor
** neighInfo[ii][0]: index of the ( triangle ) neighbor
** neighInfo[ii][1], neighInfo[ii][2]: the two vertices shared with neighbor,
** in the order that the *neighbor* should be traversing them
*/
static void
66 flipNeighborsGet( Nrrd *nTriWithVert, Nrrd *nVertWithTri,
unsigned int neighGot[3], unsigned int neighInfo[3][3],
unsigned int *intxBuff, unsigned int triIdx ) {
/* static const char me[]="flipNeighborsGet"; */
unsigned int intxNum, vertA, vertB, neighIdx, maxTriPerVert,
*vertWithTri, *triWithVert;
int ii;
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data );
maxTriPerVert = nTriWithVert->axis[0].size - 1;
for ( ii=0; ii<3; ii++ ) {
vertA = ( vertWithTri + 3*triIdx )[ii];
vertB = ( vertWithTri + 3*triIdx )[AIR_MOD( ii+1, 3 )];
/*
fprintf( stderr, "!%s: %u edge %u: vert{A, B} = %u %u\n", me,
triIdx, ii, vertA, vertB );
*/
/* find the intersection of the sets of {triangles using vertA}
and {triangles using vertB}: for reasonable surfaces should
be either 0 or 2 triangles, and if its 2, then triIdx
should be one of them */
intxNum = flipListIntx( intxBuff,
triWithVert + ( 1+maxTriPerVert )*vertA,
triWithVert + ( 1+maxTriPerVert )*vertB );
if ( 2 == intxNum ) {
neighIdx = intxBuff[0];
if ( neighIdx == triIdx ) {
neighIdx = intxBuff[1];
}
neighGot[ii] = AIR_TRUE;
neighInfo[ii][0] = neighIdx;
neighInfo[ii][1] = vertB;
neighInfo[ii][2] = vertA;
} else {
neighGot[ii] = AIR_FALSE;
}
}
return;
}
/*
** determines if triIdx needs to be flipped, given that it should
** be seeing vertices vertA and vertB in that order
*/
static int
112 flipNeed( Nrrd *nVertWithTri, unsigned int triIdx,
unsigned int vertA, unsigned int vertB ) {
unsigned int *vertWithTri, vert[3];
int ai, bi;
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
ELL_3V_COPY( vert, vertWithTri + 3*triIdx );
for ( ai=0; vert[ai] != vertA; ai++ )
;
for ( bi=0; vert[bi] != vertB; bi++ )
;
return ( 1 != AIR_MOD( bi - ai, 3 ) );
}
/*
** this is a weird dual-personality function that is the inner
** loop of both vertex winding fixing, and the learning stage of
** vertex splitting
**
** for flipping ( !splitting )
** assumes that triIdx was just popped from "okay" stack
** ( triIdx has just been fixed to have correct winding )
** then goes through the not-yet-done neighbors of triIdx,
** flipping them if needed, and
** then adding those neighbors to the stack.
** returns the number of tris added to stack
**
** NOTE: the "flipping" is done within the nVertWithTri representation,
** but *not* in the limnPolyData itself.
*/
static unsigned int
143 neighborsCheckPush( Nrrd *nTriWithVert, Nrrd *nVertWithTri,
unsigned char *triDone, airArray *okayArr,
unsigned int *intxBuff, airArray *splitArr,
unsigned int triIdx, int splitting ) {
/* static const char me[]="neighborsCheckPush"; */
unsigned int neighGot[3], neighInfo[3][3], ii, *okay, okayIdx,
*vertWithTri, pushedNum;
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
flipNeighborsGet( nTriWithVert, nVertWithTri,
neighGot, neighInfo,
intxBuff, triIdx );
/*
for ( ii=0; ii<3; ii++ ) {
fprintf( stderr, "!%s: %u neigh[%u]: ", me, triIdx, ii );
if ( neighGot[ii] ) {
fprintf( stderr, "%u ( %u %u ) ( done %u )\n",
neighInfo[ii][0], neighInfo[ii][1], neighInfo[ii][2],
triDone[neighInfo[ii][0]] );
} else {
fprintf( stderr, "nope\n" );
}
}
*/
pushedNum = 0;
for ( ii=0; ii<3; ii++ ) {
/* WARNING: complicated logic WRT triDone, splitting, and need */
if ( neighGot[ii] ) {
unsigned int tmp, *idxLine, need;
if ( !splitting ) {
if ( !triDone[neighInfo[ii][0]] ) {
/* we only take time to learn need if as yet undone */
need = flipNeed( nVertWithTri, neighInfo[ii][0],
neighInfo[ii][1], neighInfo[ii][2] );
if ( need ) {
idxLine = vertWithTri + 3*neighInfo[ii][0];
/* here is the vertex winding flip */
ELL_SWAP2( idxLine[0], idxLine[1], tmp );
}
}
} else {
/* we're here for splitting */
/* we have to learn need regardless of done-ness */
need = flipNeed( nVertWithTri, neighInfo[ii][0],
neighInfo[ii][1], neighInfo[ii][2] );
if ( need && triDone[neighInfo[ii][0]] ) {
/* we "need" to flip and yet we've already visited that triangle
==> edge between triIdx and neighInfo[ii][0] needs splitting.
See if its a new split, and add it if so */
unsigned int *split, splitIdx, splitNum, vert0, vert1;
vert0 = AIR_MIN( neighInfo[ii][1], neighInfo[ii][2] );
vert1 = AIR_MAX( neighInfo[ii][1], neighInfo[ii][2] );
splitNum = splitArr->len;
split = AIR_CAST( unsigned int*, splitArr->data );
for ( splitIdx=0; splitIdx<splitNum; splitIdx++ ) {
if ( split[2 + 5*splitIdx] == vert0
&& split[3 + 5*splitIdx] == vert1 ) {
break;
}
}
if ( splitIdx == splitNum ) {
/* this is a new split, add it */
/*
fprintf( stderr, "!%s: new split( %u, %u ) ( have %u )\n",
me, vert0, vert1, splitArr->len );
*/
splitIdx = airArrayLenIncr( splitArr, 1 );
split = AIR_CAST( unsigned int*, splitArr->data );
split[0 + 5*splitIdx] = triIdx;
split[1 + 5*splitIdx] = neighInfo[ii][0];
split[2 + 5*splitIdx] = vert0;
split[3 + 5*splitIdx] = vert1;
split[4 + 5*splitIdx] = AIR_FALSE;
}
}
}
/* regardless of splitting, we push onto the okay stack all
the un-done neighbors that we just processed */
if ( !triDone[neighInfo[ii][0]] ) {
triDone[neighInfo[ii][0]] = AIR_TRUE;
okayIdx = airArrayLenIncr( okayArr, 1 );
okay = AIR_CAST( unsigned int*, okayArr->data );
okay[okayIdx] = neighInfo[ii][0];
++pushedNum;
}
} /* if ( neighGot[ii] ) */
} /* for ii */
return pushedNum;
}
/*
** ONLY GOOD FOR limnPrimitiveTriangles!!
*/
static unsigned int
237 maxTrianglePerPrimitive( limnPolyData *pld ) {
unsigned int ret, primIdx;
ret = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
ret = AIR_MAX( ret, pld->icnt[primIdx]/3 );
}
return ret;
}
/*
** fills nTriWithVert with 2D array about which triangles use which vertices
*/
static int
251 triangleWithVertex( Nrrd *nTriWithVert, limnPolyData *pld ) {
static const char me[]="triangleWithVertex";
unsigned int *triWithVertNum, /* vert ii has triWithVertNum[ii] tris */
*triWithVert, baseVertIdx, primIdx, vertIdx,
maxTriPerVert, totTriIdx;
airArray *mop;
if ( !( nTriWithVert && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
triWithVertNum = AIR_CAST( unsigned int*,
calloc( pld->xyzwNum, sizeof( unsigned int ) ) );
if ( !triWithVertNum ) {
biffAddf( LIMN, "%s: couldn't allocate temp array", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, triWithVertNum, airFree, airMopAlways );
/* fill in triWithVertNum */
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triNum, triIdx, *indxLine, ii;
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
indxLine = pld->indx + baseVertIdx + 3*triIdx;
for ( ii=0; ii<3; ii++ ) {
triWithVertNum[indxLine[ii]]++;
}
}
baseVertIdx += pld->icnt[primIdx];
}
/* find max # tris per vert, allocate output */
maxTriPerVert = 0;
for ( vertIdx=0; vertIdx<pld->xyzwNum; vertIdx++ ) {
maxTriPerVert = AIR_MAX( maxTriPerVert, triWithVertNum[vertIdx] );
}
if ( nrrdMaybeAlloc_va( nTriWithVert, nrrdTypeUInt, 2,
AIR_CAST( size_t, 1 + maxTriPerVert ),
AIR_CAST( size_t, pld->xyzwNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data );
baseVertIdx = 0;
totTriIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triNum, *indxLine, *twvLine, ii, triIdx;
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
indxLine = pld->indx + baseVertIdx + 3*triIdx;
for ( ii=0; ii<3; ii++ ) {
twvLine = triWithVert + ( 1+maxTriPerVert )*indxLine[ii];
twvLine[1+twvLine[0]] = totTriIdx;
twvLine[0]++;
}
++totTriIdx;
}
baseVertIdx += pld->icnt[primIdx];
}
airMopOkay( mop );
return 0;
}
/*
** learns which ( three vertices ) are with which triangle
*/
static int
329 vertexWithTriangle( Nrrd *nVertWithTri, limnPolyData *pld ) {
static const char me[]="vertexWithTriangle";
unsigned int baseVertIdx, primIdx, *vertWithTri, triNum;
if ( !( nVertWithTri && pld ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
triNum = limnPolyDataPolygonNumber( pld );
if ( nrrdMaybeAlloc_va( nVertWithTri, nrrdTypeUInt, 2,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, triNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triIdx, *indxLine, totTriIdx, ii;
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
totTriIdx = triIdx + baseVertIdx/3;
indxLine = pld->indx + baseVertIdx + 3*triIdx;
for ( ii=0; ii<3; ii++ ) {
( vertWithTri + 3*totTriIdx )[ii] = indxLine[ii];
}
}
baseVertIdx += pld->icnt[primIdx];
}
return 0;
}
static int
370 splitListExtract( unsigned int *listLenP,
airArray *edgeArr, unsigned char *hitCount,
unsigned int firstVertIdx, unsigned int edgeDoneNum ) {
static const char me[]="splitListExtract";
unsigned int *edgeData, edgeNum, *edgeLine, edgeIdx, edgeTmp[5],
tmp, nextVertIdx, listLen;
edgeNum = edgeArr->len;
edgeData = AIR_CAST( unsigned int*, edgeArr->data );
edgeNum -= edgeDoneNum;
edgeData += 5*edgeDoneNum;
/* put first edge in first position */
for ( edgeIdx=0; edgeIdx<edgeNum; edgeIdx++ ) {
edgeLine = edgeData + 5*edgeIdx;
if ( edgeLine[2] == firstVertIdx || edgeLine[3] == firstVertIdx ) {
break;
}
}
if ( edgeIdx == edgeNum ) {
biffAddf( LIMN, "%s: never found first vertex %u", me, firstVertIdx );
return 1;
}
if ( edgeLine[3] == firstVertIdx ) {
ELL_SWAP2( edgeLine[2], edgeLine[3], tmp );
}
ELL_5V_COPY( edgeTmp, edgeData );
ELL_5V_COPY( edgeData, edgeLine );
ELL_5V_COPY( edgeLine, edgeTmp );
/* start looking for the rest */
listLen = 1;
hitCount[firstVertIdx]--;
nextVertIdx = edgeData[3];
hitCount[nextVertIdx]--;
/*
fprintf( stderr, "!%s: found first %u --> %u ( tris %u %u )\n", me,
firstVertIdx, nextVertIdx,
edgeData[0], edgeData[1] );
*/
/* the search start progresses so that we don't see the same edge twice */
#define SEARCH \
for ( edgeIdx=listLen; edgeIdx<edgeNum; edgeIdx++ ) { \
edgeLine = edgeData + 5*edgeIdx; \
if ( edgeLine[2] == nextVertIdx || edgeLine[3] == nextVertIdx ) { \
break; \
} \
}
SEARCH;
while ( edgeIdx < edgeNum ) {
if ( edgeLine[3] == nextVertIdx ) {
ELL_SWAP2( edgeLine[2], edgeLine[3], tmp );
}
ELL_5V_COPY( edgeTmp, edgeData + 5*listLen );
ELL_5V_COPY( edgeData + 5*listLen, edgeLine );
ELL_5V_COPY( edgeLine, edgeTmp );
hitCount[nextVertIdx]--;
/*
fprintf( stderr, "!%s: ( len %u ) found %u --> %u ( tris %u %u )\n", me,
listLen, nextVertIdx,
( edgeData + 5*listLen )[3],
( edgeData + 5*listLen )[0],
( edgeData + 5*listLen )[1] );
*/
nextVertIdx = ( edgeData + 5*listLen )[3];
hitCount[nextVertIdx]--;
listLen++;
SEARCH;
}
/*
fprintf( stderr, "!%s: finishing with Len %u, ended at %u\n", me,
listLen, nextVertIdx );
*/
*listLenP = listLen;
return 0;
#undef SEARCH
447 }
/*
** returns the element of vert[] that is not v0 or v1
*/
static unsigned int
sweepVertNext( unsigned int *vert, unsigned int v0, unsigned int v1 ) {
unsigned int v2;
v2 = vert[0];
if ( v2 == v0 || v2 == v1 ) {
v2 = vert[1];
}
if ( v2 == v0 || v2 == v1 ) {
v2 = vert[2];
}
return v2;
464 }
/*
** returns non-zero iff A and B are in {v[0], v[1], v[2]}
*/
static int
sweepHave2( unsigned int v[3], unsigned int A, unsigned B ) {
int haveA, haveB;
haveA = ( A == v[0] || A == v[1] || A == v[2] );
haveB = ( B == v[0] || B == v[1] || B == v[2] );
return ( haveA && haveB );
476 }
/*
** returns UINT_MAX if there is no other triangle
*/
static unsigned int
sweepTriNext( unsigned int *triLine, unsigned int v0, unsigned int v1,
unsigned int triNot, Nrrd *nVertWithTri ) {
unsigned int triIdx, ret, *vertLine, *vertWithTri;
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
for ( triIdx=0; triIdx<triLine[0]; triIdx++ ) {
if ( triLine[1+triIdx] == triNot ) {
continue;
}
vertLine = vertWithTri + 3*triLine[1+triIdx];
if ( sweepHave2( vertLine, v0, v1 ) ) {
break;
}
}
if ( triIdx == triLine[0] ) {
ret = UINT_MAX;
} else {
ret = triLine[1+triIdx];
}
return ret;
}
/*
** the sweep does NOT include triStart, but it does include whichever
** triStop it hit ( if any )
** returns: length of sweep
** sweep: output ( does not include triStart )
510 ** triStartIdx: what triangle to start at
** vertPivotIdx, vertStartIdx: two vertices of start triangle; sweep
** proceeds around the pivot index
** triStop{0, 1}Idx: triangles to stop sweeping at
*/
static unsigned int
splitTriSweep( unsigned int *sweep,
unsigned int triStart,
unsigned int vertPivot, unsigned int vertStart,
unsigned int triStop0, unsigned int triStop1,
Nrrd *nTriWithVert, Nrrd *nVertWithTri ) {
/* static const char me[]="splitTriSweep"; */
unsigned int sweepLen;
unsigned int maxTriPerVert, *triWithVert,
*vertWithTri, *triLine, *vertLine, triCurr, vertLast, vertNext;
maxTriPerVert = AIR_CAST( unsigned int, nTriWithVert->axis[0].size-1 );
triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data );
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
/*
fprintf( stderr, "!%s: hi, triStart %u, pivot %u, start %u, "
"stop = %u, %u\n", me,
triStart, vertPivot, vertStart, triStop0, triStop1 );
*/
if ( triStart == triStop0 || triStart == triStop1 ) {
/* nowhere to go */
return 0;
}
triLine = triWithVert + ( 1+maxTriPerVert )*vertPivot;
vertLast = vertStart;
triCurr = triStart;
sweepLen = 0;
do {
if ( !( triCurr == triStart ) ) {
sweep[sweepLen++] = triCurr;
/*
fprintf( stderr, "!%s: saving sweep[%u] = %u\n", me,
sweepLen-1, triCurr );
*/
}
vertLine = vertWithTri + 3*triCurr;
vertNext = sweepVertNext( vertLine, vertPivot, vertLast );
/*
fprintf( stderr, "!%s: vertNext( %u, %u ) = %u\n", me,
vertPivot, vertLast, vertNext );
*/
triCurr = sweepTriNext( triLine, vertPivot, vertNext,
triCurr, nVertWithTri );
/*
fprintf( stderr, "!%s: triNext( %u, %u ) = %u\n", me,
vertPivot, vertNext, triCurr );
*/
vertLast = vertNext;
} while ( !( UINT_MAX == triCurr
|| triStart == triCurr
|| triStop0 == triCurr
|| triStop1 == triCurr ) );
if ( !( UINT_MAX == triCurr ) ) {
sweep[sweepLen++] = triCurr;
/*
fprintf( stderr, "!%s: saving sweep[%u] = %u\n", me,
sweepLen-1, triCurr );
*/
}
return sweepLen;
}
/*
** track0: first triangle track, length *track0LenP
** track1: first triangle track, length *track1LenP
** sweep: buffer for sweep
**
** NOTE: triangles may be internally repeated in a track
586 **
** when vert path a loop on a non-orientable surface ( e.g. mobius strip ),
** then track0 will NOT include the endpoint triangles
** ( or its not supposed to ), and track1 will include them.
*/
static int
splitTriTrack( unsigned int *track0, unsigned int *track0LenP,
unsigned int *track1, unsigned int *track1LenP,
unsigned int *sweep,
Nrrd *nTriWithVert, Nrrd *nVertWithTri,
airArray *edgeArr, unsigned startIdx, unsigned int listLen,
int looping ) {
static const char me[]="splitTriTrack";
unsigned int len0, len1, *edgeData, *edgeLine, edgeIdx, triIdx,
/* maxTriPerVert, *triWithVert, *vertWithTri, */
sweepLen, loopEnd0, loopEnd1, loopStart0, loopStart1;
int doBack0, doBack1;
len0 = len1 = 0;
edgeData = AIR_CAST( unsigned int*, edgeArr->data );
edgeData += 5*startIdx;
/* maxTriPerVert = AIR_CAST( unsigned int, nTriWithVert->axis[0].size-1 ); */
/* triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data ); */
/* vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data ); */
if ( looping ) {
loopStart0 = ( edgeData )[0];
loopStart1 = ( edgeData )[1];
loopEnd0 = ( edgeData + 5*( listLen - 1 ) )[0];
loopEnd1 = ( edgeData + 5*( listLen - 1 ) )[1];
/*
fprintf( stderr, "!%s: loop start = %u, %u, end = %u, %u\n", me,
loopStart0, loopStart1, loopEnd0, loopEnd1 );
*/
} else {
loopStart0 = loopStart1 = UINT_MAX;
loopEnd0 = loopEnd1 = UINT_MAX;
}
/* , , , , , , , , , , , , , , , , , , , , ,
fprintf( stderr, "!%s: 1st 2 tris %u %u, verts %u %u\n", me,
edgeData[0], edgeData[1], edgeData[2], edgeData[3] );
fprintf( stderr, "!%s: triangles at start vert %u:\n", me, edgeData[2] );
triLine = triWithVert + edgeData[2]*( 1+maxTriPerVert );
for ( triIdx=0; triIdx<triLine[0]; triIdx++ ) {
unsigned int *vertLine;
vertLine = vertWithTri + 3*triLine[1+triIdx];
fprintf( stderr, "!%s: %u: %u ( verts %u %u %u )\n",
me, triIdx, triLine[1+triIdx],
vertLine[0], vertLine[1], vertLine[2] );
}
````````````````````` */
/* we turn on backward sweeping for the initial edge;
doBack{0, 1} will be set explicitly at each edge thereafter */
doBack0 = doBack1 = AIR_TRUE;
for ( edgeIdx=0; edgeIdx<( looping
? listLen-1
: listLen ); edgeIdx++ ) {
unsigned int stop0, stop1;
edgeLine = edgeData + 5*edgeIdx;
/* , , , , , , , , , , , , , , , , , , , , ,
fprintf( stderr, "!%s: edge %u: vert %u->%u, tris %u, %u\n", me,
edgeIdx, edgeLine[2], edgeLine[3],
edgeLine[0], edgeLine[1] );
fprintf( stderr, "!%s: triangles at next vert %u:\n", me, edgeLine[3] );
triLine = triWithVert + edgeLine[3]*( 1+maxTriPerVert );
for ( triIdx=0; triIdx<triLine[0]; triIdx++ ) {
vertLine = vertWithTri + 3*triLine[1+triIdx];
fprintf( stderr, "!%s: %u: %u ( verts %u %u %u )\n",
me, triIdx, triLine[1+triIdx],
vertLine[0], vertLine[1], vertLine[2] );
}
````````````````````` */
if ( 0 == edgeIdx && looping ) {
/* sweeps from 1st link on loop are stopped by a tris on last edge */
stop0 = loopEnd0;
stop1 = loopEnd1;
} else {
stop0 = UINT_MAX;
stop1 = UINT_MAX;
}
if ( doBack0 ) {
sweepLen = splitTriSweep( sweep, edgeLine[0], edgeLine[2], edgeLine[3],
stop0, stop1, nTriWithVert, nVertWithTri );
if ( 0 == edgeIdx && looping && sweepLen > 0 ) {
/* don't include either stop triangle on track 0 */
for ( triIdx=0; triIdx<sweepLen-1; triIdx++ ) {
track0[len0++] = sweep[sweepLen-2-triIdx];
}
} else {
for ( triIdx=0; triIdx<sweepLen; triIdx++ ) {
track0[len0++] = sweep[sweepLen-1-triIdx];
}
}
track0[len0++] = edgeLine[0];
}
if ( doBack1 ) {
sweepLen = splitTriSweep( sweep, edgeLine[1], edgeLine[2], edgeLine[3],
stop0, stop1, nTriWithVert, nVertWithTri );
/* on this side we *do* include the stop triangle */
for ( triIdx=0; triIdx<sweepLen; triIdx++ ) {
track1[len1++] = sweep[sweepLen-1-triIdx];
}
track1[len1++] = edgeLine[1];
}
if ( edgeIdx<listLen-1 ) {
stop0 = ( edgeLine + 5 )[0];
stop1 = ( edgeLine + 5 )[1];
} else {
if ( looping ) {
stop0 = loopStart0;
stop1 = loopStart1;
} else {
stop0 = UINT_MAX;
stop1 = UINT_MAX;
}
}
sweepLen = splitTriSweep( sweep, edgeLine[0], edgeLine[3], edgeLine[2],
stop0, stop1, nTriWithVert, nVertWithTri );
for ( triIdx=0; triIdx<sweepLen; triIdx++ ) {
track0[len0++] = sweep[triIdx];
}
sweepLen = splitTriSweep( sweep, edgeLine[1], edgeLine[3], edgeLine[2],
stop0, stop1, nTriWithVert, nVertWithTri );
for ( triIdx=0; triIdx<sweepLen; triIdx++ ) {
track1[len1++] = sweep[triIdx];
}
if ( edgeIdx<listLen-1 ) {
unsigned int *nextLine, tmp;
/* re-arrange the next edgeLine according to sweep results */
nextLine = edgeData + 5*( 1 + edgeIdx );
if ( track0[len0-1] == nextLine[0]
&& track1[len1-1] == nextLine[1] ) {
/* fprintf( stderr, "!%s: tracking went 0->0, 1->1\n", me ); */
doBack0 = doBack1 = AIR_FALSE;
} else if ( track0[len0-1] == nextLine[1]
&& track1[len1-1] == nextLine[0] ) {
/* fprintf( stderr, "!%s: tracking went 0->1, 0->1\n", me ); */
ELL_SWAP2( nextLine[0], nextLine[1], tmp );
doBack0 = doBack1 = AIR_FALSE;
} else if ( track0[len0-1] == nextLine[0] ) {
/* fprintf( stderr, "!%s: tracking went 0->0, 1->x\n", me ); */
doBack0 = AIR_FALSE;
doBack1 = AIR_TRUE;
} else if ( track1[len1-1] == nextLine[1] ) {
/* fprintf( stderr, "!%s: tracking went 0->x, 1->1\n", me ); */
doBack0 = AIR_TRUE;
doBack1 = AIR_FALSE;
} else if ( track0[len0-1] == nextLine[1] ) {
/* fprintf( stderr, "!%s: tracking went 0->1, 1->x\n", me ); */
ELL_SWAP2( nextLine[0], nextLine[1], tmp );
doBack0 = AIR_FALSE;
doBack1 = AIR_TRUE;
} else if ( track1[len1-1] == nextLine[0] ) {
/* fprintf( stderr, "!%s: tracking went 0->x, 1->0\n", me ); */
ELL_SWAP2( nextLine[0], nextLine[1], tmp );
doBack0 = AIR_TRUE;
doBack1 = AIR_FALSE;
} else {
biffAddf( LIMN, "%s: edge %u/%u, sweep ends %u, %u != want %u, %u", me,
edgeIdx, listLen, track0[len0-1], track1[len1-1],
nextLine[0], nextLine[1] );
return 1;
}
} else {
doBack0 = doBack1 = AIR_FALSE;
}
}
if ( looping ) {
/* the end of track0 shouldn't include the stop */
len0--;
}
761 *track0LenP = len0;
*track1LenP = len1;
return 0;
}
static int
splitVertDup( limnPolyData *pld, airArray *edgeArr,
unsigned int edgeDoneNum, unsigned int listLen,
unsigned int *track, unsigned int trackLen,
int looping ) {
static const char me[]="splitVertDup";
unsigned int *vixLut, ii, vixLutLen, oldVertNum, newVertNum, *edgeData,
bitflag, trackIdx, vert0, vert1;
airArray *mop;
limnPolyData pldTmp;
mop = airMopNew( );
edgeData = AIR_CAST( unsigned int*, edgeArr->data );
edgeData += 5*edgeDoneNum;
oldVertNum = pld->xyzwNum;
vixLutLen = looping ? listLen : listLen+1;
newVertNum = oldVertNum + vixLutLen;
/* quiet compiler warnings */
pldTmp.rgba = NULL;
pldTmp.norm = NULL;
pldTmp.tex2 = NULL;
pldTmp.tang = NULL;
if ( looping ) {
vert0 = edgeData[2]; /* don't use dupe of this on first triangle */
vert1 = edgeData[3]; /* don't use dupe of this on last triangle */
} else {
vert0 = vert1 = UINT_MAX;
}
/* HEY: sneakily preserve the old per-vertex arrays; we own them now */
pldTmp.xyzw = pld->xyzw;
airMopAdd( mop, pldTmp.xyzw, airFree, airMopAlways );
pld->xyzw = NULL;
pld->xyzwNum = 0;
bitflag = limnPolyDataInfoBitFlag( pld );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
pldTmp.rgba = pld->rgba;
airMopAdd( mop, pldTmp.rgba, airFree, airMopAlways );
pld->rgba = NULL;
pld->rgbaNum = 0;
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
pldTmp.norm = pld->norm;
airMopAdd( mop, pldTmp.norm, airFree, airMopAlways );
pld->norm = NULL;
pld->normNum = 0;
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
pldTmp.tex2 = pld->tex2;
airMopAdd( mop, pldTmp.tex2, airFree, airMopAlways );
pld->tex2 = NULL;
pld->tex2Num = 0;
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
pldTmp.tang = pld->tang;
airMopAdd( mop, pldTmp.tang, airFree, airMopAlways );
pld->tang = NULL;
pld->tangNum = 0;
}
if ( limnPolyDataAlloc( pld, bitflag, newVertNum,
pld->indxNum, pld->primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate new vert # %u", me, newVertNum );
airMopError( mop ); return 1;
}
/* copy old data */
memcpy( pld->xyzw, pldTmp.xyzw, oldVertNum*4*sizeof( float ) );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
memcpy( pld->rgba, pldTmp.rgba, oldVertNum*4*sizeof( unsigned char ) );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
memcpy( pld->norm, pldTmp.norm, oldVertNum*3*sizeof( float ) );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
memcpy( pld->tex2, pldTmp.tex2, oldVertNum*2*sizeof( float ) );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
memcpy( pld->tang, pldTmp.tang, oldVertNum*3*sizeof( float ) );
}
vixLut = AIR_CAST( unsigned int *, calloc( 2*vixLutLen,
sizeof( unsigned int ) ) );
airMopAdd( mop, vixLut, airFree, airMopAlways );
if ( looping ) {
for ( ii=0; ii<vixLutLen; ii++ ) {
vixLut[0 + 2*ii] = edgeData[2 + 5*ii];
vixLut[1 + 2*ii] = oldVertNum + ii;
}
} else {
for ( ii=0; ii<vixLutLen-1; ii++ ) {
vixLut[0 + 2*ii] = edgeData[2 + 5*ii];
vixLut[1 + 2*ii] = oldVertNum + ii;
}
/* now ii == vixLutLen-1 == listLen */
vixLut[0 + 2*ii] = edgeData[3 + 5*( ii-1 )];
vixLut[1 + 2*ii] = oldVertNum + ii;
}
/* copy pld's vertex information to duped vertices */
for ( ii=0; ii<vixLutLen; ii++ ) {
ELL_4V_COPY( pld->xyzw + 4*vixLut[1 + 2*ii],
pld->xyzw + 4*vixLut[0 + 2*ii] );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
ELL_4V_COPY( pld->rgba + 4*vixLut[1 + 2*ii],
pld->rgba + 4*vixLut[0 + 2*ii] );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
ELL_3V_COPY( pld->norm + 3*vixLut[1 + 2*ii],
pld->norm + 3*vixLut[0 + 2*ii] );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
ELL_2V_COPY( pld->tex2 + 2*vixLut[1 + 2*ii],
pld->tex2 + 2*vixLut[0 + 2*ii] );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
ELL_3V_COPY( pld->tang + 3*vixLut[1 + 2*ii],
pld->tang + 3*vixLut[0 + 2*ii] );
}
}
/* for triangles in track, update indices of duped vertices */
/* we do this updating ONLY in the limnPolyData, and that's okay:
the split information is computed entirely from nVertWithTri
and nTriWithVert ( which were based on the original polydata ),
but not the current polydata */
/* HEY: this is one place where we really exploit the fact that we only
have triangles: it makes the indxLine computation much much easier */
for ( trackIdx=0; trackIdx<trackLen; trackIdx++ ) {
unsigned int *indxLine, jj;
indxLine = pld->indx + 3*track[trackIdx];
for ( ii=0; ii<vixLutLen; ii++ ) {
for ( jj=0; jj<3; jj++ ) {
if ( indxLine[jj] == vixLut[0 + 2*ii]
&& !( ( 0 == trackIdx && indxLine[jj] == vert0 )
|| ( trackLen-1 == trackIdx && indxLine[jj] == vert1 ) ) ) {
indxLine[jj] = vixLut[1 + 2*ii];
}
}
}
}
airMopOkay( mop );
return 0;
}
/*
** edge[0, 1]: two neighboring triangles,
** edge[2, 3]: their shared vertices
** edge[4]: non-zero if this split has been processed
917 **
** this really makes no effort to be fast ( or comprehensible )
**
** HEY should we be returning some statistics ( e.g. how many points added )?
*/
static int
doSplitting( limnPolyData *pld, Nrrd *nTriWithVert, Nrrd *nVertWithTri,
airArray *edgeArr ) {
static const char me[]="doSplitting";
unsigned int edgeIdx, *edgeData,
*edgeLine=NULL, vertIdx, vertNum, splitNum, edgeDoneNum, listLen=0,
*track0, track0Len=0, *track1, *sweep, track1Len=0, maxTriPerVert;
unsigned char *hitCount;
airArray *mop;
int passIdx;
if ( !edgeArr->len ) {
/* actually, no splitting was required! */
return 0;
}
mop = airMopNew( );
/* NOTE: It is necessary to save out the number of ( initial )
number of vertices here, because as we do the splitting
( which is done once per track, as tracks are computed ),
pld->xyzwNum will increase ... */
vertNum = pld->xyzwNum;
hitCount = AIR_CAST( unsigned char *, calloc( vertNum,
sizeof( unsigned char ) ) );
maxTriPerVert = AIR_CAST( unsigned int, nTriWithVert->axis[0].size - 1 );
track0 = AIR_CAST( unsigned int *, calloc( maxTriPerVert*edgeArr->len,
sizeof( unsigned int ) ) );
track1 = AIR_CAST( unsigned int *, calloc( maxTriPerVert*edgeArr->len,
sizeof( unsigned int ) ) );
sweep = AIR_CAST( unsigned int *, calloc( maxTriPerVert,
sizeof( unsigned int ) ) );
if ( !( hitCount && track0 && track1 && sweep ) ) {
biffAddf( LIMN, "%s: couldn't alloc buffers", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, hitCount, airFree, airMopAlways );
airMopAdd( mop, track0, airFree, airMopAlways );
airMopAdd( mop, track1, airFree, airMopAlways );
airMopAdd( mop, sweep, airFree, airMopAlways );
edgeData = AIR_CAST( unsigned int*, edgeArr->data );
/* initialize hitCount */
for ( edgeIdx=0; edgeIdx<edgeArr->len; edgeIdx++ ) {
unsigned int ha, hb;
edgeLine = edgeData + 5*edgeIdx;
ha = hitCount[edgeLine[2]]++;
hb = hitCount[edgeLine[3]]++;
if ( ha > 2 || hb > 2 ) {
biffAddf( LIMN, "%s: edge %u ( vert %u %u ) created hit counts %u %u", me,
edgeIdx, edgeLine[2], edgeLine[3], ha, hb );
airMopError( mop ); return 1;
}
}
/* scan hitCount */
#define SEARCH( x ) \
for ( vertIdx=0; vertIdx<vertNum; vertIdx++ ) { \
if ( ( x ) == hitCount[vertIdx] ) { \
break; \
} \
}
splitNum = 0;
edgeDoneNum = 0;
/* pass 0: look for singleton hits ==> non-loop tracks
pass 1: look for hitCount[2] ==> loop tracks
*/
for ( passIdx=0; passIdx<2; passIdx++ ) {
if ( 0 == passIdx ) {
SEARCH( 1 );
} else {
SEARCH( 2 );
}
while ( vertIdx < vertNum ) {
unsigned int E;
E = 0;
if ( 1 ) {
unsigned int hitIdx, hitSum;
hitSum = 0;
for ( hitIdx=0; hitIdx<vertNum; hitIdx++ ) {
hitSum += hitCount[hitIdx];
}
/*
fprintf( stderr, "!%s: PRE hitSum = %u ( pass %u )\n", me,
hitSum, passIdx );
*/
}
if ( !E ) E |= splitListExtract( &listLen, edgeArr, hitCount,
vertIdx, edgeDoneNum );
/* HEY: should do a splitListShorten( ) that cuts across repeated
triangles, and then shifting downward the rest of the list.
take care with loops. iterate until there is no shortening */
/*
if ( 1 ) {
unsigned int hitIdx, hitSum;
hitSum = 0;
for ( hitIdx=0; hitIdx<vertNum; hitIdx++ ) {
hitSum += hitCount[hitIdx];
}
fprintf( stderr, "!%s: ( %d ) POST hitSum = %u ( pass %u )\n", me, E,
hitSum, passIdx );
}
if ( 1 == passIdx ) {
fprintf( stderr, "!%s: loop len %u, verts %u, %u --- %u, %u\n"
" tris %u, %u --- %u, %u\n", me,
listLen,
( edgeData + 5*( edgeDoneNum + listLen - 1 ) )[2],
( edgeData + 5*( edgeDoneNum + listLen - 1 ) )[3],
( edgeData + 5*edgeDoneNum )[2],
( edgeData + 5*edgeDoneNum )[3],
( edgeData + 5*( edgeDoneNum + listLen - 1 ) )[0],
( edgeData + 5*( edgeDoneNum + listLen - 1 ) )[1],
( edgeData + 5*edgeDoneNum )[0],
( edgeData + 5*edgeDoneNum )[1] );
}
*/
if ( !E ) E |= splitTriTrack( track0, &track0Len, track1, &track1Len,
sweep, nTriWithVert, nVertWithTri,
edgeArr, edgeDoneNum, listLen, passIdx );
/* , , , , , , , , , , , , , , , , , , , , ,
if ( !E ) {
fprintf( stderr, "!%s: track0:\n", me );
for ( triIdx=0; triIdx<track0Len; triIdx++ ) {
fprintf( stderr, "!%s: %u: %u\n", me, triIdx, track0[triIdx] );
}
fprintf( stderr, "!%s: track1:\n", me );
for ( triIdx=0; triIdx<track1Len; triIdx++ ) {
fprintf( stderr, "!%s: %u: %u\n", me, triIdx, track1[triIdx] );
}
}
````````````````````` */
/* see- this is the only time pld is used ( so it can be modified ) */
/* HEY: we should be using track1, since that's the one that includes
the endpoint triangles, but on a mobius strip demo it looked worse...
this still needs debugging */
if ( !E ) E |= splitVertDup( pld, edgeArr, edgeDoneNum, listLen,
track0, track0Len, passIdx );
if ( E ) {
biffAddf( LIMN, "%s: trouble on split %u ( done %u/%u )", me,
splitNum, edgeDoneNum, AIR_CAST( unsigned int, edgeArr->len ) );
return 1;
}
edgeDoneNum += listLen;
/*
fprintf( stderr, "!%s: edgeDoneNum now %u ( %u )\n", me,
edgeDoneNum, AIR_CAST( unsigned int, edgeArr->len ) );
*/
if ( 0 == passIdx ) {
SEARCH( 1 );
1072 } else {
SEARCH( 2 );
}
}
}
#undef SEARCH
airMopOkay( mop );
return 0;
}
int
_limnPolyDataVertexWindingProcess( limnPolyData *pld, int splitting ) {
static const char me[]="limnPolyDataVertexWindingProcess";
unsigned int
primIdx, /* for indexing through primitives */
triIdx, /* for indexing through triangles in each primitive */
maxTriPerPrim, /* max # triangles per primitive, which is essential for
the indexing of each triangle ( in each primitive )
into a single triangle index */
totTriIdx, /* another triangle index */
totTriNum, /* total # triangles */
trueTriNum, /* correct total # triangles in all primitives */
baseVertIdx, /* first vertex for current primitive */
maxTriPerVert, /* max # of tris on single vertex */
/* *triWithVert, 2D array ( ( 1+maxTriPerVert ) x pld->xyzwNum )
of per-vertex triangles */
*vertWithTri, /* 3D array ( 3 x maxTriPerPrim x pld->primNum )
of per-tri vertices ( vertex indices ), which is just
a repackaging of the information in the lpld */
doneTriNum, /* # triangles finished so far */
*intxBuff, /* stupid buffer */
*okay, /* the stack of triangles with okay ( possibly fixed )
winding, but with some neighbors that may as yet
need fixing */
*split; /* stack of 5-tuples about edges needing vertex splits:
split[0, 1]: two neighboring triangles,
split[2, 3]: their shared vertices
split[4]: non-zero if this split has been processed */
unsigned char
*triDone; /* 1D array ( len totTriNum ) record of done-ness */
Nrrd *nTriWithVert, *nVertWithTri;
airArray *mop, /* house-keeping */
*okayArr, /* airArray around "okay" */
*splitArr; /* airArray around "split" */
airPtrPtrUnion appu;
/*
fprintf( stderr, "!%s: hi\n", me );
*/
if ( !pld ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( pld->xyzwNum && pld->primNum ) ) {
/* this is empty? */
return 0;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
maxTriPerPrim = maxTrianglePerPrimitive( pld );
totTriNum = limnPolyDataPolygonNumber( pld );
mop = airMopNew( );
triDone = AIR_CAST( unsigned char *, calloc( totTriNum,
sizeof( unsigned char ) ) );
airMopAdd( mop, triDone, airFree, airMopAlways );
if ( !triDone ) {
biffAddf( LIMN, "%s: couldn't allocate temp array", me );
airMopError( mop ); return 1;
}
/* allocate TriWithVert, VertWithTri, intxBuff */
nTriWithVert = nrrdNew( );
airMopAdd( mop, nTriWithVert, ( airMopper )nrrdNuke, airMopAlways );
nVertWithTri = nrrdNew( );
airMopAdd( mop, nVertWithTri, ( airMopper )nrrdNuke, airMopAlways );
if ( triangleWithVertex( nTriWithVert, pld )
|| vertexWithTriangle( nVertWithTri, pld ) ) {
biffAddf( LIMN, "%s: couldn't set nTriWithVert or nVertWithTri", me );
airMopError( mop ); return 1;
}
vertWithTri = AIR_CAST( unsigned int*, nVertWithTri->data );
/* triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data ); */
maxTriPerVert = nTriWithVert->axis[0].size - 1;
intxBuff = AIR_CAST( unsigned int*, calloc( maxTriPerVert,
sizeof( unsigned int ) ) );
if ( !intxBuff ) {
biffAddf( LIMN, "%s: failed to alloc an itty bitty buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, intxBuff, airFree, airMopAlways );
/*
nrrdSave( "triWithVert.nrrd", nTriWithVert, NULL );
nrrdSave( "vertWithTri.nrrd", nVertWithTri, NULL );
*/
/* create the stack of recently fixed triangles */
appu.ui = &okay;
okayArr = airArrayNew( appu.v, NULL, sizeof( unsigned int ),
maxTriPerPrim );
airMopAdd( mop, okayArr, ( airMopper )airArrayNuke, airMopAlways );
if ( splitting ) {
appu.ui = &split;
splitArr = airArrayNew( appu.v, NULL, 5*sizeof( unsigned int ),
maxTriPerPrim );
/* split set as it is used */
} else {
splitArr = NULL;
split = NULL;
}
/* the skinny */
doneTriNum = 0;
trueTriNum = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
trueTriNum += pld->icnt[primIdx]/3;
}
/*
fprintf( stderr, "!%s: trueTriNum %u; other tri num %u\n", me,
trueTriNum, limnPolyDataPolygonNumber( pld ) );
*/
while ( doneTriNum < trueTriNum ) {
/* find first undone triangle, which should be on a different
connected component than any processed so far */
for ( totTriIdx=0; triDone[totTriIdx]; totTriIdx++ )
;
/* we use the winding of this triangle to determine the correct
winding of all neighboring trianges, so this one is now done */
triDone[totTriIdx] = AIR_TRUE;
++doneTriNum;
/*
fprintf( stderr, "!%s: considering tri %u done ( %u )\n",
me, totTriIdx, doneTriNum );
*/
doneTriNum += neighborsCheckPush( nTriWithVert, nVertWithTri,
triDone, okayArr, intxBuff, splitArr,
totTriIdx, splitting );
while ( okayArr->len ) {
unsigned int popped;
popped = okay[okayArr->len-1];
airArrayLenIncr( okayArr, -1 );
/*
fprintf( stderr, "!%s: popped %u\n", me, popped );
*/
doneTriNum += neighborsCheckPush( nTriWithVert, nVertWithTri,
triDone, okayArr, intxBuff, splitArr,
popped, splitting );
}
}
if ( splitting ) {
if ( doSplitting( pld, nTriWithVert, nVertWithTri, splitArr ) ) {
biffAddf( LIMN, "%s: problem doing vertex splitting", me );
return 1;
}
} else {
/* Copy from nVertWithTri back into polydata */
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triNum, *indxLine, ii;
triNum = pld->icnt[primIdx]/3;
for ( triIdx=0; triIdx<triNum; triIdx++ ) {
totTriIdx = triIdx + baseVertIdx/3;
indxLine = pld->indx + baseVertIdx + 3*triIdx;
for ( ii=0; ii<3; ii++ ) {
indxLine[ii] = ( vertWithTri + 3*totTriIdx )[ii];
}
}
baseVertIdx += pld->icnt[primIdx];
}
}
airMopOkay( mop );
return 0;
}
1254
/*
** with non-zero splitting, this does vertex splitting so that
** non-orientable surfaces can be rendered without seams. Took longer
** to implement than intended.
**
** HEY: still has a bug in handling which triangles get which
** ( new ) vertices when the seam in the non-orientable surface
** is a closed loop. Can be debugged later...
*/
int
limnPolyDataVertexWindingFix( limnPolyData *pld, int splitting ) {
static const char me[]="limnPolyDataVertexWindingFix";
if ( !splitting ) {
if ( _limnPolyDataVertexWindingProcess( pld, AIR_FALSE ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
1273 } else {
if ( _limnPolyDataVertexWindingProcess( pld, AIR_FALSE )
|| _limnPolyDataVertexWindingProcess( pld, AIR_TRUE ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
}
return 0;
}
int
limnPolyDataCCFind( limnPolyData *pld ) {
static const char me[]="limnPolyDataCCFind";
unsigned int realTriNum, *triMap, *triWithVert, vertIdx,
*indxOld, *indxNew, primNumOld, *icntOld, *icntNew, *baseIndx,
primIdxNew, primNumNew, passIdx, eqvNum=0;
unsigned char *typeOld, *typeNew;
Nrrd *nTriWithVert, *nccSize, *nTriMap;
airArray *mop, *eqvArr;
if ( !pld ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( pld->xyzwNum && pld->primNum ) ) {
/* this is empty? */
return 0;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
mop = airMopNew( );
realTriNum = limnPolyDataPolygonNumber( pld );
eqvArr = airArrayNew( NULL, NULL, 2*sizeof( unsigned int ),
/* this is only a heuristic */ pld->xyzwNum );
airMopAdd( mop, eqvArr, ( airMopper )airArrayNuke, airMopAlways );
nTriWithVert = nrrdNew( );
airMopAdd( mop, nTriWithVert, ( airMopper )nrrdNuke, airMopAlways );
if ( triangleWithVertex( nTriWithVert, pld ) ) {
biffAddf( LIMN, "%s: couldn't set nTriWithVert", me );
airMopError( mop ); return 1;
}
/* simple profiling showed that stupid amount of time was spent
adding the equivalences. So we go in two passes- first two see
how many equivalences are needed, and then actually adding them */
/* yea, so, its like you don't really even need an airArray ... */
triWithVert = AIR_CAST( unsigned int*, nTriWithVert->data );
for ( passIdx=0; passIdx<2; passIdx++ ) {
if ( 0 == passIdx ) {
eqvNum = 0;
} else {
airArrayLenPreSet( eqvArr, eqvNum );
}
for ( vertIdx=0; vertIdx<nTriWithVert->axis[1].size; vertIdx++ ) {
unsigned int *triLine, triIdx;
triLine = triWithVert + vertIdx*( nTriWithVert->axis[0].size );
for ( triIdx=1; triIdx<triLine[0]; triIdx++ ) {
if ( 0 == passIdx ) {
++eqvNum;
} else {
airEqvAdd( eqvArr, triLine[1], triLine[1+triIdx] );
}
}
}
}
nTriMap = nrrdNew( );
airMopAdd( mop, nTriMap, ( airMopper )nrrdNuke, airMopAlways );
nccSize = nrrdNew( );
airMopAdd( mop, nccSize, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nTriMap, nrrdTypeUInt, 1,
AIR_CAST( size_t, realTriNum ) ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate equivalence map", me );
airMopError( mop ); return 1;
}
triMap = AIR_CAST( unsigned int*, nTriMap->data );
primNumNew = airEqvMap( eqvArr, triMap, realTriNum );
if ( nrrdHisto( nccSize, nTriMap, NULL, NULL, primNumNew, nrrdTypeUInt ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't histogram CC map", me );
airMopError( mop ); return 1;
}
/* indxNumOld == indxNumNew */
indxOld = pld->indx;
primNumOld = pld->primNum;
if ( 1 != primNumOld ) {
biffAddf( LIMN, "%s: sorry! stupid implementation can't "
"do primNum %u ( only 1 )",
me, primNumOld );
airMopError( mop ); return 1;
}
typeOld = pld->type;
icntOld = pld->icnt;
indxNew = AIR_CAST( unsigned int*,
calloc( pld->indxNum, sizeof( unsigned int ) ) );
typeNew = AIR_CAST( unsigned char*,
calloc( primNumNew, sizeof( unsigned char ) ) );
icntNew = AIR_CAST( unsigned int*,
calloc( primNumNew, sizeof( unsigned int ) ) );
if ( !( indxNew && typeNew && icntNew ) ) {
biffAddf( LIMN, "%s: couldn't allocate new polydata arrays", me );
airMopError( mop ); return 1;
}
pld->indx = indxNew;
pld->primNum = primNumNew;
pld->type = typeNew;
pld->icnt = icntNew;
airMopAdd( mop, indxOld, airFree, airMopAlways );
airMopAdd( mop, typeOld, airFree, airMopAlways );
airMopAdd( mop, icntOld, airFree, airMopAlways );
/* this multi-pass thing is really stupid
( and assumes stupid primNumOld = 1 ) */
baseIndx = pld->indx;
for ( primIdxNew=0; primIdxNew<pld->primNum; primIdxNew++ ) {
unsigned int realTriIdx;
pld->type[primIdxNew] = limnPrimitiveTriangles;
pld->icnt[primIdxNew] = 0;
for ( realTriIdx=0; realTriIdx<realTriNum; realTriIdx++ ) {
if ( triMap[realTriIdx] == primIdxNew ) {
ELL_3V_COPY( baseIndx, indxOld + 3*realTriIdx );
1402 baseIndx += 3;
pld->icnt[primIdxNew] += 3;
}
}
}
airMopOkay( mop );
return 0;
}
int
limnPolyDataPrimitiveSort( limnPolyData *pld, const Nrrd *_nval ) {
static const char me[]="limnPolyDataPrimitiveSort";
Nrrd *nval, *nrec;
const Nrrd *ntwo[2];
airArray *mop;
double *rec;
unsigned int primIdx, **startIndx, *indxNew, *baseIndx, *icntNew;
unsigned char *typeNew;
int E;
if ( !( pld && _nval ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( 1 == _nval->dim
&& nrrdTypeBlock != _nval->type
&& _nval->axis[0].size == pld->primNum ) ) {
biffAddf( LIMN, "%s: need 1-D %u-len scalar nrrd "
"( not %u-D type %s, axis[0].size %u )", me,
pld->primNum,
_nval->dim, airEnumStr( nrrdType, _nval->type ),
AIR_CAST( unsigned int, _nval->axis[0].size ) );
return 1;
}
mop = airMopNew( );
nval = nrrdNew( );
airMopAdd( mop, nval, ( airMopper )nrrdNuke, airMopAlways );
nrec = nrrdNew( );
airMopAdd( mop, nrec, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( !E ) E |= nrrdConvert( nval, _nval, nrrdTypeDouble );
ntwo[0] = nval;
ntwo[1] = nval;
if ( !E ) E |= nrrdJoin( nrec, ntwo, 2, 0, AIR_TRUE );
if ( E ) {
biffMovef( LIMN, NRRD, "%s: problem creating records", me );
airMopError( mop ); return 1;
}
rec = AIR_CAST( double *, nrec->data );
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
rec[1 + 2*primIdx] = primIdx;
}
qsort( rec, pld->primNum, 2*sizeof( double ),
nrrdValCompareInv[nrrdTypeDouble] );
startIndx = AIR_CAST( unsigned int**, calloc( pld->primNum,
sizeof( unsigned int* ) ) );
indxNew = AIR_CAST( unsigned int*, calloc( pld->indxNum,
sizeof( unsigned int ) ) );
icntNew = AIR_CAST( unsigned int*, calloc( pld->primNum,
sizeof( unsigned int ) ) );
typeNew = AIR_CAST( unsigned char*, calloc( pld->primNum,
sizeof( unsigned char ) ) );
if ( !( startIndx && indxNew && icntNew && typeNew ) ) {
biffAddf( LIMN, "%s: couldn't allocated temp buffers", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, startIndx, airFree, airMopAlways );
baseIndx = pld->indx;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
startIndx[primIdx] = baseIndx;
baseIndx += pld->icnt[primIdx];
}
baseIndx = indxNew;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int sortIdx;
sortIdx = AIR_CAST( unsigned int, rec[1 + 2*primIdx] );
memcpy( baseIndx, startIndx[sortIdx],
pld->icnt[sortIdx]*sizeof( unsigned int ) );
icntNew[primIdx] = pld->icnt[sortIdx];
typeNew[primIdx] = pld->type[sortIdx];
baseIndx += pld->icnt[sortIdx];
}
airFree( pld->indx );
1490 pld->indx = indxNew;
airFree( pld->type );
pld->type = typeNew;
airFree( pld->icnt );
pld->icnt = icntNew;
airMopOkay( mop );
return 0;
}
int
limnPolyDataVertexWindingFlip( limnPolyData *pld ) {
static const char me[]="limnPolyDataVertexWindingFlip";
unsigned int baseVertIdx, primIdx;
if ( !pld ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
baseVertIdx = 0;
for ( primIdx=0; primIdx<pld->primNum; primIdx++ ) {
unsigned int triNum, triIdx, *indxLine, tmpIdx;
triNum = pld->icnt[primIdx]/3;
1519 for ( triIdx=0; triIdx<triNum; triIdx++ ) {
indxLine = pld->indx + baseVertIdx + 3*triIdx;
ELL_SWAP2( indxLine[0], indxLine[2], tmpIdx );
}
baseVertIdx += pld->icnt[primIdx];
}
return 0;
}
int
limnPolyDataPrimitiveSelect( limnPolyData *pldOut,
const limnPolyData *pldIn,
const Nrrd *_nmask ) {
static const char me[]="limnPolyDataPrimitiveSelect";
Nrrd *nmask;
double *mask;
unsigned int oldBaseVertIdx, oldPrimIdx, oldVertIdx, bitflag,
*old2newMap, *new2oldMap,
newPrimNum, newBaseVertIdx, newPrimIdx, newIndxNum, newVertIdx, newVertNum;
unsigned char *vertUsed;
airArray *mop;
if ( !( pldOut && pldIn && _nmask ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( !( 1 == _nmask->dim
&& nrrdTypeBlock != _nmask->type
&& _nmask->axis[0].size == pldIn->primNum ) ) {
biffAddf( LIMN, "%s: need 1-D %u-len scalar nrrd "
"( not %u-D type %s, axis[0].size %u )", me,
pldIn->primNum, _nmask->dim, airEnumStr( nrrdType, _nmask->type ),
AIR_CAST( unsigned int, _nmask->axis[0].size ) );
return 1;
}
mop = airMopNew( );
nmask = nrrdNew( );
airMopAdd( mop, nmask, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nmask, _nmask, nrrdTypeDouble ) ) {
biffMovef( LIMN, NRRD, "%s: trouble converting mask to %s", me,
airEnumStr( nrrdType, nrrdTypeDouble ) );
return 1;
}
mask = AIR_CAST( double *, nmask->data );
old2newMap = AIR_CAST( unsigned int *, calloc( pldIn->xyzwNum,
sizeof( unsigned int ) ) );
airMopAdd( mop, old2newMap, airFree, airMopAlways );
vertUsed = AIR_CAST( unsigned char *, calloc( pldIn->xyzwNum,
sizeof( unsigned char ) ) );
airMopAdd( mop, vertUsed, airFree, airMopAlways );
/* initialize all verts as unused */
for ( oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++ ) {
vertUsed[oldVertIdx] = AIR_FALSE;
}
/* mark the used verts, and count # new indices and primitives */
oldBaseVertIdx = 0;
newPrimNum = 0;
newIndxNum = 0;
for ( oldPrimIdx=0; oldPrimIdx<pldIn->primNum; oldPrimIdx++ ) {
unsigned indxIdx;
if ( mask[oldPrimIdx] ) {
for ( indxIdx=0; indxIdx<pldIn->icnt[oldPrimIdx]; indxIdx++ ) {
vertUsed[( pldIn->indx + oldBaseVertIdx )[indxIdx]] = AIR_TRUE;
}
newIndxNum += pldIn->icnt[oldPrimIdx];
newPrimNum++;
}
oldBaseVertIdx += pldIn->icnt[oldPrimIdx];
}
/* count the used verts, and set up map from old to new indices */
newVertNum = 0;
for ( oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++ ) {
if ( vertUsed[oldVertIdx] ) {
old2newMap[oldVertIdx] = newVertNum++;
}
}
/* allocate and fill reverse map */
new2oldMap = AIR_CAST( unsigned int *, calloc( newVertNum,
sizeof( unsigned int ) ) );
airMopAdd( mop, new2oldMap, airFree, airMopAlways );
newVertIdx = 0;
for ( oldVertIdx=0; oldVertIdx<pldIn->xyzwNum; oldVertIdx++ ) {
if ( vertUsed[oldVertIdx] ) {
new2oldMap[newVertIdx++] = oldVertIdx;
}
}
/* allocate output polydata */
bitflag = limnPolyDataInfoBitFlag( pldIn );
if ( limnPolyDataAlloc( pldOut, bitflag, newVertNum, newIndxNum, newPrimNum ) ) {
biffAddf( LIMN, "%s: trouble allocating output", me );
return 1;
}
/* transfer per-primitive information from old to new */
oldBaseVertIdx = 0;
newBaseVertIdx = 0;
newPrimIdx = 0;
for ( oldPrimIdx=0; oldPrimIdx<pldIn->primNum; oldPrimIdx++ ) {
if ( mask[oldPrimIdx] ) {
unsigned indxIdx;
pldOut->icnt[newPrimIdx] = pldIn->icnt[oldPrimIdx];
pldOut->type[newPrimIdx] = pldIn->type[oldPrimIdx];
for ( indxIdx=0; indxIdx<pldIn->icnt[oldPrimIdx]; indxIdx++ ) {
oldVertIdx = ( pldIn->indx + oldBaseVertIdx )[indxIdx];
( pldOut->indx + newBaseVertIdx )[indxIdx] = old2newMap[oldVertIdx];
}
newBaseVertIdx += pldIn->icnt[oldPrimIdx];
newPrimIdx++;
}
oldBaseVertIdx += pldIn->icnt[oldPrimIdx];
}
/* transfer per-vertex info */
for ( newVertIdx=0; newVertIdx<newVertNum; newVertIdx++ ) {
oldVertIdx = new2oldMap[newVertIdx];
ELL_4V_COPY( pldOut->xyzw + 4*newVertIdx, pldIn->xyzw + 4*oldVertIdx );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
ELL_4V_COPY( pldOut->rgba + 4*newVertIdx, pldIn->rgba + 4*oldVertIdx );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
ELL_3V_COPY( pldOut->norm + 3*newVertIdx, pldIn->norm + 3*oldVertIdx );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
ELL_3V_COPY( pldOut->tex2 + 2*newVertIdx, pldIn->tex2 + 2*oldVertIdx );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
ELL_3V_COPY( pldOut->tang + 3*newVertIdx, pldIn->tang + 3*oldVertIdx );
}
}
1652
airMopOkay( mop );
return 0;
}
/* Helper function for limnPolyDataClipMulti - clips the edge between
* disc and kept that partially fulfills the thresholds and maintains
* a data structure that keeps track of edges we have clipped already,
* to avoid creating duplicate vertices.
*/
static int
clipEdge( int disc, int kept, Nrrd *nval, double *thresh, int *newIdx,
airArray *llistArr, limnPolyData *pld, unsigned int bitflag,
limnPolyData *newpld, airArray *xyzwArr, airArray *rgbaArr,
airArray *normArr, airArray *tex2Arr, airArray *tangArr ) {
int ref=-1, *llist=( int* )llistArr->data;
int next=newIdx[disc];
double alpha=0;
unsigned int i, q, p, nk;
double ( *lup )( const void *v, size_t I )=nrrdDLookup[nval->type];
/* check if we clipped the edge previously */
while ( next!=-1 ) {
if ( llist[next]==kept ) /* found the desired vertex */
return llist[next+1];
ref=next+2;
next=llist[next+2];
}
/* we need to interpolate - find the weight */
nk=( nval->dim==1 )?1:nval->axis[0].size;
for ( i=0; i<nk; i++ ) {
double discval = lup( nval->data, nk*disc+i );
double keptval = lup( nval->data, nk*kept+i );
double thisalpha = AIR_AFFINE( discval, thresh[i], keptval, 0.0, 1.0 );
if ( thisalpha<1.0 && thisalpha>alpha )
alpha=thisalpha;
}
/* add interpolated vertex */
q=airArrayLenIncr( xyzwArr, 1 );
ELL_4V_LERP_TT( newpld->xyzw+4*q, float, alpha, pld->xyzw+4*disc, pld->xyzw+4*kept );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
airArrayLenIncr( rgbaArr, 1 );
ELL_4V_LERP_TT( newpld->rgba+4*q, unsigned char, alpha, pld->rgba+4*disc, pld->rgba+4*kept );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
float fnorm[3];
double len;
/* take special care to treat non-orientable surface normals correctly */
if ( ELL_3V_DOT( pld->norm+3*disc, pld->norm+3*kept )<0 ) {
ELL_3V_SCALE_TT( fnorm, float, -1.0, pld->norm+3*kept );
} else {
ELL_3V_COPY( fnorm, pld->norm+3*kept );
}
airArrayLenIncr( normArr, 1 );
ELL_3V_LERP_TT( newpld->norm+3*q, float, alpha, pld->norm+3*disc, fnorm );
/* re-normalize */
len=ELL_3V_LEN( newpld->norm+3*q );
if ( len>1e-20 ) {
ELL_3V_SCALE_TT( newpld->norm+3*q, float, 1.0/len, newpld->norm+3*q );
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
airArrayLenIncr( tex2Arr, 1 );
ELL_2V_LERP_TT( newpld->tex2+2*q, float, alpha, pld->tex2+2*disc, pld->tex2+2*kept );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
airArrayLenIncr( tangArr, 1 );
ELL_3V_LERP_TT( newpld->tang+3*q, float, alpha, pld->tang+3*disc, pld->tang+3*kept );
}
/* add new vertex to linked list */
p=airArrayLenIncr( llistArr, 1 );
llist=( int* )llistArr->data; /* update in case of re-allocation */
llist[3*p]=kept;
llist[3*p+1]=q;
llist[3*p+2]=-1;
if ( ref==-1 ) newIdx[disc]=3*p;
else llist[ref]=3*p;
return q;
}
/*
* Clips the given triangles ( limnPrimitiveTriangles ) according to the
1733 * input matrix nval and the threshold array thresh. First axis of
* nval are different clipping criteria, second axis are vertex
* indices. The length of thresh has to equal the size of the first
* axis, the vertex count in pld has to equal the size of the second
* axis. If nval is 1D, it is assumed to have a single criterion.
*
* A vertex is preserved if all values are >= the respective
* threshold; triangles with partially discarded vertices are clipped,
* potentially generating a quad that is then triangulated arbitrarily.
*/
int
limnPolyDataClipMulti( limnPolyData *pld, Nrrd *nval, double *thresh ) {
static const char me[]="limnPolyDataClipMulti";
unsigned char *keepVert=NULL;
airArray *mop;
unsigned int E, i, idx=0;
double ( *lup )( const void *v, size_t I );
airArray *xyzwArr, *rgbaArr=NULL, *normArr=NULL, *tex2Arr=NULL,
*tangArr=NULL, *indxArr, *typeArr, *icntArr, *llistArr=NULL;
limnPolyData *newpld=NULL;
int *newIdx=NULL, *llist=NULL;
unsigned int bitflag, nk, nvert;
airPtrPtrUnion appu;
if ( !( pld && nval ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nval->type ) {
biffAddf( LIMN, "%s: need scalar type ( not %s )", me,
airEnumStr( nrrdType, nval->type ) );
return 1;
}
if ( nval->dim==1 ) {
nk=1; nvert=nval->axis[0].size;
} else if ( nval->dim==2 ) {
nk=nval->axis[0].size; nvert=nval->axis[1].size;
} else {
biffAddf( LIMN, "%s: need 1D or 2D input array, got %uD", me, nval->dim );
return 1;
}
if ( nvert!=pld->xyzwNum ) {
biffAddf( LIMN, "%s: # verts %u != # values %u", me,
pld->xyzwNum, nvert );
return 1;
}
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pld ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
/* Memory allocation in C IS a headache */
mop=airMopNew( );
E = AIR_FALSE;
if ( !E ) {
E|=!( keepVert = AIR_CAST( unsigned char *,
calloc( pld->xyzwNum, sizeof( char ) ) ) );
}
if ( !E ) {
airMopAdd( mop, keepVert, airFree, airMopAlways );
E|=!( newIdx = AIR_CAST( int *, malloc( pld->xyzwNum*sizeof( int ) ) ) );
}
if ( !E ) {
unsigned int incr;
airMopAdd( mop, newIdx, airFree, airMopAlways );
memset( newIdx, -1, sizeof( int )*pld->xyzwNum );
/* This setting of incr is arbitrary and was not optimized in any way: */
incr = pld->xyzwNum/10; /* 10% of previous vertex count... */
if ( incr<50 ) incr=50; /* ...but at least 50. */
appu.i = &llist;
E|=!( llistArr=airArrayNew( appu.v, NULL, 3*sizeof( int ), incr ) );
}
if ( !E ) {
airMopAdd( mop, llistArr, ( airMopper )airArrayNuke, airMopAlways );
E|=!( newpld = limnPolyDataNew( ) );
}
bitflag = limnPolyDataInfoBitFlag( pld );
if ( !E ) {
unsigned int incr;
airMopAdd( mop, newpld, airFree, airMopAlways ); /* "shallow" free */
incr = pld->xyzwNum/20; /* 5% of previous vertex count... */
if ( incr<10 ) incr=10; /* ...but at least 10. */
appu.f = &( newpld->xyzw );
E|=!( xyzwArr=airArrayNew( appu.v, &( newpld->xyzwNum ),
4*sizeof( float ), incr ) );
if ( !E ) {
airMopAdd( mop, xyzwArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, xyzwArr, ( airMopper )airArrayNix, airMopOnOkay );
}
if ( !E && ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
appu.uc = &( newpld->rgba );
E|=!( rgbaArr=airArrayNew( appu.v, &( newpld->rgbaNum ),
4*sizeof( unsigned char ), incr ) );
if ( !E ) {
airMopAdd( mop, rgbaArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, rgbaArr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
if ( !E && ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
appu.f = &( newpld->norm );
E|=!( normArr=airArrayNew( appu.v, &( newpld->normNum ),
3*sizeof( float ), incr ) );
if ( !E ) {
airMopAdd( mop, normArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, normArr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
if ( !E && ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
appu.f = &( newpld->tex2 );
E|=!( tex2Arr=airArrayNew( appu.v, &( newpld->tex2Num ),
2*sizeof( float ), incr ) );
if ( !E ) {
airMopAdd( mop, tex2Arr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, tex2Arr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
if ( !E && ( 1 << limnPolyDataInfoTang ) & bitflag ) {
appu.f = &( newpld->tang );
E|=!( tangArr=airArrayNew( appu.v, &( newpld->tangNum ),
3*sizeof( float ), incr ) );
if ( !E ) {
airMopAdd( mop, tangArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, tangArr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
if ( !E ) {
incr = pld->indxNum/20; /* 5% of previous index count... */
if ( incr<10 ) incr=10; /* ...but at least 10. */
appu.ui = &( newpld->indx );
E|=!( indxArr=airArrayNew( appu.v, &( newpld->indxNum ),
sizeof( unsigned int ), incr ) );
if ( !E ) {
airMopAdd( mop, indxArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, indxArr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
if ( !E ) {
incr = pld->primNum/10; /* 10% of previous primNum... */
if ( incr<1 ) incr=1; /* ...but at least 1. */
appu.uc = &( newpld->type );
E|=!( typeArr=airArrayNew( appu.v, &( newpld->primNum ),
sizeof( unsigned char ), incr ) );
if ( !E ) {
airMopAdd( mop, typeArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, typeArr, ( airMopper )airArrayNix, airMopOnOkay );
}
appu.ui = &( newpld->icnt );
E|=!( icntArr=airArrayNew( appu.v, NULL,
sizeof( unsigned int ), incr ) );
if ( !E ) {
airMopAdd( mop, icntArr, ( airMopper )airArrayNuke, airMopOnError );
airMopAdd( mop, icntArr, ( airMopper )airArrayNix, airMopOnOkay );
}
}
}
if ( E ) {
biffAddf( LIMN, "%s: couldn't allocate buffers", me );
airMopError( mop ); return 1;
}
/* mark vertices, leaving at 0 means "discard" */
lup = nrrdDLookup[nval->type];
for ( i=0; i<pld->xyzwNum; i++ ) {
unsigned int j, keep = AIR_TRUE;
for ( j=0; j<nk; j++, idx++ ) {
if ( lup( nval->data, idx ) < thresh[j] )
keep = AIR_FALSE;
}
if ( keep ) {
keepVert[i]=AIR_TRUE;
}
}
/* now, iterate over all primitives and triangles */
/* Note: If keepVert[i]==AIR_TRUE, newIdx[i] is its new index; else, it is
* an index j into llist, which is a linked list:
* llist[j] == other ( kept ) end of the edge
* llist[j+1] == index of new vertex for that edge
* llist[j+2] == next index into llist
*/
/* TODO: All the airArray stuff should have allocation error checking */
idx=0;
for ( i=0; i<pld->primNum; i++ ) {
int j, oldTriNum=pld->icnt[i]/3, newTriNum=0;
unsigned int kept=0, disck=0; /* index of last kept / discarded vertex */
for ( j=0; j<oldTriNum; j++, idx+=3 ) {
unsigned int p, quad[4];
int k, keepN=0;
for ( k=0; k<3; k++ ) {
unsigned int oldidx=pld->indx[idx+k];
if ( keepVert[oldidx] ) {
keepN++; kept=oldidx;
/* make sure the vertex is copied over */
if ( newIdx[oldidx]==-1 ) {
unsigned int q=newIdx[oldidx]=airArrayLenIncr( xyzwArr, 1 );
ELL_4V_COPY( newpld->xyzw+4*q, pld->xyzw+4*oldidx );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
airArrayLenIncr( rgbaArr, 1 );
ELL_4V_COPY( newpld->rgba+4*q, pld->rgba+4*oldidx );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
airArrayLenIncr( normArr, 1 );
ELL_3V_COPY( newpld->norm+3*q, pld->norm+3*oldidx );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
airArrayLenIncr( tex2Arr, 1 );
ELL_2V_COPY( newpld->tex2+2*q, pld->tex2+2*oldidx );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
airArrayLenIncr( tangArr, 1 );
ELL_3V_COPY( newpld->tang+3*q, pld->tang+3*oldidx );
}
}
} else {
disck=k;
}
}
switch ( keepN ) {
case 0: /* nothing to be done; discard this triangle */
break;
case 1: /* result of clipping is a single triangle */
newTriNum++;
p=airArrayLenIncr( indxArr, 3 );
for ( k=0; k<3; k++ ) {
if ( keepVert[pld->indx[idx+k]] )
newpld->indx[p+k]=newIdx[pld->indx[idx+k]];
else
newpld->indx[p+k]=clipEdge( pld->indx[idx+k], kept, nval, thresh,
newIdx, llistArr, pld, bitflag, newpld,
xyzwArr, rgbaArr, normArr,
tex2Arr, tangArr );
}
break;
case 2: /* result of clipping is a quad, triangulate */
newTriNum+=2;
p=0;
for ( k=0; k<3; k++ ) {
if ( keepVert[pld->indx[idx+k]] ) quad[p++]=newIdx[pld->indx[idx+k]];
else {
quad[p++]=clipEdge( pld->indx[idx+k], pld->indx[idx+( disck+2 )%3],
nval, thresh, newIdx, llistArr,
pld, bitflag, newpld, xyzwArr, rgbaArr,
normArr, tex2Arr, tangArr );
quad[p++]=clipEdge( pld->indx[idx+k], pld->indx[idx+( disck+1 )%3],
nval, thresh, newIdx, llistArr,
pld, bitflag, newpld, xyzwArr, rgbaArr,
normArr, tex2Arr, tangArr );
}
}
p=airArrayLenIncr( indxArr, 6 );
ELL_3V_SET( newpld->indx+p, quad[0], quad[1], quad[3] );
ELL_3V_SET( newpld->indx+p+3, quad[1], quad[2], quad[3] );
break;
case 3: /* simply copy the existing triangle */
newTriNum++;
p=airArrayLenIncr( indxArr, 3 );
for ( k=0; k<3; k++ ) {
newpld->indx[p+k]=newIdx[pld->indx[idx+k]];
}
break;
}
}
if ( newTriNum>0 ) {
unsigned int p=airArrayLenIncr( typeArr, 1 );
airArrayLenIncr( icntArr, 1 );
newpld->type[p]=limnPrimitiveTriangles;
newpld->icnt[p]=newTriNum*3;
}
}
/* finally, replace contents of pld with new data */
airFree( pld->xyzw );
airFree( pld->rgba );
airFree( pld->norm );
airFree( pld->tex2 );
airFree( pld->tang );
airFree( pld->indx );
airFree( pld->type );
2016 airFree( pld->icnt );
memcpy( pld, newpld, sizeof( limnPolyData ) );
airMopOkay( mop );
return 0;
}
/* Simple wrapper around limnPolyDataClipMulti, in case of only one
* clipping criterion.
*/
2026 int
limnPolyDataClip( limnPolyData *pld, Nrrd *nval, double thresh ) {
return limnPolyDataClipMulti( pld, nval, &thresh );
}
/* limnPolyDataCompress:
* returns a "compressed" copy of the given limnPolyData pld that only
* contains vertices that are referenced by some primitive
* returns NULL and adds a message to biff upon error
*/
limnPolyData *
limnPolyDataCompress( const limnPolyData *pld ) {
static const char me[]="limnPolyDataCompress";
limnPolyData *ret = NULL;
unsigned int infoBitFlag=0, vertNum=0, i, used_indxNum=0;
int *vertMap;
if ( pld==NULL ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return NULL;
}
infoBitFlag=limnPolyDataInfoBitFlag( pld );
vertMap=( int* ) calloc( pld->xyzwNum, sizeof( int ) );
if ( vertMap==NULL ) {
biffAddf( LIMN, "%s: could not allocate memory", me );
return NULL;
}
/* how many indices are actually used? */
for ( i=0; i<pld->primNum; i++ ) {
used_indxNum+=pld->icnt[i];
}
/* loop over all indices and mark referenced vertices in vertMap */
for ( i=0; i<used_indxNum; i++ ) {
vertMap[pld->indx[i]]=1;
}
/* turn vertMap into an index map */
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]==0 )
vertMap[i]=-1;
else
vertMap[i]=vertNum++;
}
/* allocate new limnPolyData */
if ( NULL == ( ret=limnPolyDataNew( ) ) ||
0!=limnPolyDataAlloc( ret, infoBitFlag, vertNum,
used_indxNum, pld->primNum ) ) {
biffAddf( LIMN, "%s: Could not allocate result", me );
free( vertMap );
return NULL;
}
/* fill the newly allocated structure */
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]>=0 ) {
ELL_4V_COPY( ret->xyzw+4*vertMap[i], pld->xyzw+4*i );
}
}
if ( ret->rgba!=NULL ) {
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]>=0 ) {
ELL_4V_COPY( ret->rgba+4*vertMap[i], pld->rgba+4*i );
}
}
}
if ( ret->norm!=NULL ) {
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]>=0 ) {
ELL_3V_COPY( ret->norm+3*vertMap[i], pld->norm+3*i );
}
}
}
if ( ret->tex2!=NULL ) {
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]>=0 ) {
ELL_2V_COPY( ret->tex2+2*vertMap[i], pld->tex2+2*i );
}
}
}
if ( ret->tang!=NULL ) {
for ( i=0; i<pld->xyzwNum; i++ ) {
if ( vertMap[i]>=0 ) {
ELL_3V_COPY( ret->tang+3*vertMap[i], pld->tang+3*i );
}
}
}
for ( i=0; i<used_indxNum; i++ ) {
ret->indx[i]=vertMap[pld->indx[i]];
}
memcpy( ret->type, pld->type, sizeof( char )*pld->primNum );
memcpy( ret->icnt, pld->icnt, sizeof( int )*pld->primNum );
free( vertMap );
2116
return ret;
}
/* limnPolyDataJoin:
* concatenates the primitives in all num limnPolyDatas given in plds
* and returns the result as a newly allocated limnPolyData
* the new limnPolyData will only have color/normals/texture coordinates
* if _all_ input limnPolyDatas had the respective attribute
* returns NULL and adds a message to biff upon error
*/
limnPolyData *limnPolyDataJoin( const limnPolyData **plds,
unsigned int num ) {
static const char me[]="limnPolyDataJoin";
limnPolyData *ret = NULL;
unsigned int infoBitFlag=( 1 << limnPolyDataInfoRGBA ) |
( 1 << limnPolyDataInfoNorm ) |
( 1 << limnPolyDataInfoTex2 ) |
( 1 << limnPolyDataInfoTang ); /* by default, assume we have all these */
unsigned int vertNum=0, indxNum=0, primNum=0;
unsigned int i;
if ( plds==NULL ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return NULL;
}
/* loop over all input plds to find infoBitFlag and the total number of
* vertices / indices / primitives */
for ( i=0; i<num; i++ ) {
if ( plds[i]==NULL ) {
biffAddf( LIMN, "%s: plds[%d] is a NULL pointer", me, i );
return NULL;
}
infoBitFlag &= limnPolyDataInfoBitFlag( plds[i] );
vertNum += plds[i]->xyzwNum;
indxNum += plds[i]->indxNum;
primNum += plds[i]->primNum;
}
if ( NULL == ( ret=limnPolyDataNew( ) ) ||
0!=limnPolyDataAlloc( ret, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: Could not allocate result", me );
return NULL;
}
/* loop again over all input plds and fill the newly allocated structure */
vertNum=indxNum=primNum=0;
for ( i=0; i<num; i++ ) {
unsigned int j, used_indxNum=0;
memcpy( ret->xyzw+4*vertNum, plds[i]->xyzw,
sizeof( float )*4*plds[i]->xyzwNum );
if ( ret->rgba!=NULL ) {
memcpy( ret->rgba+4*vertNum, plds[i]->rgba,
sizeof( unsigned char )*4*plds[i]->xyzwNum );
}
if ( ret->norm!=NULL ) {
memcpy( ret->norm+3*vertNum, plds[i]->norm,
sizeof( float )*3*plds[i]->xyzwNum );
}
if ( ret->tex2!=NULL ) {
memcpy( ret->tex2+2*vertNum, plds[i]->tex2,
sizeof( float )*2*plds[i]->xyzwNum );
}
if ( ret->tang!=NULL ) {
memcpy( ret->tang+3*vertNum, plds[i]->tang,
sizeof( float )*3*plds[i]->xyzwNum );
}
for ( j=0; j<plds[i]->indxNum; j++ ) {
ret->indx[indxNum+j]=vertNum+plds[i]->indx[j];
}
for ( j=0; j<plds[i]->primNum; j++ ) {
ret->type[primNum+j]=plds[i]->type[j];
ret->icnt[primNum+j]=plds[i]->icnt[j];
2186 /* need to keep track of how many indices are actually used */
used_indxNum+=plds[i]->icnt[j];
}
vertNum+=plds[i]->xyzwNum;
indxNum+=used_indxNum;
primNum+=plds[i]->primNum;
}
return ret;
}
int
limnPolyDataEdgeHalve( limnPolyData *pldOut,
const limnPolyData *pldIn ) {
static const char me[]="limnPolyDataEdgeHalve";
Nrrd *nnewvert;
unsigned int *newvert, nvold, nvidx, triidx, trinum, vlo, vhi, bitflag;
airArray *mop;
if ( ( 1 << limnPrimitiveTriangles ) != limnPolyDataPrimitiveTypes( pldIn ) ) {
biffAddf( LIMN, "%s: sorry, can only handle %s primitives", me,
airEnumStr( limnPrimitive, limnPrimitiveTriangles ) );
return 1;
}
if ( 1 != pldIn->primNum ) {
biffAddf( LIMN, "%s: sorry, can only handle a single primitive", me );
return 1;
}
mop = airMopNew( );
nnewvert = nrrdNew( );
airMopAdd( mop, nnewvert, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
nvold = pldIn->xyzwNum;
if ( nrrdMaybeAlloc_va( nnewvert, nrrdTypeUInt, 2, nvold, nvold ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't allocate buffer", me );
airMopError( mop ); return 1;
}
newvert = AIR_CAST( unsigned int*, nnewvert->data );
/* run through triangles, recording edges with the new vertex index */
nvidx = nvold;
trinum = pldIn->indxNum/3;
for ( triidx=0; triidx<trinum; triidx++ ) {
vlo = pldIn->indx[0 + 3*triidx];
vhi = pldIn->indx[1 + 3*triidx];
if ( !newvert[vlo + nvold*vhi] ) {
newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
}
vlo = pldIn->indx[1 + 3*triidx];
vhi = pldIn->indx[2 + 3*triidx];
if ( !newvert[vlo + nvold*vhi] ) {
newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
}
vlo = pldIn->indx[2 + 3*triidx];
vhi = pldIn->indx[0 + 3*triidx];
if ( !newvert[vlo + nvold*vhi] ) {
newvert[vlo + nvold*vhi] = newvert[vhi + nvold*vlo] = nvidx++;
}
}
/* allocate output */
bitflag = limnPolyDataInfoBitFlag( pldIn );
if ( limnPolyDataAlloc( pldOut, bitflag, nvidx, 3*4*trinum, 1 ) ) {
biffAddf( LIMN, "%s: trouble allocating output", me );
airMopError( mop ); return 1;
}
pldOut->type[0] = limnPrimitiveTriangles;
pldOut->icnt[0] = 3*4*trinum;
/* set output indx */
for ( triidx=0; triidx<trinum; triidx++ ) {
unsigned int aa, ab, bb, bc, cc, ac;
aa = pldIn->indx[0 + 3*triidx];
bb = pldIn->indx[1 + 3*triidx];
cc = pldIn->indx[2 + 3*triidx];
ab = newvert[aa + nvold*bb];
bc = newvert[bb + nvold*cc];
ac = newvert[aa + nvold*cc];
ELL_3V_SET( pldOut->indx + 3*( 0 + 4*triidx ), aa, ab, ac );
ELL_3V_SET( pldOut->indx + 3*( 1 + 4*triidx ), ab, bc, ac );
ELL_3V_SET( pldOut->indx + 3*( 2 + 4*triidx ), ab, bb, bc );
ELL_3V_SET( pldOut->indx + 3*( 3 + 4*triidx ), ac, bc, cc );
}
/* set output vertex info */
for ( vlo=0; vlo<nvold; vlo++ ) {
ELL_4V_COPY( pldOut->xyzw + 4*vlo, pldIn->xyzw + 4*vlo );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
ELL_4V_COPY( pldOut->rgba + 4*vlo, pldIn->rgba + 4*vlo );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
ELL_3V_COPY( pldOut->norm + 3*vlo, pldIn->norm + 3*vlo );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
ELL_2V_COPY( pldOut->tex2 + 2*vlo, pldIn->tex2 + 2*vlo );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
ELL_3V_COPY( pldOut->tang + 3*vlo, pldIn->tang + 3*vlo );
}
for ( vhi=vlo+1; vhi<nvold; vhi++ ) {
unsigned int mid;
mid = newvert[vlo + nvold*vhi];
if ( !mid ) {
continue;
}
ELL_4V_LERP( pldOut->xyzw + 4*mid, 0.5f,
pldIn->xyzw + 4*vlo,
pldIn->xyzw + 4*vhi );
if ( ( 1 << limnPolyDataInfoRGBA ) & bitflag ) {
ELL_4V_LERP_TT( pldOut->rgba + 4*mid, unsigned char, 0.5f,
pldIn->rgba + 4*vlo,
pldIn->rgba + 4*vhi );
}
if ( ( 1 << limnPolyDataInfoNorm ) & bitflag ) {
float tmp;
ELL_3V_LERP( pldOut->norm + 3*mid, 0.5f,
pldIn->norm + 3*vlo,
pldIn->norm + 3*vhi );
ELL_3V_NORM_TT( pldOut->norm + 3*mid, float,
pldOut->norm + 3*mid, tmp );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & bitflag ) {
ELL_2V_LERP( pldOut->tex2 + 2*mid, 0.5f,
pldIn->tex2 + 2*vlo,
pldIn->tex2 + 2*vhi );
}
if ( ( 1 << limnPolyDataInfoTang ) & bitflag ) {
float tmp;
ELL_3V_LERP( pldOut->tang + 3*mid, 0.5f,
pldIn->tang + 3*vlo,
pldIn->tang + 3*vhi );
ELL_3V_NORM_TT( pldOut->tang + 3*mid, float,
2316 pldOut->tang + 3*mid, tmp );
}
}
}
airMopOkay( mop );
return 0;
}
/* helper function for the limnPolyDataNeighborList below */
static void
registerNeighbor( unsigned int *nblist, size_t *len, unsigned int *maxnb,
unsigned int u, unsigned int v ) {
unsigned int idx=nblist[u], pointer=u, depth=1;
while ( idx!=0 ) {
if ( nblist[idx]==v )
return; /* has already been registered */
pointer=idx+1;
idx=nblist[pointer];
depth++;
}
if ( depth>*maxnb )
*maxnb=depth;
/* do the registration */
nblist[pointer]=*len;
nblist[*len]=v;
nblist[*len+1]=0;
( *len )+=2;
/* now the other way around */
idx=nblist[v]; pointer=v;
while ( idx!=0 ) {
/* do not have to check nblist[idx]==u due to symmetry */
pointer=idx+1;
idx=nblist[pointer];
}
nblist[pointer]=*len;
nblist[*len]=u;
nblist[*len+1]=0;
( *len )+=2;
}
/* Here's the thing with all these limnPolyDataNeighbor* functions:
*
* *List is used for building up the information and called by all others
* *Array is a great representation if all vertices have a similar number
* of neighbors ( in particular, no gross outliers )
* The output of this is used by glyph coloring in elf.
* *ArrayComp is a compressed representation that is best if vertices have
* a more variable number of neighbors
* The output of this is used by surface smoothing in limn.
*/
/* Mallocs *nblist and fills it with a linked list that contains all neighbors
* of all n vertices in the given limnPolyData. The format is as follows:
2370 * For v<n, ( *nblist )[v] is an index i ( i>=n ) into *nblist, or 0 if vertex v
* does not have any neighbors.
* For an index i obtained this way, ( *nblist )[i] is a neighbor of v,
* ( *nblist )[i+1] is the index of the next list element ( or 0 ).
* If non-NULL, *len is set to the required size of *nblist - the initial malloc
* makes a conservative guess and you may want to realloc the result to *len
* bytes in order to free memory that ended up unused
* If non-NULL, *m is set to the maximum number of neighbors ( over all vertices )
* Return value is 0 upon success, -1 upon error. Biff is used for errors.
*/
int
limnPolyDataNeighborList( unsigned int **nblist, size_t *len,
unsigned int *maxnb, const limnPolyData *pld ) {
static const char me[]="limnPolyDataNeighborList";
unsigned int i, j, m, estimate=0, *indx;
size_t last;
/* estimate the maximum number of neighborhood relations */
for ( i=0; i<pld->primNum; i++ ) {
switch ( pld->type[i] ) {
case limnPrimitiveTriangles:
case limnPrimitiveQuads:
estimate+=pld->icnt[i]*2;
break;
case limnPrimitiveTriangleStrip:
case limnPrimitiveTriangleFan:
estimate+=4*( pld->icnt[i]-2 )+2;
break;
case limnPrimitiveLineStrip:
estimate+=2*( pld->icnt[i]-1 );
break;
case limnPrimitiveLines:
estimate+=pld->icnt[i];
break;
default: /* should be a noop; silently ignore it */
break;
}
}
/* allocate *nblist */
*nblist = ( unsigned int* ) malloc( sizeof( unsigned int )*
( pld->xyzwNum+2*estimate ) );
if ( *nblist==NULL ) {
biffAddf( LIMN, "%s: couldn't allocate nblist buffer", me );
return -1;
}
/* populate the list */
memset( *nblist, 0, sizeof( unsigned int )*pld->xyzwNum );
last=pld->xyzwNum; m=0; indx=pld->indx;
for ( i=0; i<pld->primNum; i++ ) {
switch ( pld->type[i] ) {
case limnPrimitiveTriangles:
for ( j=0; j<pld->icnt[i]; j+=3 ) { /* go through all triangles */
registerNeighbor( *nblist, &last, &m, indx[j], indx[j+1] );
registerNeighbor( *nblist, &last, &m, indx[j+1], indx[j+2] );
registerNeighbor( *nblist, &last, &m, indx[j+2], indx[j] );
}
break;
case limnPrimitiveTriangleStrip:
if ( pld->icnt[i]>0 )
registerNeighbor( *nblist, &last, &m, indx[0], indx[1] );
for ( j=0; j<pld->icnt[i]-2; j++ ) {
registerNeighbor( *nblist, &last, &m, indx[j], indx[j+2] );
registerNeighbor( *nblist, &last, &m, indx[j+1], indx[j+2] );
}
break;
case limnPrimitiveTriangleFan:
if ( pld->icnt[i]>0 )
registerNeighbor( *nblist, &last, &m, indx[0], indx[1] );
for ( j=0; j<pld->icnt[i]-2; j++ ) {
registerNeighbor( *nblist, &last, &m, indx[0], indx[j+2] );
registerNeighbor( *nblist, &last, &m, indx[j+1], indx[j+2] );
}
break;
case limnPrimitiveQuads:
for ( j=0; j<pld->icnt[i]; j+=4 ) { /* go through all quads */
registerNeighbor( *nblist, &last, &m, indx[j], indx[j+1] );
registerNeighbor( *nblist, &last, &m, indx[j+1], indx[j+2] );
registerNeighbor( *nblist, &last, &m, indx[j+2], indx[j+3] );
registerNeighbor( *nblist, &last, &m, indx[j+3], indx[j] );
}
break;
case limnPrimitiveLineStrip:
for ( j=0; j<pld->icnt[i]-1; j++ ) {
registerNeighbor( *nblist, &last, &m, indx[j], indx[j+1] );
}
break;
case limnPrimitiveLines:
for ( j=0; j<pld->icnt[i]; j+=2 ) {
registerNeighbor( *nblist, &last, &m, indx[j], indx[j+1] );
}
break;
default: /* should be a noop; silently ignore it */
break;
}
indx+=pld->icnt[i];
}
if ( len!=NULL ) *len=last*sizeof( unsigned int );
if ( maxnb!=NULL ) *maxnb=m;
2467 return 0;
}
/* Over the set of all n vertices in a given limnPolyData, finds the
* maximum number m of neighbors. Sets *neighbors to a malloc'ed block
* of ( n*m ) indices and lists the neighbors of vertex v at position
* ( v*m ) of the list, padded with -1s.
* *maxnb will be set to m ( and is assumed to be non-NULL! )
* Returns -1 and adds a biff error if there was a problem allocating memory.
*/
int
limnPolyDataNeighborArray( int **neighbors, unsigned int *maxnb,
const limnPolyData *pld ) {
static const char me[]="limnPolyDataNeighborArray";
unsigned int i, *nblist, m;
/* get the neighbors as a linked list */
if ( -1==limnPolyDataNeighborList( &nblist, NULL, &m, pld ) ) {
return -1;
}
/* convert the result into an array */
*neighbors = ( int * ) malloc( sizeof( int )*m*pld->xyzwNum );
if ( NULL==*neighbors ) {
biffAddf( LIMN, "%s: couldn't allocate neighbors buffer", me );
free( nblist ); return -1;
}
for ( i=0; i<pld->xyzwNum; i++ ) {
unsigned int aidx=0, lidx=nblist[i];
while ( lidx!=0 ) {
( *neighbors )[m*i+aidx]=nblist[lidx];
lidx=nblist[lidx+1];
aidx++;
}
while ( aidx<m ) {
( *neighbors )[m*i+aidx]=-1;
aidx++;
}
}
*maxnb=m;
free( nblist );
2506 return 0;
}
/* Returns a compressed form of the above, rather than padding with -1s.
* *neighbors is malloc'ed to an array that holds the indices of all neighbors
* *idx is malloc'ed to an array of length pld->xyzwNum+1;
* ( *idx )[i] will give you the position in *neighbors at which the neighbors of
* vertex i start
* Returns -1 and adds a biff error if there was a problem allocating memory.
*/
int
limnPolyDataNeighborArrayComp( int **neighbors, int **idx,
const limnPolyData *pld ) {
static const char me[]="limnPolyDataNeighborArrayComp";
unsigned int i, ct, *nblist;
size_t len;
airArray *mop;
mop = airMopNew( );
/* get the neighbors as a linked list */
if ( -1==limnPolyDataNeighborList( &nblist, &len, NULL, pld ) ) {
return -1;
}
airMopAdd( mop, nblist, airFree, airMopAlways );
/* convert the result into compressed form */
*neighbors = ( int * ) malloc( sizeof( int )*( len-pld->xyzwNum )/2 );
if ( NULL==*neighbors ) {
biffAddf( LIMN, "%s: couldn't allocate neighbors buffer", me );
airMopError( mop ); return -1;
}
airMopAdd( mop, neighbors, airFree, airMopOnError );
*idx = ( int * ) malloc( sizeof( int )*( pld->xyzwNum+1 ) );
if ( NULL==*idx ) {
biffAddf( LIMN, "%s: couldn't allocate index buffer", me );
airMopError( mop ); return -1;
}
airMopAdd( mop, idx, airFree, airMopOnError );
for ( ct=i=0; i<pld->xyzwNum; i++ ) {
unsigned int lidx=nblist[i];
( *idx )[i]=ct;
while ( lidx!=0 ) {
( *neighbors )[ct]=nblist[lidx];
lidx=nblist[lidx+1];
ct++;
}
}
( *idx )[pld->xyzwNum]=ct;
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2010, 2009, 2008 Thomas Schultz
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
29 limnPolyDataCube( limnPolyData *pld,
unsigned int infoBitFlag,
int sharpEdge ) {
static const char me[]="limnPolyDataCube";
unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci;
vertNum = sharpEdge ? 6*4 : 8;
primNum = 1;
indxNum = 6*4;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
pld->type[0] = limnPrimitiveQuads;
pld->icnt[0] = indxNum;
vertIdx = 0;
cnum = sharpEdge ? 3 : 1;
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, -1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, 1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, 1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, -1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, -1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, 1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, 1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, -1, 1, 1 ); vertIdx++;
}
vertIdx = 0;
if ( sharpEdge ) {
ELL_4V_SET( pld->indx + vertIdx, 0, 3, 6, 9 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 2, 14, 16, 5 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 4, 17, 18, 8 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 7, 19, 21, 10 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 1, 11, 23, 13 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 12, 22, 20, 15 ); vertIdx += 4;
} else {
ELL_4V_SET( pld->indx + vertIdx, 0, 1, 2, 3 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 0, 4, 5, 1 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 1, 5, 6, 2 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 2, 6, 7, 3 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 0, 3, 7, 4 ); vertIdx += 4;
ELL_4V_SET( pld->indx + vertIdx, 4, 7, 6, 5 ); vertIdx += 4;
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
if ( sharpEdge ) {
ELL_3V_SET( pld->norm + 3*0, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*3, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*6, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*9, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*2, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*5, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*14, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*16, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*4, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*8, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*17, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*18, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*7, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*10, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*19, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*21, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*1, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*11, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*13, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*23, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*12, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3*15, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3*20, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3*22, 0, 0, 1 );
} else {
float cn;
cn = AIR_CAST( float, sqrt( 3.0 ) );
ELL_3V_SET( pld->norm + 3*0, -cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*1, -cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*2, cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*3, cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*4, -cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*5, -cn, cn, cn );
ELL_3V_SET( pld->norm + 3*6, cn, cn, cn );
ELL_3V_SET( pld->norm + 3*7, cn, -cn, cn );
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
int
139 limnPolyDataCubeTriangles( limnPolyData *pld,
unsigned int infoBitFlag,
int sharpEdge ) {
static const char me[]="limnPolyDataCubeTriangles";
unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci;
vertNum = sharpEdge ? 6*4 : 8;
primNum = 1;
indxNum = 6*6;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
pld->type[0] = limnPrimitiveTriangles;
pld->icnt[0] = indxNum;
vertIdx = 0;
cnum = sharpEdge ? 3 : 1;
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, -1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, -1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, 1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, 1, 1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, 1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, 1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, -1, -1, 1 ); vertIdx++;
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, -1, -1, 1 ); vertIdx++;
}
vertIdx = 0;
if ( sharpEdge ) {
ELL_3V_SET( pld->indx + vertIdx, 0, 6, 3 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 3, 6, 9 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 7, 12, 10 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 10, 12, 16 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 11, 15, 4 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 4, 15, 21 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 17, 14, 22 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 22, 14, 20 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 2, 5, 19 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 19, 5, 23 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 13, 8, 18 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 18, 8, 1 ); vertIdx += 3;
} else {
ELL_3V_SET( pld->indx + vertIdx, 0, 2, 1 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 1, 2, 3 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 2, 4, 3 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 3, 4, 5 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 3, 5, 1 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 1, 5, 7 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 5, 4, 7 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 7, 4, 6 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 7, 6, 1 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 1, 6, 0 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 4, 2, 6 ); vertIdx += 3;
ELL_3V_SET( pld->indx + vertIdx, 2, 6, 0 ); vertIdx += 3;
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
if ( sharpEdge ) {
ELL_3V_SET( pld->norm + 3*14, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*17, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*20, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3*22, 0, 0, -1 );
ELL_3V_SET( pld->norm + 3* 4, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*11, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*15, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3*21, -1, 0, 0 );
ELL_3V_SET( pld->norm + 3* 7, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*10, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*12, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3*16, 0, 1, 0 );
ELL_3V_SET( pld->norm + 3* 1, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3* 8, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*13, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3*18, 1, 0, 0 );
ELL_3V_SET( pld->norm + 3* 2, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3* 5, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*19, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3*23, 0, -1, 0 );
ELL_3V_SET( pld->norm + 3* 0, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3* 3, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3* 6, 0, 0, 1 );
ELL_3V_SET( pld->norm + 3* 9, 0, 0, 1 );
} else {
float cn;
cn = AIR_CAST( float, 1.0/sqrt( 3.0 ) );
ELL_3V_SET( pld->norm + 3*0, cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*1, -cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*2, cn, cn, cn );
ELL_3V_SET( pld->norm + 3*3, -cn, cn, cn );
ELL_3V_SET( pld->norm + 3*4, cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*5, -cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*6, cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*7, -cn, -cn, -cn );
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
if ( sharpEdge ) {
ELL_2V_SET( pld->tex2 + 2*14, 1, 1 );
ELL_2V_SET( pld->tex2 + 2*17, 0, 1 );
ELL_2V_SET( pld->tex2 + 2*20, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*22, 0, 0 );
ELL_2V_SET( pld->tex2 + 2* 4, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*11, 0, 0 );
ELL_2V_SET( pld->tex2 + 2*15, 0, 1 );
ELL_2V_SET( pld->tex2 + 2*21, 1, 1 );
ELL_2V_SET( pld->tex2 + 2* 7, 0, 0 );
ELL_2V_SET( pld->tex2 + 2*10, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*12, 0, 1 );
ELL_2V_SET( pld->tex2 + 2*16, 1, 1 );
ELL_2V_SET( pld->tex2 + 2* 1, 0, 0 );
ELL_2V_SET( pld->tex2 + 2* 8, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*13, 1, 1 );
ELL_2V_SET( pld->tex2 + 2*18, 0, 1 );
ELL_2V_SET( pld->tex2 + 2* 2, 1, 0 );
ELL_2V_SET( pld->tex2 + 2* 5, 0, 0 );
ELL_2V_SET( pld->tex2 + 2*19, 1, 1 );
ELL_2V_SET( pld->tex2 + 2*23, 0, 1 );
ELL_2V_SET( pld->tex2 + 2* 0, 1, 1 );
ELL_2V_SET( pld->tex2 + 2* 3, 0, 1 );
ELL_2V_SET( pld->tex2 + 2* 6, 1, 0 );
ELL_2V_SET( pld->tex2 + 2* 9, 0, 0 );
} else {
ELL_2V_SET( pld->tex2 + 2*0, 1, 1 );
ELL_2V_SET( pld->tex2 + 2*1, 0, 1 );
ELL_2V_SET( pld->tex2 + 2*2, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*3, 0, 0 );
ELL_2V_SET( pld->tex2 + 2*4, 1, 0 );
ELL_2V_SET( pld->tex2 + 2*5, 0, 0 );
ELL_2V_SET( pld->tex2 + 2*6, 1, 1 );
ELL_2V_SET( pld->tex2 + 2*7, 0, 1 );
}
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
if ( sharpEdge ) {
ELL_3V_SET( pld->tang + 3*14, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3*17, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3*20, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3*22, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 4, 0, -1, 0 );
ELL_3V_SET( pld->tang + 3*11, 0, -1, 0 );
ELL_3V_SET( pld->tang + 3*15, 0, -1, 0 );
ELL_3V_SET( pld->tang + 3*21, 0, -1, 0 );
ELL_3V_SET( pld->tang + 3* 7, -1, 0, 0 );
ELL_3V_SET( pld->tang + 3*10, -1, 0, 0 );
ELL_3V_SET( pld->tang + 3*12, -1, 0, 0 );
ELL_3V_SET( pld->tang + 3*16, -1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 1, 0, 1, 0 );
ELL_3V_SET( pld->tang + 3* 8, 0, 1, 0 );
ELL_3V_SET( pld->tang + 3*13, 0, 1, 0 );
ELL_3V_SET( pld->tang + 3*18, 0, 1, 0 );
ELL_3V_SET( pld->tang + 3* 2, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 5, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3*19, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3*23, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 0, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 3, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 6, 1, 0, 0 );
ELL_3V_SET( pld->tang + 3* 9, 1, 0, 0 );
} else {
float sn;
sn = AIR_CAST( float, 1.0/sqrt( 2.0 ) );
ELL_3V_SET( pld->tang + 3*0, -sn, sn, 0 );
ELL_3V_SET( pld->tang + 3*1, sn, sn, 0 );
ELL_3V_SET( pld->tang + 3*2, -sn, -sn, 0 );
ELL_3V_SET( pld->tang + 3*3, sn, -sn, 0 );
ELL_3V_SET( pld->tang + 3*4, -sn, -sn, 0 );
ELL_3V_SET( pld->tang + 3*5, sn, -sn, 0 );
ELL_3V_SET( pld->tang + 3*6, -sn, sn, 0 );
ELL_3V_SET( pld->tang + 3*7, sn, sn, 0 );
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
int
349 limnPolyDataOctahedron( limnPolyData *pld,
unsigned int infoBitFlag,
int sharpEdge ) {
static const char me[]="limnPolyDataOctahedron";
unsigned int vertNum, vertIdx, primNum, indxNum, cnum, ci;
vertNum = sharpEdge ? 4*6 : 6;
primNum = 1;
indxNum = 8*3;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
pld->type[0] = limnPrimitiveTriangles;
pld->icnt[0] = indxNum;
vertIdx = 0;
cnum = sharpEdge ? 4 : 1;
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 0, 1, 1 ); vertIdx++; /* 0 */
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 1, 0, 1 ); vertIdx++; /* 1 */
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 1, 0, 0, 1 ); vertIdx++; /* 2 */
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, -1, 0, 1 ); vertIdx++; /* 3 */
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, -1, 0, 0, 1 ); vertIdx++; /* 4 */
}
for ( ci=0; ci<cnum; ci++ ) {
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 0, -1, 1 ); vertIdx++; /* 5 */
}
vertIdx = 0;
if ( sharpEdge ) {
ELL_3V_SET( pld->indx + vertIdx, 0, 8, 4 ); vertIdx += 3; /* 0 */
ELL_3V_SET( pld->indx + vertIdx, 1, 15, 9 ); vertIdx += 3; /* 1 */
ELL_3V_SET( pld->indx + vertIdx, 2, 16, 12 ); vertIdx += 3; /* 2 */
ELL_3V_SET( pld->indx + vertIdx, 3, 6, 19 ); vertIdx += 3; /* 3 */
ELL_3V_SET( pld->indx + vertIdx, 5, 11, 23 ); vertIdx += 3; /* 4 */
ELL_3V_SET( pld->indx + vertIdx, 10, 14, 20 ); vertIdx += 3; /* 5 */
ELL_3V_SET( pld->indx + vertIdx, 17, 21, 13 ); vertIdx += 3; /* 6 */
ELL_3V_SET( pld->indx + vertIdx, 7, 22, 18 ); vertIdx += 3; /* 7 */
} else {
ELL_3V_SET( pld->indx + vertIdx, 0, 2, 1 ); vertIdx += 3; /* 0 */
ELL_3V_SET( pld->indx + vertIdx, 0, 3, 2 ); vertIdx += 3; /* 1 */
ELL_3V_SET( pld->indx + vertIdx, 0, 4, 3 ); vertIdx += 3; /* 2 */
ELL_3V_SET( pld->indx + vertIdx, 0, 1, 4 ); vertIdx += 3; /* 3 */
ELL_3V_SET( pld->indx + vertIdx, 1, 2, 5 ); vertIdx += 3; /* 4 */
ELL_3V_SET( pld->indx + vertIdx, 2, 3, 5 ); vertIdx += 3; /* 5 */
ELL_3V_SET( pld->indx + vertIdx, 4, 5, 3 ); vertIdx += 3; /* 6 */
ELL_3V_SET( pld->indx + vertIdx, 1, 5, 4 ); vertIdx += 3; /* 7 */
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
if ( sharpEdge ) {
float cn;
cn = AIR_CAST( float, 1.0/sqrt( 3 ) );
/* 0 */
ELL_3V_SET( pld->norm + 3*0, cn, cn, cn );
ELL_3V_SET( pld->norm + 3*8, cn, cn, cn );
ELL_3V_SET( pld->norm + 3*4, cn, cn, cn );
/* 1 */
ELL_3V_SET( pld->norm + 3*1, cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*15, cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*9, cn, -cn, cn );
/* 2 */
ELL_3V_SET( pld->norm + 3*2, -cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*16, -cn, -cn, cn );
ELL_3V_SET( pld->norm + 3*12, -cn, -cn, cn );
/* 3 */
ELL_3V_SET( pld->norm + 3*3, -cn, cn, cn );
ELL_3V_SET( pld->norm + 3*6, -cn, cn, cn );
ELL_3V_SET( pld->norm + 3*19, -cn, cn, cn );
/* 4 */
ELL_3V_SET( pld->norm + 3*5, cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*11, cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*23, cn, cn, -cn );
/* 5 */
ELL_3V_SET( pld->norm + 3*10, cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*14, cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*20, cn, -cn, -cn );
/* 6 */
ELL_3V_SET( pld->norm + 3*17, -cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*21, -cn, -cn, -cn );
ELL_3V_SET( pld->norm + 3*13, -cn, -cn, -cn );
/* 7 */
ELL_3V_SET( pld->norm + 3*7, -cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*22, -cn, cn, -cn );
ELL_3V_SET( pld->norm + 3*18, -cn, cn, -cn );
} else {
ELL_3V_SET( pld->norm + 3*0, 0, 0, 1 ); /* 0 */
ELL_3V_SET( pld->norm + 3*1, 0, 1, 0 ); /* 1 */
ELL_3V_SET( pld->norm + 3*2, 1, 0, 0 ); /* 2 */
ELL_3V_SET( pld->norm + 3*3, 0, -1, 0 ); /* 3 */
ELL_3V_SET( pld->norm + 3*4, -1, 0, 0 ); /* 4 */
ELL_3V_SET( pld->norm + 3*5, 0, 0, -1 ); /* 5 */
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
int
462 limnPolyDataCylinder( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int thetaRes, int sharpEdge ) {
static const char me[]="limnPolyDataCylinder";
unsigned int vertNum, primNum, primIdx, indxNum, thetaIdx, vertIdx, blah;
double theta, cth, sth, sq2;
/* sanity bounds */
thetaRes = AIR_MAX( 3, thetaRes );
vertNum = sharpEdge ? 4*thetaRes : 2*thetaRes;
primNum = 3;
indxNum = 2*thetaRes + 2*( thetaRes+1 ); /* 2 fans + 1 strip */
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
vertIdx = 0;
for ( blah=0; blah < ( sharpEdge ? 2u : 1u ); blah++ ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
cos( theta ), sin( theta ), 1, 1 );
/*
fprintf( stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx,
( pld->xyzw + 4*vertIdx )[0],
( pld->xyzw + 4*vertIdx )[1],
( pld->xyzw + 4*vertIdx )[2] );
*/
++vertIdx;
}
}
for ( blah=0; blah < ( sharpEdge ? 2u : 1u ); blah++ ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
cos( theta ), sin( theta ), -1, 1 );
/*
fprintf( stderr, "!%s: vert[%u] = %g %g %g\n", me, vertIdx,
( pld->xyzw + 4*vertIdx )[0],
( pld->xyzw + 4*vertIdx )[1],
( pld->xyzw + 4*vertIdx )[2] );
*/
++vertIdx;
}
}
primIdx = 0;
vertIdx = 0;
/* fan on top */
/* fprintf( stderr, "!%s: fan on top:\n", me ); */
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
/* fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx, thetaIdx ); */
pld->indx[vertIdx++] = thetaIdx;
}
pld->type[primIdx] = limnPrimitiveTriangleFan;
pld->icnt[primIdx] = thetaRes;
primIdx++;
/* single strip around */
/* fprintf( stderr, "!%s: strip around:\n", me ); */
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
/*
fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx,
( sharpEdge ? 1 : 0 )*thetaRes + thetaIdx );
*/
pld->indx[vertIdx++] = ( sharpEdge ? 1 : 0 )*thetaRes + thetaIdx;
/*
fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx,
( sharpEdge ? 2 : 1 )*thetaRes + thetaIdx );
*/
pld->indx[vertIdx++] = ( sharpEdge ? 2 : 1 )*thetaRes + thetaIdx;
}
/*
fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx,
( sharpEdge ? 1 : 0 )*thetaRes );
*/
pld->indx[vertIdx++] = ( sharpEdge ? 1 : 0 )*thetaRes;
/*
fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx,
( sharpEdge ? 2 : 1 )*thetaRes );
*/
pld->indx[vertIdx++] = ( sharpEdge ? 2 : 1 )*thetaRes;
pld->type[primIdx] = limnPrimitiveTriangleStrip;
pld->icnt[primIdx] = 2*( thetaRes+1 );
primIdx++;
/* fan on bottom */
/* fprintf( stderr, "!%s: fan on bottom:\n", me ); */
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
/*
fprintf( stderr, "!%s: indx[%u] = %u\n", me, vertIdx,
( sharpEdge ? 3 : 1 )*thetaRes + thetaIdx );
*/
pld->indx[vertIdx++] = ( sharpEdge ? 3 : 1 )*thetaRes + thetaIdx;
}
pld->type[primIdx] = limnPrimitiveTriangleFan;
pld->icnt[primIdx] = thetaRes;
primIdx++;
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
sq2 = sqrt( 2.0 );
if ( sharpEdge ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
cth = cos( theta );
sth = sin( theta );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 0*thetaRes ),
float, 0, 0, 1 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 1*thetaRes ),
float, cth, sth, 0 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 2*thetaRes ),
float, cth, sth, 0 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 3*thetaRes ),
float, 0, 0, -1 );
}
} else {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
cth = sq2*cos( theta );
sth = sq2*sin( theta );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 0*thetaRes ), float,
cth, sth, sq2 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 1*thetaRes ), float,
cth, sth, -sq2 );
}
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
if ( sharpEdge ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 1 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 0*thetaRes ), float, theta, 0 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 1*thetaRes ), float, theta, 0 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 2*thetaRes ), float, theta, 1 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 3*thetaRes ), float, theta, 1 );
}
} else {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 1 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 0*thetaRes ), float, theta, 0 );
ELL_2V_SET_TT( pld->tex2 + 2*( thetaIdx + 1*thetaRes ), float, theta, 1 );
}
}
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
float xx, yy, tang[3], tlen;
if ( sharpEdge ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
xx = ( pld->xyzw + 4*( thetaIdx + 0*thetaRes ) )[0];
yy = ( pld->xyzw + 4*( thetaIdx + 0*thetaRes ) )[1];
ELL_3V_SET( tang, -yy, xx, 0.0 );
ELL_3V_NORM_TT( tang, float, tang, tlen );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 0*thetaRes ), tang );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 1*thetaRes ), tang );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 2*thetaRes ), tang );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 3*thetaRes ), tang );
}
} else {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
xx = ( pld->xyzw + 4*( thetaIdx + 0*thetaRes ) )[0];
yy = ( pld->xyzw + 4*( thetaIdx + 0*thetaRes ) )[1];
ELL_3V_SET( tang, -yy, xx, 0.0 );
ELL_3V_NORM_TT( tang, float, tang, tlen );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 0*thetaRes ), tang );
ELL_3V_COPY( pld->tang + 3*( thetaIdx + 1*thetaRes ), tang );
}
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
int
645 limnPolyDataCone( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int thetaRes, int sharpEdge ) {
static const char me[]="limnPolyDataCone";
unsigned int vertNum, primNum, primIdx, indxNum, thetaIdx, vertIdx, blah;
double theta, cth, sth;
/* sanity bounds */
thetaRes = AIR_MAX( 3, thetaRes );
vertNum = sharpEdge ? 3*thetaRes : 1 + thetaRes;
primNum = 2;
indxNum = thetaRes + 2*( thetaRes+1 ); /* 1 fans + 1 strip */
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
/* top point( s ) */
vertIdx = 0;
if ( sharpEdge ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
0, 0, 1, 1 );
++vertIdx;
}
} else {
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
0, 0, 1, 1 );
++vertIdx;
}
/* bottom edge */
for ( blah=0; blah < ( sharpEdge ? 2u : 1u ); blah++ ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
cos( theta ), sin( theta ), -1, 1 );
++vertIdx;
}
}
primIdx = 0;
vertIdx = 0;
/* single strip around */
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = sharpEdge ? thetaIdx : 0;
pld->indx[vertIdx++] = ( sharpEdge ? thetaRes : 1 ) + thetaIdx;
}
pld->indx[vertIdx++] = 0;
pld->indx[vertIdx++] = sharpEdge ? thetaRes : 1;
pld->type[primIdx] = limnPrimitiveTriangleStrip;
pld->icnt[primIdx] = 2*( thetaRes+1 );
primIdx++;
/* fan on bottom */
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = ( sharpEdge ? 2*thetaRes : 1 ) + thetaIdx;
}
pld->type[primIdx] = limnPrimitiveTriangleFan;
pld->icnt[primIdx] = thetaRes;
primIdx++;
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
double isq3;
isq3 = 1/sqrt( 3.0 );
if ( sharpEdge ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
cth = cos( theta );
sth = sin( theta );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 0*thetaRes ),
float, cth*isq3, sth*isq3, isq3 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 1*thetaRes ),
float, cth*isq3, sth*isq3, isq3 );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 2*thetaRes ),
float, 0, 0, -1 );
}
} else {
ELL_3V_SET_TT( pld->norm + 3*( 0 ), float,
0, 0, 1 );
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
cth = cos( theta );
sth = sin( theta );
ELL_3V_SET_TT( pld->norm + 3*( thetaIdx + 1 ),
float, cth*isq3, sth*isq3, -isq3 ); /* close enough */
}
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
/*
******** limnPolyDataSuperquadric
**
** makes a superquadric parameterized around the Z axis
*/
int
750 limnPolyDataSuperquadric( limnPolyData *pld,
unsigned int infoBitFlag,
float alpha, float beta,
unsigned int thetaRes, unsigned int phiRes ) {
static const char me[]="limnPolyDataSuperquadric";
unsigned int vertIdx, vertNum, fanNum, stripNum, primNum, indxNum,
thetaIdx, phiIdx, primIdx;
double theta, phi;
/* sanity bounds */
thetaRes = AIR_MAX( 3u, thetaRes );
phiRes = AIR_MAX( 2u, phiRes );
alpha = AIR_MAX( 0.00001f, alpha );
beta = AIR_MAX( 0.00001f, beta );
vertNum = 2 + thetaRes*( phiRes-1 );
fanNum = 2;
stripNum = phiRes-2;
primNum = fanNum + stripNum;
indxNum = ( thetaRes+2 )*fanNum + 2*( thetaRes+1 )*stripNum;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
vertIdx = 0;
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 0, 1, 1 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_SET( pld->norm + 3*vertIdx, 0, 0, 1 );
}
++vertIdx;
for ( phiIdx=1; phiIdx<phiRes; phiIdx++ ) {
double cost, sint, cosp, sinp;
phi = AIR_AFFINE( 0, phiIdx, phiRes, 0, AIR_PI );
cosp = cos( phi );
sinp = sin( phi );
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0, 2*AIR_PI );
cost = cos( theta );
sint = sin( theta );
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float,
airSgnPow( cost, alpha ) * airSgnPow( sinp, beta ),
airSgnPow( sint, alpha ) * airSgnPow( sinp, beta ),
airSgnPow( cosp, beta ),
1.0 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
if ( 1 == alpha && 1 == beta ) {
ELL_3V_COPY( pld->norm + 3*vertIdx, pld->xyzw + 4*vertIdx );
} else {
ELL_3V_SET_TT( pld->norm + 3*vertIdx, float,
2*airSgnPow( cost, 2-alpha )*airSgnPow( sinp, 2-beta )/beta,
2*airSgnPow( sint, 2-alpha )*airSgnPow( sinp, 2-beta )/beta,
2*airSgnPow( cosp, 2-beta )/beta );
}
}
++vertIdx;
}
}
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 0, -1, 1 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_SET( pld->norm + 3*vertIdx, 0, 0, -1 );
}
++vertIdx;
/* triangle fan at top */
vertIdx = 0;
primIdx = 0;
pld->indx[vertIdx++] = 0;
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = thetaIdx + 1;
}
pld->indx[vertIdx++] = 1;
pld->type[primIdx] = limnPrimitiveTriangleFan;
pld->icnt[primIdx++] = thetaRes + 2;
/* tristrips around */
for ( phiIdx=1; phiIdx<phiRes-1; phiIdx++ ) {
/*
fprintf( stderr, "!%s: prim[%u] = vert[%u] =", me, primIdx, vertIdx );
*/
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
/*
fprintf( stderr, " [%u %u] %u %u",
vertIdx, vertIdx + 1,
( phiIdx-1 )*thetaRes + thetaIdx + 1,
phiIdx*thetaRes + thetaIdx + 1 );
*/
pld->indx[vertIdx++] = ( phiIdx-1 )*thetaRes + thetaIdx + 1;
pld->indx[vertIdx++] = phiIdx*thetaRes + thetaIdx + 1;
}
/*
fprintf( stderr, " [%u %u] %u %u ( %u verts )\n",
vertIdx, vertIdx + 1,
( phiIdx-1 )*thetaRes + 1,
phiIdx*thetaRes + 1, 2*( thetaRes+1 ) );
*/
pld->indx[vertIdx++] = ( phiIdx-1 )*thetaRes + 1;
pld->indx[vertIdx++] = phiIdx*thetaRes + 1;
pld->type[primIdx] = limnPrimitiveTriangleStrip;
pld->icnt[primIdx++] = 2*( thetaRes+1 );
}
/* triangle fan at bottom */
pld->indx[vertIdx++] = vertNum-1;
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = thetaRes*( phiRes-2 ) + thetaRes - thetaIdx;
}
pld->indx[vertIdx++] = thetaRes*( phiRes-2 ) + thetaRes;
pld->type[primIdx] = limnPrimitiveTriangleFan;
pld->icnt[primIdx++] = thetaRes + 2;
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
/*
******** limnPolyDataSpiralBetterquadric
**
** puts a "betterquadric" into a single spiral triangle strip
*/
int
876 limnPolyDataSpiralBetterquadric( limnPolyData *pld,
unsigned int infoBitFlag,
float alpha, float beta, float cee,
float minRad,
unsigned int thetaRes, unsigned int phiRes ) {
static const char me[]="limnPolyDataSpiralBetterquadric";
unsigned int vertIdx, vertNum, indxNum, thetaIdx, phiIdx;
/* sanity bounds */
thetaRes = AIR_MAX( 3u, thetaRes );
phiRes = AIR_MAX( 2u, phiRes );
alpha = AIR_MAX( 0.00001f, alpha );
beta = AIR_MAX( 0.00001f, beta );
vertNum = thetaRes*phiRes + 1;
indxNum = 2*thetaRes*( phiRes+1 ) - 2;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, 1 ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
vertIdx = 0;
for ( phiIdx=0; phiIdx<phiRes; phiIdx++ ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
double cost, sint, cosp, sinp, xx, yy, zz;
double phi = ( AIR_AFFINE( 0, phiIdx, phiRes, 0, AIR_PI )
+ AIR_AFFINE( 0, thetaIdx, thetaRes, 0, AIR_PI )/phiRes );
double theta = AIR_AFFINE( 0, thetaIdx, thetaRes, 0.0, 2*AIR_PI );
cosp = cos( phi );
sinp = sin( phi );
cost = cos( theta );
sint = sin( theta );
xx = airSgnPow( cost, alpha ) * airSgnPow( sinp, beta );
yy = airSgnPow( sint, alpha ) * airSgnPow( sinp, beta );
zz = airSgnPow( cosp, beta );
if ( cee != beta ) {
/* expand profile along y axis to match having beta=cee */
double yp, ymax;
yp = airSgnPow( sin( acos( airSgnPow( zz, 1/cee ) ) ), cee );
ymax = airSgnPow( sinp, beta );
if ( ymax ) {
yy *= yp/ymax;
}
}
ELL_4V_SET_TT( pld->xyzw + 4*vertIdx, float, xx, yy, zz, 1.0 );
if ( minRad > 0.0 ) {
/* add thickness to small radius */
double rr;
xx = ( pld->xyzw + 4*vertIdx )[0];
yy = ( pld->xyzw + 4*vertIdx )[1];
rr = sqrt( xx*xx + yy*yy );
if ( rr ) {
( pld->xyzw + 4*vertIdx )[0] *= AIR_CAST( float, AIR_AFFINE( 0, rr, 1, minRad/rr, 1/rr ) );
( pld->xyzw + 4*vertIdx )[1] *= AIR_CAST( float, AIR_AFFINE( 0, rr, 1, minRad/rr, 1/rr ) );
}
}
if ( ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag )
|| ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) ) {
double norm[3], nlen;
if ( 1 == alpha && 1 == beta ) {
ELL_3V_COPY( norm, pld->xyzw + 4*vertIdx );
} else {
if ( !vertIdx ) {
ELL_3V_SET( norm, 0.0, 0.0, 1.0 );
} else {
ELL_3V_SET( norm,
( 2*airSgnPow( cost, 2-alpha )*airSgnPow( sinp, 2-beta )/beta ),
( 2*airSgnPow( sint, 2-alpha )*airSgnPow( sinp, 2-beta )/beta ),
2*airSgnPow( cosp, 2-beta )/beta );
}
}
if ( ( nlen = ELL_3V_LEN( norm ) ) ) {
ELL_3V_SCALE( norm, 1.0/nlen, norm );
} else {
ELL_3V_SET( norm, 0.0, 0.0, 1.0 );
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_COPY_TT( pld->norm + 3*vertIdx, float, norm );
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
double tang[3], tlen;
ELL_3V_SET( tang, -norm[1], norm[0], 0.0 );
if ( ( tlen = ELL_3V_LEN( tang ) ) ) {
ELL_3V_SCALE( tang, 1.0/tlen, tang );
} else {
ELL_3V_SET( tang, 1.0, 0.0, 0.0 );
}
ELL_3V_COPY_TT( pld->tang + 3*vertIdx, float, tang );
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
ELL_2V_SET_TT( pld->tex2 + 2*vertIdx, float,
AIR_AFFINE( 0.0, theta, 2*AIR_PI, 0.0, 1.0 ),
AIR_AFFINE( 0.0, phi, AIR_PI, 0.0, 1.0 ) );
}
++vertIdx;
}
}
ELL_4V_SET( pld->xyzw + 4*vertIdx, 0, 0, -1, 1 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_SET( pld->norm + 3*vertIdx, 0.0, 0.0, -1.0 );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
ELL_2V_SET( pld->tex2 + 2*vertIdx, 0.5, 1.0 );
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
ELL_3V_SET( pld->tang + 3*vertIdx, 1.0, 0.0, 0.0 );
}
++vertIdx;
/* single triangle strip */
pld->type[0] = limnPrimitiveTriangleStrip;
pld->icnt[0] = indxNum;
vertIdx = 0;
for ( thetaIdx=1; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = 0;
pld->indx[vertIdx++] = thetaIdx;
}
for ( phiIdx=0; phiIdx<phiRes-1; phiIdx++ ) {
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = ( ( phiIdx + 0 ) * thetaRes ) + thetaIdx;
pld->indx[vertIdx++] = ( ( phiIdx + 1 ) * thetaRes ) + thetaIdx;
}
}
for ( thetaIdx=0; thetaIdx<thetaRes; thetaIdx++ ) {
pld->indx[vertIdx++] = ( phiRes - 1 )*thetaRes + thetaIdx;
pld->indx[vertIdx++] = ( phiRes - 0 )*thetaRes;
}
#if 0
if ( ( cee != beta || minRad > 0.0 )
&& ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) ) {
/* have deformed object in some way that confounds analytic normals */
if ( limnPolyDataVertexNormals( pld ) ) {
biffAddf( LIMN, "%s: trouble getting normals", me ); return 1;
}
}
#endif
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( vertIdx=0; vertIdx<pld->rgbaNum; vertIdx++ ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
}
return 0;
}
/*
******** limnPolyDataSpiralSuperquadric
**
** puts a superquadric into a single spiral triangle strip
*/
int
1028 limnPolyDataSpiralSuperquadric( limnPolyData *pld,
unsigned int infoBitFlag,
float alpha, float beta,
unsigned int thetaRes, unsigned int phiRes ) {
static const char me[]="limnPolyDataSpiralSuperquadric";
if ( limnPolyDataSpiralBetterquadric( pld, infoBitFlag,
alpha, beta, beta, 0.0,
thetaRes, phiRes ) ) {
biffAddf( LIMN, "%s: trouble", me ); return 1;
}
return 0;
}
/*
******** limnPolyDataPolarSphere
**
** makes a unit sphere, centered at the origin, parameterized around Z axis
*/
int
1048 limnPolyDataPolarSphere( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int thetaRes, unsigned int phiRes ) {
static const char me[]="limnPolyDataPolarSphere";
if ( limnPolyDataSuperquadric( pld, infoBitFlag,
1.0, 1.0, thetaRes, phiRes ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
return 0;
}
int
1062 limnPolyDataSpiralSphere( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int thetaRes,
unsigned int phiRes ) {
static const char me[]="limnPolyDataSpiralSphere";
if ( limnPolyDataSpiralSuperquadric( pld, infoBitFlag,
1.0, 1.0, thetaRes, phiRes ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
return 0;
}
/* Geometry for an icosahedron */
#define ICO_ONE 0.5257311121f
#define ICO_TAU 0.8506508084f
static float icovertices[36] = {
0.0f, ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE, -ICO_TAU,
0.0f, -ICO_ONE, ICO_TAU, 0.0f, ICO_ONE, -ICO_TAU,
ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE, -ICO_TAU, 0.0f,
ICO_ONE, -ICO_TAU, 0.0f, -ICO_ONE, ICO_TAU, 0.0f,
ICO_TAU, 0.0f, ICO_ONE, -ICO_TAU, 0.0f, -ICO_ONE,
-ICO_TAU, 0.0f, ICO_ONE, ICO_TAU, 0.0f, -ICO_ONE
};
#undef ICO_ONE
#undef ICO_TAU
static unsigned int icoedges[60] = {
0, 2, 1, 3, 0, 4, 1, 5, 0, 8, 1, 9, 0, 10, 1, 11, /* 0-7 */
0, 7, 1, 6, 2, 6, 3, 7, 2, 8, 3, 9, 2, 10, 3, 11, /* 8-15 */
2, 5, 3, 4, 4, 8, 5, 9, 4, 7, 5, 6, 4, 11, 5, 10, /* 16-23*/
6, 8, 7, 9, 6, 11, 7, 10, 8, 11, 9, 10 /* 24-29 */
};
static unsigned int icofaces[60] = {
0, 4, 12, 1, 5, 13, 0, 6, 14, 1, 7, 15,
6, 8, 27, 7, 9, 26, 2, 4, 18, 3, 5, 19,
10, 12, 24, 11, 13, 25, 14, 16, 23, 15, 17, 22,
2, 8, 20, 3, 9, 21, 10, 16, 21, 11, 17, 20,
24, 26, 28, 25, 27, 29, 18, 22, 28, 19, 23, 29
};
/*
******** limnPolyDataIcoSphere
**
** Makes a unit sphere, centered at the origin, by refining an icosahedron
** level times. Each refinement step subdivides each edge at its center,
** turning each triangle into four smaller ones.
**
** In the output, vertex 2*i and 2*i+1 are antipodal points, which allows for
** a more efficient implementation of operations that produce the same or
** a mirrored result for antipodal points ( e.g., tensor glyphs ).
*/
int
1118 limnPolyDataIcoSphere( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int level ) {
static const char me[]="limnPolyDataIcoSphere";
unsigned int vertNum=12, edgeNum=30, faceNum=20, center;
float *verts, *xyzwp, *vertp;
unsigned char *rgbap;
unsigned int *edges, *faces;
unsigned int i, e, f; /* loop counters */
airArray *mop; /* free memory in case of allocation error */
/* sanity checks */
if ( !pld ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
/* x/y/z positions */
verts = ( float * ) malloc ( sizeof( float )*12*3 );
if ( verts==NULL ) goto error_and_exit;
airMopAdd( mop, verts, airFree, airMopAlways );
memcpy( verts, icovertices, sizeof( float )*12*3 );
/* endpoints for each edge */
edges = ( unsigned int * ) malloc ( sizeof( unsigned int )*30*2 );
if ( edges==NULL ) goto error_and_exit;
airMopAdd( mop, edges, airFree, airMopAlways );
memcpy( edges, icoedges, sizeof( unsigned int )*60 );
/* vertices of each face */
faces = ( unsigned int * ) malloc ( sizeof( unsigned int )*20*3 );
if ( faces==NULL ) goto error_and_exit;
airMopAdd( mop, faces, airFree, airMopAlways );
memcpy( faces, icofaces, sizeof( unsigned int )*60 );
for ( i=0; i<level; ++i ) {
/* subdivision step */
unsigned int nvertNum=vertNum+edgeNum;
unsigned int nedgeNum=2*edgeNum+3*faceNum;
unsigned int nfaceNum=4*faceNum;
float *newverts;
unsigned int *newedges, *newfaces;
newverts = ( float * ) malloc( sizeof( float )*3*nvertNum );
if ( newverts==NULL ) goto error_and_exit;
airMopAdd( mop, newverts, airFree, airMopAlways );
newedges = ( unsigned int * ) malloc( sizeof( unsigned int )*2*nedgeNum );
if ( newedges==NULL ) goto error_and_exit;
airMopAdd( mop, newedges, airFree, airMopAlways );
newfaces = ( unsigned int * ) malloc( sizeof( unsigned int )*3*nfaceNum );
if ( newfaces==NULL ) goto error_and_exit;
airMopAdd( mop, newfaces, airFree, airMopAlways );
memcpy( newverts, verts, sizeof( float )*3*vertNum );
for ( e=0; e<edgeNum; e+=2 ) { /* split both edge and anti-edge */
float norm;
ELL_3V_ADD2( newverts+3*( vertNum+e ), verts+3*( edges[2*e] ),
verts+3*( edges[2*e+1] ) );
/* project new vertex to unit sphere */
ELL_3V_NORM_TT( newverts+3*( vertNum+e ), float, newverts+3*( vertNum+e ), norm );
ELL_3V_SCALE_TT( newverts+3*( vertNum+e+1 ), float, -1.0, newverts+3*( vertNum+e ) );
/* split the edges such that anti-edge follows edge */
newedges[4*e]=edges[2*e];
newedges[4*e+1]=vertNum+e;
newedges[4*e+2]=edges[2*e+2];
newedges[4*e+3]=vertNum+e+1;
newedges[4*e+4]=vertNum+e;
newedges[4*e+5]=edges[2*e+1];
newedges[4*e+6]=vertNum+e+1;
newedges[4*e+7]=edges[2*e+3];
}
for ( f=0; f<faceNum; f+=2 ) { /* split both face and anti-face */
unsigned int oldedge11=faces[3*f], oldedge12=faces[3*f+1],
oldedge13=faces[3*f+2], oldedge21=faces[3*f+3],
oldedge22=faces[3*f+4], oldedge23=faces[3*f+5];
unsigned int eidx=2*edgeNum+3*f; /* index of the first edge to add */
char pol11=0, pol12=0, pol13=0, pol21=0, pol22=0, pol23=0; /* polarity */
/* add three edges per face - anti-edge has to follow edge! */
newedges[2*eidx]=newedges[2*eidx+9]=vertNum+oldedge11;
newedges[2*eidx+1]=newedges[2*eidx+4]=vertNum+oldedge12;
newedges[2*eidx+5]=newedges[2*eidx+8]=vertNum+oldedge13;
newedges[2*eidx+2]=newedges[2*eidx+11]=vertNum+oldedge21;
newedges[2*eidx+3]=newedges[2*eidx+6]=vertNum+oldedge22;
newedges[2*eidx+7]=newedges[2*eidx+10]=vertNum+oldedge23;
/* split the faces - we do not have directed half-edges!
* determine the "polarity" of the edges ( 0 forward / 2 backward ) */
if ( edges[2*oldedge11+1]==edges[2*oldedge13+1] ||
edges[2*oldedge11+1]==edges[2*oldedge13] )
pol11=2;
if ( edges[2*oldedge12+1]==edges[2*oldedge11+1] ||
edges[2*oldedge12+1]==edges[2*oldedge11] )
pol12=2;
if ( edges[2*oldedge13+1]==edges[2*oldedge12+1] ||
edges[2*oldedge13+1]==edges[2*oldedge12] )
pol13=2;
if ( edges[2*oldedge21+1]==edges[2*oldedge23+1] ||
edges[2*oldedge21+1]==edges[2*oldedge23] )
pol21=2;
if ( edges[2*oldedge22+1]==edges[2*oldedge21+1] ||
edges[2*oldedge22+1]==edges[2*oldedge21] )
pol22=2;
if ( edges[2*oldedge23+1]==edges[2*oldedge22+1] ||
edges[2*oldedge23+1]==edges[2*oldedge22] )
pol23=2;
newfaces[12*f] = 2*oldedge11-( oldedge11%2 )+pol11; /* bottom/left */
newfaces[12*f+1] = eidx+4;
newfaces[12*f+2] = 2*oldedge13-( oldedge13%2 )+2-pol13;
newfaces[12*f+3] = 2*oldedge21-( oldedge21%2 )+pol21; /* anti */
newfaces[12*f+4] = eidx+5;
newfaces[12*f+5] = 2*oldedge23-( oldedge23%2 )+2-pol23;
newfaces[12*f+6] = 2*oldedge11-( oldedge11%2 )+2-pol11; /* bottom/right */
newfaces[12*f+7] = 2*oldedge12-( oldedge12%2 )+pol12;
newfaces[12*f+8] = eidx;
newfaces[12*f+9] = 2*oldedge21-( oldedge21%2 )+2-pol21; /* anti */
newfaces[12*f+10]= 2*oldedge22-( oldedge22%2 )+pol22;
newfaces[12*f+11]= eidx+1;
newfaces[12*f+12]= 2*oldedge12-( oldedge12%2 )+2-pol12; /* top */
newfaces[12*f+13]= 2*oldedge13-( oldedge13%2 )+pol13;
newfaces[12*f+14]= eidx+2;
newfaces[12*f+15]= 2*oldedge22-( oldedge22%2 )+2-pol22; /* anti */
newfaces[12*f+16]= 2*oldedge23-( oldedge23%2 )+pol23;
newfaces[12*f+17]= eidx+3;
newfaces[12*f+18]= eidx; /* center */
newfaces[12*f+19]= eidx+2;
newfaces[12*f+20]= eidx+4;
newfaces[12*f+21]= eidx+1; /* anti */
newfaces[12*f+22]= eidx+3;
newfaces[12*f+23]= eidx+5;
}
/* make subdivided mesh the current one */
airMopSub( mop, verts, airFree );
airMopSub( mop, edges, airFree );
airMopSub( mop, faces, airFree );
free( verts ); free( edges ); free( faces );
verts=newverts; edges=newedges; faces=newfaces;
vertNum=nvertNum; edgeNum=nedgeNum; faceNum=nfaceNum;
}
/* done; now copy to a limnPolyData struct */
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, 3*faceNum, 1 ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
xyzwp=pld->xyzw; vertp=verts;
for ( i=0; i<vertNum; i++ ) {
ELL_4V_SET( xyzwp, vertp[0], vertp[1], vertp[2], 1.0 );
xyzwp+=4; vertp+=3;
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
/* normals equal the vertex coordinates */
memcpy( pld->norm, verts, sizeof( float )*3*vertNum );
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
rgbap=pld->rgba; vertp=verts;
for ( i=0; i<vertNum; i++ ) {
ELL_4V_SET_TT( rgbap, unsigned char, 255, 255, 255, 255 );
rgbap+=4; vertp+=3;
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
for ( i=0; i<vertNum; i++ ) {
double rr, xyz[3], phi, theta;
ELL_3V_COPY( xyz, pld->xyzw + 4*i );
rr = sqrt( xyz[0]*xyz[0] + xyz[1]*xyz[1] );
phi = atan2( rr, xyz[2] );
theta = atan2( xyz[1], xyz[0] );
ELL_2V_SET_TT( pld->tex2 + 2*i, float,
AIR_AFFINE( -AIR_PI, theta, AIR_PI, 0.0, 1.0 ),
AIR_AFFINE( 0.0, phi, AIR_PI, 0.0, 1.0 ) );
}
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
for ( i=0; i<vertNum; i++ ) {
double rr, xyz[3], tang[3], len;
ELL_3V_COPY( xyz, pld->xyzw + 4*i );
rr = sqrt( xyz[0]*xyz[0] + xyz[1]*xyz[1] );
if ( rr ) {
ELL_3V_SET( tang, -xyz[1], xyz[0], 0.0 );
ELL_3V_NORM_TT( pld->tang + 3*i, float, tang, len );
} else {
ELL_3V_SET( pld->tang + 3*i, 1.0f, 0.0f, 0.0f );
}
}
}
/* We need to replace reference to edges in faces with references to
* vertices. Make sure that they are ordered CCW */
pld->type[0] = limnPrimitiveTriangles;
pld->icnt[0] = faceNum*3;
for ( f=0; f<faceNum; ++f ) {
unsigned int vertices[3]; /* find the right vertices */
float diff1[3], diff2[3], cross[3];
vertices[0]=edges[2*faces[3*f]];
vertices[1]=edges[2*faces[3*f]+1];
if ( edges[2*faces[3*f+1]]==vertices[0] ||
edges[2*faces[3*f+1]]==vertices[1] )
vertices[2]=edges[2*faces[3*f+1]+1];
else
vertices[2]=edges[2*faces[3*f+1]];
/* put them into correct order */
ELL_3V_SUB( diff1, verts+3*vertices[1], verts+3*vertices[0] );
ELL_3V_SUB( diff2, verts+3*vertices[2], verts+3*vertices[0] );
ELL_3V_CROSS( cross, diff1, diff2 );
pld->indx[3*f]=vertices[0];
if ( ELL_3V_DOT( cross, verts+3*vertices[0] )<0 ) {
pld->indx[3*f+1]=vertices[2];
pld->indx[3*f+2]=vertices[1];
} else {
pld->indx[3*f+1]=vertices[1];
pld->indx[3*f+2]=vertices[2];
}
}
/* re-order the triangles */
center=3*( faceNum/2 );
for ( i=0; i<faceNum/2; i++ ) {
ELL_3V_COPY( faces+3*i, pld->indx+6*i );
ELL_3V_COPY( faces+center+3*i, pld->indx+6*i+3 );
}
airMopAdd( mop, pld->indx, airFree, airMopAlways );
airMopSub( mop, faces, airFree );
pld->indx=faces;
/* done */
airMopOkay( mop );
return 0;
error_and_exit:
biffAddf( LIMN, "%s: memory allocation failed", me );
airMopError( mop );
return 1;
}
int
1350 limnPolyDataPlane( limnPolyData *pld,
unsigned int infoBitFlag,
unsigned int uRes, unsigned int vRes ) {
static const char me[]="limnPolyDataPlane";
unsigned int vertNum, indxNum, primNum, uIdx, vIdx, vertIdx, primIdx;
float uu, vv;
/* sanity */
uRes = AIR_MAX( 2, uRes );
vRes = AIR_MAX( 2, vRes );
vertNum = uRes*vRes;
primNum = vRes-1;
indxNum = primNum*2*uRes;
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum, indxNum, primNum ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
vertIdx = 0;
for ( vIdx=0; vIdx<vRes; vIdx++ ) {
vv = AIR_CAST( float, AIR_AFFINE( 0, vIdx, vRes-1, -1.0, 1.0 ) );
for ( uIdx=0; uIdx<uRes; uIdx++ ) {
uu = AIR_CAST( float, AIR_AFFINE( 0, uIdx, uRes-1, -1.0, 1.0 ) );
ELL_4V_SET( pld->xyzw + 4*vertIdx, uu, vv, 0.0, 1.0 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_SET_TT( pld->norm + 3*vertIdx, float, 0.0, 0.0, 1.0 );
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
ELL_4V_SET( pld->rgba + 4*vertIdx, 255, 255, 255, 255 );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
ELL_2V_SET_TT( pld->tex2 + 2*vertIdx, float, ( uu+1.0 )/2.0, ( vv+1.0 )/2.0 );
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
ELL_3V_SET_TT( pld->tang + 3*vertIdx, float, 1.0, 0.0, 0.0 );
}
++vertIdx;
}
}
vertIdx = 0;
for ( primIdx=0; primIdx<primNum; primIdx++ ) {
for ( uIdx=0; uIdx<uRes; uIdx++ ) {
pld->indx[vertIdx++] = uIdx + uRes*( primIdx+1 );
pld->indx[vertIdx++] = uIdx + uRes*( primIdx );
}
pld->type[primIdx] = limnPrimitiveTriangleStrip;
pld->icnt[primIdx] = 2*uRes;
}
return 0;
}
int
1405 limnPolyDataSquare( limnPolyData *pld, unsigned int infoBitFlag ) {
static const char me[]="limnPolyDataSquare";
if ( limnPolyDataAlloc( pld, infoBitFlag, 4, 4, 1 ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
ELL_4V_SET( pld->xyzw + 4*0, -1.0, -1.0, 0.0, 1.0 );
ELL_4V_SET( pld->xyzw + 4*1, 1.0, -1.0, 0.0, 1.0 );
ELL_4V_SET( pld->xyzw + 4*2, -1.0, 1.0, 0.0, 1.0 );
ELL_4V_SET( pld->xyzw + 4*3, 1.0, 1.0, 0.0, 1.0 );
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
ELL_3V_SET( pld->norm + 3*0, 0.0, 0.0, 1.0 );
ELL_3V_SET( pld->norm + 3*1, 0.0, 0.0, 1.0 );
ELL_3V_SET( pld->norm + 3*2, 0.0, 0.0, 1.0 );
ELL_3V_SET( pld->norm + 3*3, 0.0, 0.0, 1.0 );
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
ELL_4V_SET( pld->rgba + 4*0, 255, 255, 255, 255 );
ELL_4V_SET( pld->rgba + 4*1, 255, 255, 255, 255 );
ELL_4V_SET( pld->rgba + 4*2, 255, 255, 255, 255 );
ELL_4V_SET( pld->rgba + 4*3, 255, 255, 255, 255 );
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
ELL_2V_SET( pld->tex2 + 2*0, 0.0, 1.0 );
ELL_2V_SET( pld->tex2 + 2*1, 1.0, 1.0 );
ELL_2V_SET( pld->tex2 + 2*2, 0.0, 0.0 );
ELL_2V_SET( pld->tex2 + 2*3, 1.0, 0.0 );
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
ELL_3V_SET( pld->tang + 3*0, 1.0, 0.0, 0.0 );
ELL_3V_SET( pld->tang + 3*1, 1.0, 0.0, 0.0 );
ELL_3V_SET( pld->tang + 3*2, 1.0, 0.0, 0.0 );
ELL_3V_SET( pld->tang + 3*3, 1.0, 0.0, 0.0 );
}
pld->type[0] = limnPrimitiveTriangleStrip;
ELL_4V_SET( pld->indx, 0, 1, 2, 3 );
pld->icnt[0] = 4;
return 0;
}
int
1448 limnPolyDataSuperquadric2D( limnPolyData *pld,
unsigned int infoBitFlag,
float alpha, unsigned int res ) {
static const char me[]="limnPolyDataSuperquadric2D";
unsigned int i, vertNum;
float phi;
vertNum = res+1; /* initial vertex at origin */
if ( limnPolyDataAlloc( pld, infoBitFlag, vertNum,
vertNum+1, /* last index wraps around */
1 ) ) {
biffAddf( LIMN, "%s: couldn't allocate output", me );
return 1;
}
/* initial vertex at origin */
ELL_4V_SET( pld->xyzw + 4*0, 0.0, 0.0, 0.0, 1.0 );
/* printf( "!%s: xyzw[0] = %g %g %g %g\n", me, ( pld->xyzw + 4*0 )[0], ( pld->xyzw + 4*0 )[1], ( pld->xyzw + 4*0 )[2], ( pld->xyzw + 4*0 )[3] ); */
for ( i=1; i<vertNum; i++ ) {
phi = AIR_AFFINE( 1, i, vertNum, 0, 2*AIR_PI );
ELL_4V_SET( pld->xyzw + 4*i,
airSgnPow( cos( phi ), alpha ),
airSgnPow( sin( phi ), alpha ),
0.0, 1.0 );
/* printf( "!%s: xyzw[%u] = %g %g %g %g\n", me, i , ( pld->xyzw + 4*i )[0], ( pld->xyzw + 4*i )[1], ( pld->xyzw + 4*i )[2], ( pld->xyzw + 4*i )[3] ); */
}
if ( ( 1 << limnPolyDataInfoNorm ) & infoBitFlag ) {
for ( i=0; i<vertNum; i++ ) {
ELL_3V_SET( pld->norm + 3*i, 0.0, 0.0, 1.0 );
}
}
if ( ( 1 << limnPolyDataInfoRGBA ) & infoBitFlag ) {
for ( i=0; i<vertNum; i++ ) {
ELL_4V_SET( pld->rgba + 4*i, 255, 255, 255, 255 );
}
}
if ( ( 1 << limnPolyDataInfoTex2 ) & infoBitFlag ) {
/* punting */
ELL_2V_COPY( pld->tex2 + 2*i, pld->xyzw + 4*i );
}
if ( ( 1 << limnPolyDataInfoTang ) & infoBitFlag ) {
/* punting */
ELL_3V_SET( pld->tang + 3*i, 0.0, 0.0, 0.0 );
}
pld->type[0] = limnPrimitiveTriangleFan;
for ( i=0; i<vertNum; i++ ) {
pld->indx[i] = i;
/* printf( "!%s: idx[%u] = %u\n", me, i, pld->indx[i] ); */
}
/* very last index loops around to first non-origin point */
pld->indx[i] = 1;
/* printf( "!%s: idx[%u] = %u\n", me, i, pld->indx[i] ); */
pld->icnt[0] = vertNum+1;
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
/*
** "16simple": 16 bit representation based on
** <http://www.gamedev.net/reference/articles/article1252.asp>
**
** info: [z sign] [y sign] [x sign] [y component] [x component]
** bits: 1 1 1 7 6
**
** despite appearances, this is isotropic in X and Y
*/
void
38 _limnQN16simple_QNtoV_f( float *vec, unsigned int qn ) {
int xi, yi;
float x, y, z, n;
xi = qn & 0x3F;
yi = ( qn >> 6 ) & 0x7F;
if ( xi + yi >= 127 ) {
xi = 127 - xi;
yi = 127 - yi;
}
x = xi/126.0f;
y = yi/126.0f;
z = 1.0f - x - y;
x = ( qn & 0x2000 ) ? -x : x;
y = ( qn & 0x4000 ) ? -y : y;
z = ( qn & 0x8000 ) ? -z : z;
n = AIR_CAST( float, 1.0/sqrt( x*x + y*y + z*z ) );
vec[0] = x*n;
vec[1] = y*n;
vec[2] = z*n;
}
unsigned int
61 _limnQN16simple_VtoQN_f( const float *vec ) {
float x, y, z, L, w;
unsigned int sgn = 0, xi, yi;
x = vec[0];
y = vec[1];
z = vec[2];
if ( x < 0 ) {
sgn |= 0x2000;
x = -x;
}
if ( y < 0 ) {
sgn |= 0x4000;
y = -y;
}
if ( z < 0 ) {
sgn |= 0x8000;
z = -z;
}
L = x + y + z;
if ( !L ) {
return 0;
}
w = 126.0f/L;
xi = AIR_CAST( unsigned int, x*w );
yi = AIR_CAST( unsigned int, y*w );
if ( xi >= 64 ) {
xi = 127 - xi;
yi = 127 - yi;
}
return sgn | ( yi << 6 ) | xi;
}
/* ---------------------------------------------------------------- */
void
97 _limnQN16border1_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
float u, v, x, y, z, n;
ui = qn & 0xFF;
vi = qn >> 8;
u = AIR_CAST( float, AIR_AFFINE( 0.5, ui, 254.5, -0.5, 0.5 ) );
v = AIR_CAST( float, AIR_AFFINE( 0.5, vi, 254.5, -0.5, 0.5 ) );
x = u + v;
y = u - v;
z = 1 - AIR_ABS( x ) - AIR_ABS( y );
z *= AIR_CAST( int, ( ( ui&1 ) ^ ( vi&1 ) ) << 1 ) - 1;
n = AIR_CAST( float, 1.0/sqrt( x*x + y*y + z*z ) );
vec[0] = x*n;
vec[1] = y*n;
vec[2] = z*n;
}
unsigned int
116 _limnQN16border1_VtoQN_f( const float *vec ) {
float L, u, v, x, y, z;
unsigned int ui, vi, zi;
char me[]="limnQNVto16PB1";
x = vec[0];
y = vec[1];
z = vec[2];
L = AIR_ABS( x ) + AIR_ABS( y ) + AIR_ABS( z );
if ( !L ) {
return 0;
}
x /= L;
y /= L;
u = x + y;
v = x - y;
ui = airIndex( -1, u, 1, 254 ); ui++;
vi = airIndex( -1, v, 1, 254 ); vi++;
zi = ( ui ^ vi ) & 0x01;
if ( !zi && z > 1.0/128.0 ) {
ui -= ( ( ( ui >> 7 ) & 0x01 ) << 1 ) - 1;
}
else if ( zi && z < -1.0/128.0 ) {
vi -= ( ( ( vi >> 7 ) & 0x01 ) << 1 ) - 1;
}
zi = ( ui ^ vi ) & 0x01;
if ( !zi && z > 1.0/127.0 ) {
printf( "%s: panic01\n", me );
}
else if ( zi && z < -1.0/127.0 ) {
printf( "%s: panic02\n", me );
}
return ( vi << 8 ) | ui;
}
/* ---------------------------------------------------------------- */
/*
x = [ 1.0 1.0 ] u
y [ 1.0 -1.0 ] v
*/
/*
u = [ 0.5 0.5 ] x
v [ 0.5 -0.5 ] y
*/
/* xor of low bits == 0 --> z<0 ; xor == 1 --> z>0 */
/* May 11 2003 GK ( visually ) verified that except at equator seam,
the error due to quantization is unbiased */
#define _EVEN_CHECK_QtoV( HNB, TT, vec, qn ) \
ui = ( qn ) & ( ( 1<<HNB )-1 ); \
vi = ( ( qn ) >> HNB ) & ( ( 1<<HNB )-1 ); \
u = AIR_AFFINE( 0, ui, ( ( 1<<HNB )-1 ), -0.5, 0.5 ); \
v = AIR_AFFINE( 0, vi, ( ( 1<<HNB )-1 ), -0.5, 0.5 ); \
x = u + v; \
y = u - v; \
z = 1 - AIR_ABS( x ) - AIR_ABS( y ); \
z *= AIR_CAST( int, ( ( ui & 1 ) ^ ( vi & 1 ) ) << 1 ) - 1; \
n = 1.0/sqrt( x*x + y*y + z*z ); \
178 ( vec )[0] = AIR_CAST( TT, x*n ); \
179 ( vec )[1] = AIR_CAST( TT, y*n ); \
( vec )[2] = AIR_CAST( TT, z*n )
#define _EVEN_CHECK_VtoQ( HNB, vec ) \
x = ( vec )[0]; \
y = ( vec )[1]; \
z = ( vec )[2]; \
L = AIR_ABS( x ) + AIR_ABS( y ) + AIR_ABS( z ); \
if ( !L ) { \
return 0; \
} \
x /= L; \
y /= L; \
if ( z > 0 ) { \
xi = airIndex( -1.0, x, 1.0, ( ( 1<<HNB )-1 ) ); \
yi = airIndex( -1.0 - 1.0/( ( 1<<HNB )-1 ), y, 1.0 + 1.0/( ( 1<<HNB )-1 ), \
( 1<<HNB ) ); \
ui = xi + yi - ( ( 1<<( HNB-1 ) )-1 ); \
vi = xi - yi + ( 1<<( HNB-1 ) ); \
} \
else { \
xi = airIndex( -1.0 - 1.0/( ( 1<<HNB )-1 ), x, 1.0 + 1.0/( ( 1<<HNB )-1 ), \
( 1<<HNB ) ); \
yi = airIndex( -1, y, 1, ( ( 1<<HNB )-1 ) ); \
ui = xi + yi - ( ( 1<<( HNB-1 ) )-1 ); \
vi = xi - yi + ( ( 1<<( HNB-1 ) )-1 ); \
} \
return ( vi << HNB ) | ui
#define _EVEN_OCTA_QtoV( HNB, TT, vec, qn ) \
xi = ( qn ) & ( ( 1<<HNB )-1 ); \
yi = ( ( qn ) >> HNB ) & ( ( 1<<HNB )-1 ); \
x = AIR_AFFINE( -0.5, xi, ( ( 1<<HNB )-1 )+0.5, -1, 1 ); \
y = AIR_AFFINE( -0.5, yi, ( ( 1<<HNB )-1 )+0.5, -1, 1 ); \
z = 1 - AIR_ABS( x ) - AIR_ABS( y ); \
if ( z < 0 ) { \
if ( x > 0 ) { \
x += z; \
} else { \
x -= z; \
} \
if ( y > 0 ) { \
y += z; \
} else { \
y -= z; \
} \
} \
n = 1.0/sqrt( x*x + y*y + z*z ); \
( vec )[0] = AIR_CAST( TT, x*n ); \
( vec )[1] = AIR_CAST( TT, y*n ); \
( vec )[2] = AIR_CAST( TT, z*n )
#define _EVEN_OCTA_VtoQ( HNB, vec ) \
x = ( vec )[0]; \
y = ( vec )[1]; \
z = ( vec )[2]; \
L = AIR_ABS( x ) + AIR_ABS( y ) + AIR_ABS( z ); \
if ( !L ) { \
return 0; \
} \
x /= L; \
y /= L; \
z /= L; \
if ( z < 0 ) { \
if ( x > 0 ) { \
x -= z; \
} else { \
x += z; \
} \
if ( y > 0 ) { \
y -= z; \
} else { \
y += z; \
} \
} \
xi = airIndex( -1, x, 1, ( 1<<HNB ) ); \
yi = airIndex( -1, y, 1, ( 1<<HNB ) ); \
return ( yi << HNB ) | xi
/* _16_CHECK_QtoV and _16_CHECK_VtoQ are not actually used */
#define _16_CHECK_QtoV( vec, qn ) \
ui = ( qn ) & 0xFF; \
vi = ( ( qn ) >> 8 ) & 0xFF; \
u = AIR_AFFINE( 0, ui, 255, -0.5, 0.5 ); \
v = AIR_AFFINE( 0, vi, 255, -0.5, 0.5 ); \
x = u + v; \
y = u - v; \
z = 1 - AIR_ABS( x ) - AIR_ABS( y ); \
/* would this be better served by a branch? */ \
z *= ( ( ( ui ^ vi ) & 0x01 ) << 1 ) - 1; \
n = 1.0/sqrt( x*x + y*y + z*z ); \
( vec )[0] = x*n; \
( vec )[1] = y*n; \
( vec )[2] = z*n
#define _16_CHECK_VtoQ( vec ) \
x = ( vec )[0]; \
y = ( vec )[1]; \
z = ( vec )[2]; \
L = AIR_ABS( x ) + AIR_ABS( y ) + AIR_ABS( z ); \
if ( !L ) { \
return 0; \
} \
x /= L; \
y /= L; \
if ( z > 0 ) { \
xi = airIndex( -1.0, x, 1.0, 255 ); \
yi = airIndex( -1.0 - 1.0/255, y, 1.0 + 1.0/255, 256 ); \
ui = xi + yi - 127; \
vi = xi - yi + 128; \
} \
else { \
xi = airIndex( -1.0 - 1.0/255, x, 1.0 + 1.0/255, 256 ); \
yi = airIndex( -1, y, 1, 255 ); \
ui = xi + yi - 127; \
vi = xi - yi + 127; \
}
void
_limnQN16checker_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 8, float, vec, qn );
}
void
_limnQN16checker_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 8, double, vec, qn );
}
unsigned int
_limnQN16checker_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 8, vec );
}
unsigned int
_limnQN16checker_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 8, vec );
}
/* ---------------------------------------------------------------- */
void
_limnQN16octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 8, float, vec, qn );
}
void
_limnQN16octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 8, double, vec, qn );
}
unsigned int
_limnQN16octa_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 8, vec );
}
unsigned int
_limnQN16octa_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 8, vec );
}
/* ---------------------------------------------------------------- */
/* 15 bit --> HNB == 7 */
#define _ODD_OCTA_QtoV( HNB, TT, vec, qn ) \
ui = qn & ( ( 1<<HNB )-1 ); \
vi = ( qn >> HNB ) & ( ( 1<<HNB )-1 ); \
zi = ( qn >> ( 2*HNB ) ) & 0x01; \
u = AIR_CAST( TT, AIR_AFFINE( -0.5, ui, ( ( 1<<HNB )-1 )+0.5, -0.5, 0.5 ) ); \
v = AIR_CAST( TT, AIR_AFFINE( -0.5, vi, ( ( 1<<HNB )-1 )+0.5, -0.5, 0.5 ) ); \
x = u + v; \
y = u - v; \
z = 1 - AIR_ABS( x ) - AIR_ABS( y ); \
z *= AIR_CAST( int, zi << 1 ) - 1; \
n = AIR_CAST( TT, 1.0/sqrt( x*x + y*y + z*z ) ); \
vec[0] = AIR_CAST( TT, x*n ); \
vec[1] = AIR_CAST( TT, y*n ); \
vec[2] = AIR_CAST( TT, z*n )
#define _ODD_OCTA_VtoQ( HNB, vec ) \
x = vec[0]; \
y = vec[1]; \
z = vec[2]; \
L = AIR_ABS( x ) + AIR_ABS( y ) + AIR_ABS( z ); \
if ( !L ) { \
return 0; \
} \
x /= L; \
y /= L; \
u = x + y; \
v = x - y; \
ui = airIndex( -1, u, 1, ( 1<<HNB ) ); \
vi = airIndex( -1, v, 1, ( 1<<HNB ) ); \
zi = ( z > 0 ); \
return ( zi << ( 2*HNB ) ) | ( vi << HNB ) | ui
void
_limnQN15octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
float u, v, x, y, z, n;
_ODD_OCTA_QtoV( 7, float, vec, qn );
}
void
_limnQN15octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
double u, v, x, y, z, n;
_ODD_OCTA_QtoV( 7, double, vec, qn );
}
unsigned int
_limnQN15octa_VtoQN_f( const float *vec ) {
float L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 7, vec );
}
unsigned int
_limnQN15octa_VtoQN_d( const double *vec ) {
double L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 7, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN14checker_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 7, float, vec, qn );
}
void
_limnQN14checker_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 7, double, vec, qn );
}
unsigned int
_limnQN14checker_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 7, vec );
}
unsigned int
_limnQN14checker_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 7, vec );
}
/* ---------------------------------------------------------------- */
void
_limnQN14octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 7, float, vec, qn );
}
void
_limnQN14octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 7, double, vec, qn );
}
unsigned int
_limnQN14octa_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 7, vec );
}
unsigned int
_limnQN14octa_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 7, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN13octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
float u, v, x, y, z, n;
_ODD_OCTA_QtoV( 6, float, vec, qn );
}
void
_limnQN13octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
double u, v, x, y, z, n;
_ODD_OCTA_QtoV( 6, double, vec, qn );
}
unsigned int
_limnQN13octa_VtoQN_f( const float *vec ) {
float L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 6, vec );
}
unsigned int
_limnQN13octa_VtoQN_d( const double *vec ) {
double L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 6, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN12checker_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 6, float, vec, qn );
}
void
_limnQN12checker_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 6, double, vec, qn );
}
unsigned int
_limnQN12checker_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 6, vec );
}
unsigned int
_limnQN12checker_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 6, vec );
}
/* ---------------------------------------------------------------- */
void
_limnQN12octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 6, float, vec, qn );
}
void
_limnQN12octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 6, double, vec, qn );
}
unsigned int
_limnQN12octa_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 6, vec );
}
unsigned int
_limnQN12octa_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 6, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN11octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
float u, v, x, y, z, n;
_ODD_OCTA_QtoV( 5, float, vec, qn );
}
void
_limnQN11octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
double u, v, x, y, z, n;
_ODD_OCTA_QtoV( 5, double, vec, qn );
}
unsigned int
_limnQN11octa_VtoQN_f( const float *vec ) {
float L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 5, vec );
}
unsigned int
_limnQN11octa_VtoQN_d( const double *vec ) {
double L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 5, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN10checker_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 5, float, vec, qn );
}
void
_limnQN10checker_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 5, double, vec, qn );
}
unsigned int
_limnQN10checker_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 5, vec );
}
unsigned int
_limnQN10checker_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 5, vec );
}
/* ---------------------------------------------------------------- */
void
_limnQN10octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 5, float, vec, qn );
}
void
_limnQN10octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 5, double, vec, qn );
}
unsigned int
_limnQN10octa_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 5, vec );
}
unsigned int
_limnQN10octa_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 5, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN9octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
float u, v, x, y, z, n;
_ODD_OCTA_QtoV( 4, float, vec, qn );
}
void
_limnQN9octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi, zi;
double u, v, x, y, z, n;
_ODD_OCTA_QtoV( 4, double, vec, qn );
}
unsigned int
_limnQN9octa_VtoQN_f( const float *vec ) {
float L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 4, vec );
}
unsigned int
_limnQN9octa_VtoQN_d( const double *vec ) {
double L, u, v, x, y, z;
unsigned int ui, vi, zi;
_ODD_OCTA_VtoQ( 4, vec );
}
/* ----------------------------------------------------------- */
void
_limnQN8checker_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 4, float, vec, qn );
}
void
_limnQN8checker_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int ui, vi;
double u, v, x, y, z, n;
_EVEN_CHECK_QtoV( 4, double, vec, qn );
}
unsigned int
_limnQN8checker_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 4, vec );
}
unsigned int
_limnQN8checker_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi, ui, vi;
_EVEN_CHECK_VtoQ( 4, vec );
}
/* ---------------------------------------------------------------- */
void
_limnQN8octa_QNtoV_f( float *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 4, float, vec, qn );
}
void
_limnQN8octa_QNtoV_d( double *vec, unsigned int qn ) {
unsigned int xi, yi;
double x, y, z, n;
_EVEN_OCTA_QtoV( 4, double, vec, qn );
}
unsigned int
_limnQN8octa_VtoQN_f( const float *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 4, vec );
}
unsigned int
_limnQN8octa_VtoQN_d( const double *vec ) {
double L, x, y, z;
unsigned int xi, yi;
_EVEN_OCTA_VtoQ( 4, vec );
}
/* ----------------------------------------------------------- */
void ( *
limnQNtoV_f[LIMN_QN_MAX+1] )( float *, unsigned int ) = {
NULL,
_limnQN16simple_QNtoV_f,
_limnQN16border1_QNtoV_f,
_limnQN16checker_QNtoV_f,
_limnQN16octa_QNtoV_f,
_limnQN15octa_QNtoV_f,
_limnQN14checker_QNtoV_f,
_limnQN14octa_QNtoV_f,
_limnQN13octa_QNtoV_f,
_limnQN12checker_QNtoV_f,
_limnQN12octa_QNtoV_f,
_limnQN11octa_QNtoV_f,
_limnQN10checker_QNtoV_f,
_limnQN10octa_QNtoV_f,
_limnQN9octa_QNtoV_f,
_limnQN8checker_QNtoV_f,
_limnQN8octa_QNtoV_f
};
void ( *
limnQNtoV_d[LIMN_QN_MAX+1] )( double *, unsigned int ) = {
NULL,
NULL,
NULL,
_limnQN16checker_QNtoV_d,
_limnQN16octa_QNtoV_d,
_limnQN15octa_QNtoV_d,
_limnQN14checker_QNtoV_d,
_limnQN14octa_QNtoV_d,
_limnQN13octa_QNtoV_d,
_limnQN12checker_QNtoV_d,
_limnQN12octa_QNtoV_d,
_limnQN11octa_QNtoV_d,
_limnQN10checker_QNtoV_d,
_limnQN10octa_QNtoV_d,
_limnQN9octa_QNtoV_d,
_limnQN8checker_QNtoV_d,
_limnQN8octa_QNtoV_d
};
unsigned int ( *
limnVtoQN_f[LIMN_QN_MAX+1] )( const float *vec ) = {
NULL,
_limnQN16simple_VtoQN_f,
_limnQN16border1_VtoQN_f,
_limnQN16checker_VtoQN_f,
_limnQN16octa_VtoQN_f,
_limnQN15octa_VtoQN_f,
_limnQN14checker_VtoQN_f,
_limnQN14octa_VtoQN_f,
_limnQN13octa_VtoQN_f,
_limnQN12checker_VtoQN_f,
_limnQN12octa_VtoQN_f,
_limnQN11octa_VtoQN_f,
_limnQN10checker_VtoQN_f,
_limnQN10octa_VtoQN_f,
_limnQN9octa_VtoQN_f,
_limnQN8checker_VtoQN_f,
_limnQN8octa_VtoQN_f
};
unsigned int ( *
limnVtoQN_d[LIMN_QN_MAX+1] )( const double *vec ) = {
NULL,
NULL,
NULL,
_limnQN16checker_VtoQN_d,
_limnQN16octa_VtoQN_d,
_limnQN15octa_VtoQN_d,
_limnQN14checker_VtoQN_d,
_limnQN14octa_VtoQN_d,
_limnQN13octa_VtoQN_d,
_limnQN12checker_VtoQN_d,
_limnQN12octa_VtoQN_d,
_limnQN11octa_VtoQN_d,
_limnQN10checker_VtoQN_d,
_limnQN10octa_VtoQN_d,
_limnQN9octa_VtoQN_d,
_limnQN8checker_VtoQN_d,
_limnQN8octa_VtoQN_d
};
unsigned int
limnQNBins[LIMN_QN_MAX+1] = {
0,
( 1 << 16 ),
( 1 << 16 ),
( 1 << 16 ),
( 1 << 16 ),
( 1 << 15 ),
( 1 << 14 ),
( 1 << 14 ),
( 1 << 13 ),
( 1 << 12 ),
( 1 << 12 ),
( 1 << 11 ),
( 1 << 10 ),
( 1 << 10 ),
( 1 << 9 ),
( 1 << 8 ),
( 1 << 8 )
};
/*
** can use via test/tqn:
foreach Q ( 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 )
echo $Q
test/tqn -s 400 -q $Q \
| unu tile -a 2 0 1 -s 2 3 \
| unu tile -a 2 0 1 -s 2 1 | unu quantize -b 8 -o q${Q}.png
end
*/
int
limnQNDemo( Nrrd *nqn, unsigned int reso, int qni ) {
static const char me[]="limnQNDemo";
unsigned int ui, vi, oi;
double *qdata, ll, uu, vv, ww, vecd[3], unqd[3];
float vecf[3], unqf[3];
if ( !nqn ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdMaybeAlloc_va( nqn, nrrdTypeDouble, 4,
AIR_CAST( size_t, reso ),
AIR_CAST( size_t, reso ),
AIR_CAST( size_t, 6 ),
AIR_CAST( size_t, 2 ) ) ){
biffMovef( LIMN, NRRD, "%s: couldn't alloc output", me );
return 1;
}
if ( !( limnQNUnknown < qni && qni < limnQNLast ) ) {
biffAddf( LIMN, "%s: qni %d not in valid range [%d, %d]", me,
qni, limnQNUnknown+1, limnQNLast-1 );
return 1;
}
qdata = AIR_CAST( double *, nqn->data );
for ( oi=0; oi<6; oi++ ) {
for ( vi=0; vi<reso; vi++ ) {
vv = AIR_AFFINE( 0, vi, reso-1, 1, -1 );
for ( ui=0; ui<reso; ui++ ) {
uu = AIR_AFFINE( 0, ui, reso-1, -1, 1 );
ll = uu*uu + vv*vv;
if ( ll <= 1 ) {
ww = sqrt( 1 - ll );
if ( oi % 2 ) {
ww *= -1;
}
} else {
continue;
}
switch ( oi ) {
case 0:
case 1:
ELL_3V_SET( vecd, uu, vv, ww );
ELL_3V_SET_TT( vecf, float, uu, vv, ww );
break;
case 2:
case 3:
ELL_3V_SET( vecd, uu, ww, vv );
ELL_3V_SET_TT( vecf, float, uu, ww, vv );
break;
case 4:
case 5:
ELL_3V_SET( vecd, ww, uu, vv );
ELL_3V_SET_TT( vecf, float, ww, uu, vv );
break;
}
if ( limnVtoQN_d[qni] && limnQNtoV_d[qni] ) {
( limnQNtoV_d[qni] )( unqd, ( limnVtoQN_d[qni] )( vecd ) );
qdata[ui + reso*( vi + reso*( oi + 6 ) )] = ell_3v_angle_d( unqd, vecd );
}
if ( limnVtoQN_f[qni] && limnQNtoV_f[qni] ) {
( limnQNtoV_f[qni] )( unqf, ( limnVtoQN_f[qni] )( vecf ) );
qdata[ui + reso*( vi + reso*( oi + 0 ) )] = ell_3v_angle_f( unqf, vecf );
}
}
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
28 limnObjectRender( limnObject *obj, limnCamera *cam, limnWindow *win ) {
static const char me[]="limnObjectRender";
int E;
E = 0;
if ( !E ) E |= limnCameraUpdate( cam );
/*
fprintf( stderr, "%s: true up = %g %g %g\n", me,
-cam->V[0], -cam->V[1], -cam->V[2] );
fprintf( stderr, "%s: true right = %g %g %g\n", me,
cam->U[0], cam->U[1], cam->U[2] );
*/
if ( !E ) E |= limnObjectWorldHomog( obj );
if ( !E ) E |= limnObjectFaceNormals( obj, limnSpaceWorld );
if ( !E ) E |= limnObjectSpaceTransform( obj, cam, win, limnSpaceView );
if ( !E ) E |= limnObjectSpaceTransform( obj, cam, win, limnSpaceScreen );
if ( !E ) E |= limnObjectFaceNormals( obj, limnSpaceScreen );
if ( !E ) E |= limnObjectSpaceTransform( obj, cam, win, limnSpaceDevice );
if ( E ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
return 0;
}
void
54 _limnPSPreamble( limnObject *obj, limnCamera *cam, limnWindow *win ) {
AIR_UNUSED( obj );
AIR_UNUSED( cam );
fprintf( win->file, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
fprintf( win->file, "%%%%Creator: limn\n" );
fprintf( win->file, "%%%%Pages: 1\n" );
fprintf( win->file, "%%%%BoundingBox: %d %d %d %d\n",
( int )( win->bbox[0] ),
( int )( win->bbox[1] ),
( int )( win->bbox[2] ),
( int )( win->bbox[3] ) );
fprintf( win->file, "%%%%EndComments\n" );
fprintf( win->file, "%%%%EndProlog\n" );
fprintf( win->file, "%%%%Page: 1 1\n" );
fprintf( win->file, "gsave\n" );
fprintf( win->file, "%g %g moveto\n", win->bbox[0], win->bbox[1] );
fprintf( win->file, "%g %g lineto\n", win->bbox[2], win->bbox[1] );
fprintf( win->file, "%g %g lineto\n", win->bbox[2], win->bbox[3] );
fprintf( win->file, "%g %g lineto\n", win->bbox[0], win->bbox[3] );
fprintf( win->file, "closepath\n" );
if ( !win->ps.noBackground ) {
fprintf( win->file, "gsave %g %g %g setrgbcolor fill grestore\n",
win->ps.bg[0], win->ps.bg[1], win->ps.bg[2] );
}
fprintf( win->file, "clip\n" );
fprintf( win->file, "gsave newpath\n" );
fprintf( win->file, "1 setlinejoin\n" );
fprintf( win->file, "1 setlinecap\n" );
fprintf( win->file, "/M {moveto} bind def\n" );
fprintf( win->file, "/L {lineto} bind def\n" );
fprintf( win->file, "/W {setlinewidth} bind def\n" );
fprintf( win->file, "/F {fill} bind def\n" );
fprintf( win->file, "/S {stroke} bind def\n" );
fprintf( win->file, "/CP {closepath} bind def\n" );
fprintf( win->file, "/RGB {setrgbcolor} bind def\n" );
fprintf( win->file, "/Gr {setgray} bind def\n" );
fprintf( win->file, "\n" );
}
void
95 _limnPSEpilogue( limnObject *obj, limnCamera *cam, limnWindow *win ) {
AIR_UNUSED( obj );
AIR_UNUSED( cam );
fprintf( win->file, "grestore\n" );
fprintf( win->file, "grestore\n" );
if ( win->ps.showpage ) {
fprintf( win->file, "showpage\n" );
}
fprintf( win->file, "%%%%Trailer\n" );
}
void
108 _limnPSDrawFace( limnObject *obj, limnFace *face,
limnCamera *cam, Nrrd *nmap, limnWindow *win ) {
/* static const char me[]="_limnPSDrawFace"; */
unsigned int vii;
limnVertex *vert;
limnLook *look;
int qn;
float *map, R, G, B;
AIR_UNUSED( cam );
look = obj->look + face->lookIdx;
for ( vii=0; vii<face->sideNum; vii++ ) {
vert = obj->vert + face->vertIdx[vii];
fprintf( win->file, "%g %g %s\n",
vert->coord[0], vert->coord[1], vii ? "L" : "M" );
}
R = look->kads[0]*look->rgba[0];
G = look->kads[0]*look->rgba[1];
B = look->kads[0]*look->rgba[2];
if ( nmap ) {
qn = limnVtoQN_f[limnQN16octa]( face->worldNormal );
map = ( float * )nmap->data;
R += look->kads[1]*look->rgba[0]*map[0 + 3*qn];
G += look->kads[1]*look->rgba[1]*map[1 + 3*qn];
B += look->kads[1]*look->rgba[2]*map[2 + 3*qn];
} else {
R += look->kads[1]*look->rgba[0];
G += look->kads[1]*look->rgba[1];
B += look->kads[1]*look->rgba[2];
}
/* HEY: not evaluating phong specular for now */
R = AIR_CLAMP( 0, R, 1 );
G = AIR_CLAMP( 0, G, 1 );
B = AIR_CLAMP( 0, B, 1 );
if ( 0 && R == G && G == B ) {
/* As of Sat Mar 1 23:06:14 CST 2014 some version of ghostscript
and/or imagemagick will assign ( when rasterizing ) different
colors for RGB color ( g, g, g ) and graylevel g, which caused
strange appearance bugs that were hard to track down. Even if
there's a way of preventing this from happening with the right
incantation in the EPS header, for now it is simpler to forego
the small economy implemented here */
fprintf( win->file, "CP %g Gr F\n", R );
}
else {
fprintf( win->file, "CP %g %g %g RGB F\n", R, G, B );
}
}
void
158 _limnPSDrawEdge( limnObject *obj, limnEdge *edge,
limnCamera *cam, limnWindow *win ) {
limnVertex *vert0, *vert1;
float R, G, B;
AIR_UNUSED( cam );
if ( win->ps.lineWidth[edge->type] ) {
vert0 = obj->vert + edge->vertIdx[0];
vert1 = obj->vert + edge->vertIdx[1];
fprintf( win->file, "%g %g M ", vert0->coord[0], vert0->coord[1] );
fprintf( win->file, "%g %g L ", vert1->coord[0], vert1->coord[1] );
fprintf( win->file, "%g W ", win->ps.lineWidth[edge->type] );
R = win->ps.edgeColor[0];
G = win->ps.edgeColor[1];
B = win->ps.edgeColor[2];
if ( R == G && G == B ) {
fprintf( win->file, "%g Gr S\n", R );
} else {
fprintf( win->file, "%g %g %g RGB S\n", R, G, B );
}
}
}
/*
******** limnObjectPSDraw
**
** draws a "rendered" limn object to postscript.
** limnObjectRender MUST be called first.
**
** The current ( feeble ) justification for using an environment map is
** that its an expressive way of shading things based on surface
** normal, in a context where, if flat shading is all you have,
** correct specular lighting is not possible
*/
int
193 limnObjectPSDraw( limnObject *obj, limnCamera *cam,
Nrrd *nmap, limnWindow *win ) {
static const char me[]="limnObjectPSDraw";
int inside;
float angle;
limnFace *face, *face0, *face1; unsigned int fii;
limnEdge *edge; unsigned int eii;
limnPart *part; unsigned int partIdx;
limnVertex *vert; unsigned int vii;
if ( limnSpaceDevice != obj->vertSpace ) {
biffAddf( LIMN, "%s: object's verts in %s ( not %s ) space", me,
airEnumStr( limnSpace, obj->vertSpace ),
airEnumStr( limnSpace, limnSpaceDevice ) );
return 1;
}
if ( nmap ) {
if ( limnEnvMapCheck( nmap ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
}
limnObjectDepthSortParts( obj );
_limnPSPreamble( obj, cam, win );
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
part = obj->part[partIdx];
/* only draw the parts that are inside the field of view */
inside = 0;
for ( vii=0; vii<part->vertIdxNum; vii++ ) {
vert = obj->vert + part->vertIdx[vii];
inside |= ( AIR_IN_CL( win->bbox[0], vert->coord[0], win->bbox[2] ) &&
AIR_IN_CL( win->bbox[1], vert->coord[1], win->bbox[3] ) );
if ( inside ) {
/* at least vertex is in, we know we can't skip this part */
break;
}
}
if ( !inside ) {
/* none of the vertices were in, we can skip this part */
continue;
}
/* draw the part */
if ( 1 == part->edgeIdxNum ) {
/* this part is just one lone edge */
/* HEY: this is a mess */
/*
e = &( obj->e[r->eBase] );
widthTmp = win->ps.lineWidth[e->type];
fprintf( win->file, "%g setgray\n", 1 - win->ps.bg[0] );
win->ps.edgeWidth[e->type] = 8;
_limnPSDrawEdge( obj, r, e, cam, win );
fprintf( win->file, "%g %g %g RGB\n",
r->rgba[0], r->rgba[1], r->rgba[2] );
win->ps.edgeWidth[e->visible] = 4;
_limnPSDrawEdge( obj, r, e, cam, win );
win->ps.edgeWidth[e->visible] = widthTmp;
*/
} else {
/* this part is either a lone face or a solid:
draw the front-facing, shaded faces */
for ( fii=0; fii<part->faceIdxNum; fii++ ) {
face = obj->face + part->faceIdx[fii];
/* The consequence of having a left-handed frame is that world-space
CC-wise vertex traversal becomes C-wise screen-space traversal, so
all the normals are backwards of what we want */
face->visible = ( cam->rightHanded
? face->screenNormal[2] < 0
: face->screenNormal[2] > 0 );
if ( face->sideNum == part->vertIdxNum && !face->visible ) {
/* lone faces are always visible */
face->visible = AIR_TRUE;
ELL_3V_SCALE( face->worldNormal, -1, face->worldNormal );
}
if ( !win->ps.wireFrame && face->visible ) {
_limnPSDrawFace( obj, face, cam, nmap, win );
}
}
/* draw ALL edges */
for ( eii=0; eii<part->edgeIdxNum; eii++ ) {
/* hack to change contour of particular object/glyph
if ( 24 == partIdx ) {
win->ps.lineWidth[limnEdgeTypeContour] = 1.2;
} else {
win->ps.lineWidth[limnEdgeTypeContour] = 0.4;
}
*/
edge = obj->edge + part->edgeIdx[eii];
face0 = obj->face + edge->faceIdx[0];
face1 = ( -1 == edge->faceIdx[1]
? NULL
: obj->face + edge->faceIdx[1] );
if ( !face1 ) {
edge->type = limnEdgeTypeBorder;
} else {
angle = AIR_CAST( float,
180/AIR_PI*acos( ELL_3V_DOT( face0->worldNormal,
face1->worldNormal ) ) );
if ( face0->visible && face1->visible ) {
edge->type = ( angle > win->ps.creaseAngle
? limnEdgeTypeFrontCrease
: limnEdgeTypeFrontFacet );
} else if ( face0->visible ^ face1->visible ) {
edge->type = limnEdgeTypeContour;
} else {
edge->type = ( angle > win->ps.creaseAngle
? limnEdgeTypeBackCrease
: limnEdgeTypeBackFacet );
}
}
_limnPSDrawEdge( obj, edge, cam, win );
}
}
}
_limnPSEpilogue( obj, cam, win );
return 0;
}
/*
******** limnObjectPSDrawConcave
**
** new version of the above, which works per-face instead of per-part,
** thus better handling self-occlusions, but at the cost of not getting
** contours near oblique faces correct...
*/
int
326 limnObjectPSDrawConcave( limnObject *obj, limnCamera *cam,
Nrrd *nmap, limnWindow *win ) {
static const char me[]="limnObjectPSDrawConcave";
float angle;
limnPart *part;
limnFace *face, *face0, *face1; unsigned int faceIdx;
limnEdge *edge; unsigned int edgeIdx, eii;
if ( limnSpaceDevice != obj->vertSpace ) {
biffAddf( LIMN, "%s: object's verts in %s ( not %s ) space", me,
airEnumStr( limnSpace, obj->vertSpace ),
airEnumStr( limnSpace, limnSpaceDevice ) );
return 1;
}
if ( nmap ) {
if ( limnEnvMapCheck( nmap ) ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
}
limnObjectDepthSortFaces( obj );
_limnPSPreamble( obj, cam, win );
/* set every face's visibility */
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->face + faceIdx;
part = obj->part[face->partIdx];
face->visible = ( cam->rightHanded
? face->screenNormal[2] < 0
: face->screenNormal[2] > 0 );
if ( face->sideNum == part->vertIdxNum && !face->visible ) {
/* lone faces are always visible */
face->visible = AIR_TRUE;
ELL_3V_SCALE( face->worldNormal, -1, face->worldNormal );
}
}
/* categorize all edges by traversing edge array, and looking
at each of their two faces */
for ( edgeIdx=0; edgeIdx<obj->edgeNum; edgeIdx++ ) {
edge = obj->edge + edgeIdx;
part = obj->part[edge->partIdx];
face0 = obj->face + edge->faceIdx[0];
face1 = ( -1 == edge->faceIdx[1]
? NULL
: obj->face + edge->faceIdx[1] );
if ( !face1 ) {
edge->type = limnEdgeTypeBorder;
} else {
angle = AIR_CAST( float, 180/AIR_PI*acos( ELL_3V_DOT( face0->worldNormal,
face1->worldNormal ) ) );
if ( face0->visible && face1->visible ) {
edge->type = ( angle > win->ps.creaseAngle
? limnEdgeTypeFrontCrease
: limnEdgeTypeFrontFacet );
} else if ( face0->visible ^ face1->visible ) {
edge->type = limnEdgeTypeContour;
} else {
edge->type = ( angle > win->ps.creaseAngle
? limnEdgeTypeBackCrease
: limnEdgeTypeBackFacet );
}
}
}
/* draw front-faces and their edges
( contours, front crease, front non-crease ) */
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->faceSort[faceIdx];
part = obj->part[face->partIdx];
if ( !face->visible ) {
continue;
}
if ( !win->ps.wireFrame ) {
_limnPSDrawFace( obj, face, cam, nmap, win );
}
/* draw those edges around the face that won't be seen again by
future faces in the depth-first traversal */
for ( eii=0; eii<face->sideNum; eii++ ) {
edge = obj->edge + face->edgeIdx[eii];
if ( limnEdgeTypeContour == edge->type ) {
_limnPSDrawEdge( obj, edge, cam, win );
} else if ( limnEdgeTypeFrontCrease == edge->type
|| limnEdgeTypeFrontFacet == edge->type ) {
if ( edge->once ) {
/* its been seen once already, okay to draw */
_limnPSDrawEdge( obj, edge, cam, win );
edge->once = AIR_FALSE;
} else {
/* we're the first to see it, and we're not the last, don't draw */
edge->once = AIR_TRUE;
}
}
}
}
_limnPSEpilogue( obj, cam, win );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
28 limnObjectCubeAdd( limnObject *obj, unsigned int lookIdx ) {
unsigned int vII[4], vII0, partIdx;
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
/*
7 6
z 4 5
| y
| / 3 2
| /
| / 0 1
------ x
*/
vII0 = limnObjectVertexAdd( obj, partIdx, -1, -1, -1 );
limnObjectVertexAdd( obj, partIdx, 1, -1, -1 );
limnObjectVertexAdd( obj, partIdx, 1, 1, -1 );
limnObjectVertexAdd( obj, partIdx, -1, 1, -1 );
limnObjectVertexAdd( obj, partIdx, -1, -1, 1 );
limnObjectVertexAdd( obj, partIdx, 1, -1, 1 );
limnObjectVertexAdd( obj, partIdx, 1, 1, 1 );
limnObjectVertexAdd( obj, partIdx, -1, 1, 1 );
ELL_4V_SET( vII, vII0+3, vII0+2, vII0+1, vII0+0 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
ELL_4V_SET( vII, vII0+1, vII0+5, vII0+4, vII0+0 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
ELL_4V_SET( vII, vII0+2, vII0+6, vII0+5, vII0+1 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
ELL_4V_SET( vII, vII0+3, vII0+7, vII0+6, vII0+2 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
ELL_4V_SET( vII, vII0+0, vII0+4, vII0+7, vII0+3 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
ELL_4V_SET( vII, vII0+5, vII0+6, vII0+7, vII0+4 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
return partIdx;
}
int
70 limnObjectSquareAdd( limnObject *obj, unsigned int lookIdx ) {
unsigned int vII0, vII[4], partIdx;
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
vII0 = limnObjectVertexAdd( obj, partIdx, 0, 0, 0 );
limnObjectVertexAdd( obj, partIdx, 1, 0, 0 );
limnObjectVertexAdd( obj, partIdx, 1, 1, 0 );
limnObjectVertexAdd( obj, partIdx, 0, 1, 0 );
ELL_4V_SET( vII, vII0+0, vII0+1, vII0+2, vII0+3 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
return partIdx;
}
/*
******** limnObjectCylinderAdd
**
** adds a cylinder that fills up the bi-unit cube [-1, 1]^3,
** with axis "axis" ( 0:X, 1:Y, 2:Z ), with discretization "res"
*/
int
93 limnObjectCylinderAdd( limnObject *obj, unsigned int lookIdx,
unsigned int axis, unsigned int res ) {
unsigned int partIdx, ii, jj, tmp, vII0=0, *vII;
double theta;
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
vII = ( unsigned int * )calloc( res, sizeof( unsigned int ) );
for ( ii=0; ii<=res-1; ii++ ) {
theta = AIR_AFFINE( 0, ii, res, 0, 2*AIR_PI );
switch( axis ) {
case 0:
tmp = limnObjectVertexAdd( obj, partIdx,
1,
AIR_CAST( float, -sin( theta ) ),
AIR_CAST( float, cos( theta ) ) );
limnObjectVertexAdd( obj, partIdx,
-1,
AIR_CAST( float, -sin( theta ) ),
AIR_CAST( float, cos( theta ) ) );
break;
case 1:
tmp = limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, sin( theta ) ),
1,
AIR_CAST( float, cos( theta ) ) );
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, sin( theta ) ),
-1,
AIR_CAST( float, cos( theta ) ) );
break;
case 2: default:
tmp = limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, cos( theta ) ),
AIR_CAST( float, sin( theta ) ),
1 );
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, cos( theta ) ),
AIR_CAST( float, sin( theta ) ),
-1 );
break;
}
if ( !ii ) {
vII0 = tmp;
}
}
/* add all side faces */
for ( ii=0; ii<=res-1; ii++ ) {
jj = ( ii+1 ) % res;
ELL_4V_SET( vII, vII0 + 2*ii, vII0 + 2*ii + 1,
vII0 + 2*jj + 1, vII0 + 2*jj );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
}
/* add top */
for ( ii=0; ii<=res-1; ii++ ) {
vII[ii] = vII0 + 2*ii;
}
limnObjectFaceAdd( obj, partIdx, lookIdx, res, vII );
/* add bottom */
for ( ii=0; ii<=res-1; ii++ ) {
vII[ii] = vII0 + 2*( res-1-ii ) + 1;
}
limnObjectFaceAdd( obj, partIdx, lookIdx, res, vII );
free( vII );
return partIdx;
}
int
165 limnObjectConeAdd( limnObject *obj, unsigned int lookIdx,
unsigned int axis, unsigned int res ) {
double th;
unsigned int partIdx, tmp, vII0=0, ii, jj, *vII;
vII = ( unsigned int * )calloc( res, sizeof( unsigned int ) );
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
for ( ii=0; ii<=res-1; ii++ ) {
th = AIR_AFFINE( 0, ii, res, 0, 2*AIR_PI );
switch( axis ) {
case 0:
tmp = limnObjectVertexAdd( obj, partIdx,
0,
AIR_CAST( float, -sin( th ) ),
AIR_CAST( float, cos( th ) ) );
break;
case 1:
tmp = limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, sin( th ) ),
0,
AIR_CAST( float, cos( th ) ) );
break;
case 2: default:
tmp = limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, cos( th ) ),
AIR_CAST( float, sin( th ) ),
0 );
break;
}
if ( !ii ) {
vII0 = tmp;
}
}
switch( axis ) {
case 0:
limnObjectVertexAdd( obj, partIdx, 1, 0, 0 );
break;
case 1:
limnObjectVertexAdd( obj, partIdx, 0, 1, 0 );
break;
case 2: default:
limnObjectVertexAdd( obj, partIdx, 0, 0, 1 );
break;
}
for ( ii=0; ii<=res-1; ii++ ) {
jj = ( ii+1 ) % res;
ELL_3V_SET( vII, vII0+ii, vII0+jj, vII0+res );
limnObjectFaceAdd( obj, partIdx, lookIdx, 3, vII );
}
for ( ii=0; ii<=res-1; ii++ ) {
vII[ii] = vII0+res-1-ii;
}
limnObjectFaceAdd( obj, partIdx, lookIdx, res, vII );
free( vII );
return partIdx;
}
int
228 limnObjectPolarSphereAdd( limnObject *obj, unsigned int lookIdx,
unsigned int axis, unsigned int thetaRes,
unsigned int phiRes ) {
unsigned int partIdx, vII0, nti, ti, pi, vII[4], pl;
double t, p;
thetaRes = AIR_MAX( thetaRes, 3 );
phiRes = AIR_MAX( phiRes, 2 );
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
switch( axis ) {
case 0:
vII0 = limnObjectVertexAdd( obj, partIdx, 1, 0, 0 );
break;
case 1:
vII0 = limnObjectVertexAdd( obj, partIdx, 0, 1, 0 );
break;
case 2: default:
vII0 = limnObjectVertexAdd( obj, partIdx, 0, 0, 1 );
break;
}
for ( pi=1; pi<=phiRes-1; pi++ ) {
p = AIR_AFFINE( 0, pi, phiRes, 0, AIR_PI );
for ( ti=0; ti<=thetaRes-1; ti++ ) {
t = AIR_AFFINE( 0, ti, thetaRes, 0, 2*AIR_PI );
switch( axis ) {
case 0:
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, cos( p ) ),
AIR_CAST( float, -sin( t )*sin( p ) ),
AIR_CAST( float, cos( t )*sin( p ) ) );
break;
case 1:
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, sin( t )*sin( p ) ),
AIR_CAST( float, cos( p ) ),
AIR_CAST( float, cos( t )*sin( p ) ) );
break;
case 2: default:
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, cos( t )*sin( p ) ),
AIR_CAST( float, sin( t )*sin( p ) ),
AIR_CAST( float, cos( p ) ) );
break;
}
}
}
switch( axis ) {
case 0:
pl = limnObjectVertexAdd( obj, partIdx, -1, 0, 0 );
break;
case 1:
pl = limnObjectVertexAdd( obj, partIdx, 0, -1, 0 );
break;
case 2: default:
pl = limnObjectVertexAdd( obj, partIdx, 0, 0, -1 );
break;
}
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_3V_SET( vII, vII0+ti, vII0+nti, vII0+0 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 3, vII );
}
for ( pi=0; pi<=phiRes-3; pi++ ) {
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_4V_SET( vII, vII0+pi*thetaRes + ti, vII0+( pi+1 )*thetaRes + ti,
vII0+( pi+1 )*thetaRes + nti, vII0+pi*thetaRes + nti );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
}
}
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_3V_SET( vII, vII0+pi*thetaRes + ti, pl, vII0+pi*thetaRes + nti );
limnObjectFaceAdd( obj, partIdx, lookIdx, 3, vII );
}
return partIdx;
}
int
312 limnObjectPolarSuperquadFancyAdd( limnObject *obj,
unsigned int lookIdx, unsigned int axis,
float A, float B, float C, float R,
unsigned int thetaRes, unsigned int phiRes ) {
unsigned int partIdx, vII0, nti, ti, pi, vII[4], pl;
double x, y, z, t, p;
AIR_UNUSED( R );
thetaRes = AIR_MAX( thetaRes, 3 );
phiRes = AIR_MAX( phiRes, 2 );
partIdx = limnObjectPartAdd( obj );
/* HEY: we have to set this first so that
obj->setVertexRGBAFromLook can do the right thing */
obj->part[partIdx]->lookIdx = lookIdx;
switch( axis ) {
case 0:
vII0 = limnObjectVertexAdd( obj, partIdx, 1, 0, 0 );
break;
case 1:
vII0 = limnObjectVertexAdd( obj, partIdx, 0, 1, 0 );
break;
case 2: default:
vII0 = limnObjectVertexAdd( obj, partIdx, 0, 0, 1 );
break;
}
for ( pi=1; pi<=phiRes-1; pi++ ) {
p = AIR_AFFINE( 0, pi, phiRes, 0, AIR_PI );
for ( ti=0; ti<=thetaRes-1; ti++ ) {
t = AIR_AFFINE( 0, ti, thetaRes, 0, 2*AIR_PI );
switch( axis ) {
case 0:
x = airSgnPow( cos( p ), B );
y = -airSgnPow( sin( t ), A ) * airSgnPow( sin( p ), B );
z = airSgnPow( cos( t ), A ) * airSgnPow( sin( p ), B );
if ( C != B ) {
/* modify profile along y axis to create beta=C */
double yp, ymax;
yp = airSgnPow( sin( acos( airSgnPow( x, 1/C ) ) ), C );
ymax = airSgnPow( sin( p ), B );
if ( ymax ) {
y *= yp/ymax;
}
}
break;
case 1:
x = airSgnPow( sin( t ), A ) * airSgnPow( sin( p ), B );
y = airSgnPow( cos( p ), B );
z = airSgnPow( cos( t ), A ) * airSgnPow( sin( p ), B );
break;
case 2: default:
x = airSgnPow( cos( t ), A ) * airSgnPow( sin( p ), B );
y = airSgnPow( sin( t ), A ) * airSgnPow( sin( p ), B );
z = airSgnPow( cos( p ), B );
if ( C != B ) {
/* modify profile along y axis to create beta=C */
double yp, ymax;
yp = airSgnPow( sin( acos( airSgnPow( z, 1/C ) ) ), C );
ymax = airSgnPow( sin( p ), B );
if ( ymax ) {
y *= yp/ymax;
}
}
break;
}
limnObjectVertexAdd( obj, partIdx,
AIR_CAST( float, x ),
AIR_CAST( float, y ),
AIR_CAST( float, z ) );
}
}
switch( axis ) {
case 0:
pl = limnObjectVertexAdd( obj, partIdx, -1, 0, 0 );
break;
case 1:
pl = limnObjectVertexAdd( obj, partIdx, 0, -1, 0 );
break;
case 2: default:
pl = limnObjectVertexAdd( obj, partIdx, 0, 0, -1 );
break;
}
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_3V_SET( vII, vII0+ti, vII0+nti, vII0+0 );
limnObjectFaceAdd( obj, partIdx, lookIdx, 3, vII );
}
for ( pi=0; pi<=phiRes-3; pi++ ) {
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_4V_SET( vII, vII0+pi*thetaRes + ti, vII0+( pi+1 )*thetaRes + ti,
vII0+( pi+1 )*thetaRes + nti, vII0+pi*thetaRes + nti );
limnObjectFaceAdd( obj, partIdx, lookIdx, 4, vII );
}
}
for ( ti=1; ti<=thetaRes; ti++ ) {
nti = ti < thetaRes ? ti+1 : 1;
ELL_3V_SET( vII, vII0+pi*thetaRes + ti, pl, vII0+pi*thetaRes + nti );
limnObjectFaceAdd( obj, partIdx, lookIdx, 3, vII );
}
return partIdx;
}
int
417 limnObjectPolarSuperquadAdd( limnObject *obj,
unsigned int lookIdx, unsigned int axis,
float A, float B,
unsigned int thetaRes, unsigned int phiRes ) {
return limnObjectPolarSuperquadFancyAdd( obj, lookIdx, axis,
A, B, B, 0,
thetaRes, phiRes );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
void
28 _limnSplineIntervalFind_Unknown( int *ii, double *ff,
limnSpline *spline, double tt ) {
static const char me[]="_limnSplineIntervalFind_Unknown";
AIR_UNUSED( ii );
AIR_UNUSED( ff );
AIR_UNUSED( spline );
AIR_UNUSED( tt );
fprintf( stderr, "%s: WARNING: spline type unset somewhere\n", me );
return;
}
void
41 _limnSplineIntervalFind_NonWarp( int *ii, double *ff,
limnSpline *spline, double tt ) {
int N;
N = spline->ncpt->axis[2].size + ( spline->loop ? 1 : 0 );
tt = AIR_CLAMP( 0, tt, N-1 );
*ii = ( int )tt;
*ff = tt - *ii;
return;
}
void
53 _limnSplineIntervalFind_Warp( int *ii, double *ff,
limnSpline *spline, double tt ) {
int N;
N = spline->ncpt->axis[2].size;
tt = AIR_CLAMP( spline->time[0], tt, spline->time[N-1] );
*ii = AIR_CLAMP( 0, *ii, N-2 );
/* the last value of ii may be the right one */
if ( !AIR_IN_CL( spline->time[*ii], tt, spline->time[*ii+1] ) ) {
/* HEY: make this a binary search */
for ( *ii=0; *ii<N-2; ( *ii )++ ) {
if ( AIR_IN_CL( spline->time[*ii], tt, spline->time[*ii+1] ) ) {
break;
}
}
}
*ff = ( tt - spline->time[*ii] )/( spline->time[*ii+1] - spline->time[*ii] );
return;
}
typedef void ( *_limnSplineIntervalFind_t )( int *, double *,
limnSpline *, double );
_limnSplineIntervalFind_t
_limnSplineIntervalFind[LIMN_SPLINE_TYPE_MAX+1] = {
_limnSplineIntervalFind_Unknown,
_limnSplineIntervalFind_NonWarp,
_limnSplineIntervalFind_Warp,
_limnSplineIntervalFind_NonWarp,
_limnSplineIntervalFind_NonWarp,
_limnSplineIntervalFind_NonWarp
};
void
86 _limnSplineWeightsFind_Unknown( double *wght, limnSpline *spline, double f ) {
static const char me[]="_limnSplineWeights_Unknown";
AIR_UNUSED( wght );
AIR_UNUSED( spline );
AIR_UNUSED( f );
fprintf( stderr, "%s: WARNING: spline type unset somewhere\n", me );
return;
}
void
97 _limnSplineWeightsFind_Linear( double *wght, limnSpline *spline, double f ) {
AIR_UNUSED( spline );
ELL_4V_SET( wght, 0, 1-f, f, 0 );
/*
fprintf( stderr, "%g ----> %g %g %g %g\n", f,
wght[0], wght[1], wght[2], wght[3] );
*/
return;
}
void
109 _limnSplineWeightsFind_Hermite( double *wght, limnSpline *spline, double f ) {
double f3, f2;
AIR_UNUSED( spline );
f3 = f*( f2 = f*f );
ELL_4V_SET( wght,
2*f3 - 3*f2 + 1,
f3 - 2*f2 + f,
f3 - f2,
-2*f3 + 3*f2 );
return;
}
void
123 _limnSplineWeightsFind_CubicBezier( double *wght,
limnSpline *spline, double f ) {
double g;
AIR_UNUSED( spline );
g = 1 - f;
ELL_4V_SET( wght,
g*g*g,
3*g*g*f,
3*g*f*f,
f*f*f );
return;
}
/* lifted from nrrd/kernel.c */
#define _BCCUBIC( x, B, C ) \
( ( x ) >= 2.0 ? 0 : \
( ( x ) >= 1.0 \
141 ? ( ( ( -B/6 - C )*( x ) + B + 5*C )*( x ) -2*B - 8*C )*( x ) + 4*B/3 + 4*C \
: ( ( 2 - 3*B/2 - C )*( x ) - 3 + 2*B + C )*( x )*( x ) + 1 - B/3 ) )
void
_limnSplineWeightsFind_BC( double *wght, limnSpline *spline, double f ) {
double B, C, f0, f1, f2, f3;
B = spline->B;
C = spline->C;
f0 = f+1;
f1 = f;
f2 = AIR_ABS( f-1 );
f3 = AIR_ABS( f-2 );
ELL_4V_SET( wght,
_BCCUBIC( f0, B, C ),
_BCCUBIC( f1, B, C ),
_BCCUBIC( f2, B, C ),
_BCCUBIC( f3, B, C ) );
return;
}
typedef void ( *_limnSplineWeightsFind_t )( double *, limnSpline *, double );
_limnSplineWeightsFind_t
_limnSplineWeightsFind[LIMN_SPLINE_TYPE_MAX+1] = {
_limnSplineWeightsFind_Unknown,
_limnSplineWeightsFind_Linear,
_limnSplineWeightsFind_Hermite, /* TimeWarp */
_limnSplineWeightsFind_Hermite,
_limnSplineWeightsFind_CubicBezier,
171 _limnSplineWeightsFind_BC
};
void
_limnSplineIndexFind( int *idx, limnSpline *spline, int ii ) {
int N, ti[4];
N = spline->ncpt->axis[2].size;
if ( limnSplineTypeHasImplicitTangents[spline->type] ) {
if ( spline->loop ) {
ELL_4V_SET( ti,
AIR_MOD( ii-1, N ),
AIR_MOD( ii+0, N ),
AIR_MOD( ii+1, N ),
AIR_MOD( ii+2, N ) );
} else {
ELL_4V_SET( ti,
AIR_CLAMP( 0, ii-1, N-1 ),
AIR_CLAMP( 0, ii+0, N-1 ),
AIR_CLAMP( 0, ii+1, N-1 ),
AIR_CLAMP( 0, ii+2, N-1 ) );
}
ELL_4V_SET( idx, 1 + 3*ti[0], 1 + 3*ti[1], 1 + 3*ti[2], 1 + 3*ti[3] );
} else {
if ( spline->loop ) {
ELL_4V_SET( ti,
AIR_MOD( ii+0, N ),
AIR_MOD( ii+0, N ),
AIR_MOD( ii+1, N ),
AIR_MOD( ii+1, N ) );
} else {
ELL_4V_SET( ti,
AIR_CLAMP( 0, ii+0, N-1 ),
AIR_CLAMP( 0, ii+0, N-1 ),
AIR_CLAMP( 0, ii+1, N-1 ),
AIR_CLAMP( 0, ii+1, N-1 ) );
}
ELL_4V_SET( idx, 1 + 3*ti[0], 2 + 3*ti[1], 0 + 3*ti[2], 1 + 3*ti[3] );
209 }
}
void
_limnSplineFinish_Unknown( double *out, limnSpline *spline,
int ii, double *wght ) {
static const char me[]="_limnSplineFinish_Unknown";
AIR_UNUSED( out );
AIR_UNUSED( spline );
AIR_UNUSED( ii );
AIR_UNUSED( wght );
fprintf( stderr, "%s: WARNING: spline info unset somewhere\n", me );
222 return;
}
void
_limnSplineFinish_Scalar( double *out, limnSpline *spline,
int ii, double *wght ) {
int idx[4];
double *cpt;
cpt = ( double* )( spline->ncpt->data );
_limnSplineIndexFind( idx, spline, ii );
*out = ( wght[0]*cpt[idx[0]] + wght[1]*cpt[idx[1]]
+ wght[2]*cpt[idx[2]] + wght[3]*cpt[idx[3]] );
235 return;
}
void
_limnSplineFinish_2Vec( double *out, limnSpline *spline,
int ii, double *wght ) {
int idx[4];
double *cpt;
cpt = ( double* )( spline->ncpt->data );
_limnSplineIndexFind( idx, spline, ii );
out[0] = ( wght[0]*cpt[0 + 2*idx[0]] + wght[1]*cpt[0 + 2*idx[1]]
+ wght[2]*cpt[0 + 2*idx[2]] + wght[3]*cpt[0 + 2*idx[3]] );
out[1] = ( wght[0]*cpt[1 + 2*idx[0]] + wght[1]*cpt[1 + 2*idx[1]]
+ wght[2]*cpt[1 + 2*idx[2]] + wght[3]*cpt[1 + 2*idx[3]] );
250 return;
}
void
_limnSplineFinish_3Vec( double *out, limnSpline *spline,
int ii, double *wght ) {
int idx[4];
double *cpt;
cpt = ( double* )( spline->ncpt->data );
_limnSplineIndexFind( idx, spline, ii );
out[0] = ( wght[0]*cpt[0 + 3*idx[0]] + wght[1]*cpt[0 + 3*idx[1]]
+ wght[2]*cpt[0 + 3*idx[2]] + wght[3]*cpt[0 + 3*idx[3]] );
out[1] = ( wght[0]*cpt[1 + 3*idx[0]] + wght[1]*cpt[1 + 3*idx[1]]
+ wght[2]*cpt[1 + 3*idx[2]] + wght[3]*cpt[1 + 3*idx[3]] );
out[2] = ( wght[0]*cpt[2 + 3*idx[0]] + wght[1]*cpt[2 + 3*idx[1]]
+ wght[2]*cpt[2 + 3*idx[2]] + wght[3]*cpt[2 + 3*idx[3]] );
267 return;
}
void
_limnSplineFinish_Normal( double *out, limnSpline *spline,
int ii, double *wght ) {
AIR_UNUSED( out );
AIR_UNUSED( spline );
AIR_UNUSED( ii );
AIR_UNUSED( wght );
fprintf( stderr, "%s: NOT IMPLEMENTED\n", "_limnSplineFinish_Normal" );
279 return;
}
void
_limnSplineFinish_4Vec( double *out, limnSpline *spline,
int ii, double *wght ) {
int idx[4];
double *cpt;
cpt = ( double* )( spline->ncpt->data );
_limnSplineIndexFind( idx, spline, ii );
out[0] = ( wght[0]*cpt[0 + 4*idx[0]] + wght[1]*cpt[0 + 4*idx[1]]
+ wght[2]*cpt[0 + 4*idx[2]] + wght[3]*cpt[0 + 4*idx[3]] );
out[1] = ( wght[0]*cpt[1 + 4*idx[0]] + wght[1]*cpt[1 + 4*idx[1]]
+ wght[2]*cpt[1 + 4*idx[2]] + wght[3]*cpt[1 + 4*idx[3]] );
out[2] = ( wght[0]*cpt[2 + 4*idx[0]] + wght[1]*cpt[2 + 4*idx[1]]
+ wght[2]*cpt[2 + 4*idx[2]] + wght[3]*cpt[2 + 4*idx[3]] );
out[3] = ( wght[0]*cpt[3 + 4*idx[0]] + wght[1]*cpt[3 + 4*idx[1]]
+ wght[2]*cpt[3 + 4*idx[2]] + wght[3]*cpt[3 + 4*idx[3]] );
return;
}
301 /*
** HEY: I have no whether Hermite splines work with this
*/
void
_limnSplineFinish_Quaternion( double *out, limnSpline *spline,
int ii, double *wght ) {
int idx[4];
double *cpt;
cpt = ( double* )( spline->ncpt->data );
_limnSplineIndexFind( idx, spline, ii );
ell_q_avg4_d( out, NULL,
cpt + 4*idx[0], cpt + 4*idx[1],
cpt + 4*idx[2], cpt + 4*idx[3],
wght, LIMN_SPLINE_Q_AVG_EPS, 30 /* maxIter */ );
return;
}
typedef void ( *_limnSplineFinish_t )( double *, limnSpline *, int, double * );
_limnSplineFinish_t
_limnSplineFinish[LIMN_SPLINE_INFO_MAX+1] = {
_limnSplineFinish_Unknown,
_limnSplineFinish_Scalar,
_limnSplineFinish_2Vec,
_limnSplineFinish_3Vec,
_limnSplineFinish_Normal,
_limnSplineFinish_4Vec,
328 _limnSplineFinish_Quaternion
};
void
limnSplineEvaluate( double *out, limnSpline *spline, double tt ) {
int ii=0;
double ff, wght[4];
if ( out && spline ) {
_limnSplineIntervalFind[spline->type]( &ii, &ff, spline, tt );
_limnSplineWeightsFind[spline->type]( wght, spline, ff );
_limnSplineFinish[spline->info]( out, spline, ii, wght );
}
341 return;
}
int
limnSplineNrrdEvaluate( Nrrd *nout, limnSpline *spline, Nrrd *nin ) {
static const char me[]="limnSplineNrrdEvaluate";
double tt, *out, ( *lup )( const void *, size_t );
int odim, infoSize;
size_t I, M, size[NRRD_DIM_MAX+1];
if ( !( nout && spline && nin ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( limnSplineInfoScalar == spline->info ) {
nrrdAxisInfoGet_va( nin, nrrdAxisInfoSize, size );
infoSize = 1;
odim = nin->dim;
} else {
nrrdAxisInfoGet_va( nin, nrrdAxisInfoSize, size+1 );
infoSize = size[0] = limnSplineInfoSize[spline->info];
odim = 1 + nin->dim;
}
if ( nrrdMaybeAlloc_nva( nout, nrrdTypeDouble, odim, size ) ) {
biffMovef( LIMN, NRRD, "%s: output allocation failed", me );
return 1;
}
lup = nrrdDLookup[nin->type];
out = ( double* )( nout->data );
M = nrrdElementNumber( nin );
for ( I=0; I<M; I++ ) {
tt = lup( nin->data, I );
limnSplineEvaluate( out, spline, tt );
out += infoSize;
}
/* HEY: peripheral info copying? */
379 return 0;
}
int
limnSplineSample( Nrrd *nout, limnSpline *spline,
double minT, size_t M, double maxT ) {
static const char me[]="limnSplineSample";
airArray *mop;
Nrrd *ntt;
double *tt;
size_t I;
if ( !( nout && spline ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, ntt=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( ntt, nrrdTypeDouble, 1,
M ) ) {
biffMovef( LIMN, NRRD, "%s: trouble allocating tmp nrrd", me );
airMopError( mop ); return 1;
}
tt = ( double* )( ntt->data );
for ( I=0; I<M; I++ ) {
tt[I] = AIR_AFFINE( 0, I, M-1, minT, maxT );
}
if ( limnSplineNrrdEvaluate( nout, spline, ntt ) ) {
biffAddf( LIMN, "%s: trouble", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
limnSplineTypeSpec *
28 limnSplineTypeSpecNew( int type, ... ) {
static const char me[]="limnSplineTypeSpecNew";
limnSplineTypeSpec *spec;
va_list ap;
if ( airEnumValCheck( limnSplineType, type ) ) {
biffAddf( LIMN, "%s: given type %d not a valid limnSplineType", me, type );
return NULL;
}
spec = AIR_CALLOC( 1, limnSplineTypeSpec );
spec->type = type;
va_start( ap, type );
if ( limnSplineTypeBC == type ) {
spec->B = va_arg( ap, double );
spec->C = va_arg( ap, double );
}
va_end( ap );
return spec;
}
limnSplineTypeSpec *
49 limnSplineTypeSpecNix( limnSplineTypeSpec *spec ) {
airFree( spec );
return NULL;
}
/*
** _limnSplineTimeWarpSet
**
** implements GK's patented time warping technology
*/
int
61 _limnSplineTimeWarpSet( limnSpline *spline ) {
static const char me[]="_limnSplineTimeWarpSet";
double *cpt, *time, ss;
int ii, N;
cpt = ( double* )( spline->ncpt->data );
N = spline->ncpt->axis[2].size;
time = spline->time;
for ( ii=0; ii<N; ii++ ) {
if ( !AIR_EXISTS( time[ii] ) ) {
biffAddf( LIMN, "%s: time[%d] doesn't exist", me, ii );
return 1;
}
if ( ii && !( time[ii-1] < time[ii] ) ) {
biffAddf( LIMN, "%s: time[%d] = %g not < time[%d] = %g", me,
ii-1, time[ii-1], ii, time[ii] );
return 1;
}
/* this will be used below */
/* HEY: is there any way or reason to do any other kind of time warp? */
cpt[1 + 3*ii] = ii;
}
for ( ii=1; ii<N-1; ii++ ) {
ss = ( cpt[1+3*( ii+1 )] - cpt[1+3*( ii-1 )] )/( time[ii+1] - time[ii-1] );
cpt[0 + 3*ii] = ss*( time[ii] - time[ii-1] );
cpt[2 + 3*ii] = ss*( time[ii+1] - time[ii] );
}
if ( spline->loop ) {
ss = ( ( cpt[1+3*1] - cpt[1+3*0] + cpt[1+3*( N-1 )] - cpt[1+3*( N-2 )] )
/ ( time[1] - time[0] + time[N-1] - time[N-2] ) );
cpt[2 + 3*0] = ss*( time[1] - time[0] );
cpt[0 + 3*( N-1 )] = ss*( time[N-1] - time[N-2] );
} else {
cpt[2 + 3*0] = ( ( cpt[1+3*1] - cpt[1+3*0] )
* ( time[1] - time[0] ) );
cpt[0 + 3*( N-1 )] = ( ( cpt[1+3*( N-1 )] - cpt[1+3*( N-2 )] )
* ( time[N-1] - time[N-2] ) );
}
/*
fprintf( stderr, "s[0]=%g, post = %g; s[1]=%g pre = %g\n",
cpt[1 + 3*0], cpt[2 + 3*0], cpt[1 + 3*1], cpt[0 + 3*1] );
*/
return 0;
}
/*
******** limnSplineNew
**
** constructor for limnSplines. We take all the control point information
** here, and copy it internally, in an effort to simplify the management of
** state. The control point nrrd is 3-D, as explained in limn.h
**
** To confuse matters, the Time type of spline doesn't need the control
** point information in the traditional sense, but it still needs to know
** "when" the control points are. So, the _ncpt nrrd is still needed, but
** it is only a 1-D array of times. In this case, the internal ncpt values
** are set automatically.
**
** The benefit of this approach is that if this constructor returns
** successfully, then there is no more information or state that needs to
** be set-- the returned spline can be passed to evaluate or sample.
** LIES LIES LIES: For BC-splines, you still have to call limnSplineBCSet,
** but that's the only exception...
*/
limnSpline *
128 limnSplineNew( Nrrd *_ncpt, int info, limnSplineTypeSpec *spec ) {
static const char me[]="limnSplineNew";
limnSpline *spline;
size_t N;
unsigned int size;
airArray *mop;
Nrrd *nin;
char stmp[2][AIR_STRLEN_SMALL];
if ( airEnumValCheck( limnSplineInfo, info ) ) {
biffAddf( LIMN, "%s: info %d not a valid limnSplineInfo", me, info );
return NULL;
}
if ( nrrdCheck( _ncpt ) ) {
biffMovef( LIMN, NRRD, "%s: given nrrd has problems", me );
return NULL;
}
if ( limnSplineTypeTimeWarp == spec->type ) {
if ( !( limnSplineInfoScalar == info ) ) {
biffAddf( LIMN, "%s: can only time warp scalars", me );
return NULL;
}
if ( !( 1 == _ncpt->dim ) ) {
biffAddf( LIMN, "%s: given nrrd has dimension %d, not 1", me, _ncpt->dim );
return NULL;
}
N = _ncpt->axis[0].size;
} else {
if ( !( 3 == _ncpt->dim ) ) {
biffAddf( LIMN, "%s: given nrrd has dimension %d, not 3", me, _ncpt->dim );
return NULL;
}
size = limnSplineInfoSize[info];
if ( !( size == _ncpt->axis[0].size && 3 == _ncpt->axis[1].size ) ) {
biffAddf( LIMN, "%s: expected %ux3xN nrrd, not %sx%sxN", me, size,
airSprintSize_t( stmp[0], _ncpt->axis[0].size ),
airSprintSize_t( stmp[1], _ncpt->axis[1].size ) );
return NULL;
}
N = _ncpt->axis[2].size;
}
if ( 1 == N ) {
biffAddf( LIMN, "%s: need at least two control points", me );
return NULL;
}
mop = airMopNew( );
if ( !( spline = AIR_CALLOC( 1, limnSpline ) ) ) {
biffAddf( LIMN, "%s: couldn't allocate new spline", me );
airMopError( mop ); return NULL;
}
airMopAdd( mop, spline, ( airMopper )limnSplineNix, airMopOnError );
spline->time = NULL;
spline->ncpt = NULL;
spline->type = spec->type;
spline->info = info;
spline->loop = AIR_FALSE;
spline->B = spec->B;
spline->C = spec->C;
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdConvert( nin, _ncpt, nrrdTypeDouble ) ) {
biffMovef( LIMN, NRRD, "%s: trouble allocating internal nrrd", me );
airMopError( mop ); return NULL;
}
if ( limnSplineTypeTimeWarp == spec->type ) {
/* set the time array to the data of the double-converted nin,
but the nin itself is scheduled to be nixed */
airMopAdd( mop, nin, ( airMopper )nrrdNix, airMopOnOkay );
spline->time = ( double* )( nin->data );
/* now allocate the real control point information */
spline->ncpt = nrrdNew( );
airMopAdd( mop, spline->ncpt, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdMaybeAlloc_va( spline->ncpt, nrrdTypeDouble, 3,
AIR_CAST( size_t, 1 ),
AIR_CAST( size_t, 3 ),
_ncpt->axis[0].size ) ) {
biffMovef( LIMN, NRRD,
"%s: trouble allocating real control points", me );
airMopError( mop ); return NULL;
}
/* and set it all to something useful */
if ( _limnSplineTimeWarpSet( spline ) ) {
biffAddf( LIMN, "%s: trouble setting time warp", me );
airMopError( mop ); return NULL;
}
} else {
/* we set the control point to the double-converted nin, and we're done */
spline->ncpt = nin;
}
airMopOkay( mop );
return spline;
}
limnSpline *
224 limnSplineNix( limnSpline *spline ) {
if ( spline ) {
spline->ncpt = ( Nrrd * )nrrdNuke( spline->ncpt );
spline->time = ( double * )airFree( spline->time );
airFree( spline );
}
return NULL;
}
/*
******** limnSplineNrrdCleverFix
**
** given that the ncpt nrrd for limnSplineNew( ) has to be a particular
** dimension and shape, and given that convenient ways of creating nrrds
** don't always lead to such configurations, we supply some minimal
** cleverness to bridge the gap. As the name implies, you should be
** wary of this function.
**
** The job of this function is NOT to check the validity of a nrrd for
** a given spline. That is up to limnSplineNew( ).
**
** Currently, this function is used by limnHestSpline, but it probably
** won't be used anywhere else within limn.
**
** If requested, we also take a stab at guessing limnSplineInfo.
*/
int
252 limnSplineNrrdCleverFix( Nrrd *nout, Nrrd *nin, int info, int type ) {
static const char me[]="limnSplineNrrdCleverFix";
ptrdiff_t min[3], max[3];
size_t N;
unsigned int wantSize;
Nrrd *ntmpA, *ntmpB;
airArray *mop;
char stmp[AIR_STRLEN_SMALL];
if ( !( nout && nin ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( limnSplineInfo, info )
|| airEnumValCheck( limnSplineType, type ) ) {
biffAddf( LIMN, "%s: invalid spline info ( %d ) or type ( %d )",
me, info, type );
return 1;
}
if ( nrrdCheck( nin ) ) {
biffMovef( LIMN, NRRD, "%s: nrrd has problems", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, ntmpA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmpB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
wantSize = limnSplineInfoSize[info];
switch( nin->dim ) {
case 3:
/* we assume that things are okay */
if ( nrrdCopy( nout, nin ) ) {
biffMovef( LIMN, NRRD, "%s: trouble setting output", me );
airMopError( mop ); return 1;
}
break;
case 2:
N = nin->axis[1].size;
if ( wantSize != nin->axis[0].size ) {
biffAddf( LIMN, "%s: expected axis[0].size %d for info %s, but got %s",
me, wantSize, airEnumStr( limnSplineInfo, info ),
airSprintSize_t( stmp, nin->axis[0].size ) );
airMopError( mop ); return 1;
}
if ( limnSplineTypeTimeWarp == type ) {
/* time-warp handled differently */
if ( nrrdAxesDelete( nout, nin, 0 ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't make data 1-D", me );
airMopError( mop ); return 1;
}
} else {
if ( limnSplineTypeHasImplicitTangents[type] ) {
ELL_3V_SET( min, 0, -1, 0 );
ELL_3V_SET( max, wantSize-1, 1, N-1 );
if ( nrrdAxesInsert( ntmpA, nin, 1 )
|| nrrdPad_va( nout, ntmpA, min, max, nrrdBoundaryPad, 0.0 ) ) {
biffMovef( LIMN, NRRD, "%s: trouble with axinsert/pad", me );
airMopError( mop ); return 1;
}
} else {
/* the post- and pre- point information may be interlaced with the
main control point values */
if ( 1 != AIR_MOD( ( int )N, 3 ) ) {
biffAddf( LIMN,
"%s: axis[1].size must be 1+( multiple of 3 ) when using "
"interlaced tangent information, not %s", me,
airSprintSize_t( stmp, N ) );
airMopError( mop ); return 1;
}
ELL_2V_SET( min, 0, -1 );
ELL_2V_SET( max, wantSize-1, N );
if ( nrrdPad_va( ntmpA, nin, min, max, nrrdBoundaryPad, 0.0 )
|| nrrdAxesSplit( nout, ntmpA, 1, 3, ( N+2 )/3 ) ) {
biffMovef( LIMN, NRRD, "%s: trouble with pad/axsplit", me );
airMopError( mop ); return 1;
}
}
}
break;
case 1:
N = nin->axis[0].size;
if ( limnSplineInfoScalar != info ) {
biffAddf( LIMN, "%s: can't have %s spline with 1-D nrrd", me,
airEnumStr( limnSplineInfo, info ) );
airMopError( mop ); return 1;
}
if ( limnSplineTypeTimeWarp == type ) {
/* nothing fancey needed for time-warp */
if ( nrrdCopy( nout, nin ) ) {
biffMovef( LIMN, NRRD, "%s: trouble setting output", me );
airMopError( mop ); return 1;
}
} else {
if ( limnSplineTypeHasImplicitTangents[type] ) {
ELL_3V_SET( min, 0, -1, 0 );
ELL_3V_SET( max, 0, 1, N-1 );
if ( nrrdAxesInsert( ntmpA, nin, 0 )
|| nrrdAxesInsert( ntmpB, ntmpA, 0 )
|| nrrdPad_va( nout, ntmpB, min, max, nrrdBoundaryPad, 0.0 ) ) {
biffMovef( LIMN, NRRD, "%s: trouble with axinsert/axinsert/pad", me );
airMopError( mop ); return 1;
}
} else {
/* the post- and pre- point information may be interlaced with the
main control point values */
if ( 1 != AIR_MOD( ( int )N, 3 ) ) {
biffAddf( LIMN,
"%s: axis[1].size must be 1+( multiple of 3 ) when using "
"interlaced tangent information, not %s", me,
airSprintSize_t( stmp, N ) );
airMopError( mop ); return 1;
}
ELL_2V_SET( min, 0, -1 );
ELL_2V_SET( max, 0, N+1 );
if ( nrrdAxesInsert( ntmpA, nin, 0 )
|| nrrdPad_va( ntmpB, ntmpA, min, max, nrrdBoundaryPad, 0.0 )
|| nrrdAxesSplit( nout, ntmpB, 1, 3, ( N+2 )/3 ) ) {
biffMovef( LIMN, NRRD, "%s: trouble with axinsert/pad/axsplit", me );
airMopError( mop ); return 1;
}
}
}
break;
default:
biffAddf( LIMN, "%s: input nrrd dim %d baffling", me, nin->dim );
return 1;
}
if ( nrrdCheck( nout ) ) {
biffMovef( LIMN, NRRD, "%s: oops: didn't create valid output", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
limnSpline *
388 limnSplineCleverNew( Nrrd *ncpt, int info, limnSplineTypeSpec *spec ) {
static const char me[]="limnSplineCleverNew";
limnSpline *spline;
Nrrd *ntmp;
airArray *mop;
if ( !( ncpt && spec ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, ntmp = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( limnSplineNrrdCleverFix( ntmp, ncpt, info, spec->type ) ) {
biffAddf( LIMN, "%s: couldn't fix up given control point nrrd", me );
airMopError( mop ); return NULL;
}
if ( !( spline = limnSplineNew( ntmp, info, spec ) ) ) {
biffAddf( LIMN, "%s: couldn't create spline", me );
airMopError( mop ); return NULL;
}
airMopOkay( mop );
return spline;
}
int
414 limnSplineUpdate( limnSpline *spline, Nrrd *_ncpt ) {
static const char me[]="limnSplineUpdate";
Nrrd *ntmp;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( spline && _ncpt ) ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( _ncpt ) ) {
biffMovef( LIMN, NRRD, "%s: given nrrd has problems", me );
return 1;
}
if ( limnSplineTypeTimeWarp == spline->type ) {
if ( !( 1 == _ncpt->dim ) ) {
biffAddf( LIMN, "%s: given nrrd has dimension %d, not 1", me, _ncpt->dim );
return 1;
}
if ( !( spline->ncpt->axis[2].size == _ncpt->axis[0].size ) ) {
biffAddf( LIMN, "%s: have %s time points, but got %s", me,
airSprintSize_t( stmp[0], spline->ncpt->axis[2].size ),
airSprintSize_t( stmp[1], _ncpt->axis[0].size ) );
return 1;
}
} else {
if ( !( nrrdSameSize( spline->ncpt, _ncpt, AIR_TRUE ) ) ) {
biffMovef( LIMN, NRRD, "%s: given ncpt doesn't match original one", me );
return 1;
}
}
if ( limnSplineTypeTimeWarp == spline->type ) {
ntmp = nrrdNew( );
if ( nrrdWrap_va( ntmp, spline->time, nrrdTypeDouble, 1,
_ncpt->axis[0].size )
|| nrrdConvert( ntmp, _ncpt, nrrdTypeDouble ) ) {
biffMovef( LIMN, NRRD, "%s: trouble copying info", me );
nrrdNix( ntmp ); return 1;
}
if ( _limnSplineTimeWarpSet( spline ) ) {
biffAddf( LIMN, "%s: trouble setting time warp", me );
nrrdNix( ntmp ); return 1;
}
nrrdNix( ntmp );
} else {
if ( nrrdConvert( spline->ncpt, _ncpt, nrrdTypeDouble ) ) {
biffMovef( LIMN, NRRD, "%s: trouble converting to internal nrrd", me );
return 1;
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
const char *
_limnSplineTypeStr[LIMN_SPLINE_TYPE_MAX+1] = {
"( unknown_spline_type )",
"linear",
"timewarp",
"hermite",
"cubic-bezier",
"BC"
};
const char *
_limnSplineTypeDesc[LIMN_SPLINE_TYPE_MAX+1] = {
"unknown spline type",
"simple linear interpolation between control points",
"pseudo-Hermite spline for warping time to uniform ( integral ) "
"control point locations",
"Hermite cubic interpolating spline",
"cubic Bezier spline",
"Mitchell-Netravalli BC-family of cubic splines"
};
const char *
_limnSplineTypeStrEqv[] = {
"linear", "lin", "line", "tent",
"timewarp", "time-warp", "warp",
"hermite",
"cubicbezier", "cubic-bezier", "bezier", "bez",
"BC", "BC-spline",
""
};
const int
_limnSplineTypeValEqv[] = {
limnSplineTypeLinear, limnSplineTypeLinear, limnSplineTypeLinear,
limnSplineTypeLinear,
limnSplineTypeTimeWarp, limnSplineTypeTimeWarp, limnSplineTypeTimeWarp,
limnSplineTypeHermite,
limnSplineTypeCubicBezier, limnSplineTypeCubicBezier,
limnSplineTypeCubicBezier, limnSplineTypeCubicBezier,
limnSplineTypeBC, limnSplineTypeBC
};
const airEnum
_limnSplineType = {
"spline-type",
LIMN_SPLINE_TYPE_MAX,
_limnSplineTypeStr, NULL,
_limnSplineTypeDesc,
_limnSplineTypeStrEqv, _limnSplineTypeValEqv,
AIR_FALSE
};
78 const airEnum *const
limnSplineType = &_limnSplineType;
const char *
_limnSplineInfoStr[LIMN_SPLINE_INFO_MAX+1] = {
"( unknown_spline_info )",
"scalar",
"2vector",
"3vector",
"normal",
"4vector",
"quaternion"
};
const char *
_limnSplineInfoDesc[LIMN_SPLINE_INFO_MAX+1] = {
"unknown spline info",
"scalar",
"2-vector",
"3-vector",
"surface normal, interpolated in S^2",
"4-vector, interpolated in R^4",
"quaternion, interpolated in S^3"
};
const char *
_limnSplineInfoStrEqv[] = {
"scalar", "scale", "s", "t",
"2-vector", "2vector", "2vec", "2v", "v2", "vec2", "vector2", "vector-2",
"3-vector", "3vector", "3vec", "3v", "v3", "vec3", "vector3", "vector-3",
"normal", "norm", "n",
"4-vector", "4vector", "4vec", "4v", "v4", "vec4", "vector4", "vector-4",
"quaternion", "quat", "q",
""
};
#define SISS limnSplineInfoScalar
#define SI2V limnSplineInfo2Vector
#define SI3V limnSplineInfo3Vector
#define SINN limnSplineInfoNormal
#define SI4V limnSplineInfo4Vector
#define SIQQ limnSplineInfoQuaternion
const int
_limnSplineInfoValEqv[] = {
SISS, SISS, SISS, SISS,
SI2V, SI2V, SI2V, SI2V, SI2V, SI2V, SI2V, SI2V,
SI3V, SI3V, SI3V, SI3V, SI3V, SI3V, SI3V, SI3V,
SINN, SINN, SINN,
SI4V, SI4V, SI4V, SI4V, SI4V, SI4V, SI4V, SI4V,
SIQQ, SIQQ, SIQQ
};
const airEnum
_limnSplineInfo = {
"spline-info",
LIMN_SPLINE_INFO_MAX,
_limnSplineInfoStr, NULL,
_limnSplineInfoDesc,
_limnSplineInfoStrEqv, _limnSplineInfoValEqv,
AIR_FALSE
};
140 const airEnum *const
limnSplineInfo = &_limnSplineInfo;
/*
******** limnSplineInfoSize[]
**
** gives the number of scalars per "value" for each splineInfo
*/
unsigned int
limnSplineInfoSize[LIMN_SPLINE_INFO_MAX+1] = {
0, /* limnSplineInfoUnknown */
1, /* limnSplineInfoScalar */
2, /* limnSplineInfo2Vector */
3, /* limnSplineInfo3Vector */
3, /* limnSplineInfoNormal */
4, /* limnSplineInfo4Vector */
4 /* limnSplineInfoQuaternion */
};
/*
******** limnSplineTypeHasImplicitTangents[]
**
** this is non-zero when the spline path is determined solely the
** main control point values, without needing additional control
** points ( as in cubic Bezier ) or tangent information ( as in Hermite )
*/
int
limnSplineTypeHasImplicitTangents[LIMN_SPLINE_TYPE_MAX+1] = {
AIR_FALSE, /* limnSplineTypeUnknown */
AIR_TRUE, /* limnSplineTypeLinear */
AIR_FALSE, /* limnSplineTypeTimeWarp */
AIR_FALSE, /* limnSplineTypeHermite */
AIR_FALSE, /* limnSplineTypeCubicBezier */
AIR_TRUE /* limnSplineTypeBC */
};
int
177 limnSplineNumPoints( limnSpline *spline ) {
int ret;
ret = -1;
if ( spline ) {
ret = spline->ncpt->axis[2].size;
}
return ret;
}
double
188 limnSplineMinT( limnSpline *spline ) {
double ret;
ret = AIR_NAN;
if ( spline ) {
ret = spline->time ? spline->time[0] : 0;
}
return ret;
}
double
199 limnSplineMaxT( limnSpline *spline ) {
double ret;
int N;
ret = AIR_NAN;
if ( spline ) {
N = spline->ncpt->axis[2].size;
if ( spline->time ) {
ret = spline->time[N-1];
} else {
ret = spline->loop ? N : N-1;
}
}
return ret;
}
void
216 limnSplineBCSet( limnSpline *spline, double B, double C ) {
if ( spline ) {
spline->B = B;
spline->C = C;
}
}
limnSplineTypeSpec *
225 limnSplineTypeSpecParse( char *_str ) {
static const char me[]="limnSplineTypeSpecParse";
limnSplineTypeSpec *spec;
int type;
double B, C;
char *str, *col, *bcS;
airArray *mop;
if ( !( _str && airStrlen( _str ) ) ) {
biffAddf( LIMN, "%s: got NULL or emptry string", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, str=airStrdup( _str ), airFree, airMopAlways );
col = strchr( str, ':' );
if ( col ) {
*col = 0;
bcS = col+1;
} else {
bcS = NULL;
}
if ( limnSplineTypeUnknown == ( type = airEnumVal( limnSplineType, str ) ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" as spline type", me, str );
airMopError( mop ); return NULL;
}
if ( !( ( limnSplineTypeBC == type ) == !!bcS ) ) {
biffAddf( LIMN, "%s: spline type %s %s, but %s a parameter string %s%s%s",
me,
( limnSplineTypeBC == type ) ? "is" : "is not",
airEnumStr( limnSplineType, limnSplineTypeBC ),
!!bcS ? "got unexpected" : "did not get",
!!bcS ? "\"" : "",
!!bcS ? bcS : "",
!!bcS ? "\"" : "" );
airMopError( mop ); return NULL;
}
if ( limnSplineTypeBC == type ) {
if ( 2 != sscanf( bcS, "%lg, %lg", &B, &C ) ) {
biffAddf( LIMN, "%s: couldn't parse \"B, C\" parameters from \"%s\"", me,
bcS );
airMopError( mop ); return NULL;
}
}
spec = ( limnSplineTypeBC == type
? limnSplineTypeSpecNew( type, B, C )
: limnSplineTypeSpecNew( type ) );
if ( !spec ) {
biffAddf( LIMN, "%s: limnSplineTypeSpec allocation failed", me );
airMopError( mop ); return NULL;
}
airMopOkay( mop );
return spec;
}
limnSpline *
282 limnSplineParse( char *_str ) {
static const char me[]="limnSplineParse";
char *str, *col, *fnameS, *infoS, *typeS, *tmpS;
int info;
limnSpline *spline;
limnSplineTypeSpec *spec;
Nrrd *ninA, *ninB;
airArray *mop;
if ( !( _str && airStrlen( _str ) ) ) {
biffAddf( LIMN, "%s: got NULL or empty string", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, str=airStrdup( _str ), airFree, airMopAlways );
/* find separation between filename and "<splineInfo>:<splineType>[:B, C]" */
col = strchr( str, ':' );
if ( !col ) {
biffAddf( LIMN, "%s: saw no colon separator ( between nrrd filename and "
"spline info ) in \"%s\"", me, _str );
airMopError( mop ); return NULL;
}
fnameS = str;
*col = 0;
tmpS = col+1;
airMopAdd( mop, ninA = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( ninA, fnameS, NULL ) ) {
biffMovef( LIMN, NRRD, "%s: couldn't read control point nrrd:\n", me );
airMopError( mop ); return NULL;
}
/* find separation between splineInfo and "<splineType>[:B, C]" */
col = strchr( tmpS, ':' );
if ( !col ) {
biffAddf( LIMN, "%s: saw no colon separator ( between spline info "
"and spline type ) in \"%s\"", me, tmpS );
airMopError( mop ); return NULL;
}
infoS = tmpS;
*col = 0;
typeS = col+1;
if ( limnSplineInfoUnknown == ( info = airEnumVal( limnSplineInfo, infoS ) ) ) {
biffAddf( LIMN, "%s: couldn't parse \"%s\" as spline info", me, infoS );
airMopError( mop ); return NULL;
}
/* now parse <splineType>[:B, C] */
if ( !( spec = limnSplineTypeSpecParse( typeS ) ) ) {
biffAddf( LIMN, "%s: couldn't parse spline type in \"%s\":\n", me, typeS );
airMopError( mop ); return NULL;
}
if ( limnSplineTypeTimeWarp == spec->type
&& limnSplineInfoScalar != info ) {
biffAddf( LIMN, "%s: can only time-warp %s info, not %s", me,
airEnumStr( limnSplineInfo, limnSplineInfoScalar ),
airEnumStr( limnSplineInfo, info ) );
airMopError( mop ); return NULL;
}
airMopAdd( mop, ninB = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( limnSplineNrrdCleverFix( ninB, ninA, info, spec->type ) ) {
biffAddf( LIMN, "%s: couldn't reshape given nrrd:\n", me );
airMopError( mop ); return NULL;
}
if ( !( spline = limnSplineNew( ninB, info, spec ) ) ) {
biffAddf( LIMN, "%s: couldn't create spline:\n", me );
airMopError( mop ); return NULL;
}
airMopOkay( mop );
return spline;
}
/*
** the spline command-line spline type specification is of the form
** <splineType>[:B, C]
*/
int
361 _limnHestSplineTypeSpecParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[] = "_limnHestSplineTypeSpecParse";
char *err2;
limnSplineTypeSpec **specP;
if ( !( ptr && str && airStrlen( str ) ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
specP = ( limnSplineTypeSpec ** )ptr;
if ( !( *specP = limnSplineTypeSpecParse( str ) ) ) {
err2 = biffGetDone( LIMN );
sprintf( err, "%s: couldn't parse \"%s\":\n", me, str );
strncat( err, err2, AIR_STRLEN_HUGE-1-strlen( err ) );
free( err2 ); return 1;
}
return 0;
}
hestCB
_limnHestSplineTypeSpec = {
sizeof( limnSplineTypeSpec * ),
"spline type specification",
_limnHestSplineTypeSpecParse,
( airMopper )limnSplineTypeSpecNix
};
hestCB *
limnHestSplineTypeSpec = &_limnHestSplineTypeSpec;
/*
** the spline command-line specification is of the form
** <nrrdFileName>:<splineInfo>:<splineType>[:B, C]
*/
int
399 _limnHestSplineParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[] = "_limnHestSplineParse";
char *err2;
limnSpline **splineP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
splineP = ( limnSpline ** )ptr;
if ( !airStrlen( str ) ) {
/* got an empty string, which for now we take as an okay way
to ask for NO spline */
*splineP = NULL;
return 0;
}
if ( !( *splineP = limnSplineParse( str ) ) ) {
err2 = biffGetDone( LIMN );
sprintf( err, "%s: couldn't parse \"%s\":\n", me, str );
strncat( err, err2, AIR_STRLEN_HUGE-1-strlen( err ) );
free( err2 ); return 1;
}
return 0;
}
hestCB
_limnHestSpline = {
sizeof( limnSpline * ),
"spline specification",
_limnHestSplineParse,
( airMopper )limnSplineNix
};
hestCB *
limnHestSpline = &_limnHestSpline;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "teem/limn.h"
char *info = ( "test limnPolyDataClip" );
int
28 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
limnPolyData *pld;
FILE *file;
Nrrd *nin;
double thresh;
int bitflag;
me = argv[0];
hestOptAdd( &hopt, "vi", "nin", airTypeOther, 1, 1, &nin, NULL,
"input values",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "pi", "lpld", airTypeOther, 1, 1, &pld, NULL,
"input polydata",
NULL, NULL, limnHestPolyDataLMPD );
hestOptAdd( &hopt, "th", "thresh", airTypeDouble, 1, 1, &thresh, NULL,
"threshold value" );
hestOptAdd( &hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd",
"output file to save LMPD into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
bitflag = limnPolyDataInfoBitFlag( pld );
fprintf( stderr, "!%s: bitflag = %d\n", me, bitflag );
fprintf( stderr, "!%s: rgba %d, norm %d, tex2 %d\n", me,
( 1 << limnPolyDataInfoRGBA ) & bitflag,
( 1 << limnPolyDataInfoNorm ) & bitflag,
( 1 << limnPolyDataInfoTex2 ) & bitflag );
file = airFopen( outS, stdout, "w" );
if ( !file ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing", me, outS );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( limnPolyDataClip( pld, nin, thresh )
|| limnPolyDataWriteLMPD( file, pld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "test intersection of two lists." );
static unsigned int
29 flipListIntx( unsigned int *dstC,
const unsigned int *_srcA, const unsigned int *_srcB ) {
const unsigned int *srcA, *srcB;
unsigned int numA, numB, numC, idxA, idxB;
numA = _srcA[0];
srcA = _srcA + 1;
numB = _srcB[0];
srcB = _srcB + 1;
numC = 0;
for ( idxA=0; idxA<numA; idxA++ ) {
for ( idxB=0; idxB<numB; idxB++ ) {
if ( srcA[idxA] == srcB[idxB] ) {
dstC[numC++] = srcA[idxA];
}
}
}
return numC;
}
int
50 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
unsigned int *srcA, *srcB, *dstC, *_srcA, *_srcB, numA, numB, numC, idx;
me = argv[0];
hestOptAdd( &hopt, "a", "vals", airTypeUInt, 1, -1, &srcA, NULL,
"list of values", &numA );
hestOptAdd( &hopt, "b", "vals", airTypeUInt, 1, -1, &srcB, NULL,
"list of values", &numB );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
fprintf( stderr, "a =" );
for ( idx=0; idx<numA; idx++ ) {
fprintf( stderr, " %u", srcA[idx] );
}
fprintf( stderr, "\n" );
fprintf( stderr, "b =" );
for ( idx=0; idx<numB; idx++ ) {
fprintf( stderr, " %u", srcB[idx] );
}
fprintf( stderr, "\n" );
_srcA = AIR_CAST( unsigned int*, calloc( 1+numA, sizeof( unsigned int ) ) );
airMopAdd( mop, _srcA, airFree, airMopAlways );
_srcB = AIR_CAST( unsigned int*, calloc( 1+numB, sizeof( unsigned int ) ) );
airMopAdd( mop, _srcB, airFree, airMopAlways );
dstC = AIR_CAST( unsigned int*, calloc( 1+AIR_MAX( numA, numB ),
sizeof( unsigned int ) ) );
airMopAdd( mop, dstC, airFree, airMopAlways );
_srcA[0] = numA;
for ( idx=0; idx<numA; idx++ ) {
_srcA[1+idx] = srcA[idx];
}
_srcB[0] = numB;
for ( idx=0; idx<numB; idx++ ) {
_srcB[1+idx] = srcB[idx];
}
numC = flipListIntx( dstC, _srcA, _srcB );
fprintf( stderr, "intx( a, b ) =" );
for ( idx=0; idx<numC; idx++ ) {
fprintf( stderr, " %u", dstC[idx] );
}
fprintf( stderr, "\n" );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
char *me;
#include "../limn.h"
int
29 main( int argc, char *argv[] ) {
limnLight *lit;
limnCamera *cam;
Nrrd *map, *ppm;
NrrdRange *range;
AIR_UNUSED( argc );
me = argv[0];
cam = limnCameraNew( );
ELL_3V_SET( cam->from, 10, 0, 0 );
ELL_3V_SET( cam->at, 0, 0, 0 );
ELL_3V_SET( cam->up, 0, 0, 1 );
cam->uRange[0] = -( cam->uRange[1] = 4 );
cam->vRange[0] = -( cam->vRange[1] = 3 );
cam->neer = -5;
cam->dist = 0;
cam->faar = 5;
cam->atRelative = AIR_TRUE;
lit = limnLightNew( );
limnLightSet( lit, 0, AIR_TRUE, 1, 0, 0, 1, 0, 0 );
limnLightSet( lit, 1, AIR_TRUE, 0, 1, 0, 0, 1, 0 );
limnLightSet( lit, 2, AIR_TRUE, 0, 0, 1, 0, 0, 1 );
limnLightUpdate( lit, cam );
if ( limnEnvMapFill( map=nrrdNew( ), limnLightDiffuseCB,
limnQN16checker, lit ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( LIMN ) );
exit( 1 );
}
range = nrrdRangeNew( 0, 1 );
if ( nrrdQuantize( ppm=nrrdNew( ), map, range, 8 ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( NRRD ) );
exit( 1 );
}
if ( nrrdSave( "map.ppm", ppm, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( NRRD ) );
exit( 1 );
}
nrrdNuke( map );
nrrdNuke( ppm );
nrrdRangeNix( range );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
#define LPU "lpu"
/* massive copy and paste from teem/src/bin/tend.c ... */
int
30 main( int argc, const char **argv ) {
int i, ret;
const char *me;
char *err, *argv0 = NULL;
hestParm *hparm;
airArray *mop;
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* no harm done in making sure we're sane */
if ( !nrrdSanity( ) ) {
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s: nrrd sanity check FAILED.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, " This means that either nrrd can't work on this "
"platform, or ( more likely )\n" );
fprintf( stderr, " there was an error in the compilation options "
"and variable definitions\n" );
fprintf( stderr, " for how Teem was built here.\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s\n", err = biffGetDone( NRRD ) );
fprintf( stderr, "\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
free( err );
return 1;
}
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_FALSE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->cleverPluralizeOtherY = AIR_TRUE;
hparm->columns = 78;
/* if there are no arguments, then we give general usage information */
if ( 1 >= argc ) {
limnpuUsage( LPU, hparm );
airMopError( mop );
exit( 1 );
}
/* else, we should see if they're asking for a command we know about */
for ( i=0; limnpuCmdList[i]; i++ ) {
if ( !strcmp( argv[1], limnpuCmdList[i]->name ) ) {
break;
}
}
if ( limnpuCmdList[i] ) {
/* yes, we have that command */
/* initialize variables used by the various commands */
argv0 = ( char * )calloc( strlen( LPU ) + strlen( argv[1] ) + 2, sizeof( char ) );
airMopMem( mop, &argv0, airMopAlways );
sprintf( argv0, "%s %s", LPU, argv[1] );
/* run the individual unu program, saving its exit status */
ret = limnpuCmdList[i]->main( argc-2, argv+2, argv0, hparm );
} else {
fprintf( stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for "
"complete list\n", me, argv[1], me );
ret = 1;
}
airMopDone( mop, ret );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
void
27 cb( float rgb[3], float vec[3], void *blah ) {
float r, g, b;
AIR_UNUSED( blah );
ELL_3V_GET( r, g, b, vec );
r = AIR_MAX( 0, r );
g = AIR_MAX( 0, g );
b = AIR_MAX( 0, b );
ELL_3V_SET( rgb, r, g, b );
return;
}
char *me;
int
42 main( int argc, char *argv[] ) {
Nrrd *map, *ppm;
NrrdRange *range;
AIR_UNUSED( argc );
me = argv[0];
if ( limnEnvMapFill( map=nrrdNew( ), cb, limnQN16checker, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( LIMN ) );
exit( 1 );
}
range = nrrdRangeNew( 0, 1 );
if ( nrrdQuantize( ppm=nrrdNew( ), map, range, 8 ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( NRRD ) );
exit( 1 );
}
if ( nrrdSave( "map.ppm", ppm, NULL ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( NRRD ) );
exit( 1 );
}
nrrdNuke( map );
nrrdNuke( ppm );
nrrdRangeNix( range );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Renders an OFF file to an EPS file." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err, *inS, *outS;
limnCamera *cam;
float bg[3], winscale, edgeWidth[5], creaseAngle;
hestOpt *hopt=NULL;
airArray *mop;
limnObject *obj;
limnWindow *win;
Nrrd *nmap;
FILE *file;
int wire, concave, describe, reverse, nobg;
mop = airMopNew( );
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
me = argv[0];
hestOptAdd( &hopt, "i", "input OFF", airTypeString, 1, 1, &inS, NULL,
"input OFF file" );
hestOptAdd( &hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, "4 4 4",
"position of camera, used to determine view vector" );
hestOptAdd( &hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
"camera look-at point, used to determine view vector" );
hestOptAdd( &hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
"camera pseudo-up vector, used to determine view coordinates" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestOptAdd( &hopt, "or", NULL, airTypeInt, 0, 0, &( cam->orthographic ), NULL,
"use orthogonal projection" );
hestOptAdd( &hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
"-1 1", "range in U direction of image plane" );
hestOptAdd( &hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
"-1 1", "range in V direction of image plane" );
hestOptAdd( &hopt, "e", "envmap", airTypeOther, 1, 1, &nmap, "",
"16octa-based environment map",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ws", "winscale", airTypeFloat, 1, 1, &winscale,
"200", "world to points ( PostScript ) scaling" );
hestOptAdd( &hopt, "wire", NULL, airTypeInt, 0, 0, &wire, NULL,
"just do wire-frame rendering" );
hestOptAdd( &hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL,
"use slightly buggy rendering method suitable for "
"concave or self-occluding objects" );
hestOptAdd( &hopt, "reverse", NULL, airTypeInt, 0, 0, &reverse, NULL,
"reverse ordering of vertices per face ( needed if they "
"specified in clockwise order )" );
hestOptAdd( &hopt, "describe", NULL, airTypeInt, 0, 0, &describe, NULL,
"for debugging: list object definition of OFF read" );
hestOptAdd( &hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1",
"background RGB color; each component in range [0.0, 1.0]" );
hestOptAdd( &hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL,
"don't initially fill with background color" );
hestOptAdd( &hopt, "wd", "5 widths", airTypeFloat, 5, 5, edgeWidth,
"0.0 0.0 3.0 2.0 0.0",
"width of edges drawn for five kinds of "
"edges: back non-crease, back crease, "
"silohuette, front crease, front non-crease" );
hestOptAdd( &hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "30",
"dihedral angles greater than this are creases" );
hestOptAdd( &hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps",
"output file to render postscript into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
cam->neer = -0.000000001;
cam->dist = 0;
cam->faar = 0.0000000001;
cam->atRelative = AIR_TRUE;
if ( limnCameraUpdate( cam ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, err = biffGet( LIMN ) );
free( err );
return 1;
}
obj = limnObjectNew( 10, AIR_TRUE );
airMopAdd( mop, obj, ( airMopper )limnObjectNix, airMopAlways );
if ( !( file = airFopen( inS, stdin, "r" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for reading\n", me, inS );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( limnObjectReadOFF( obj, file ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( describe ) {
fprintf( stdout, "----------------- POST-READ -----------------\n" );
limnObjectDescribe( stdout, obj );
fprintf( stdout, "----------------- POST-READ -----------------\n" );
}
if ( reverse ) {
if ( limnObjectFaceReverse( obj ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( describe ) {
fprintf( stdout, "----------------- POST-REVERSE -----------------\n" );
limnObjectDescribe( stdout, obj );
fprintf( stdout, "----------------- POST-REVERSE -----------------\n" );
}
win = limnWindowNew( limnDevicePS );
win->ps.lineWidth[limnEdgeTypeBackFacet] = edgeWidth[0];
win->ps.lineWidth[limnEdgeTypeBackCrease] = edgeWidth[1];
win->ps.lineWidth[limnEdgeTypeContour] = edgeWidth[2];
win->ps.lineWidth[limnEdgeTypeFrontCrease] = edgeWidth[3];
win->ps.lineWidth[limnEdgeTypeFrontFacet] = edgeWidth[4];
win->ps.wireFrame = wire;
win->ps.creaseAngle = creaseAngle;
win->ps.noBackground = nobg;
ELL_3V_COPY( win->ps.bg, bg );
win->file = airFopen( outS, stdout, "w" );
airMopAdd( mop, win, ( airMopper )limnWindowNix, airMopAlways );
win->scale = winscale;
if ( limnObjectRender( obj, cam, win )
|| ( concave
? limnObjectPSDrawConcave( obj, cam, nmap, win )
: limnObjectPSDraw( obj, cam, nmap, win ) ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
fclose( win->file );
if ( describe ) {
fprintf( stdout, "----------------- POST-RENDER -----------------\n" );
limnObjectDescribe( stdout, obj );
fprintf( stdout, "----------------- POST-RENDER -----------------\n" );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Plot!" );
typedef struct {
FILE *file;
double psc;
double bbox[4];
int nobg;
double bgColor[3];
double maxX, maxY; /* set by plotPreamble */
} plotPS;
typedef struct {
/* these are allocated per-graph BY HEST */
double *graphThick, *graphGray, *dotDiameter, *dotGray;
/* also hest-allocated */
char *axisHorzLabel, *axisVertLabel;
double *horzTick, *vertTick;
int numHorzTick, numVertTick;
int labelHorzTick, labelVertTick;
double axisThick, tickThick, tickLabelSize, tickLength;
double horzTickLabelOffset, vertTickLabelOffset;
double dotInnerDiameterFraction;
double axisOrig[2];
double dbox[4];
} plotParm;
#define PPS_X( x ) AIR_AFFINE( pps->bbox[0], ( x ), pps->bbox[2], 0, pps->maxX )
#define PPS_Y( y ) AIR_AFFINE( pps->bbox[1], ( y ), pps->bbox[3], 0, pps->maxY )
#define PPS_S( s ) AIR_DELTA( pps->bbox[1], ( s ), pps->bbox[3], 0, pps->maxY )
void
60 plotPreamble( plotPS *pps, plotParm *pparm ) {
AIR_UNUSED( pparm );
pps->maxX = pps->psc*( pps->bbox[2] - pps->bbox[0] );
pps->maxY = pps->psc*( pps->bbox[3] - pps->bbox[1] );
fprintf( pps->file, "%%!PS-Adobe-2.0 EPSF-2.0\n" );
fprintf( pps->file, "%%%%Creator: plot\n" );
fprintf( pps->file, "%%%%Pages: 1\n" );
fprintf( pps->file, "%%%%BoundingBox: 0 0 %d %d\n",
( int )( pps->maxX ),
( int )( pps->maxY ) );
fprintf( pps->file, "%%%%EndComments\n" );
fprintf( pps->file, "%%%%EndProlog\n" );
fprintf( pps->file, "%%%%Page: 1 1\n" );
fprintf( pps->file, "gsave\n" );
fprintf( pps->file, "0 0 moveto\n" );
fprintf( pps->file, "%g 0 lineto\n", pps->maxX );
fprintf( pps->file, "%g %g lineto\n", pps->maxX, pps->maxY );
fprintf( pps->file, "0 %g lineto\n", pps->maxY );
fprintf( pps->file, "closepath\n" );
if ( !pps->nobg ) {
fprintf( pps->file, "gsave %g %g %g setrgbcolor fill grestore\n",
pps->bgColor[0], pps->bgColor[1], pps->bgColor[2] );
}
fprintf( pps->file, "clip\n" );
fprintf( pps->file, "gsave newpath\n" );
fprintf( pps->file, "1 setlinejoin\n" );
fprintf( pps->file, "1 setlinecap\n" );
fprintf( pps->file, "/M {moveto} bind def\n" );
fprintf( pps->file, "/L {lineto} bind def\n" );
fprintf( pps->file, "/W {setlinewidth} bind def\n" );
fprintf( pps->file, "/F {fill} bind def\n" );
fprintf( pps->file, "/S {stroke} bind def\n" );
fprintf( pps->file, "/CP {closepath} bind def\n" );
fprintf( pps->file, "/RGB {setrgbcolor} bind def\n" );
fprintf( pps->file, "/Gr {setgray} bind def\n" );
fprintf( pps->file, "\n" );
fprintf( pps->file, "0 setlinecap\n" );
return;
}
void
103 plotWidth( plotPS *pps, plotParm *pparm, double width ) {
AIR_UNUSED( pparm );
fprintf( pps->file, "%g W\n", pps->psc*width );
return;
}
void
111 plotGray( plotPS *pps, plotParm *pparm, double gray ) {
AIR_UNUSED( pparm );
fprintf( pps->file, "%g Gr\n", gray );
return;
}
void
119 plotLine( plotPS *pps, plotParm *pparm,
double x0, double y0, double x1, double y1 ) {
AIR_UNUSED( pparm );
fprintf( pps->file, "%g %g M\n", PPS_X( x0 ), PPS_Y( y0 ) );
fprintf( pps->file, "%g %g L S\n", PPS_X( x1 ), PPS_Y( y1 ) );
return;
}
void
129 plotLabel( plotPS *pps, plotParm *pparm, int centered,
double x, double y, char *str ) {
fprintf( pps->file, "gsave\n" );
plotWidth( pps, pparm, 0 );
if ( centered ) {
fprintf( pps->file,
"0 0 M ( %s ) false charpath pathbbox\n"
"exch 4 1 roll sub 2 div 3 1 roll sub -2 div\n" /* don't ask */
"newpath %g add exch %g add M ( %s ) show\n",
str, PPS_X( x ), PPS_Y( y ), str );
} else {
fprintf( pps->file, "%g %g M ( %s ) show\n", PPS_X( x ), PPS_Y( y ), str );
}
fprintf( pps->file, "grestore\n" );
return;
}
void
148 plotAxes( plotPS *pps, plotParm *pparm, Nrrd *ndata ) {
double axX, axY, xx, yy, toff;
char buff[AIR_STRLEN_SMALL];
int ti;
AIR_UNUSED( ndata );
axX = AIR_AFFINE( pparm->dbox[0], pparm->axisOrig[0], pparm->dbox[2],
pps->bbox[0], pps->bbox[2] );
axY = AIR_AFFINE( pparm->dbox[1], pparm->axisOrig[1], pparm->dbox[3],
pps->bbox[1], pps->bbox[3] );
plotGray( pps, pparm, 0 );
plotWidth( pps, pparm, pparm->axisThick );
plotLine( pps, pparm,
axX, pps->bbox[1], axX, pps->bbox[3] );
plotLine( pps, pparm,
pps->bbox[0], axY, pps->bbox[2], axY );
if ( strlen( pparm->axisHorzLabel ) || strlen( pparm->axisVertLabel ) ) {
fprintf( pps->file, "/Helvetica findfont 20 scalefont setfont\n" );
if ( strlen( pparm->axisHorzLabel ) ) {
plotLabel( pps, pparm, AIR_FALSE,
pps->bbox[2], axY, pparm->axisHorzLabel );
}
if ( strlen( pparm->axisVertLabel ) ) {
plotLabel( pps, pparm, AIR_FALSE,
axX, pps->bbox[3], pparm->axisVertLabel );
}
}
if ( pparm->numHorzTick ) {
if ( pparm->tickThick > 0 ) {
toff = pparm->tickLength/2;
plotGray( pps, pparm, 0 );
plotWidth( pps, pparm, pparm->tickThick );
for ( ti=0; ti<pparm->numHorzTick; ti++ ) {
xx = AIR_AFFINE( pparm->dbox[0], pparm->horzTick[ti], pparm->dbox[2],
pps->bbox[0], pps->bbox[2] );
plotLine( pps, pparm, xx, axY - toff, xx, axY + toff );
}
}
if ( pparm->tickLabelSize ) {
fprintf( pps->file, "/Helvetica findfont %g scalefont setfont\n",
pparm->tickLabelSize );
for ( ti=0; ti<pparm->numHorzTick; ti++ ) {
xx = AIR_AFFINE( pparm->dbox[0], pparm->horzTick[ti], pparm->dbox[2],
pps->bbox[0], pps->bbox[2] );
yy = axY + pparm->horzTickLabelOffset;
sprintf( buff, "%g", pparm->horzTick[ti] );
plotLabel( pps, pparm, AIR_TRUE, xx, yy, buff );
}
}
}
if ( pparm->numVertTick ) {
if ( pparm->tickThick > 0 ) {
toff = pparm->tickLength/2;
plotGray( pps, pparm, 0 );
plotWidth( pps, pparm, pparm->tickThick );
for ( ti=0; ti<pparm->numVertTick; ti++ ) {
yy = AIR_AFFINE( pparm->dbox[1], pparm->vertTick[ti], pparm->dbox[3],
pps->bbox[1], pps->bbox[3] );
plotLine( pps, pparm, axX - toff, yy, axX + toff, yy );
}
}
if ( pparm->tickLabelSize ) {
fprintf( pps->file, "/Helvetica findfont %g scalefont setfont\n",
pparm->tickLabelSize );
for ( ti=0; ti<pparm->numVertTick; ti++ ) {
yy = AIR_AFFINE( pparm->dbox[1], pparm->vertTick[ti], pparm->dbox[3],
pps->bbox[1], pps->bbox[3] );
xx = axX + pparm->vertTickLabelOffset;
sprintf( buff, "%g", pparm->vertTick[ti] );
plotLabel( pps, pparm, AIR_TRUE, xx, yy, buff );
}
}
}
}
void
226 plotGraph( plotPS *pps, plotParm *pparm, Nrrd **ndata, int nidx ) {
int ii, npts;
double xx, yy, *data, val;
if ( !( pparm->graphThick[nidx] > 0 ) ) {
return;
}
data = ( double * )( ndata[nidx]->data );
npts = ndata[nidx]->axis[1].size;
plotGray( pps, pparm, pparm->graphGray[nidx] );
fprintf( pps->file, "%g W\n", pps->psc*pparm->graphThick[nidx] );
for ( ii=0; ii<npts; ii++ ) {
val = data[ii];
xx = AIR_AFFINE( 0, ii, npts-1,
ndata[nidx]->axis[1].min, ndata[nidx]->axis[1].max );
xx = AIR_AFFINE( pparm->dbox[0], xx, pparm->dbox[2],
pps->bbox[0], pps->bbox[2] );
yy = AIR_AFFINE( pparm->dbox[1], val, pparm->dbox[3],
pps->bbox[1], pps->bbox[3] );
fprintf( pps->file, "%g %g %s\n", PPS_X( xx ), PPS_Y( yy ),
ii ? "L" : "M" );
}
fprintf( pps->file, "S\n" );
}
void
253 plotDots( plotPS *pps, plotParm *pparm, Nrrd **ndata, int nidx ) {
int ii, npts;
double xx, yy, orad, irad, *data, val;
if ( !( pparm->dotDiameter[nidx] > 0 ) ) {
return;
}
fprintf( pps->file, "gsave\n" );
fprintf( pps->file, "newpath\n" );
plotWidth( pps, pparm, 0 );
data = ( double * )( ndata[nidx]->data );
npts = ndata[nidx]->axis[1].size;
orad = pparm->dotDiameter[nidx]/2;
irad = pparm->dotInnerDiameterFraction*orad;
for ( ii=0; ii<npts; ii++ ) {
val = data[ii];
xx = AIR_AFFINE( 0, ii, npts-1,
ndata[nidx]->axis[1].min, ndata[nidx]->axis[1].max );
xx = AIR_AFFINE( pparm->dbox[0], xx, pparm->dbox[2],
pps->bbox[0], pps->bbox[2] );
yy = AIR_AFFINE( pparm->dbox[1], val, pparm->dbox[3],
pps->bbox[1], pps->bbox[3] );
plotGray( pps, pparm, pparm->dotGray[nidx] );
fprintf( pps->file, "%g %g %g 0 360 arc closepath fill\n",
PPS_X( xx ), PPS_Y( yy ), PPS_S( orad ) );
if ( irad ) {
plotGray( pps, pparm, 1.0 );
fprintf( pps->file, "%g %g %g 0 360 arc closepath fill\n",
PPS_X( xx ), PPS_Y( yy ), PPS_S( irad ) );
}
}
fprintf( pps->file, "grestore\n" );
}
void
289 plotEpilog( plotPS *pps, plotParm *pparm ) {
AIR_UNUSED( pparm );
fprintf( pps->file, "grestore\n" );
fprintf( pps->file, "grestore\n" );
fprintf( pps->file, "%%%%Trailer\n" );
return;
}
int
299 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int numGrth, numDtdi, numGrgr, numDtgr, numNrrd, ni;
plotPS pps;
plotParm pparm;
Nrrd **_ndata, **ndata;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "data", airTypeOther, 1, -1, &_ndata, NULL,
"input nrrd containing data to plot",
&numNrrd, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "dbox", "minX minY maxX maxY", airTypeDouble,
4, 4, pparm.dbox, NULL,
"bounding box, in data space" );
hestOptAdd( &hopt, "bbox", "minX minY maxX maxY", airTypeDouble,
4, 4, pps.bbox, NULL,
"bounding box, in graph space" );
hestOptAdd( &hopt, "psc", "PS scale", airTypeDouble, 1, 1, &( pps.psc ), "300",
"scaling from graph space to PostScript points" );
hestOptAdd( &hopt, "nobg", NULL, airTypeInt, 0, 0, &( pps.nobg ), NULL,
"don't fill with background color" );
hestOptAdd( &hopt, "bg", "background", airTypeDouble, 3, 3,
&( pps.bgColor ), "1 1 1",
"background RGB color; each component in range [0.0, 1.0]" );
hestOptAdd( &hopt, "grth", "graph thickness", airTypeDouble,
1, -1, &( pparm.graphThick ), "0.01",
"thickness of line for graph, or \"0\" for no graph line",
&numGrth );
hestOptAdd( &hopt, "grgr", "graph gray", airTypeDouble,
1, -1, &( pparm.graphGray ), "0",
"grayscale to use for graph", &numGrgr );
hestOptAdd( &hopt, "dtdi", "dot diameter", airTypeDouble,
1, -1, &( pparm.dotDiameter ), "0.1",
"radius of dot drawn at data points, or \"0\" for no dots",
&numDtdi );
hestOptAdd( &hopt, "dtgr", "dot gray", airTypeDouble,
1, -1, &( pparm.dotGray ), "0",
"grayscale to use for dots", &numDtgr );
hestOptAdd( &hopt, "dtid", "dot inner diam frac", airTypeDouble,
1, 1, &( pparm.dotInnerDiameterFraction ), "0.0",
"fractional radius of white dot drawn within dot" );
hestOptAdd( &hopt, "tihz", "pos", airTypeDouble,
0, -1, &( pparm.horzTick ), "",
"locations for tickmarks on horizontal axis",
&( pparm.numHorzTick ) );
hestOptAdd( &hopt, "tivt", "pos", airTypeDouble,
0, -1, &( pparm.vertTick ), "",
"locations for tickmarks on vertical axis",
&( pparm.numVertTick ) );
hestOptAdd( &hopt, "tiho", "offset", airTypeDouble,
1, 1, &( pparm.horzTickLabelOffset ), "0",
"horizontal tick label offset" );
hestOptAdd( &hopt, "tivo", "offset", airTypeDouble,
1, 1, &( pparm.vertTickLabelOffset ), "0",
"vertical tick label offset" );
hestOptAdd( &hopt, "tils", "size", airTypeDouble,
1, 1, &( pparm.tickLabelSize ), "0",
"font size for labels on tick marks, or \"0\" for no labels" );
hestOptAdd( &hopt, "tith", "tick thickness", airTypeDouble,
1, 1, &( pparm.tickThick ), "0.01",
"thickness of lines for tick marks" );
hestOptAdd( &hopt, "tiln", "tick length", airTypeDouble,
1, 1, &( pparm.tickLength ), "0.08",
"length of lines for tick marks" );
hestOptAdd( &hopt, "axth", "axis thickness", airTypeDouble,
1, 1, &( pparm.axisThick ), "0.01",
"thickness of lines for axes" );
hestOptAdd( &hopt, "axor", "axis origin", airTypeDouble,
2, 2, &( pparm.axisOrig ), "0 0",
"origin of lines for axes, in data space" );
hestOptAdd( &hopt, "axhl", "horiz axis label", airTypeString,
1, 1, &( pparm.axisHorzLabel ), "",
"label on horizontal axis" );
hestOptAdd( &hopt, "axvl", "vert axis label", airTypeString,
1, 1, &( pparm.axisVertLabel ), "",
"label on vertical axis" );
hestOptAdd( &hopt, "o", "output PS", airTypeString,
1, 1, &outS, "out.ps",
"output file to render postscript into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( numGrth == numDtdi
&& numDtdi == numGrgr
&& numGrgr == numDtgr ) ) {
fprintf( stderr, "%s: number of arguments given to grth ( %d ), dtdi ( %d ), "
"grgr ( %d ), dtgr ( %d ) not all equal\n", me,
numGrth, numDtdi, numGrgr, numDtgr );
airMopError( mop ); return 1;
}
if ( !( numNrrd == numGrth ) ) {
fprintf( stderr, "%s: number of nrrds ( %d ) != number graph options ( %d )\n",
me, numNrrd, numGrth );
airMopError( mop ); return 1;
}
/* check nrrds */
for ( ni=0; ni<numNrrd; ni++ ) {
if ( !( ( 1 == _ndata[ni]->dim || 2 == _ndata[ni]->dim )
&& nrrdTypeBlock != _ndata[ni]->type ) ) {
fprintf( stderr, "%s: input nrrd must be 1-D or 2-D array of scalars",
me );
airMopError( mop ); return 1;
}
}
ndata = ( Nrrd** )calloc( numNrrd, sizeof( Nrrd * ) );
airMopAdd( mop, ndata, airFree, airMopAlways );
for ( ni=0; ni<numNrrd; ni++ ) {
ndata[ni] = nrrdNew( );
airMopAdd( mop, ndata[ni], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ndata[ni], _ndata[ni], nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't convert input %d to %s:\n%s\n",
me, ni, airEnumStr( nrrdType, nrrdTypeDouble ), err );
airMopError( mop ); return 1;
}
if ( 1 == ndata[ni]->dim ) {
if ( nrrdAxesInsert( ndata[ni], ndata[ni], 0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't insert axis 0 on nrrd %d:\n%s\n",
me, ni, err );
airMopError( mop ); return 1;
}
}
/* currently assuming node centering */
if ( !AIR_EXISTS( ndata[ni]->axis[1].min ) ) {
ndata[ni]->axis[1].min = 0;
}
if ( !AIR_EXISTS( ndata[ni]->axis[1].max ) ) {
ndata[ni]->axis[1].max = ndata[ni]->axis[1].size-1;
}
}
if ( !( pps.file = airFopen( outS, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't open output file\n", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, pps.file, ( airMopper )airFclose, airMopAlways );
plotPreamble( &pps, &pparm );
plotAxes( &pps, &pparm, ndata[0] );
for ( ni=0; ni<numNrrd; ni++ ) {
plotGraph( &pps, &pparm, ndata, ni );
plotDots( &pps, &pparm, ndata, ni );
}
plotEpilog( &pps, &pparm );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Save a single ellipsoid or superquadric into an OFF file." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
float p[3], q[4], mR[9], eval[3]={0, 0, 0}, scale[3], len, sh, cl, cp, qA, qB;
float matA[16], matB[16], os, rad, AB[2];
hestOpt *hopt=NULL;
airArray *mop;
limnObject *obj;
limnLook *look; int lookRod, lookSoid;
int partIdx=-1; /* sssh */
int res, axis, sphere;
FILE *file;
me = argv[0];
hestOptAdd( &hopt, "sc", "scalings", airTypeFloat, 3, 3, scale, "1 1 1",
"axis-aligned scaling to do on ellipsoid" );
hestOptAdd( &hopt, "AB", "A, B exponents", airTypeFloat, 2, 2, AB, "nan nan",
"Directly set the A, B parameters to the superquadric surface, "
"over-riding the default behavior of determining them from the "
"scalings \"-sc\" as superquadric tensor glyphs" );
hestOptAdd( &hopt, "os", "over-all scaling", airTypeFloat, 1, 1, &os, "1",
"over-all scaling ( multiplied by scalings )" );
hestOptAdd( &hopt, "sh", "superquad sharpness", airTypeFloat, 1, 1, &sh, "0",
"how much to sharpen edges as a "
"function of differences between eigenvalues" );
hestOptAdd( &hopt, "sphere", NULL, airTypeInt, 0, 0, &sphere, NULL,
"use a sphere instead of a superquadric" );
hestOptAdd( &hopt, "p", "x y z", airTypeFloat, 3, 3, p, "0 0 0",
"location in quaternion quotient space" );
hestOptAdd( &hopt, "r", "radius", airTypeFloat, 1, 1, &rad, "0.015",
"black axis cylinder radius ( or 0.0 to not drawn these )" );
hestOptAdd( &hopt, "res", "resolution", airTypeInt, 1, 1, &res, "25",
"tesselation resolution for both glyph and axis cylinders" );
hestOptAdd( &hopt, "o", "output OFF", airTypeString, 1, 1, &outS, "out.off",
"output file to save OFF into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
obj = limnObjectNew( 100, AIR_FALSE );
airMopAdd( mop, obj, ( airMopper )limnObjectNix, airMopAlways );
/* create limnLooks for ellipsoid and for rods */
lookSoid = limnObjectLookAdd( obj );
look = obj->look + lookSoid;
ELL_4V_SET( look->rgba, 1, 1, 1, 1 );
ELL_3V_SET( look->kads, 0.2, 0.8, 0 );
look->spow = 0;
lookRod = limnObjectLookAdd( obj );
look = obj->look + lookRod;
ELL_4V_SET( look->rgba, 0, 0, 0, 1 );
ELL_3V_SET( look->kads, 1, 0, 0 );
look->spow = 0;
ELL_4V_SET( q, 1, p[0], p[1], p[2] );
ELL_4V_NORM( q, q, len );
ell_q_to_3m_f( mR, q );
if ( AIR_EXISTS( AB[0] ) && AIR_EXISTS( AB[1] ) ) {
qA = AB[0];
qB = AB[1];
axis = 2;
} else {
ELL_3V_SCALE( scale, os, scale );
ELL_3V_COPY( eval, scale );
ELL_SORT3( eval[0], eval[1], eval[2], cl );
cl = ( eval[0] - eval[1] )/( eval[0] + eval[1] + eval[2] );
cp = 2*( eval[1] - eval[2] )/( eval[0] + eval[1] + eval[2] );
if ( cl > cp ) {
axis = ELL_MAX3_IDX( scale[0], scale[1], scale[2] );
qA = pow( 1-cp, sh );
qB = pow( 1-cl, sh );
} else {
axis = ELL_MIN3_IDX( scale[0], scale[1], scale[2] );
qA = pow( 1-cl, sh );
qB = pow( 1-cp, sh );
}
/*
fprintf( stderr, "eval = %g %g %g -> cl=%g %s cp=%g -> axis = %d\n",
eval[0], eval[1], eval[2], cl, cl > cp ? ">" : "<", cp, axis );
*/
}
if ( sphere ) {
partIdx = limnObjectPolarSphereAdd( obj, lookSoid,
0, 2*res, res );
} else {
partIdx = limnObjectPolarSuperquadAdd( obj, lookSoid,
axis, qA, qB, 2*res, res );
}
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, scale[0], scale[1], scale[2] );
ell_4m_post_mul_f( matA, matB );
ELL_43M_INSET( matB, mR );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
if ( rad ) {
partIdx = limnObjectCylinderAdd( obj, lookRod, 0, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, ( 1-eval[0] )/2, rad, rad );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, ( 1+eval[0] )/2, 0.0, 0.0 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectCylinderAdd( obj, lookRod, 0, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, ( 1-eval[0] )/2, rad, rad );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, -( 1+eval[0] )/2, 0.0, 0.0 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectCylinderAdd( obj, lookRod, 1, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, rad, ( 1-eval[1] )/2, rad );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, ( 1+eval[1] )/2, 0.0 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectCylinderAdd( obj, lookRod, 1, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, rad, ( 1-eval[1] )/2, rad );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, -( 1+eval[1] )/2, 0.0 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectCylinderAdd( obj, lookRod, 2, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, rad, rad, ( 1-eval[2] )/2 );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 0.0, ( 1+eval[2] )/2 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectCylinderAdd( obj, lookRod, 2, res );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, rad, rad, ( 1-eval[2] )/2 );
ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 0.0, -( 1+eval[2] )/2 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
}
file = airFopen( outS, stdout, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( limnObjectWriteOFF( file, obj ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
test/tbc -s 0.95 -i s.txt -loop -n 13 -m 800 -t 55 170 > ! out.ps
*/
/* s.txt:
-10 -20
-20 -10
-10 0
-20 10
-10 20
0 10
10 20
20 10
10 0
20 -10
10 -20
0 -10
10 0
0 10
-10 0
0 -10
*/
#include "../limn.h"
char *info = ( "Visualize the space of BC cubics with a spline." );
int
53 main( int argc, const char *argv[] ) {
const char *me;
char *err;
limnSpline *spline;
hestOpt *hopt=NULL;
airArray *mop;
int bi, ci, i, N, M, loop;
Nrrd *ncptA, *ncptB, *nout;
double *out, minT, maxT, scale, tran[2], B, C;
limnSplineTypeSpec *spec;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "spline data", airTypeOther, 1, 1, &ncptA, NULL,
"data points for the spline, must be 2-vectors",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "loop", NULL, airTypeInt, 0, 0, &loop, NULL,
"the last control point is in fact the first" );
hestOptAdd( &hopt, "n", "N", airTypeInt, 1, 1, &N, "10",
"how many samples along each edge of BC space" );
hestOptAdd( &hopt, "m", "M", airTypeInt, 1, 1, &M, "512",
"the number of sample points at which to evalute the spline" );
hestOptAdd( &hopt, "t", "tx ty", airTypeDouble, 2, 2, tran, "0.0 0.0",
"translation for drawing" );
hestOptAdd( &hopt, "s", "scale", airTypeDouble, 1, 1, &scale, "1.0",
"scaling for drawing" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 2 == ncptA->dim && 2 == ncptA->axis[0].size ) ) {
fprintf( stderr, "%s: didn't get a 2-D 2xN nrrd )\n", me );
airMopError( mop );
return 1;
}
spec = limnSplineTypeSpecNew( limnSplineTypeBC, 0, 0 );
airMopAdd( mop, ncptB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( limnSplineNrrdCleverFix( ncptB, ncptA, limnSplineInfo2Vector,
limnSplineTypeBC )
|| !( spline = limnSplineNew( ncptB, limnSplineInfo2Vector, spec ) ) ) {
airMopAdd( mop, err=biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
spline->loop = loop;
airMopAdd( mop, spline, ( airMopper )limnSplineNix, airMopAlways );
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
minT = limnSplineMinT( spline );
maxT = limnSplineMaxT( spline );
/* try one for error checking */
if ( limnSplineSample( nout, spline, minT, M, maxT ) ) {
airMopAdd( mop, err=biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
printf( "%%!\n" );
printf( "1 setlinewidth\n" );
printf( "%g %g translate\n", tran[0], tran[1] );
for ( ci=0; ci<N; ci++ ) {
C = AIR_AFFINE( 0, ci, N-1, 0.0, 1.0 );
for ( bi=0; bi<N; bi++ ) {
B = AIR_AFFINE( 0, bi, N-1, 0.0, 1.0 );
limnSplineBCSet( spline, B, C );
limnSplineSample( nout, spline, minT, M, maxT );
out = ( double* )( nout->data ); /* shouldn't actually change */
printf( "gsave\n" );
printf( "%g %g translate\n", bi*500.0/( N-1 ), ci*500.0/( N-1 ) );
printf( "%g %g scale\n", scale, scale );
printf( "%g %g moveto\n", out[0 + 2*0], out[1 + 2*0] );
for ( i=1; i<M; i++ ) {
printf( "%g %g lineto\n", out[0 + 2*i], out[1 + 2*i] );
}
if ( spline->loop ) {
printf( "closepath\n" );
}
printf( "stroke\n" );
printf( "grestore\n" );
}
}
printf( "showpage\n" );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
const char *info = ( "dumb little demonstration of calculating the "
"world-to-view and view-to-world transforms given "
"from, at, and up locations." );
int
31 main( int argc, const char *argv[] ) {
const char *me;
char *err;
limnCamera *cam;
float mat[16];
hestOpt *hopt=NULL;
airArray *mop;
mop = airMopNew( );
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
me = argv[0];
hestOptAdd( &hopt, "fr", "eye pos", airTypeDouble, 3, 3, cam->from,
NULL, "camera eye point" );
hestOptAdd( &hopt, "at", "at pos", airTypeDouble, 3, 3, cam->at,
"0 0 0", "camera look-at point" );
hestOptAdd( &hopt, "up", "up dir", airTypeDouble, 3, 3, cam->up,
"0 0 1", "camera pseudo up vector" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
cam->neer = -1;
cam->dist = 0;
cam->faar = 1;
cam->atRelative = AIR_TRUE;
if ( limnCameraUpdate( cam ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, err = biffGet( LIMN ) );
free( err );
return 1;
}
printf( "%s: W2V:\n", me );
ELL_4M_COPY( mat, cam->W2V );
ell_4m_print_f( stdout, mat );
printf( "\n" );
printf( "%s: V2W:\n", me );
ELL_4M_COPY( mat, cam->V2W );
ell_4m_print_f( stdout, mat );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Works with camanim.tcl to test camera path splines." );
#define _LIMNMAGIC "LIMN0000"
int
31 _limnReadCamanim( int imgSize[2], limnCamera **keycamP, double **timeP,
unsigned int *numKeysP, FILE *fin ) {
char me[]="_limnReadCamanim", err[AIR_STRLEN_MED];
char line[AIR_STRLEN_HUGE];
unsigned int ki;
double *tmp, *dwell, di, dn, df, fr[3], at[3], up[3], va;
airArray *mop, *camA, *dwellA;
if ( !( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE )
&& !strcmp( _LIMNMAGIC, line ) ) ) {
sprintf( err, "%s: couldn't read first line or it wasn't \"%s\"",
me, _LIMNMAGIC );
biffAdd( LIMN, err ); return 1;
}
if ( !( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE )
&& 2 == ( airStrtrans( airStrtrans( line, '{', ' ' ), '}', ' ' ),
sscanf( line, "imgSize %d %d", imgSize+0, imgSize+1 ) ) ) ) {
sprintf( err, "%s: couldn't read second line or it wasn't "
"\"imgSize <sizeX> <sizeY>\"", me );
biffAdd( LIMN, err ); return 1;
}
mop = airMopNew( );
camA = airArrayNew( ( void ** )keycamP, numKeysP, sizeof( limnCamera ), 1 );
dwellA = airArrayNew( ( void ** )&dwell, NULL, sizeof( double ), 1 );
airMopAdd( mop, camA, ( airMopper )airArrayNix, airMopAlways );
airMopAdd( mop, dwellA, ( airMopper )airArrayNuke, airMopAlways );
while ( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE ) ) {
airStrtrans( airStrtrans( line, '{', ' ' ), '}', ' ' );
ki = airArrayLenIncr( camA, 1 );
airArrayLenIncr( dwellA, 1 );
if ( 14 != sscanf( line, "cam.di %lg cam.at %lg %lg %lg "
"cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg "
"relDwell %lg cam.fr %lg %lg %lg",
&di, at+0, at+1, at+2,
up+0, up+1, up+2, &dn, &df, &va,
dwell+ki, fr+0, fr+1, fr+2 ) ) {
sprintf( err, "%s: trouble parsing line %d: \"%s\"", me, ki, line );
biffAdd( LIMN, err ); airMopError( mop ); return 1;
}
( *keycamP )[ki].neer = dn;
( *keycamP )[ki].faar = df;
( *keycamP )[ki].dist = di;
ELL_3V_COPY( ( *keycamP )[ki].from, fr );
ELL_3V_COPY( ( *keycamP )[ki].at, at );
ELL_3V_COPY( ( *keycamP )[ki].up, up );
( *keycamP )[ki].fov = va;
( *keycamP )[ki].aspect = ( double )imgSize[0]/imgSize[1];
( *keycamP )[ki].atRelative = AIR_FALSE;
( *keycamP )[ki].orthographic = AIR_FALSE;
( *keycamP )[ki].rightHanded = AIR_TRUE;
}
tmp = ( double* )calloc( *numKeysP, sizeof( double ) );
airMopAdd( mop, tmp, airFree, airMopAlways );
*timeP = ( double* )calloc( *numKeysP, sizeof( double ) );
for ( ki=0; ki<*numKeysP; ki++ ) {
dwell[ki] = AIR_CLAMP( 0, dwell[ki], 2 );
tmp[ki] = tan( AIR_AFFINE( -0.01, dwell[ki], 2.01, 0.0, AIR_PI/2 ) );
}
( *timeP )[0] = 0;
for ( ki=1; ki<*numKeysP; ki++ ) {
( *timeP )[ki] = ( *timeP )[ki-1] + ( tmp[ki-1] + tmp[ki] )/2;
}
for ( ki=0; ki<*numKeysP; ki++ ) {
( *timeP )[ki] /= ( *timeP )[*numKeysP-1];
}
airMopOkay( mop );
return 0;
}
int
105 _limnWriteCamanim( FILE *fout, int imgSize[2],
limnCamera *cam, int numFrames ) {
/* char me[]="_limnWriteCamanim", err[AIR_STRLEN_MED]; */
int fi;
fprintf( fout, "%s\n", _LIMNMAGIC );
fprintf( fout, "imgSize {%d %d}\n", imgSize[0], imgSize[1] );
for ( fi=0; fi<numFrames; fi++ ) {
fprintf( fout, "cam.di %g cam.at {%g %g %g } "
"cam.up {%g %g %g } cam.dn %g cam.df %g cam.va %g "
"relDwell 1.0 cam.fr {%g %g %g }\n",
cam[fi].dist,
cam[fi].at[0], cam[fi].at[1], cam[fi].at[2],
cam[fi].up[0], cam[fi].up[1], cam[fi].up[2],
cam[fi].neer, cam[fi].faar, cam[fi].fov,
cam[fi].from[0], cam[fi].from[1], cam[fi].from[2] );
}
return 0;
}
int
126 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
char *inS, *outS, *err;
limnCamera *keycam, *cam;
limnSplineTypeSpec *quatType, *posType, *distType, *viewType;
double *time;
FILE *fin, *fout;
int N, imgSize[2], trackWhat;
unsigned int numKeys;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "input", airTypeString, 1, 1, &inS, NULL,
"keyframe output from camanim.tcl" );
hestOptAdd( &hopt, "n", "# frames", airTypeInt, 1, 1, &N, "128",
"number of frames in output" );
hestOptAdd( &hopt, "t", "track what", airTypeEnum, 1, 1, &trackWhat, "both",
"what to track", NULL, limnCameraPathTrack );
hestOptAdd( &hopt, "q", "spline", airTypeOther, 1, 1,
&quatType, "tent", "spline type for quaternions",
NULL, NULL, limnHestSplineTypeSpec );
hestOptAdd( &hopt, "p", "spline", airTypeOther, 1, 1,
&posType, "tent", "spline type for from/at/up",
NULL, NULL, limnHestSplineTypeSpec );
hestOptAdd( &hopt, "d", "spline", airTypeOther, 1, 1,
&distType, "tent", "spline type for image plane distances",
NULL, NULL, limnHestSplineTypeSpec );
hestOptAdd( &hopt, "v", "spline", airTypeOther, 1, 1,
&viewType, "tent", "spline type for image fov and aspect",
NULL, NULL, limnHestSplineTypeSpec );
hestOptAdd( &hopt, "o", "output", airTypeString, 1, 1, &outS, NULL,
"frame info for camanim.tcl" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( fin = airFopen( inS, stdin, "r" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for reading\n", me, inS );
airMopError( mop ); return 1;
}
if ( !( fout = airFopen( outS, stdout, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing\n", me, outS );
airMopError( mop ); return 1;
}
airMopAdd( mop, fin, ( airMopper )airFclose, airMopAlways );
airMopAdd( mop, fout, ( airMopper )airFclose, airMopAlways );
if ( _limnReadCamanim( imgSize, &keycam, &time, &numKeys, fin ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble reading keyframe file:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopAdd( mop, keycam, airFree, airMopAlways );
airMopAdd( mop, time, airFree, airMopAlways );
cam = ( limnCamera * )calloc( N, sizeof( limnCamera ) );
airMopAdd( mop, cam, airFree, airMopAlways );
if ( limnCameraPathMake( cam, N, keycam, time, numKeys, trackWhat,
quatType, posType, distType, viewType ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making camera path:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( _limnWriteCamanim( fout, imgSize, cam, N ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing frame file:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "throw-away." );
int
29 main( int argc, char *argv[] ) {
char *me, *err;
FILE *file;
limnPolyData *lmpd;
AIR_UNUSED( argc );
me = argv[0];
lmpd = limnPolyDataNew( );
file = fopen( "in.lmpd", "r" );
if ( limnPolyDataReadLMPD( lmpd, file ) ) {
err = biffGetDone( LIMN ),
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
return 1;
}
fclose( file );
file = fopen( "out.vtk", "w" );
if ( limnPolyDataWriteVTK( file, lmpd ) ) {
err = biffGetDone( LIMN );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
return 1;
}
fclose( file );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Render something in postscript." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
limnCamera *cam;
float matA[16], matB[16], winscale, edgeWidth[5];
hestOpt *hopt=NULL;
airArray *mop;
limnObject *obj;
limnLook *look; int lookIdx;
limnWindow *win;
int partIdx, wire, concave;
Nrrd *nmap;
mop = airMopNew( );
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
me = argv[0];
hestOptAdd( &hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, "4 4 4",
"position of camera, used to determine view vector" );
hestOptAdd( &hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
"camera look-at point, used to determine view vector" );
hestOptAdd( &hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
"camera pseudo-up vector, used to determine view coordinates" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestOptAdd( &hopt, "or", NULL, airTypeInt, 0, 0, &( cam->orthographic ), NULL,
"use orthogonal projection" );
hestOptAdd( &hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
"-1 1", "range in U direction of image plane" );
hestOptAdd( &hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
"-1 1", "range in V direction of image plane" );
hestOptAdd( &hopt, "e", "envmap", airTypeOther, 1, 1, &nmap, NULL,
"16checker-based environment map",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ws", "winscale", airTypeFloat, 1, 1, &winscale,
"200", "world to points ( PostScript ) scaling" );
hestOptAdd( &hopt, "wire", NULL, airTypeInt, 0, 0, &wire, NULL,
"just do wire-frame rendering" );
hestOptAdd( &hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL,
"use slightly buggy rendering method suitable for "
"concave or self-occluding objects" );
hestOptAdd( &hopt, "wd", "5 widths", airTypeFloat, 5, 5, edgeWidth,
"0.0 0.0 3.0 2.0 0.0",
"width of edges drawn for five kinds of "
"edges: back non-crease, back crease, "
"silohuette, front crease, front non-crease" );
hestOptAdd( &hopt, "o", "output PS", airTypeString, 1, 1, &outS, "out.ps",
"output file to render postscript into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
cam->neer = -0.000000001;
cam->dist = 0;
cam->faar = 0.0000000001;
cam->atRelative = AIR_TRUE;
if ( limnCameraUpdate( cam ) ) {
fprintf( stderr, "%s: trouble:\n%s\n", me, err = biffGet( LIMN ) );
free( err );
return 1;
}
obj = limnObjectNew( 10, AIR_TRUE );
airMopAdd( mop, obj, ( airMopper )limnObjectNix, airMopAlways );
/* create limnLooks for diffuse ( #0 ) and flat ( #1 ) shading */
lookIdx = airArrayLenIncr( obj->lookArr, 2 );
look = obj->look + lookIdx + 0;
ELL_4V_SET( look->rgba, 1, 1, 1, 1 );
ELL_3V_SET( look->kads, 0, 1, 0 );
look->spow = 0;
look = obj->look + lookIdx + 1;
ELL_4V_SET( look->rgba, 1, 1, 1, 1 );
ELL_3V_SET( look->kads, 1, 0, 0 );
look->spow = 0;
/* X axis: rod */
partIdx = limnObjectCylinderAdd( obj, 0, 0, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 1, 0.2, 0.2 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 1.3, 0.0, 0.0 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
/* Y axis: rod + ball */
partIdx = limnObjectCylinderAdd( obj, 0, 1, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 0.2, 1, 0.2 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 1.3, 0.0 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectPolarSphereAdd( obj, 0, 0, 32, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 0.28, 0.28, 0.28 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 2.6, 0.0 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
/* Z axis: rod + ball + ball */
partIdx = limnObjectCylinderAdd( obj, 0, 2, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 0.2, 0.2, 1 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 0.0, 1.3 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectPolarSphereAdd( obj, 0, 1, 32, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 0.28, 0.28, 0.28 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 0.0, 2.6 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
partIdx = limnObjectPolarSphereAdd( obj, 0, 2, 32, 16 );
ELL_4M_IDENTITY_SET( matA );
ELL_4M_SCALE_SET( matB, 0.28, 0.28, 0.28 ); ell_4m_post_mul_f( matA, matB );
ELL_4M_TRANSLATE_SET( matB, 0.0, 0.0, 3.2 ); ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
win = limnWindowNew( limnDevicePS );
win->scale = winscale;
win->ps.wireFrame = wire;
win->ps.lineWidth[limnEdgeTypeBackFacet] = edgeWidth[0];
win->ps.lineWidth[limnEdgeTypeBackCrease] = edgeWidth[1];
win->ps.lineWidth[limnEdgeTypeContour] = edgeWidth[2];
win->ps.lineWidth[limnEdgeTypeFrontCrease] = edgeWidth[3];
win->ps.lineWidth[limnEdgeTypeFrontFacet] = edgeWidth[4];
win->file = fopen( outS, "w" );
airMopAdd( mop, win, ( airMopper )limnWindowNix, airMopAlways );
if ( limnObjectRender( obj, cam, win )
|| ( concave
? limnObjectPSDrawConcave( obj, cam, nmap, win )
: limnObjectPSDraw( obj, cam, nmap, win ) ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
fclose( win->file );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
const char *info = ( "inspect QN schemes." );
int
30 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
char *outS;
unsigned int reso;
int qni;
Nrrd *nqn;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "s", "size", airTypeUInt, 1, 1, &reso, "256",
"resolution" );
hestOptAdd( &hopt, "q", "which", airTypeInt, 1, 1, &qni, NULL,
"which quantization scheme" );
hestOptAdd( &hopt, "o", "out", airTypeString, 1, 1, &outS, "-",
"output image" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nqn = nrrdNew( );
airMopAdd( mop, nqn, ( airMopper )nrrdNuke, airMopAlways );
if ( limnQNDemo( nqn, reso, qni ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nqn, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../limn.h"
char *info = ( "Save a triangular piece of an image to an EPS file. "
"You might want to ilk -t 1, -0.5, 0, 0, 0.866, 0 -k tent "
"-0 u:0, 1 -b pad -bg 0 before you use this. " );
int
31 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
NrrdIoState *nio;
FILE *file;
char *outS;
Nrrd *nin;
float width, scale, hack, minX, maxX, minY, maxY;
int gray, sx, sy, labels;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input image. Must be SQUARE 8-bit RGB or gray",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "w", "width", airTypeFloat, 1, 1, &width, "0.0",
"border width to put around triangle, in pixels, "
"or \"0\" to not have any border" );
hestOptAdd( &hopt, "labels", NULL, airTypeInt, 0, 0, &labels, NULL,
"put little labels on things; fix with psfrag in LaTeX" );
hestOptAdd( &hopt, "o", "output EPS", airTypeString, 1, 1, &outS, NULL,
"output file to render postscript into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 2 == nin->dim || 3 == nin->dim ) ) {
fprintf( stderr, "%s: input nrrd must be 2-D or 3-D ( not %d-D )\n", me,
nin->dim );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeUChar == nin->type ) ) {
fprintf( stderr, "%s: input nrrd must be type %s ( not %s )\n", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, nin->type ) );
airMopError( mop ); return 1;
}
sx = ( 2 == nin->dim ? nin->axis[0].size : nin->axis[1].size );
sy = ( 2 == nin->dim ? nin->axis[1].size : nin->axis[2].size );
gray = 2 == nin->dim || 1 == nin->axis[0].size;
if ( !( sx == sy ) ) {
fprintf( stderr, "%s: image must be square ( not %d x %d )\n", me, sx, sy );
airMopError( mop ); return 1;
}
if ( !( file = airFopen( outS, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing", me, outS );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
hack = sqrt( 3 )/2;
/* sorry, copied from nrrd/formatEPS.c */
minX = 0.5;
maxX = 8.0;
minY = 5.50 - 7.5*hack*sy/sx/2;
maxY = 5.50 + 7.5*hack*sy/sx/2;
scale = 7.5/sx;
minX *= 72; minY *= 72;
maxX *= 72; maxY *= 72;
scale *= 72;
fprintf( file, "%%!PS-Adobe-3.0 EPSF-3.0\n" );
fprintf( file, "%%%%Creator: hairy pathetic monster\n" );
fprintf( file, "%%%%Title: raving lunatic\n" );
fprintf( file, "%%%%Pages: 1\n" );
fprintf( file, "%%%%BoundingBox: %d %d %d %d\n",
( int )floor( minX ), ( int )floor( minY ),
( int )ceil( maxX ), ( int )ceil( maxY ) );
fprintf( file, "%%%%HiResBoundingBox: %g %g %g %g\n",
minX, minY, maxX, maxY );
fprintf( file, "%%%%EndComments\n" );
fprintf( file, "%%%%BeginProlog\n" );
fprintf( file, "%% linestr creates an empty string to hold "
"one scanline\n" );
fprintf( file, "/linestr %d string def\n", sx*( gray ? 1 : 3 ) );
fprintf( file, "%%%%EndProlog\n" );
fprintf( file, "%%%%Page: 1 1\n" );
fprintf( file, "gsave\n" );
fprintf( file, "%g %g moveto\n", minX, minY );
fprintf( file, "%g %g lineto\n", maxX, minY );
fprintf( file, "%g %g lineto\n",
( minX + maxX )/2, minY + hack*( maxX - minX ) );
fprintf( file, "closepath\n" );
fprintf( file, "clip\n" );
fprintf( file, "gsave newpath\n" );
fprintf( file, "%g %g translate\n", minX, minY );
fprintf( file, "%g %g scale\n", sx*scale, sy*scale );
fprintf( file, "%d %d 8\n", sx, sy );
fprintf( file, "[%d 0 0 -%d 0 %d]\n", sx, sy, sy );
fprintf( file, "{currentfile linestr readhexstring pop} %s\n",
gray ? "image" : "false 3 colorimage" );
nrrdEncodingHex->write( file, nin->data, nrrdElementNumber( nin ),
nin, nio );
nio->dataFile = NULL;
fprintf( file, "\n" );
fprintf( file, "grestore\n" );
if ( width ) {
fprintf( file, "%g %g moveto\n", minX, minY );
fprintf( file, "%g %g lineto\n", maxX, minY );
fprintf( file, "%g %g lineto\n",
( minX + maxX )/2, minY + hack*( maxX - minX ) );
fprintf( file, "closepath\n" );
fprintf( file, "%g setlinewidth 0 setgray stroke\n",
2*width*scale );
}
if ( labels ) {
/* happily, psfrag doesn't respect the clipping path */
fprintf( file, "/Helvetica findfont 20 scalefont setfont\n" );
fprintf( file, "%g %g moveto ( A ) show\n", maxX, minY );
fprintf( file, "%g %g moveto ( B ) show\n", ( minX + maxX )/2,
minY + hack*( maxX - minX ) );
fprintf( file, "%g %g moveto ( C ) show\n", minX, minY );
}
fprintf( file, "grestore\n" );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
test/tspline -loop -t 300 300 -s 10 -i s.txt:2v:bc:1, 0 -m 1000 > ! out.ps
*/
/* s.txt:
-10 -20
-20 -10
-10 0
-20 10
-10 20
0 10
10 20
20 10
10 0
20 -10
10 -20
0 -10
10 0
0 10
-10 0
0 -10
*/
#include "../limn.h"
char *info = ( "Test limnSplines by drawing postscript curves. "
"As such, the only limnSpline allowed is 2vector. "
"The output is written to standard out." );
int
55 main( int argc, const char *argv[] ) {
const char *me;
char *err;
limnSpline *spline, *warp;
hestOpt *hopt=NULL;
airArray *mop;
int i, M, ret, pause, loop;
Nrrd *nout, *ntmp;
double *out, minT, maxT, scale, tran[2];
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "spline", airTypeOther, 1, 1, &spline, NULL,
"the spline that we want to sample", NULL, NULL, limnHestSpline );
hestOptAdd( &hopt, "w", "timewarp", airTypeOther, 1, 1, &warp, "",
"how to ( optionally ) warp the spline domain",
NULL, NULL, limnHestSpline );
hestOptAdd( &hopt, "loop", NULL, airTypeInt, 0, 0, &loop, NULL,
"the last control point is in fact the first" );
hestOptAdd( &hopt, "m", "M", airTypeInt, 1, 1, &M, "512",
"the number of sample points at which to evalute the spline" );
hestOptAdd( &hopt, "t", "tx ty", airTypeDouble, 2, 2, tran, "0.0 0.0",
"translation for drawing" );
hestOptAdd( &hopt, "s", "scale", airTypeDouble, 1, 1, &scale, "1.0",
"scaling for drawing" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
spline->loop = loop;
if ( !( limnSplineInfo2Vector == spline->info ) ) {
fprintf( stderr, "%s: sorry, can only have %s info for PostScript\n",
me, airEnumStr( limnSplineInfo, limnSplineInfo2Vector ) );
airMopError( mop );
return 1;
}
if ( warp ) {
warp->loop = loop;
if ( !( limnSplineTypeTimeWarp == warp->type ) ) {
fprintf( stderr, "%s: %s spline isn't; its %s\n", me,
airEnumStr( limnSplineType, limnSplineTypeTimeWarp ),
airEnumStr( limnSplineType, warp->type ) );
airMopError( mop );
return 1;
}
if ( loop ) {
if ( !( limnSplineNumPoints( warp ) == 1 + limnSplineNumPoints( spline ) ) ) {
fprintf( stderr, "%s: # warp points ( %d ) needs to be 1 more than "
"# spline points ( %d ) for looping\n", me,
limnSplineNumPoints( warp ), limnSplineNumPoints( spline ) );
airMopError( mop );
return 1;
}
} else {
if ( !( limnSplineNumPoints( warp ) == limnSplineNumPoints( spline ) ) ) {
fprintf( stderr, "%s: # warp points ( %d ) != # spline points ( %d )\n", me,
limnSplineNumPoints( warp ), limnSplineNumPoints( spline ) );
airMopError( mop );
return 1;
}
}
}
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( warp ) {
minT = limnSplineMinT( warp );
maxT = limnSplineMaxT( warp );
ret = ( limnSplineSample( ntmp, warp, minT, M, maxT )
|| limnSplineNrrdEvaluate( nout, spline, ntmp ) );
} else {
minT = limnSplineMinT( spline );
maxT = limnSplineMaxT( spline );
ret = limnSplineSample( nout, spline, minT, M, maxT );
}
if ( ret ) {
airMopAdd( mop, err=biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
out = ( double* )( nout->data );
pause = M/150;
printf( "%%!\n" );
printf( "1 setlinewidth\n" );
printf( "%g %g moveto\n",
scale*out[0 + 2*0] + tran[0], scale*out[1 + 2*0] + tran[1] );
printf( "gsave\n" );
printf( "0.2 setlinewidth\n" );
printf( "currentpoint newpath 3 0 360 arc stroke\n" );
printf( "grestore\n" );
for ( i=1; i<M; i++ ) {
printf( "%g %g lineto\n",
scale*out[0 + 2*i] + tran[0], scale*out[1 + 2*i] + tran[1] );
if ( 0 == AIR_MOD( i, pause ) ) {
printf( "gsave\n" );
printf( "0.2 setlinewidth\n" );
printf( "currentpoint newpath 3 0 360 arc stroke\n" );
printf( "grestore\n" );
}
}
printf( "stroke\n" );
printf( "showpage\n" );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "limn.h"
int
28 limnObjectWorldHomog( limnObject *obj ) {
static const char me[]="limnObjectWorldHomog";
unsigned int vertIdx;
limnVertex *vert;
float h;
if ( !obj ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
h = AIR_CAST( float, 1.0/vert->world[3] );
ELL_3V_SCALE( vert->world, h, vert->world );
vert->world[3] = 1.0;
ELL_3V_NORM_TT( vert->worldNormal, float, vert->worldNormal, h );
}
return 0;
}
int
50 limnObjectFaceNormals( limnObject *obj, int space ) {
static const char me[]="limnObjectFaceNormals";
unsigned int vii, faceIdx;
limnFace *face;
limnVertex *vert0, *vert1, *vert2;
float vec1[3], vec2[3], cross[3], nn[3], norm;
if ( space != limnSpaceWorld && space != obj->vertSpace ) {
biffAddf( LIMN, "%s: desired ( %s ) != object ( %s ) space", me,
airEnumStr( limnSpace, space ),
airEnumStr( limnSpace, obj->vertSpace ) );
return 1;
}
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->face + faceIdx;
/* add up cross products at all vertices */
ELL_3V_SET( nn, 0, 0, 0 );
for ( vii=0; vii<face->sideNum; vii++ ) {
vert0 = obj->vert + face->vertIdx[vii];
vert1 = ( obj->vert
+ face->vertIdx[AIR_MOD( ( int )vii+1, ( int )face->sideNum )] );
vert2 = ( obj->vert
+ face->vertIdx[AIR_MOD( ( int )vii-1, ( int )face->sideNum )] );
if ( limnSpaceWorld == space ) {
ELL_3V_SUB( vec1, vert1->world, vert0->world );
ELL_3V_SUB( vec2, vert2->world, vert0->world );
}
else {
ELL_3V_SUB( vec1, vert1->coord, vert0->coord );
ELL_3V_SUB( vec2, vert2->coord, vert0->coord );
}
ELL_3V_CROSS( cross, vec1, vec2 );
ELL_3V_ADD2( nn, nn, cross );
}
if ( limnSpaceWorld == space ) {
ELL_3V_NORM_TT( face->worldNormal, float, nn, norm );
/*
printf( "%s: worldNormal[%d] = %g %g %g\n", me, faceIdx,
face->worldNormal[0], face->worldNormal[1],
face->worldNormal[2] );
*/
}
else {
ELL_3V_NORM_TT( face->screenNormal, float, nn, norm );
/*
printf( "%s: sn[%d] = %g %g %g\n", me, faceIdx,
f->sn[0], f->sn[1], f->sn[2] );
*/
}
}
return 0;
}
int
106 limnObjectVertexNormals( limnObject *obj ) {
/* static const char me[]="limnObjectVertexNormals"; */
unsigned int vertIdx, vertIdxIdx, faceIdx;
limnVertex *vert;
limnFace *face;
double norm;
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
ELL_3V_SET( vert->worldNormal, 0, 0, 0 );
}
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->face + faceIdx;
for ( vertIdxIdx=0; vertIdxIdx<face->sideNum; vertIdxIdx++ ) {
vert = obj->vert + face->vertIdx[vertIdxIdx];
ELL_3V_INCR( vert->worldNormal, face->worldNormal );
}
}
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
ELL_3V_NORM_TT( vert->worldNormal, float, vert->worldNormal, norm );
}
return 0;
}
int
132 _limnObjectViewTransform( limnObject *obj, limnCamera *cam ) {
unsigned int vertIdx;
limnVertex *vert;
float d;
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
ELL_4MV_MUL_TT( vert->coord, float, cam->W2V, vert->world );
d = AIR_CAST( float, 1.0/vert->world[3] );
ELL_4V_SCALE( vert->coord, d, vert->coord );
/*
printf( "%s: w[%d] = %g %g %g %g --> v = %g %g %g\n",
"_limnObjectVTransform",
pi, p->w[0], p->w[1], p->w[2], p->w[3], p->v[0], p->v[1], p->v[2] );
*/
}
obj->vertSpace = limnSpaceView;
return 0;
}
int
153 _limnObjectScreenTransform( limnObject *obj, limnCamera *cam ) {
static const char me[]="_limnObjectScreenTransform";
unsigned int vertIdx;
limnVertex *vert;
float d;
if ( limnSpaceView != obj->vertSpace ) {
biffAddf( LIMN, "%s: object's verts in %s ( not %s ) space", me,
airEnumStr( limnSpace, obj->vertSpace ),
airEnumStr( limnSpace, limnSpaceView ) );
return 1;
}
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
d = ( cam->orthographic
? 1.0f
: AIR_CAST( float, cam->vspDist/vert->coord[2] ) );
vert->coord[0] *= d;
vert->coord[1] *= d;
/* coord[2] unchanged */
/*
printf( "%s: v[%d] = %g %g %g --> s = %g %g %g\n", "_limnObjectSTransform",
pi, p->v[0], p->v[1], p->v[2], p->s[0], p->s[1], p->s[2] );
*/
}
obj->vertSpace = limnSpaceScreen;
return 0;
}
int
183 _limnObjectDeviceTransform( limnObject *obj, limnCamera *cam,
limnWindow *win ) {
static const char me[]="_limnObjectDeviceTransform";
unsigned int vertIdx;
limnVertex *vert;
float wy0, wy1, wx0, wx1, t;
if ( limnSpaceScreen != obj->vertSpace ) {
biffAddf( LIMN, "%s: object's verts in %s ( not %s ) space", me,
airEnumStr( limnSpace, obj->vertSpace ),
airEnumStr( limnSpace, limnSpaceScreen ) );
return 1;
}
wx0 = 0;
wx1 = AIR_CAST( float, ( cam->uRange[1] - cam->uRange[0] )*win->scale );
wy0 = 0;
wy1 = AIR_CAST( float, ( cam->vRange[1] - cam->vRange[0] )*win->scale );
ELL_4V_SET( win->bbox, wx0, wy0, wx1, wy1 );
if ( win->yFlip ) {
ELL_SWAP2( wy0, wy1, t );
}
for ( vertIdx=0; vertIdx<obj->vertNum; vertIdx++ ) {
vert = obj->vert + vertIdx;
vert->coord[0] = AIR_CAST( float, AIR_AFFINE( cam->uRange[0], vert->coord[0],
cam->uRange[1], wx0, wx1 ) );
vert->coord[1] = AIR_CAST( float, AIR_AFFINE( cam->vRange[0], vert->coord[1],
cam->vRange[1], wy0, wy1 ) );
/* coord[2] unchanged */
/*
printf( "%s: s[%d] = %g %g --> s = %g %g\n", "_limnObjectDTransform",
pi, p->s[0], p->s[1], p->d[0], p->d[1] );
*/
}
obj->vertSpace = limnSpaceDevice;
return 0;
}
int
221 limnObjectSpaceTransform( limnObject *obj, limnCamera *cam,
limnWindow *win, int space ) {
static const char me[]="limnObjectSpaceTransform";
int E=0;
/* HEY: deal with cam->orthographic */
switch( space ) {
case limnSpaceView:
E = _limnObjectViewTransform( obj, cam );
break;
case limnSpaceScreen:
E = _limnObjectScreenTransform( obj, cam );
break;
case limnSpaceDevice:
E = _limnObjectDeviceTransform( obj, cam, win );
break;
default:
biffAddf( LIMN, "%s: space %d unknown or unimplemented\n", me, space );
return 1;
break;
}
if ( E ) {
biffAddf( LIMN, "%s: trouble", me );
return 1;
}
return 0;
}
int
251 limnObjectPartTransform( limnObject *obj, unsigned int partIdx,
float xform[16] ) {
unsigned int vertIdxIdx;
limnPart *part;
limnVertex *vert;
float tmp[4];
part= obj->part[partIdx];
for ( vertIdxIdx=0; vertIdxIdx<part->vertIdxNum; vertIdxIdx++ ) {
vert = obj->vert + part->vertIdx[vertIdxIdx];
ELL_4MV_MUL( tmp, xform, vert->world );
ELL_4V_COPY( vert->world, tmp );
}
return 0;
}
int
269 _limnPartDepthCompare( const void *_a, const void *_b ) {
limnPart *const *a;
limnPart *const *b;
a = ( limnPart *const * )_a;
b = ( limnPart *const * )_b;
return AIR_COMPARE( ( *b )->depth, ( *a )->depth );
}
int
279 limnObjectDepthSortParts( limnObject *obj ) {
limnPart *part;
limnVertex *vert;
limnFace *face;
limnEdge *edge;
unsigned int partIdx, ii;
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
part = obj->part[partIdx];
part->depth = 0;
for ( ii=0; ii<part->vertIdxNum; ii++ ) {
vert = obj->vert + part->vertIdx[ii];
part->depth += vert->coord[2];
}
part->depth /= part->vertIdxNum;
}
qsort( obj->part, obj->partNum, sizeof( limnPart* ), _limnPartDepthCompare );
/* re-assign partIdx, post-sorting */
for ( partIdx=0; partIdx<obj->partNum; partIdx++ ) {
part = obj->part[partIdx];
for ( ii=0; ii<part->edgeIdxNum; ii++ ) {
edge = obj->edge + part->edgeIdx[ii];
edge->partIdx = partIdx;
}
for ( ii=0; ii<part->faceIdxNum; ii++ ) {
face = obj->face + part->faceIdx[ii];
face->partIdx = partIdx;
}
}
return 0;
}
int
315 _limnFaceDepthCompare( const void *_a, const void *_b ) {
limnFace *const*a;
limnFace *const*b;
a = ( limnFace *const* )_a;
b = ( limnFace *const* )_b;
return -AIR_COMPARE( ( *a )->depth, ( *b )->depth );
}
int
325 limnObjectDepthSortFaces( limnObject *obj ) {
limnFace *face;
limnVertex *vert;
unsigned int faceIdx, vii;
obj->faceSort = AIR_MALLOC( obj->faceNum, limnFace * );
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->face + faceIdx;
face->depth = 0;
for ( vii=0; vii<face->sideNum; vii++ ) {
vert = obj->vert + face->vertIdx[vii];
face->depth += vert->coord[2];
}
face->depth /= face->sideNum;
obj->faceSort[faceIdx] = face;
}
qsort( obj->faceSort, obj->faceNum,
sizeof( limnFace * ), _limnFaceDepthCompare );
return 0;
}
int
349 limnObjectFaceReverse( limnObject *obj ) {
static const char me[]="limnObjectFaceReverse";
limnFace *face;
unsigned int faceIdx, sii;
int *buff;
if ( !obj ) {
biffAddf( LIMN, "%s: got NULL pointer", me );
return 1;
}
buff = NULL;
for ( faceIdx=0; faceIdx<obj->faceNum; faceIdx++ ) {
face = obj->face + faceIdx;
buff = ( int * )calloc( face->sideNum, sizeof( int ) );
if ( !( buff ) ) {
biffAddf( LIMN, "%s: couldn't allocate %d side buffer for face %d\n",
me, face->sideNum, faceIdx );
return 1;
}
memcpy( buff, face->vertIdx, face->sideNum*sizeof( int ) );
for ( sii=0; sii<face->sideNum; sii++ ) {
face->vertIdx[sii] = buff[face->sideNum-1-sii];
}
memcpy( buff, face->edgeIdx, face->sideNum*sizeof( int ) );
for ( sii=0; sii<face->sideNum; sii++ ) {
face->edgeIdx[sii] = buff[face->sideNum-1-sii];
}
free( buff );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mex.h"
#include <teem/nrrd.h>
mxClassID
30 typeNtoM( int ntype ) {
mxClassID mtype;
switch( ntype ) {
case nrrdTypeChar:
mtype = mxINT8_CLASS;
break;
case nrrdTypeUChar:
mtype = mxUINT8_CLASS;
break;
case nrrdTypeShort:
mtype = mxINT16_CLASS;
break;
case nrrdTypeUShort:
mtype = mxUINT16_CLASS;
break;
case nrrdTypeInt:
mtype = mxINT32_CLASS;
break;
case nrrdTypeUInt:
mtype = mxUINT32_CLASS;
break;
case nrrdTypeLLong:
mtype = mxINT64_CLASS;
break;
case nrrdTypeULLong:
mtype = mxUINT64_CLASS;
break;
case nrrdTypeFloat:
mtype = mxSINGLE_CLASS;
break;
case nrrdTypeDouble:
mtype = mxDOUBLE_CLASS;
break;
default:
mtype = mxUNKNOWN_CLASS;
break;
}
return mtype;
}
71 void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
char me[]="nrrdLoad", *filename, *errPtr, errBuff[AIR_STRLEN_MED];
int filenameLen, sizeI[NRRD_DIM_MAX];
mxClassID mtype;
size_t sizeZ[NRRD_DIM_MAX];
unsigned int axIdx;
Nrrd *nrrd;
NrrdIoState *nio;
airArray *mop;
if ( !( 1 == nrhs && mxIsChar( prhs[0] ) ) ) {
sprintf( errBuff, "%s: requires one string argument ( the name of the file )", me );
mexErrMsgTxt( errBuff );
}
mop = airMopNew( );
filenameLen = mxGetM( prhs[0] )*mxGetN( prhs[0] )+1;
filename = mxCalloc( filenameLen, sizeof( mxChar ) ); /* managed by Matlab */
mxGetString( prhs[0], filename, filenameLen );
nrrd = nrrdNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNix, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nrrdIoStateSet( nio, nrrdIoStateSkipData, AIR_TRUE );
/* read header, but no data */
if ( nrrdLoad( nrrd, filename, nio ) ) {
errPtr = biffGetDone( NRRD );
airMopAdd( mop, errPtr, airFree, airMopAlways );
sprintf( errBuff, "%s: trouble reading NRRD header:\n%s", me, errPtr );
airMopError( mop );
mexErrMsgTxt( errBuff );
}
mtype = typeNtoM( nrrd->type );
if ( mxUNKNOWN_CLASS == mtype ) {
sprintf( errBuff, "%s: sorry, can't handle type %s ( %d )", me,
airEnumStr( nrrdType, nrrd->type ), nrrd->type );
airMopError( mop );
mexErrMsgTxt( errBuff );
}
/* allocate matlab array based on nrrd struct */
for ( axIdx=0; axIdx<nrrd->dim; axIdx++ ) {
sizeI[axIdx] = nrrd->axis[axIdx].size;
}
plhs[0]=mxCreateNumericArray( nrrd->dim, sizeI, mtype, mxREAL );
/* copy data pointer */
nrrd->data = mxGetPr( plhs[0] );
/* read second time, now loading data */
if ( nrrdLoad( nrrd, filename, NULL ) ) {
errPtr = biffGetDone( NRRD );
airMopAdd( mop, errPtr, airFree, airMopAlways );
sprintf( errBuff, "%s: trouble reading NRRD:\n%s", me, errPtr );
airMopError( mop );
mexErrMsgTxt( errBuff );
}
airMopOkay( mop );
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mex.h"
#include <teem/nrrd.h>
28 void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
char me[]="nrrdLoadOrientation", *filename, *errPtr, errBuff[AIR_STRLEN_MED];
int filenameLen, sizeI[NRRD_DIM_MAX];
mxClassID mtype;
size_t sizeZ[NRRD_DIM_MAX];
unsigned int axIdx;
Nrrd *nrrd;
NrrdIoState *nio;
airArray *mop;
unsigned int domainAxisNum, domainAxisIdx[NRRD_DIM_MAX], axisIdx,
rowIdx, colIdx;
double spacing, spaceDir[NRRD_SPACE_DIM_MAX];
int spacingStatus;
if ( !( 1 == nrhs && mxIsChar( prhs[0] ) ) ) {
sprintf( errBuff, "%s: requires one string argument ( the name of the file )",
me );
mexErrMsgTxt( errBuff );
}
mop = airMopNew( );
filenameLen = mxGetM( prhs[0] )*mxGetN( prhs[0] )+1;
filename = mxCalloc( filenameLen, sizeof( mxChar ) ); /* managed by Matlab */
mxGetString( prhs[0], filename, filenameLen );
nrrd = nrrdNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNix, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nrrdIoStateSet( nio, nrrdIoStateSkipData, AIR_TRUE );
/* read header, but no data */
if ( nrrdLoad( nrrd, filename, nio ) ) {
errPtr = biffGetDone( NRRD );
airMopAdd( mop, errPtr, airFree, airMopAlways );
sprintf( errBuff, "%s: trouble reading NRRD header:\n%s", me, errPtr );
airMopError( mop );
mexErrMsgTxt( errBuff );
}
domainAxisNum = nrrdDomainAxesGet( nrrd, domainAxisIdx );
plhs[0] = mxCreateDoubleMatrix( domainAxisNum /* # rows */,
nrrd->dim /* # cols */, mxREAL );
for ( colIdx=0; colIdx<nrrd->dim; colIdx++ ) {
spacingStatus = nrrdSpacingCalculate( nrrd, domainAxisIdx[colIdx],
&spacing, spaceDir );
switch( spacingStatus ) {
case nrrdSpacingStatusNone:
for ( rowIdx=0; rowIdx<domainAxisNum; rowIdx++ ) {
mxGetPr( plhs[0] )[rowIdx + domainAxisNum*colIdx] = AIR_NAN;
}
break;
case nrrdSpacingStatusScalarNoSpace:
for ( rowIdx=0; rowIdx<domainAxisNum; rowIdx++ ) {
mxGetPr( plhs[0] )[rowIdx + domainAxisNum*colIdx] = 0;
}
if ( colIdx < domainAxisNum ) {
mxGetPr( plhs[0] )[colIdx + domainAxisNum*colIdx] = spacing;
}
break;
case nrrdSpacingStatusDirection:
for ( rowIdx=0; rowIdx<domainAxisNum; rowIdx++ ) {
mxGetPr( plhs[0] )[rowIdx + domainAxisNum*colIdx] =
nrrd->axis[colIdx].spaceDirection[rowIdx];
}
break;
case nrrdSpacingStatusUnknown:
sprintf( errBuff, "%s: error interpreting axis %u spacing "
"( nrrdSpacingStatusUnknown )", me, colIdx );
airMopError( mop );
mexErrMsgTxt( errBuff );
break;
case nrrdSpacingStatusScalarWithSpace:
sprintf( errBuff, "%s: error interpreting axis %u spacing "
"( nrrdSpacingScalarWithSpace )", me, colIdx );
airMopError( mop );
mexErrMsgTxt( errBuff );
break;
}
}
airMopOkay( mop );
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mex.h"
#include <teem/nrrd.h>
int
29 typeMtoN( mxClassID mtype ) {
int ntype;
switch( mtype ) {
case mxINT8_CLASS:
ntype = nrrdTypeChar;
break;
case mxUINT8_CLASS:
ntype = nrrdTypeUChar;
break;
case mxINT16_CLASS:
ntype = nrrdTypeShort;
break;
case mxUINT16_CLASS:
ntype = nrrdTypeUShort;
break;
case mxINT32_CLASS:
ntype = nrrdTypeInt;
break;
case mxUINT32_CLASS:
ntype = nrrdTypeUInt;
break;
case mxINT64_CLASS:
ntype = nrrdTypeLLong;
break;
case mxUINT64_CLASS:
ntype = nrrdTypeULLong;
break;
case mxSINGLE_CLASS:
ntype = nrrdTypeFloat;
break;
case mxDOUBLE_CLASS:
ntype = nrrdTypeDouble;
break;
default:
ntype = nrrdTypeUnknown;
break;
}
return ntype;
}
70 void mexFunction( int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[] )
{
char me[]="nrrdSave", *filename, *errPtr, errBuff[AIR_STRLEN_MED];
int filenameLen, ntype;
size_t sizeZ[NRRD_DIM_MAX];
unsigned int dim, axIdx;
Nrrd *nrrd;
airArray *mop;
const mxArray *filenameMx, *arrayMx;
if ( !( 2 == nrhs && mxIsChar( prhs[0] ) ) ) {
sprintf( errBuff, "%s: requires two args: one string, one array", me );
mexErrMsgTxt( errBuff );
}
filenameMx = prhs[0];
arrayMx = prhs[1];
if ( mxIsComplex( arrayMx ) ) {
sprintf( errBuff, "%s: sorry, array must be real", me );
mexErrMsgTxt( errBuff );
}
ntype = typeMtoN( mxGetClassID( arrayMx ) );
if ( nrrdTypeUnknown == ntype ) {
sprintf( errBuff, "%s: sorry, can't handle type %s",
me, mxGetClassName( arrayMx ) );
mexErrMsgTxt( errBuff );
}
dim = mxGetNumberOfDimensions( arrayMx );
if ( !( 1 <= dim && dim <= NRRD_DIM_MAX ) ) {
sprintf( errBuff, "%s: number of array dimensions %d outside range [1, %d]",
me, dim, NRRD_DIM_MAX );
mexErrMsgTxt( errBuff );
}
filenameLen = mxGetM( filenameMx )*mxGetN( filenameMx )+1;
filename = mxCalloc( filenameLen, sizeof( mxChar ) ); /* managed by Matlab */
mxGetString( filenameMx, filename, filenameLen );
for ( axIdx=0; axIdx<dim; axIdx++ ) {
sizeZ[axIdx] = mxGetDimensions( arrayMx )[axIdx];
}
nrrd = nrrdNew( );
mop = airMopNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNix, airMopAlways );
if ( nrrdWrap_nva( nrrd, mxGetPr( arrayMx ), ntype, dim, sizeZ )
|| nrrdSave( filename, nrrd, NULL ) ) {
errPtr = biffGetDone( NRRD );
airMopAdd( mop, errPtr, airFree, airMopAlways );
sprintf( errBuff, "%s: error saving NRRD:\n%s", me, errPtr );
airMopError( mop );
mexErrMsgTxt( errBuff );
}
airMopOkay( mop );
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "meet.h"
const int
meetPresent = 42;
const char *
meetBiffKey = "meet";
typedef union {
const airEnum ***enm;
void **v;
} foobarUnion;
/*
******** meetAirEnumAll
**
** ALLOCATES and returns a NULL-terminated array of
** pointers to all the airEnums in Teem
**
** It would be better if this array could be created at compile-time,
** but efforts at doing this resulted in lots of "initializer is not const"
** errors.
**
** NOTE: the order here reflects the library ordering of the LIBS
** variable in teem/src/GNUMakefile, which is the canonical dependency
** ordering of the libraries. Can manually check completeness by:
** ( TEEM_LIB_LIST )
grep "airEnum *" {air, hest, biff, nrrd, ell, unrrdu, alan, moss, tijk, gage, dye, bane, limn, echo, hoover, seek, ten, elf, pull, coil, push, mite}/?*.h | grep EXPORT | more
** ( with the ? in "}/?*.h" to stop warnings about / * inside comment )
** We could grep specifically for "const airEnum *const", but its good to
** use this occasion to also make sure that all public airEnums are
** indeed const airEnum *const
*/
const airEnum **
60 meetAirEnumAll( ) {
airArray *arr;
const airEnum **enm;
unsigned int ii;
foobarUnion fbu;
arr = airArrayNew( ( fbu.enm = &enm, fbu.v ),
NULL, sizeof( airEnum * ), 2 );
/* air */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = airEndian;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = airBool;
/* hest: no airEnums */
/* biff: no airEnums */
/* nrrd */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdFormatType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdEncodingType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdCenter;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdKind;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdField;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdSpace;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdSpacingStatus;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdOrientationHave;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdBoundary;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdMeasure;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdUnaryOp;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdBinaryOp;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdTernaryOp;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdFFTWPlanRigor;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdResampleNonExistent;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = nrrdMetaDataCanonicalVersion;
/* ell */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = ell_cubic_root;
/* unrrdu: no airEnums */
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
/* alan */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = alanStop;
#endif
/* moss: no airEnums */
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tijk_class;
#endif
/* gage */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageErr;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageKernel;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageItemPackPart;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageScl;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageVec;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = gageSigmaSampling;
/* dye */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = dyeSpace;
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
/* bane */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = baneGkmsMeasr;
#endif
/* limn */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnSpace;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnPolyDataInfo;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnCameraPathTrack;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnPrimitive;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnSplineType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = limnSplineInfo;
/* echo */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = echoJitter;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = echoType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = echoMatter;
/* hoover */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = hooverErr;
/* seek */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = seekType;
/* ten */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenAniso;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenInterpType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenGage;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenFiberType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenDwiFiberType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenFiberStop;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenFiberIntg;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenGlyphType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenEstimate1Method;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenEstimate2Method;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenTripleType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = tenDwiGage;
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
/* elf: no airEnums */
#endif
/* pull */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullInterType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullEnergyType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullInfo;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullSource;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullProp;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullProcessMode;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullTraceStop;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullInitMethod;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullCount;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pullConstraintFail;
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
/* coil */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = coilMethodType;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = coilKindType;
/* push */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = pushEnergyType;
#endif
/* mite */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = miteVal;
ii = airArrayLenIncr( arr, 1 ); enm[ii] = miteStageOp;
/* meet: no new airEnums of its own */
/* NULL-terminate the list */
ii = airArrayLenIncr( arr, 1 ); enm[ii] = NULL;
/* nix, not nuke the airArray */
airArrayNix( arr );
return enm;
}
void
201 meetAirEnumAllPrint( FILE *file ) {
const airEnum **enm, *ee;
unsigned int ei;
if ( !file ) {
return;
}
enm = meetAirEnumAll( );
ei = 0;
while ( ( ee = enm[ei] ) ) {
airEnumPrint( file, ee );
fprintf( file, "\n" );
ei++;
}
free( AIR_CAST( void *, enm ) );
return;
}
int
220 meetAirEnumAllCheck( void ) {
static const char me[]="meetAirEnumAllCheck";
const airEnum **enm, *ee;
char err[AIR_STRLEN_LARGE];
unsigned int ei;
airArray *mop;
mop = airMopNew( );
enm = meetAirEnumAll( );
airMopAdd( mop, ( void* )enm, airFree, airMopAlways );
ei = 0;
while ( ( ee = enm[ei] ) ) {
/* fprintf( stderr, "!%s: %u %s\n", me, ei, ee->name ); */
if ( airEnumCheck( err, ee ) ) {
biffAddf( MEET, "%s: problem with enum %u", me, ei );
biffAddf( MEET, "%s", err ); /* kind of a hack */
airMopError( mop );
return 1;
}
ei++;
}
airMopOkay( mop );
return 0;
}
245 const char *const
meetTeemLibs[] = {
/* TEEM_LIB_LIST */
"air",
"hest",
"biff",
"nrrd",
"ell",
"unrrdu",
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
"alan",
#endif
"moss",
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
"tijk",
#endif
"gage",
"dye",
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
"bane",
#endif
"limn",
"echo",
"hoover",
"seek",
"ten",
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
"elf",
#endif
"pull",
#if defined( TEEM_BUILD_EXPERIMENTAL_LIBS )
"coil",
"push",
#endif
"mite",
"meet",
NULL
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "meet.h"
gageKind * /*Teem: error if ( !ret ) */
28 _meetGageKindParse( const char *_str, int constOnly ) {
char *str;
gageKind *ret;
if ( !_str ) {
return NULL;
}
str = airToLower( airStrdup( _str ) );
if ( !str ) {
return NULL;
}
if ( !strcmp( gageKindScl->name, str ) ) {
ret = gageKindScl;
} else if ( !strcmp( gageKind2Vec->name, str ) ) {
ret = gageKind2Vec;
} else if ( !strcmp( gageKindVec->name, str ) ) {
ret = gageKindVec;
} else if ( !strcmp( tenGageKind->name, str ) ) {
ret = tenGageKind;
} else if ( !constOnly && !strcmp( TEN_DWI_GAGE_KIND_NAME, str ) ) {
ret = tenDwiGageKindNew( );
} else {
ret = NULL;
}
airFree( str );
return ret;
}
gageKind * /*Teem: error if ( !ret ) */
57 meetGageKindParse( const char *_str ) {
return _meetGageKindParse( _str, AIR_FALSE );
}
const gageKind * /*Teem: error if ( !ret ) */
63 meetConstGageKindParse( const char *_str ) {
return _meetGageKindParse( _str, AIR_TRUE );
}
/*
** same as _meetHestGageKindParse below but without the DWI kind,
** which isn't const
*/
int
73 _meetHestConstGageKindParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[] = "_meetHestGageConstKindParse";
const gageKind **kindP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
/* of course, the const correctness goes out the window with all
the casting that's necessary with hest ... */
kindP = ( const gageKind ** )ptr;
*kindP = meetConstGageKindParse( str );
if ( !*kindP ) {
sprintf( err, "%s: \"%s\" not \"%s\", \"%s\", \"%s\", or \"%s\"", me, str,
gageKindScl->name, gageKind2Vec->name,
gageKindVec->name, tenGageKind->name );
return 1;
}
return 0;
}
int
96 _meetHestGageKindParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[] = "_meetHestGageKindParse";
gageKind **kindP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
kindP = ( gageKind ** )ptr;
*kindP = meetGageKindParse( str );
if ( !*kindP ) {
sprintf( err, "%s: \"%s\" not \"%s\", \"%s\", \"%s\", \"%s\", or \"%s\"", me,
str, gageKindScl->name, gageKind2Vec->name, gageKindVec->name,
tenGageKind->name, TEN_DWI_GAGE_KIND_NAME );
return 1;
}
return 0;
}
void *
117 _meetHestGageKindDestroy( void *ptr ) {
gageKind *kind;
if ( ptr ) {
kind = AIR_CAST( gageKind *, ptr );
if ( !strcmp( TEN_DWI_GAGE_KIND_NAME, kind->name ) ) {
tenDwiGageKindNix( kind );
}
}
return NULL;
}
static hestCB
_meetHestGageKind = {
sizeof( gageKind * ),
"gageKind",
_meetHestGageKindParse,
_meetHestGageKindDestroy
};
static hestCB
_meetHestConstGageKind = {
sizeof( gageKind * ),
"gageKind",
_meetHestConstGageKindParse,
NULL
};
/*
******** meetHestGageKind
**
** This provides a uniform way to parse gageKinds from the command-line
*/
hestCB *
meetHestGageKind = &_meetHestGageKind;
hestCB *
meetHestConstGageKind = &_meetHestConstGageKind;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "meet.h"
typedef union {
const NrrdKernel ***k;
void **v;
} _kernu;
/*
** ALLOCATES and returns a NULL-terminated array of all the
** NrrdKernels in Teem
*/
const NrrdKernel **
36 meetNrrdKernelAll( void ) {
airArray *arr;
const NrrdKernel **kern;
unsigned int ii;
int di, ci, ai, dmax, cmax, amax;
_kernu ku;
ku.k = &kern;
arr = airArrayNew( ku.v, NULL, sizeof( NrrdKernel * ), 2 );
/* kernel.c */
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelZero;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBox;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBoxSupportDebug;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRomSupportDebug;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRomSupportDebugD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRomSupportDebugDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCos4SupportDebug;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCos4SupportDebugD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCos4SupportDebugDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCos4SupportDebugDDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCheap;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelHermiteScaleSpaceFlag;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelTent;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelForwDiff;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCentDiff;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBCCubic;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBCCubicD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBCCubicDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRom;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRomD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelCatmullRomDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelAQuartic;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelAQuarticD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelAQuarticDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC3Quintic;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC3QuinticD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC3QuinticDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC4Hexic;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC4HexicD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC4HexicDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC4HexicDDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC4HexicApproxInverse;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC5Septic;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC5SepticD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC5SepticDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC5SepticDDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelC5SepticApproxInverse;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelGaussian;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelGaussianD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelGaussianDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelDiscreteGaussian;
/* winKernel.c */
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelHann;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelHannD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelHannDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBlackman;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBlackmanD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBlackmanDD;
/* bsplKernel.c */
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline1;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline1D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline2;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline2D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline2DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline3;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline3D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline3DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline3DDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline3ApproxInverse;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline4;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline4D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline4DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline4DDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline5;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline5D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline5DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline5DDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline5ApproxInverse;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline6;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline6D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline6DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline6DDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline7;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline7D;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline7DD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline7DDD;
ii = airArrayLenIncr( arr, 1 ); kern[ii] = nrrdKernelBSpline7ApproxInverse;
/* tmfKernel.c
nrrdKernelTMF[D+1][C+1][A] is d<D>_c<C>_<A>ef:
Dth-derivative, C-order continuous ( "smooth" ), A-order accurate
( for D and C, index 0 accesses the function for -1 )
NRRD_EXPORT NrrdKernel *const nrrdKernelTMF[4][5][5];
*/
dmax = AIR_CAST( int, nrrdKernelTMF_maxD );
cmax = AIR_CAST( int, nrrdKernelTMF_maxC );
amax = AIR_CAST( int, nrrdKernelTMF_maxA );
for ( di=-1; di<=dmax; di++ ) {
for ( ci=-1; ci<=cmax; ci++ ) {
for ( ai=1; ai<=amax; ai++ ) {
ii = airArrayLenIncr( arr, 1 );
kern[ii] = nrrdKernelTMF[di+1][ci+1][ai];
}
}
}
/* NULL-terminate the list */
ii = airArrayLenIncr( arr, 1 ); kern[ii] = NULL;
/* nix, not nuke the airArray */
airArrayNix( arr );
return kern;
}
/* the knowledge here about what is a derivative of what is something
that will be built into kernels in a future Teem version */
static const NrrdKernel *
155 kintegral( const NrrdKernel *kd ) {
const NrrdKernel *ret=NULL;
/* the statement "INTGL( K )" is saying that K has a derivative with the
usual name K##D. This is made more convenient by the
consistent use of the "D" suffix for indicating a derivative */
#define INTGL( K ) if ( K##D == kd ) { ret = K; }
INTGL( nrrdKernelHann );
INTGL( nrrdKernelHannD );
INTGL( nrrdKernelBlackman );
INTGL( nrrdKernelBlackmanD );
INTGL( nrrdKernelBSpline1 );
INTGL( nrrdKernelBSpline2 );
INTGL( nrrdKernelBSpline2D );
INTGL( nrrdKernelBSpline3 );
INTGL( nrrdKernelBSpline3D );
INTGL( nrrdKernelBSpline3DD );
INTGL( nrrdKernelBSpline4 );
INTGL( nrrdKernelBSpline4D );
INTGL( nrrdKernelBSpline4DD );
INTGL( nrrdKernelBSpline5 );
INTGL( nrrdKernelBSpline5D );
INTGL( nrrdKernelBSpline5DD );
INTGL( nrrdKernelBSpline6 );
INTGL( nrrdKernelBSpline6D );
INTGL( nrrdKernelBSpline6DD );
INTGL( nrrdKernelBSpline7 );
INTGL( nrrdKernelBSpline7D );
INTGL( nrrdKernelBSpline7DD );
INTGL( nrrdKernelCos4SupportDebug );
INTGL( nrrdKernelCos4SupportDebugD );
INTGL( nrrdKernelCos4SupportDebugDD );
INTGL( nrrdKernelCatmullRomSupportDebug );
INTGL( nrrdKernelCatmullRomSupportDebugD );
INTGL( nrrdKernelBCCubic );
INTGL( nrrdKernelBCCubicD );
INTGL( nrrdKernelCatmullRom );
INTGL( nrrdKernelCatmullRomD );
INTGL( nrrdKernelAQuartic );
INTGL( nrrdKernelAQuarticD );
INTGL( nrrdKernelC3Quintic );
INTGL( nrrdKernelC3QuinticD );
INTGL( nrrdKernelC4Hexic );
INTGL( nrrdKernelC4HexicD );
INTGL( nrrdKernelC4HexicDD );
INTGL( nrrdKernelC5Septic );
INTGL( nrrdKernelC5SepticD );
INTGL( nrrdKernelC5SepticDD );
INTGL( nrrdKernelGaussian );
INTGL( nrrdKernelGaussianD );
#undef INTGL
return ret;
}
/*
** Does more than call nrrdKernelCheck on all kernels:
** makes sure that all kernels have unique names
** makes sure that derivative relationships are correct
** Also, simply calling nrrdKernelCheck requires some knowledge
** of the kernel's needed parameters
**
** HEY: its problematic that because the various kernels have different
** parameter epsilon requirements, they usually end up having to be
** enumerated in some of the if/else statements below; it would be much
** better if new kernels didn't need to be so explicitly added!
*/
int
224 meetNrrdKernelAllCheck( void ) {
static const char me[]="meetNrrdKernelAllCheck";
const NrrdKernel **kern, *kk, *ll;
unsigned int ki, kj, pnum;
airArray *mop;
double epsl, XX, YY,
parm0[NRRD_KERNEL_PARMS_NUM],
parm1_1[NRRD_KERNEL_PARMS_NUM], parm1_X[NRRD_KERNEL_PARMS_NUM],
parm[NRRD_KERNEL_PARMS_NUM];
size_t evalNum;
int EE;
mop = airMopNew( );
kern = meetNrrdKernelAll( );
airMopAdd( mop, AIR_CAST( void*, kern ), airFree, airMopAlways );
evalNum = 120000; /* success of kernel integral test is surprisingly
dependent on this, likely due to the naive way
the integral is numerically computed; the current
value here represents some experimentation */
epsl = 0.9e-5;
XX = 7.0/3.0; /* 2.333.. */
YY = 43.0/9.0; /* 4.777.. */
parm0[0] = AIR_NAN; /* shouldn't be read */
parm1_1[0] = 1.0;
parm1_X[0] = XX;
ki = 0;
while ( ( kk = kern[ki] ) ) {
kj = 0;
while ( kj < ki ) {
ll = kern[kj];
if ( kk == ll ) {
biffAddf( MEET, "%s: kern[%u] and [%u] were identical ( %s )",
me, kj, ki, kk->name );
airMopError( mop ); return 1;
}
if ( !airStrcmp( kk->name, ll->name ) ) {
biffAddf( MEET, "%s: kern[%u] and [%u] have same name ( %s )",
me, kj, ki, kk->name );
airMopError( mop ); return 1;
}
kj++;
}
pnum = kk->numParm;
EE = 0;
/* the second argument to CHECK is how much to scale up the
permissible error in kernel evaluations ( between float and double )
The kernels for which this is higher should be targets for
re-coding with an eye towards numerical accuracy */
#define CHECK( P, S, N ) \
if ( !EE ) EE |= nrrdKernelCheck( kk, ( P ), evalNum, epsl*( S ), \
N, N, \
kintegral( kk ), ( P ) );
if ( nrrdKernelBCCubic == kk ||
nrrdKernelBCCubicD == kk ||
nrrdKernelBCCubicDD == kk ) {
/* try a few settings of the 3 parms */
ELL_3V_SET( parm, 1.0, 0.0, 0.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, XX, 0.0, 0.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, 1.0, 1.0/3.0, 1.0/3.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, XX, 1.0/3.0, 1.0/3.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, 1.0, 0.0, 1.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, XX, 0.0, 1.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, 1.0, 0.5, 0.0 ); CHECK( parm, 1, 2 );
ELL_3V_SET( parm, XX, 0.5, 0.0 ); CHECK( parm, 1, 2 );
} else if ( 2 == pnum ) {
if ( nrrdKernelAQuartic == kk ||
nrrdKernelAQuarticD == kk ||
nrrdKernelAQuarticDD == kk ) {
ELL_2V_SET( parm, 1.0, 0.0 ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, 1.0, 0.5 ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, XX, 0.0 ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, XX, 0.5 ); CHECK( parm, 10, 2 );
} else if ( nrrdKernelGaussian == kk ||
nrrdKernelGaussianD == kk ||
nrrdKernelGaussianDD == kk ) {
ELL_2V_SET( parm, 0.1, XX ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, 0.1, YY ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, 1.0, XX ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, 1.0, YY ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, XX, XX ); CHECK( parm, 10, 2 );
ELL_2V_SET( parm, XX, YY ); CHECK( parm, 10, 2 );
} else if ( nrrdKernelHann == kk ||
nrrdKernelHannD == kk ||
nrrdKernelBlackman == kk ) {
ELL_2V_SET( parm, 0.5, XX ); CHECK( parm, 100, 2 );
ELL_2V_SET( parm, 0.5, YY ); CHECK( parm, 100, 2 );
ELL_2V_SET( parm, 1.0, XX ); CHECK( parm, 100, 2 );
ELL_2V_SET( parm, 1.0, YY ); CHECK( parm, 100, 2 );
ELL_2V_SET( parm, XX, XX ); CHECK( parm, 100, 2 );
ELL_2V_SET( parm, XX, YY ); CHECK( parm, 100, 2 );
} else if ( nrrdKernelHannDD == kk ||
nrrdKernelBlackmanD == kk ||
nrrdKernelBlackmanDD == kk ) {
/* there are apparently bugs in these kernels */
ELL_2V_SET( parm, 0.5, XX ); CHECK( parm, 10000000, 2 );
ELL_2V_SET( parm, 0.5, YY ); CHECK( parm, 10000000, 2 );
ELL_2V_SET( parm, 1.0, XX ); CHECK( parm, 1000000, 2 );
ELL_2V_SET( parm, 1.0, YY ); CHECK( parm, 1000000, 2 );
ELL_2V_SET( parm, XX, XX ); CHECK( parm, 100000, 2 );
ELL_2V_SET( parm, XX, YY ); CHECK( parm, 100000, 2 );
} else if ( nrrdKernelDiscreteGaussian == kk ) {
ELL_2V_SET( parm, 0.1, XX ); CHECK( parm, 1, 2 );
ELL_2V_SET( parm, 0.1, YY ); CHECK( parm, 1, 2 );
ELL_2V_SET( parm, 1.0, XX ); CHECK( parm, 1, 2 );
ELL_2V_SET( parm, 1.0, YY ); CHECK( parm, 1, 2 );
ELL_2V_SET( parm, XX, XX ); CHECK( parm, 1, 2 );
ELL_2V_SET( parm, XX, YY ); CHECK( parm, 1, 2 );
} else {
biffAddf( MEET, "%s: sorry, got unexpected 2-parm kernel %s",
me, kk->name );
airMopError( mop ); return 1;
}
} else if ( 1 == pnum ) {
if ( strstr( kk->name, "TMF" ) ) {
/* these take a single parm, but its not support */
parm[0] = 0.0; CHECK( parm, 10, 2 );
parm[0] = 1.0/3.0; CHECK( parm, 10, 2 );
} else {
/* zero, box, boxsup, cos4sup{, D, DD, DDD}, cheap,
ctmrsup{, D, DD}, tent, fordif, cendif */
/* takes a single support/scale parm[0], try two different values */
if ( nrrdKernelCos4SupportDebug == kk ||
nrrdKernelCos4SupportDebugD == kk ||
nrrdKernelCos4SupportDebugDD == kk ||
nrrdKernelCos4SupportDebugDDD == kk ||
nrrdKernelCatmullRomSupportDebugD == kk ||
nrrdKernelCatmullRomSupportDebugDD == kk ) {
CHECK( parm1_1, 10, 4 );
CHECK( parm1_X, 10, 4 );
} else {
CHECK( parm1_1, 1, 2 );
CHECK( parm1_X, 1, 2 );
}
}
} else if ( 0 == pnum ) {
/* C3Quintic{, D, DD, DD}, C4Hexic{, D, DD, DDD}, C5Septic{, D},
hermiteSS, catmull-rom{, D}, bspl{3, 5, 7}{, D, DD, DDD} */
if ( nrrdKernelC3Quintic == kk ||
nrrdKernelC3QuinticD == kk ||
nrrdKernelC3QuinticDD == kk ||
nrrdKernelC4Hexic == kk ||
nrrdKernelC4HexicD == kk ||
nrrdKernelC4HexicDD == kk ||
nrrdKernelC4HexicDDD == kk ||
nrrdKernelC5Septic == kk ||
nrrdKernelC5SepticD == kk ||
nrrdKernelC5SepticDD == kk ||
nrrdKernelC5SepticDDD == kk
) {
CHECK( parm0, 1, 2 );
CHECK( parm0, 1, 2 );
} else if ( nrrdKernelBSpline5DD == kk ||
nrrdKernelBSpline5DDD == kk ||
nrrdKernelBSpline7DD == kk ) {
CHECK( parm0, 100, 2 );
} else {
CHECK( parm0, 10, 2 );
}
} else {
biffAddf( MEET, "%s: sorry, didn't expect %u parms for %s",
me, pnum, kk->name );
airMopError( mop ); return 1;
}
#undef CHECK
if ( EE ) {
biffMovef( MEET, NRRD, "%s: problem with kern[%u] \"%s\"", me, ki,
kk->name ? kk->name : "( NULL name )" );
airMopError( mop ); return 1;
}
ki++;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "meet.h"
meetPullVol *
27 meetPullVolNew( void ) {
meetPullVol *ret;
ret = AIR_CALLOC( 1, meetPullVol );
if ( ret ) {
ret->kind = NULL;
ret->fileName = ret->volName = NULL;
ret->sbp = NULL;
ret->leeching = AIR_FALSE;
ret->derivNormSS = AIR_FALSE;
ret->recomputedSS = AIR_FALSE;
ret->derivNormBiasSS = 0.0;
ret->nin = NULL;
ret->ninSS = NULL;
}
return ret;
}
/*
** DOES use biff
*/
meetPullVol *
49 meetPullVolCopy( const meetPullVol *mpv ) {
static const char me[]="meetPullVolCopy";
meetPullVol *ret;
unsigned int si;
airArray *mop;
mop = airMopNew( );
ret = meetPullVolNew( );
airMopAdd( mop, ret, ( airMopper )meetPullVolNix, airMopOnError );
/* HEY: hope this is okay for dynamic kinds */
ret->kind = mpv->kind;
ret->fileName = airStrdup( mpv->fileName );
ret->volName = airStrdup( mpv->volName );
if ( mpv->sbp ) {
ret->sbp = gageStackBlurParmNew( );
if ( gageStackBlurParmCopy( ret->sbp, mpv->sbp ) ) {
biffMovef( MEET, GAGE, "%s: problem", me );
airMopError( mop ); return NULL;
}
}
ret->leeching = AIR_FALSE;
ret->derivNormSS = mpv->derivNormSS;
ret->recomputedSS = AIR_FALSE;
ret->derivNormBiasSS = mpv->derivNormBiasSS;
if ( mpv->sbp ) {
ret->nin = NULL;
ret->ninSS = AIR_CALLOC( ret->sbp->num, Nrrd * );
for ( si=0; si<mpv->sbp->num; si++ ) {
ret->ninSS[si] = nrrdNew( );
if ( nrrdCopy( ret->ninSS[si], mpv->ninSS[si] ) ) {
biffMovef( MEET, NRRD, "%s: problem with ninSS[%u]", me, si );
airMopError( mop ); return NULL;
}
}
} else {
ret->nin = nrrdNew( );
if ( nrrdCopy( ret->nin, mpv->nin ) ) {
biffMovef( MEET, NRRD, "%s: problem with nin", me );
airMopError( mop ); return NULL;
}
ret->ninSS = NULL;
}
airMopOkay( mop );
return ret;
}
/*
******** meetPullVolParse
**
** parses a string to extract all the information necessary to create
** the pullVolume ( this function originated in Deft/src/main-pull.c )
*/
int
102 meetPullVolParse( meetPullVol *mpv, const char *_str ) {
static const char me[]="meetPullVolParse";
#define VFMT_SHRT "<fileName>:<kind>:<volName>"
/* there are other flags and parms but these are the main ones */
#define SFMT "<minScl>-<#smp>-<maxScl>[-n][/k=kss][/b=bspec][/s=smpling]"
#define VFMT_LONG "<fileName>:<kind>:" SFMT ":<volName>"
char *str, *ctok, *clast=NULL;
airArray *mop;
int wantSS;
unsigned int ctokn;
if ( !( mpv && _str ) ) {
biffAddf( MEET, "%s: got NULL pointer", me );
return 1;
}
if ( !( str = airStrdup( _str ) ) ) {
biffAddf( MEET, "%s: couldn't strdup input", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, str, airFree, airMopAlways );
ctokn = airStrntok( str, ":" );
/* An annoying side-effect of putting the blurring kernel specification and
boundary specification inside the string representation of the
gageStackBlurParm, is that the colon in dg:1, 6" or "pad:10" is now
confused as a delimiter in the ( e.g. ) "vol.nrrd:scalar:0-8-5.5:V" string
representation of meetPullVol, as in
"vol.nrrd:scalar:0-8-5.5/k=dg:1, 6/b=pad:1:V". So we have to be more
permissive in the number of tokens ( hacky ) */
if ( !( ctokn >= 3 ) ) {
biffAddf( MEET, "%s: didn't get at least 3 \":\"-separated tokens in "
"\"%s\"; not of form " VFMT_SHRT " or " VFMT_LONG , me, _str );
airMopError( mop ); return 1;
}
wantSS = ( ctokn > 3 );
ctok = airStrtok( str, ":", &clast );
if ( !( mpv->fileName = airStrdup( ctok ) ) ) {
biffAddf( MEET, "%s: couldn't strdup fileName", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, &( mpv->fileName ), ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, mpv->fileName, airFree, airMopOnError );
ctok = airStrtok( NULL, ":", &clast );
if ( !( mpv->kind = meetConstGageKindParse( ctok ) ) ) {
biffAddf( MEET, "%s: couldn't parse \"%s\" as kind", me, ctok );
airMopError( mop ); return 1;
}
if ( wantSS ) {
int extraFlag[256]; char *extraParm=NULL, *ptok, *plast;
unsigned int efi, cti;
char *sbps;
/* the hack to make the ":" inside a blurring kernel specification or
boundary specification be unlike the ":" that delimits the real
meetPullVol fields */
sbps = airStrdup( _str ); /* to have a buffer big enough */
airMopAdd( mop, sbps, airFree, airMopAlways );
strcpy( sbps, "" );
for ( cti=0; cti<ctokn-3; cti++ ) {
if ( cti ) {
strcat( sbps, ":" );
}
ctok = airStrtok( NULL, ":", &clast );
strcat( sbps, ctok );
}
mpv->sbp = gageStackBlurParmNix( mpv->sbp );
mpv->sbp = gageStackBlurParmNew( );
if ( gageStackBlurParmParse( mpv->sbp, extraFlag, &extraParm, sbps ) ) {
biffMovef( MEET, GAGE, "%s: problem parsing sbp from \"%s\"", me, sbps );
airMopError( mop ); return 1;
}
mpv->derivNormSS = !!extraFlag['n'];
extraFlag['n'] = AIR_FALSE;
for ( efi=0; efi<256; efi++ ) {
if ( extraFlag[AIR_CAST( unsigned char, efi )] ) {
biffAddf( MEET, "%s: got unknown extra flag '%c' in \"%s\"", me,
AIR_CAST( char, efi ), sbps );
airMopError( mop ); return 1;
}
}
if ( extraParm ) {
unsigned int pmi, pmn;
static const char dnbiase[]="dnbias=";
airMopAdd( mop, extraParm, airFree, airMopAlways );
pmn = airStrntok( extraParm, "/" );
for ( pmi=0; pmi<pmn; pmi++ ) {
ptok = airStrtok( !pmi ? extraParm : NULL, "/", &plast );
if ( strstr( ptok, dnbiase ) == ptok ) {
if ( 1 != sscanf( ptok + strlen( dnbiase ), "%lg",
&( mpv->derivNormBiasSS ) ) ) {
biffAddf( MEET, "%s: couldn't parse \"%s\" as double in \"%s\"",
me, ptok + strlen( dnbiase ), ptok );
airMopError( mop ); return 1;
}
} else {
biffAddf( MEET, "%s: got unknown extra parm %s in \"%s\"",
me, ptok, extraParm );
airMopError( mop ); return 1;
}
}
}
} else {
/* no scale-space stuff wanted */
mpv->sbp = NULL;
mpv->ninSS = NULL;
}
ctok = airStrtok( NULL, ":", &clast );
if ( !( mpv->volName = airStrdup( ctok ) ) ) {
biffAddf( MEET, "%s: couldn't strdup volName", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, &( mpv->volName ), ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, mpv->volName, airFree, airMopOnError );
if ( strchr( ctok, '-' ) ) {
biffAddf( MEET, "%s: you probably don't want \"-\" in volume name \"%s\"; "
"forgot last \":\" in scale sampling specification?", me, ctok );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
228 meetHestPullVolParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[]="meetHestPullVolParse";
meetPullVol *mpv, **mpvP;
airArray *mop;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
mpvP = AIR_CAST( meetPullVol **, ptr );
*mpvP = mpv = meetPullVolNew( );
airMopAdd( mop, mpvP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, mpv, ( airMopper )meetPullVolNix, airMopOnError );
if ( meetPullVolParse( mpv, str ) ) {
char *ler;
airMopAdd( mop, ler = biffGetDone( MEET ), airFree, airMopOnError );
airStrcpy( err, AIR_STRLEN_HUGE, ler );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** meetPullVolNix
**
** this frees stuff allocated meetPullVolParse and meetPullVolLoadMulti
*/
meetPullVol *
259 meetPullVolNix( meetPullVol *mpv ) {
if ( mpv ) {
if ( !mpv->leeching && mpv->nin ) {
nrrdNuke( mpv->nin );
}
if ( mpv->sbp ) {
unsigned int ssi;
if ( mpv->ninSS ) {
/* need this check because the mpv may not have benefitted
from meetPullVolLoadMulti, so it might be incomplete */
for ( ssi=0; ssi<mpv->sbp->num; ssi++ ) {
if ( !mpv->leeching ) {
nrrdNuke( mpv->ninSS[ssi] );
}
}
airFree( mpv->ninSS );
}
gageStackBlurParmNix( mpv->sbp );
}
airFree( mpv->fileName );
airFree( mpv->volName );
airFree( mpv );
}
return NULL;
}
hestCB
_meetHestPullVol = {
sizeof( meetPullVol * ),
"meetPullVol",
meetHestPullVolParse,
( airMopper )meetPullVolNix,
};
hestCB *
meetHestPullVol = &_meetHestPullVol;
/*
******** meetPullVolLeechable
**
** indicates whether lchr can leech from orig ( saved in *can ), and if not,
** explanation is saved in explain ( if non-NULL )
**
** always uses biff
*/
int
306 meetPullVolLeechable( const meetPullVol *lchr,
const meetPullVol *orig,
int *can, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="meetPullVolLeechable";
char subexplain[AIR_STRLEN_LARGE];
if ( !( lchr && orig && can ) ) {
biffAddf( MEET, "%s: got NULL pointer ( %p %p %p )", me, lchr, orig, can );
return 1;
}
/* can leech, if not reading from stdin */
*can = !!strcmp( orig->fileName, "-" );
if ( !*can ) {
if ( explain ) {
sprintf( explain, "original loaded from stdin" );
}
return 0;
}
/* can, if coming from same file */
*can = !strcmp( orig->fileName, lchr->fileName );
if ( !*can ) {
if ( explain ) {
sprintf( explain, "not from same file ( \"%s\" vs \"%s\" )\n",
lchr->fileName, orig->fileName );
}
return 0;
}
/* can, if same kind */
*can = ( orig->kind == lchr->kind );
if ( !*can ) {
if ( explain ) {
sprintf( explain, "not same kind ( %s vs %s )\n",
lchr->kind->name, orig->kind->name );
}
return 0;
}
/* can, if both using or both not using scale-space */
*can = ( !!lchr->sbp == !!orig->sbp );
if ( !*can ) {
if ( explain ) {
sprintf( explain, "not agreeing on use of scale-space ( %s vs %s )\n",
lchr->sbp ? "yes" : "no", orig->sbp ? "yes" : "no" );
}
return 0;
}
if ( orig->sbp ) {
int differ;
if ( gageStackBlurParmCompare( lchr->sbp, "potential leecher",
orig->sbp, "original",
&differ, subexplain ) ) {
biffAddf( MEET, "%s: problem comparing sbps", me );
return 1;
}
if ( differ ) {
if ( explain ) {
sprintf( explain, "different uses of scale-space: %s", subexplain );
}
*can = AIR_FALSE;
return 0;
}
}
/* DO allow difference in derivNormSS ( the main reason for leeching ),
as well as derivNormBiasSS */
/* no differences so far */
*can = AIR_TRUE;
return 0;
}
void
375 meetPullVolLeech( meetPullVol *vol,
const meetPullVol *volPrev ) {
if ( vol && volPrev ) {
vol->nin = volPrev->nin;
if ( vol->sbp ) {
unsigned int ni;
/* have to allocate ninSS here; in volPrev it was probably allocated
by gageStackBlurManage */
vol->ninSS = AIR_CALLOC( vol->sbp->num, Nrrd * );
for ( ni=0; ni<vol->sbp->num; ni++ ) {
vol->ninSS[ni] = volPrev->ninSS[ni];
}
}
vol->leeching = AIR_TRUE;
}
return;
}
/*
** This is kind of a sad function. The big re-write of gageStackBlurParm in
** late August 2013 was motivated by the frustration of how there was no
** centralized and consistent way of representing ( by text or by command-line
** options ) all the things that determine scale-space "stack" creation.
** Having re-organized gageStackBlurParm, the meetPullVol was re-organized to
** include one inside, which is clearly better than the previous reduplication
** of the stack blur parms inside the meetPullVol. Parsing the meetPullVol
** from the command-line ( as in done in puller ) highlights the annoying fact
** that hest wants to be the origin of information: you can't have hest
** supplement existing information with whatever it learns from the
** command-line, especially when hest is parsing 1 or more of something, and
** especially when the existing information would be coming from other
** command-line arguments.
**
** So, this sad function says, "ok all you meetPullVol parsed from the
** command-line: if you don't already have a boundary or a kernel set, here's
** one to use". What makes it sad is how the whole point of the
** gageStackBlurParm re-org was that knowledge about the internals of the
** gageStackBlurParm was now going to be entirely localized in gage. But here
** we are listing off two of its fields as parameters to this function, which
** means its API might change the next time the gageStackBlurParm is updated.
**
** To help keep track of what info was actually used, *kssSetP and *bspSetP
** ( if non-NULL ) are set to the number of kernel and boundary specs that are
** "finished" in this way.
*/
int
422 meetPullVolStackBlurParmFinishMulti( meetPullVol **mpv, unsigned int mpvNum,
unsigned int *kssSetP,
unsigned int *bspSetP,
const NrrdKernelSpec *kssblur,
const NrrdBoundarySpec *bspec ) {
static const char me[]="meetPullVolStackBlurParmFinishMulti";
unsigned int ii, kssSet, bspSet;
if ( !mpv || !mpvNum ) {
biffAddf( MEET, "%s: got NULL mpv ( %p ) or 0 mpvNum ( %u )",
me, AIR_VOIDP( mpv ), mpvNum );
return 1;
}
kssSet = bspSet = 0;
for ( ii=0; ii<mpvNum; ii++ ) {
if ( kssblur && mpv[ii]->sbp && !( mpv[ii]->sbp->kspec ) ) {
if ( gageStackBlurParmKernelSet( mpv[ii]->sbp, kssblur ) ) {
biffMovef( MEET, GAGE, "%s: trouble w/ kernel on mpv[%u]", me, ii );
return 1;
}
kssSet++;
}
if ( bspec && mpv[ii]->sbp && !( mpv[ii]->sbp->bspec ) ) {
if ( gageStackBlurParmBoundarySpecSet( mpv[ii]->sbp, bspec ) ) {
biffMovef( MEET, GAGE, "%s: trouble w/ boundary on mpv[%u]", me, ii );
return 1;
}
bspSet++;
}
}
if ( kssSetP ) {
*kssSetP = kssSet;
}
if ( bspSetP ) {
*bspSetP = bspSet;
}
return 0;
}
/*
******** meetPullVolLoadMulti
**
** at this point the only per-pullVolume information required for
** loading/creating the volumes, which isn't already in the
** meetPullVol, is the cachePath, so that is passed explicitly.
*/
int
469 meetPullVolLoadMulti( meetPullVol **mpv, unsigned int mpvNum,
char *cachePath, int verbose ) {
static const char me[]="meetPullVolLoadMulti";
unsigned int mpvIdx;
airArray *mop;
meetPullVol *vol;
if ( !( mpv && cachePath ) ) {
biffAddf( MEET, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
for ( mpvIdx=0; mpvIdx<mpvNum; mpvIdx++ ) {
unsigned int pvi;
int leechable;
char explain[AIR_STRLEN_LARGE];
vol = mpv[mpvIdx];
for ( pvi=0; pvi<mpvIdx; pvi++ ) {
if ( meetPullVolLeechable( vol, mpv[pvi], &leechable, explain ) ) {
biffAddf( MEET, "%s: problem testing leechable( v[%u]->v[%u] )",
me, mpvIdx, pvi );
return 1;
}
if ( leechable ) {
meetPullVolLeech( vol, mpv[pvi] );
break; /* prevent a chain of leeching */
} else {
if ( verbose ) {
fprintf( stderr, "%s: mpv[%u] cannot leech mpv[%u]: %s\n", me,
mpvIdx, pvi, explain );
}
}
}
if ( pvi < mpvIdx ) {
/* we leeched this one, move on */
if ( verbose ) {
fprintf( stderr, "%s: vspec[%u] ( %s ) leeching off vspec[%u] ( %s )\n",
me, mpvIdx, vol->volName, pvi, mpv[pvi]->volName );
}
continue;
}
/* else we're not leeching */
vol->leeching = AIR_FALSE;
vol->nin = nrrdNew( );
airMopAdd( mop, &( vol->nin ), ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, vol->nin, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdLoad( vol->nin, vol->fileName, NULL ) ) {
biffMovef( MEET, NRRD, "%s: trouble loading mpv[%u]->nin ( \"%s\" )",
me, mpvIdx, vol->volName );
airMopError( mop ); return 1;
}
if ( vol->sbp ) {
char formatSS[AIR_STRLEN_LARGE];
sprintf( formatSS, "%s/%s-%%03u-%03u.nrrd",
cachePath, vol->volName, vol->sbp->num );
if ( verbose ) {
fprintf( stderr, "%s: managing %s ... \n", me, formatSS );
}
if ( gageStackBlurManage( &( vol->ninSS ), &( vol->recomputedSS ), vol->sbp,
formatSS, AIR_TRUE, NULL,
vol->nin, vol->kind ) ) {
biffMovef( MEET, GAGE, "%s: trouble getting volume stack ( \"%s\" )",
me, formatSS );
airMopError( mop ); return 1;
}
if ( verbose ) {
fprintf( stderr, "%s: ... done\n", me );
}
}
}
airMopOkay( mop );
return 0;
}
/*
******** meetPullVolAddMulti
**
** the spatial ( k00, k11, k22 ) and scale ( kSSrecon ) reconstruction
** kernels are not ( yet ) part of the meetPullVol, so have to be passed
** in here
*/
int
551 meetPullVolAddMulti( pullContext *pctx,
meetPullVol **mpv, unsigned int mpvNum,
const NrrdKernelSpec *k00,
const NrrdKernelSpec *k11,
const NrrdKernelSpec *k22,
const NrrdKernelSpec *kSSrecon ) {
static const char me[]="meetPullVolAddMulti";
unsigned int mpvIdx;
if ( !( pctx && mpv ) ) {
biffAddf( MEET, "%s: got NULL pointer", me );
return 1;
}
for ( mpvIdx=0; mpvIdx<mpvNum; mpvIdx++ ) {
meetPullVol *vol;
int E;
vol = mpv[mpvIdx];
if ( !vol->sbp ) {
E = pullVolumeSingleAdd( pctx, vol->kind, vol->volName,
vol->nin, k00, k11, k22 );
} else {
E = pullVolumeStackAdd( pctx, vol->kind, vol->volName, vol->nin,
AIR_CAST( const Nrrd *const *,
vol->ninSS ),
vol->sbp->sigma, vol->sbp->num,
vol->derivNormSS, vol->derivNormBiasSS,
k00, k11, k22, kSSrecon );
}
if ( E ) {
biffMovef( MEET, PULL, "%s: trouble adding volume %u/%u ( \"%s\" )",
me, mpvIdx, mpvNum, vol->volName );
return 1;
}
}
return 0;
}
meetPullInfo *
590 meetPullInfoNew( void ) {
meetPullInfo *ret;
ret = AIR_CALLOC( 1, meetPullInfo );
if ( ret ) {
ret->info = 0;
ret->source = pullSourceUnknown;
ret->prop = pullPropUnknown;
ret->constraint = AIR_FALSE;
ret->volName = ret->itemStr = NULL;
ret->zero = ret->scale = AIR_NAN;
}
return ret;
}
meetPullInfo *
606 meetPullInfoNix( meetPullInfo *minf ) {
if ( minf ) {
airFree( minf->volName );
airFree( minf->itemStr );
free( minf );
}
return NULL;
}
static int
617 zeroScaleSet( meetPullInfo *minf, int haveZS, char **lastP ) {
static const char me[]="_zeroScaleSet";
char *tok;
if ( haveZS ) {
tok = airStrtok( NULL, ":", lastP );
if ( 1 != sscanf( tok, "%lf", &( minf->zero ) ) ) {
biffAddf( MEET, "%s: couldn't parse %s as zero ( double )", me, tok );
return 1;
}
tok = airStrtok( NULL, ":", lastP );
if ( 1 != sscanf( tok, "%lf", &( minf->scale ) ) ) {
biffAddf( MEET, "%s: couldn't parse %s as scale ( double )", me, tok );
return 1;
}
} else {
minf->zero = minf->scale = AIR_NAN;
}
return 0;
}
int
639 meetPullInfoParse( meetPullInfo *minf, const char *_str ) {
static const char me[]="meetPullInfoParse";
#define IFMT_GAGE "<info>[-c]:<volname>:<item>[:<zero>:<scale>]"
#define IFMT_PROP "<info>:prop=<prop>[:<zero>:<scale>]"
#define PROP_PREFIX "prop=" /* has to end with = */
char *str, *tok, *last=NULL, *iflags;
airArray *mop;
int haveZS, source;
if ( !( minf && _str ) ) {
biffAddf( MEET, "%s: got NULL pointer", me );
return 1;
}
if ( ( 3 == airStrntok( _str, ":" ) || 5 == airStrntok( _str, ":" ) )
&& 1 == airStrntok( _str, "=" ) ) {
source = pullSourceGage;
haveZS = ( 5 == airStrntok( _str, ":" ) );
} else if ( ( 2 == airStrntok( _str, ":" ) || 4 == airStrntok( _str, ":" ) )
&& 2 == airStrntok( _str, "=" ) ) {
source = pullSourceProp;
haveZS = ( 4 == airStrntok( _str, ":" ) );
} else {
biffAddf( MEET, "%s: \"%s\" not of form " IFMT_GAGE " or " IFMT_PROP,
me, _str );
return 1;
}
mop = airMopNew( );
if ( !( str = airStrdup( _str ) ) ) {
biffAddf( MEET, "%s: couldn't strdup input", me );
return 1;
}
airMopAdd( mop, str, airFree, airMopAlways );
minf->source = source;
if ( pullSourceGage == source ) {
tok = airStrtok( str, ":", &last );
iflags = strchr( tok, '-' );
if ( iflags ) {
*iflags = '\0';
iflags++;
}
if ( !( minf->info = airEnumVal( pullInfo, tok ) ) ) {
biffAddf( MEET, "%s: couldn't parse \"%s\" as %s",
me, tok, pullInfo->name );
airMopError( mop ); return 1;
}
if ( iflags ) {
if ( strchr( iflags, 'c' ) ) {
minf->constraint = AIR_TRUE;
}
}
tok = airStrtok( NULL, ":", &last );
airFree( minf->volName );
minf->volName = airStrdup( tok );
airMopAdd( mop, minf->volName, airFree, airMopOnError );
tok = airStrtok( NULL, ":", &last );
airFree( minf->itemStr );
minf->itemStr = airStrdup( tok );
airMopAdd( mop, minf->itemStr, airFree, airMopOnError );
if ( zeroScaleSet( minf, haveZS, &last ) ) {
biffAddf( MEET, "%s: couldn't parse zero or scale", me );
airMopError( mop ); return 1;
}
} else if ( pullSourceProp == source ) {
/* "<info>:prop=<prop>[:<zero>:<scale>]" */
tok = airStrtok( str, ":", &last );
if ( !( minf->info = airEnumVal( pullInfo, tok ) ) ) {
biffAddf( MEET, "%s: couldn't parse \"%s\" as %s",
me, tok, pullInfo->name );
airMopError( mop ); return 1;
}
tok = airStrtok( NULL, ":", &last );
if ( strncmp( PROP_PREFIX, tok, strlen( PROP_PREFIX ) ) ) {
biffAddf( MEET, "%s: property info didn't start with %s",
me, PROP_PREFIX );
}
tok += strlen( PROP_PREFIX );
if ( !( minf->prop = airEnumVal( pullProp, tok ) ) ) {
biffAddf( MEET, "%s: couldn't parse \"%s\" as %s",
me, tok, pullProp->name );
airMopError( mop ); return 1;
}
if ( zeroScaleSet( minf, haveZS, &last ) ) {
biffAddf( MEET, "%s: couldn't parse zero or scale", me );
airMopError( mop ); return 1;
}
} else {
biffAddf( MEET, "%s: sorry, source %s not handled",
me, airEnumStr( pullSource, source ) );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
737 meetHestPullInfoParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[]="meetHestPullInfoParse";
airArray *mop;
meetPullInfo **minfP, *minf;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
minfP = AIR_CAST( meetPullInfo **, ptr );
*minfP = minf = meetPullInfoNew( );
airMopAdd( mop, minfP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, minf, ( airMopper )meetPullInfoNix, airMopOnError );
if ( meetPullInfoParse( minf, str ) ) {
char *ler;
airMopAdd( mop, ler = biffGetDone( MEET ), airFree, airMopOnError );
airStrcpy( err, AIR_STRLEN_HUGE, ler );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
hestCB
_meetHestPullInfo = {
sizeof( meetPullInfo * ),
"meetPullInfo",
meetHestPullInfoParse,
( airMopper )meetPullInfoNix
};
hestCB *
meetHestPullInfo = &_meetHestPullInfo;
int
774 meetPullInfoAddMulti( pullContext *pctx,
meetPullInfo **minf, unsigned int minfNum ) {
static const char me[]="meetPullInfoAddMulti";
const pullVolume *vol;
unsigned int ii;
airArray *mop;
if ( !( pctx && minf ) ) {
biffAddf( MEET, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
for ( ii=0; ii<minfNum; ii++ ) {
pullInfoSpec *ispec;
ispec = pullInfoSpecNew( );
airMopAdd( mop, ispec, ( airMopper )pullInfoSpecNix, airMopOnError );
ispec->volName = airStrdup( minf[ii]->volName );
ispec->source = minf[ii]->source;
ispec->info = minf[ii]->info;
ispec->prop = minf[ii]->prop;
ispec->zero = minf[ii]->zero;
ispec->scale = minf[ii]->scale;
ispec->constraint = minf[ii]->constraint;
/* the item is the one thing that takes some work to recover;
we need to find the volume and find the item from its kind->enm */
if ( pullSourceGage == ispec->source ) {
if ( !( vol = pullVolumeLookup( pctx, minf[ii]->volName ) ) ) {
biffMovef( MEET, PULL, "%s: can't find volName \"%s\" for minf[%u]",
me, minf[ii]->volName, ii );
airMopError( mop ); return 1;
}
if ( !( ispec->item = airEnumVal( vol->kind->enm, minf[ii]->itemStr ) ) ) {
biffAddf( MEET, "%s: can't parse \"%s\" as item of %s kind ( minf[%u] )\n",
me, minf[ii]->itemStr, vol->kind->name, ii );
airMopError( mop ); return 1;
}
}
if ( pullInfoSpecAdd( pctx, ispec ) ) {
biffMovef( MEET, PULL, "%s: trouble adding ispec from minf[%u]", me, ii );
airMopError( mop ); return 1;
}
/* else added the ispec okay. If we have an error with a different
ispec later in this loop, who's job is it to free up the ispecs
that have been added successfully? In teem/src/bin/puller, that
is done by pullContextNix. So we now extricate ourself from the
business of freeing this ispec in case of error; one of the few
times that airMopSub is really needed */
airMopSub( mop, ispec, ( airMopper )pullInfoSpecNix );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/pull.h>
#include "../meet.h"
/*
gcc -g -W -Wall -arch x86_64 strace.c -o strace -Wl, -prebind \
-I/Users/gk/teem-install/include \
-L/Users/gk/teem-install/lib/ \
-lteem -lpng -lz -lpthread -lfftw3 -lm
*/
int
37 findAndTraceMorePoints( Nrrd *nplot,
pullContext *pctx, pullVolume *scaleVol,
int strengthUse,
int smooth, int flatWght,
double scaleStep, double scaleHalfLen,
double orientTestLen,
unsigned int traceArrIncr,
pullTraceMulti *mtrc,
unsigned int pointNum ) {
static const char me[]="findAndTraceMorePoints";
unsigned int pointsSoFar, idtagBase, pidx, addedNum;
pullTrace *trace;
pullPoint *point;
Nrrd *nPosOut;
airArray *mop;
double *pos;
char doneStr[AIR_STRLEN_SMALL];
mop = airMopNew( );
pointsSoFar = pullPointNumber( pctx );
idtagBase = pctx->idtagNext;
point = NULL;
printf( "%s: adding %u new points ( to %u; %s ) . . . ",
me, pointNum, pointsSoFar,
airPrettySprintSize_t( doneStr, pullTraceMultiSizeof( mtrc ) ) );
for ( pidx=0; pidx<pointNum; pidx++ ) {
printf( "%s", airDoneStr( 0, pidx, pointNum, doneStr ) ); fflush( stdout );
if ( !point ) {
point = pullPointNew( pctx );
}
if ( pullPointInitializeRandomOrHalton( pctx, pidx + pointsSoFar,
point, scaleVol ) ) {
biffAddf( PULL, "%s: trouble trying point %u ( id %u )", me,
pidx, point->idtag );
airMopError( mop ); return 1;
}
if ( pullBinsPointAdd( pctx, point, NULL ) ) {
biffAddf( PULL, "%s: trouble binning point %u", me, point->idtag );
airMopError( mop ); return 1;
}
point = NULL;
}
printf( "%s\n", airDoneStr( 0, pidx, pointNum, doneStr ) );
if ( point ) {
/* we created a new test point, but it was never placed in the volume */
/* so, HACK: undo pullPointNew . . . */
point = pullPointNix( point );
/* pctx->idtagNext -= 1; */
}
nPosOut = nrrdNew( );
airMopAdd( mop, nPosOut, ( airMopper )nrrdNuke, airMopAlways );
if ( pullOutputGetFilter( nPosOut, NULL, NULL, NULL, 0.0,
pctx, idtagBase, 0 ) ) {
biffAddf( PULL, "%s: trouble A", me );
airMopError( mop ); return 1;
}
pos = AIR_CAST( double *, nPosOut->data );
addedNum = nPosOut->axis[1].size;
printf( "%s: tracing . . . ", me );
for ( pidx=0; pidx<addedNum; pidx++ ) {
double seedPos[4];
int added;
printf( "%s", airDoneStr( 0, pidx, addedNum, doneStr ) ); fflush( stdout );
trace = pullTraceNew( );
ELL_4V_COPY( seedPos, pos + 4*pidx );
if ( pullTraceSet( pctx, trace,
AIR_TRUE /* recordStrength */,
scaleStep, scaleHalfLen,
orientTestLen,
traceArrIncr, seedPos )
|| pullTraceMultiAdd( mtrc, trace, &added ) ) {
biffAddf( PULL, "%s: trouble on point %u", me, pidx );
airMopError( mop ); return 1;
}
if ( !added ) {
trace = pullTraceNix( trace );
}
}
printf( "%s\n", airDoneStr( 0, pidx, pointNum, doneStr ) );
if ( pullTraceMultiPlotAdd( nplot, mtrc, NULL,
strengthUse,
smooth, flatWght,
0, 0,
NULL, NULL ) ) {
biffAddf( PULL, "%s: trouble w/ PlotAdd ( A )", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
#define LOFF 1.000001
int
134 resamplePlot( Nrrd *nprob, const Nrrd *nplot ) {
static const char me[]="resamplePlot";
unsigned int ii, nn, sx, sy;
double scls[2] = {0.2, 0.2}, kparm[2] = {1.0, 0.0};
const NrrdKernel *kern = nrrdKernelTent;
/*
double scls[2] = {1.0, 1.0}, kparm[2] = {2.0, 4.0};
const NrrdKernel *kern = nrrdKernelGaussian;
*/
double sum, *prob;
if ( nrrdSimpleResample( nprob, nplot, kern, kparm, NULL, scls ) ) {
biffMovef( PULL, NRRD, "%s: trouble B", me );
return 1;
}
prob = AIR_CAST( double *, nprob->data );
sx = AIR_CAST( unsigned int, nprob->axis[0].size );
sy = AIR_CAST( unsigned int, nprob->axis[1].size );
for ( ii=0; ii<sx; ii++ ) {
/* zero out bottom line, useless crap piles up there */
prob[ii + sx*( sy-1 )] = 0;
}
sum = 0;
nn = AIR_CAST( unsigned int, nrrdElementNumber( nprob ) );
for ( ii=0; ii<nn; ii++ ) {
/* sum += log( LOFF+prob[ii] ); why? */
sum += prob[ii];
}
if ( sum ) {
for ( ii=0; ii<nn; ii++ ) {
/* prob[ii] = log( LOFF+prob[ii] )/sum; why? */
prob[ii] = prob[ii]/sum;
}
}
return 0;
}
double
174 distanceProb( Nrrd *npp, Nrrd *nqq ) {
double *pp, *qq, dist=0.0;
unsigned int ii, nn;
nn = AIR_CAST( unsigned int, nrrdElementNumber( npp ) );
pp = AIR_CAST( double *, npp->data );
qq = AIR_CAST( double *, nqq->data );
for ( ii=0; ii<nn; ii++ ) {
if ( qq[ii] ) {
dist += fabs( pp[ii]*log2( 0.0000001+pp[ii]/qq[ii] ) );
}
}
return dist;
}
void
190 savePlot( Nrrd *nout ) {
static const char me[]="saveProb";
static int count=0;
char fname[128], *err;
sprintf( fname, "pdf-%03u.nrrd", count );
if ( nrrdSave( fname, nout, NULL ) ) {
err = biffGetDone( NRRD );
fprintf( stderr, "%s: HEY couldn't save pdf to %s; moving on ...\n",
me, fname );
free( err );
}
count++;
return;
}
static const char *info = ( "Endless hacking!" );
int
208 main( int argc, const char **argv ) {
hestOpt *hopt=NULL;
hestParm *hparm;
airArray *mop;
const char *me;
char *err, *posOutS, *outS, *extraOutBaseS, *addLogS, *cachePathSS,
*tracesInS, *tracesOutS, *trcListOutS=NULL, *trcVolOutS=NULL,
*traceMaskPosOutS=NULL;
FILE *addLog, *tracesFile;
meetPullVol **vspec;
meetPullInfo **idef;
Nrrd *nPosIn=NULL, *nPosOut, *nplot, *nplotA, *nplotB, *nfilt, *nTraceMaskIn,
*nmaskedpos;
pullEnergySpec *enspR, *enspS, *enspWin;
NrrdKernelSpec *k00, *k11, *k22, *kSSrecon, *kSSblur;
NrrdBoundarySpec *bspec;
pullContext *pctx=NULL;
pullVolume *scaleVol=NULL;
pullTraceMulti *mtrc=NULL;
int ret=0;
unsigned int vspecNum, idefNum;
/* things that used to be set directly inside pullContext */
int nixAtVolumeEdgeSpace, constraintBeforeSeedThresh,
binSingle, liveThresholdOnInit, permuteOnRebin,
noAdd, unequalShapesAllow,
zeroZ, strnUse;
int verbose;
int interType, allowCodimension3Constraints, scaleIsTau,
smoothPlot, flatWght;
unsigned int samplesAlongScaleNum, pointNumInitial, pointPerVoxel,
ppvZRange[2], snap, stuckIterMax, constraintIterMax,
rngSeed, progressBinMod,
threadNum, tracePointNum, passNumMax,
kssOpi, kssFinished, bspOpi, bspFinished;
double jitter, stepInitial, constraintStepMin, radiusSpace, binWidthSpace,
radiusScale, orientTestLen,
backStepScale, opporStepScale, energyDecreaseMin, tpdThresh;
double sstep, sswin, ssrange[2];
unsigned int pres[2];
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
nPosOut = nrrdNew( );
airMopAdd( mop, nPosOut, ( airMopper )nrrdNuke, airMopAlways );
mtrc = pullTraceMultiNew( );
airMopAdd( mop, mtrc, ( airMopper )pullTraceMultiNix, airMopAlways );
nplot = nrrdNew( );
airMopAdd( mop, nplot, ( airMopper )nrrdNuke, airMopAlways );
nplotA = nrrdNew( );
airMopAdd( mop, nplotA, ( airMopper )nrrdNuke, airMopAlways );
nplotB = nrrdNew( );
airMopAdd( mop, nplotB, ( airMopper )nrrdNuke, airMopAlways );
nfilt = nrrdNew( );
airMopAdd( mop, nfilt, ( airMopper )nrrdNuke, airMopAlways );
hparm->respFileEnable = AIR_TRUE;
me = argv[0];
/* these don't need to be visible on the command-line */
interType = pullInterTypeUnivariate;
enspR = pullEnergySpecNew( );
airMopAdd( mop, enspR, ( airMopper )pullEnergySpecNix, airMopAlways );
pullEnergySpecParse( enspR, "cotan" );
enspS = pullEnergySpecNew( );
airMopAdd( mop, enspS, ( airMopper )pullEnergySpecNix, airMopAlways );
pullEnergySpecParse( enspS, "zero" );
enspWin = pullEnergySpecNew( );
airMopAdd( mop, enspWin, ( airMopper )pullEnergySpecNix, airMopAlways );
pullEnergySpecParse( enspWin, "butter:16, 0.8" );
hestOptAdd( &hopt, "zz", "bool", airTypeBool, 1, 1, &zeroZ, "false",
"always constrain Z=0, to process 2D images" );
hestOptAdd( &hopt, "su", "bool", airTypeBool, 1, 1, &strnUse, "false",
"weigh contributions to traces with strength" );
hestOptAdd( &hopt, "nave", "bool", airTypeBool, 1, 1,
&nixAtVolumeEdgeSpace, "false",
"whether or not to nix points at edge of volume, where gage had "
"to invent values for kernel support" );
hestOptAdd( &hopt, "cbst", "bool", airTypeBool, 1, 1,
&constraintBeforeSeedThresh, "false",
"during initialization, try constraint satisfaction before "
"testing seedThresh" );
hestOptAdd( &hopt, "noadd", NULL, airTypeBool, 0, 0,
&noAdd, NULL, "turn off adding during population control" );
hestOptAdd( &hopt, "usa", "bool", airTypeBool, 1, 1,
&unequalShapesAllow, "false",
"allow volumes to have different shapes ( false is safe as "
"different volume sizes are often accidental )" );
hestOptAdd( &hopt, "nobin", NULL, airTypeBool, 0, 0,
&binSingle, NULL,
"turn off spatial binning ( which prevents multi-threading "
"from being useful ), for debugging or speed-up measurement" );
hestOptAdd( &hopt, "lti", "bool", airTypeBool, 1, 1,
&liveThresholdOnInit, "true",
"impose liveThresh on initialization" );
hestOptAdd( &hopt, "por", "bool", airTypeBool, 1, 1,
&permuteOnRebin, "true",
"permute points during rebinning" );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "1",
"verbosity level" );
hestOptAdd( &hopt, "vol", "vol0 vol1", airTypeOther, 1, -1, &vspec, NULL,
"input volumes, in format <filename>:<kind>:<volname>",
&vspecNum, NULL, meetHestPullVol );
hestOptAdd( &hopt, "info", "info0 info1", airTypeOther, 1, -1, &idef, NULL,
"info definitions, in format "
"<info>[-c]:<volname>:<item>[:<zero>:<scale>]",
&idefNum, NULL, meetHestPullInfo );
hestOptAdd( &hopt, "k00", "kern00", airTypeOther, 1, 1, &k00,
"cubic:1, 0", "kernel for gageKernel00",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kern11", airTypeOther, 1, 1, &k11,
"cubicd:1, 0", "kernel for gageKernel11",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kern22", airTypeOther, 1, 1, &k22,
"cubicdd:1, 0", "kernel for gageKernel22",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "sscp", "path", airTypeString, 1, 1, &cachePathSS, "./",
"path ( without trailing / ) for where to read/write "
"pre-blurred volumes for scale-space" );
kssOpi =
hestOptAdd( &hopt, "kssb", "kernel", airTypeOther, 1, 1, &kSSblur,
"ds:1, 5", "default blurring kernel, to sample scale space",
NULL, NULL, nrrdHestKernelSpec );
bspOpi =
hestOptAdd( &hopt, "bsp", "boundary", airTypeOther, 1, 1, &bspec,
"wrap", "default boundary behavior of scale-space blurring",
NULL, NULL, nrrdHestBoundarySpec );
hestOptAdd( &hopt, "kssr", "kernel", airTypeOther, 1, 1, &kSSrecon,
"hermite", "kernel for reconstructing from scale space samples",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "nss", "# scl smpls", airTypeUInt, 1, 1,
&samplesAlongScaleNum, "1",
"if using \"-ppv\", number of samples along scale axis "
"for each spatial position" );
hestOptAdd( &hopt, "np", "# points", airTypeUInt, 1, 1,
&pointNumInitial, "1000",
"number of points to initialize with" );
hestOptAdd( &hopt, "tnp", "# points", airTypeUInt, 1, 1,
&tracePointNum, "1000",
"number of points to add in each iteration of "
"estimation of plot" );
hestOptAdd( &hopt, "pnm", "# passes", airTypeUInt, 1, 1,
&passNumMax, "10",
"max number of passes in plot estimation" );
hestOptAdd( &hopt, "tpdt", "thresh", airTypeDouble, 1, 1,
&tpdThresh, "1.0", "KL-distance threshold" );
hestOptAdd( &hopt, "ti", "fname", airTypeString, 1, 1,
&tracesInS, "", "input file of *pre-computed* traces" );
hestOptAdd( &hopt, "to", "fname", airTypeString, 1, 1,
&tracesOutS, "", "file for saving out *computed* traces" );
hestOptAdd( &hopt, "ppv", "# pnts/vox", airTypeUInt, 1, 1,
&pointPerVoxel, "0",
"number of points per voxel to start in simulation "
"( need to have a seed thresh vol, overrides \"-np\" )" );
hestOptAdd( &hopt, "ppvzr", "z range", airTypeUInt, 2, 2,
ppvZRange, "1 0",
"range of Z slices ( 1st num < 2nd num ) to do ppv in, or, "
"\"1 0\" for whole volume" );
hestOptAdd( &hopt, "jit", "jitter", airTypeDouble, 1, 1,
&jitter, "0",
"amount of jittering to do with ppv" );
hestOptAdd( &hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "",
"4-by-N array of positions to start at ( overrides \"-np\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1,
&stepInitial, "1",
"initial step size for gradient descent" );
hestOptAdd( &hopt, "csm", "step", airTypeDouble, 1, 1,
&constraintStepMin, "0.0001",
"convergence criterion for constraint satisfaction" );
hestOptAdd( &hopt, "snap", "# iters", airTypeUInt, 1, 1,
&snap, "0",
"if non-zero, # iters between saved snapshots" );
hestOptAdd( &hopt, "stim", "# iters", airTypeUInt, 1, 1,
&stuckIterMax, "5",
"if non-zero, max # iterations to allow a particle "
" to be stuck before nixing" );
hestOptAdd( &hopt, "maxci", "# iters", airTypeUInt, 1, 1,
&constraintIterMax, "15",
"if non-zero, max # iterations for contraint enforcement" );
hestOptAdd( &hopt, "irad", "scale", airTypeDouble, 1, 1,
&radiusSpace, "1",
"particle radius in spatial domain" );
hestOptAdd( &hopt, "srad", "scale", airTypeDouble, 1, 1,
&radiusScale, "1",
"particle radius in scale domain" );
hestOptAdd( &hopt, "bws", "bin width", airTypeDouble, 1, 1,
&binWidthSpace, "1.001",
"spatial bin width as multiple of spatial radius" );
hestOptAdd( &hopt, "ess", "scl", airTypeDouble, 1, 1,
&backStepScale, "0.5",
"when energy goes up instead of down, scale step "
"size by this" );
hestOptAdd( &hopt, "oss", "scl", airTypeDouble, 1, 1,
&opporStepScale, "1.0",
"opportunistic scaling ( hopefully up, >1 ) of step size "
"on every iteration" );
hestOptAdd( &hopt, "edmin", "frac", airTypeDouble, 1, 1,
&energyDecreaseMin, "0.0001",
"convergence threshold: stop when fractional improvement "
"( decrease ) in energy dips below this" );
hestOptAdd( &hopt, "ac3c", "ac3c", airTypeBool, 1, 1,
&allowCodimension3Constraints, "false",
"allow codimensions 3 constraints" );
hestOptAdd( &hopt, "sit", "sit", airTypeBool, 1, 1, &scaleIsTau, "false",
"scale is tau" );
hestOptAdd( &hopt, "rng", "seed", airTypeUInt, 1, 1,
&rngSeed, "42",
"base seed value for RNGs ( and as a hack, start index for "
"Halton-based sampling )" );
hestOptAdd( &hopt, "pbm", "mod", airTypeUInt, 1, 1,
&progressBinMod, "50",
"progress bin mod" );
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1,
&threadNum, "1",
( airThreadCapable
? "number of threads to use"
: "IF threads had beeen enabled in this Teem build ( but "
"they are NOT ), this is how you would control the number "
"of threads to use" ) );
hestOptAdd( &hopt, "addlog", "fname", airTypeString, 1, 1, &addLogS, "",
"name of file in which to log all particle additions" );
hestOptAdd( &hopt, "po", "nout", airTypeString, 1, 1, &posOutS, "",
"if a filename is given here, the positions of the initialized "
"particles are saved" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "trace.nrrd",
"plotted trace image" );
hestOptAdd( &hopt, "eob", "base", airTypeString, 1, 1, &extraOutBaseS, "",
"save extra info ( besides position ), and use this string as "
"the base of the filenames. Not using this means the extra "
"info is not saved." );
hestOptAdd( &hopt, "ss", "sstep", airTypeDouble, 1, 1, &sstep, "0.0003",
"fraction of SS range used as step along scale for tracing" );
hestOptAdd( &hopt, "sw", "swin", airTypeDouble, 1, 1, &sswin, "0.1",
"fraction of SS range that caps length of trace along scale" );
hestOptAdd( &hopt, "otl", "len", airTypeDouble, 1, 1, &orientTestLen, "0",
"when probing the tangents of the contraint surface, this is "
"( when nonzero ) the length in world-space of "
"the offset to the second test position at which to sample "
"the constraint" );
hestOptAdd( &hopt, "pr", "sx sy", airTypeUInt, 2, 2, pres, "1000 420",
"resolution of the 2D plot" );
hestOptAdd( &hopt, "sp", "smoothing", airTypeInt, 1, 1, &smoothPlot, "0",
"if greater than zero, amount of smoothing of computed "
"stabilities, prior to plotting" );
hestOptAdd( &hopt, "fw", "flatwght", airTypeInt, 1, 1, &flatWght, "0",
"if greater than zero, amount by which to increase "
"weighting of more horizontal ( flat ) sections of the plot" );
hestOptAdd( &hopt, "tlo", "fname", airTypeString, 1, 1, &trcListOutS, "",
"output filename of list of all points in all traces" );
hestOptAdd( &hopt, "tmi", "nmask", airTypeOther, 1, 1, &nTraceMaskIn, "",
"if this is an 8-bit 2D array, of the same size as given by "
"the \"-pr\" option, then it is used as a mask on the "
"pre-computed trace given by \"-ti\", to find points in traces "
"that overlap with the trace mask, and save them in the file "
"given by \"-tmpo\"",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "tmpo", "nout", airTypeString, 1, 1, &traceMaskPosOutS, "",
"if a filename is given here, this is where to save positions "
"of positions on traces masked by \"-tmi\"" );
hestOptAdd( &hopt, "tvo", "fname", airTypeString, 1, 1, &trcVolOutS, "",
"output filename for rasterized trace of scale-space volume" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
/*
airEnumPrint( stderr, gageScl );
exit( 0 );
*/
if ( airStrlen( addLogS ) ) {
if ( !( addLog = airFopen( addLogS, stdout, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing\n", me, addLogS );
airMopError( mop ); return 1;
}
airMopAdd( mop, addLog, ( airMopper )airFclose, airMopAlways );
} else {
addLog = NULL;
}
if ( nTraceMaskIn && airStrlen( traceMaskPosOutS ) ) {
nmaskedpos = nrrdNew( );
airMopAdd( mop, nmaskedpos, ( airMopper )nrrdNuke, airMopAlways );
} else {
nmaskedpos = NULL;
}
pctx = pullContextNew( );
airMopAdd( mop, pctx, ( airMopper )pullContextNix, airMopAlways );
if ( pullVerboseSet( pctx, verbose )
|| pullFlagSet( pctx, pullFlagZeroZ, zeroZ )
|| pullFlagSet( pctx, pullFlagNixAtVolumeEdgeSpace, nixAtVolumeEdgeSpace )
|| pullFlagSet( pctx, pullFlagConstraintBeforeSeedThresh,
constraintBeforeSeedThresh )
|| pullFlagSet( pctx, pullFlagBinSingle, binSingle )
|| pullFlagSet( pctx, pullFlagNoAdd, noAdd )
|| pullFlagSet( pctx, pullFlagPermuteOnRebin, permuteOnRebin )
/* want this to be true; tracing is different than regular particles */
|| pullFlagSet( pctx, pullFlagRestrictiveAddToBins, AIR_TRUE )
|| pullFlagSet( pctx, pullFlagAllowCodimension3Constraints,
allowCodimension3Constraints )
|| pullFlagSet( pctx, pullFlagScaleIsTau, scaleIsTau )
|| pullInitUnequalShapesAllowSet( pctx, unequalShapesAllow )
|| pullIterParmSet( pctx, pullIterParmSnap, snap )
|| pullIterParmSet( pctx, pullIterParmStuckMax, stuckIterMax )
|| pullIterParmSet( pctx, pullIterParmConstraintMax, constraintIterMax )
|| pullSysParmSet( pctx, pullSysParmStepInitial, stepInitial )
|| pullSysParmSet( pctx, pullSysParmConstraintStepMin, constraintStepMin )
|| pullSysParmSet( pctx, pullSysParmRadiusSpace, radiusSpace )
|| pullSysParmSet( pctx, pullSysParmRadiusScale, radiusScale )
|| pullSysParmSet( pctx, pullSysParmBinWidthSpace, binWidthSpace )
|| pullSysParmSet( pctx, pullSysParmEnergyDecreaseMin,
energyDecreaseMin )
|| pullSysParmSet( pctx, pullSysParmBackStepScale, backStepScale )
|| pullSysParmSet( pctx, pullSysParmOpporStepScale, opporStepScale )
|| pullRngSeedSet( pctx, rngSeed )
|| pullProgressBinModSet( pctx, progressBinMod )
|| pullThreadNumSet( pctx, threadNum )
|| pullInterEnergySet( pctx, interType, enspR, enspS, enspWin )
|| pullInitLiveThreshUseSet( pctx, liveThresholdOnInit )
|| pullLogAddSet( pctx, addLog ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with flags:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( pullInitHaltonSet( pctx, pointNumInitial, rngSeed ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting halton:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( meetPullVolStackBlurParmFinishMulti( vspec, vspecNum,
&kssFinished, &bspFinished,
kSSblur, bspec )
|| meetPullVolLoadMulti( vspec, vspecNum, cachePathSS, verbose )
|| meetPullVolAddMulti( pctx, vspec, vspecNum,
k00, k11, k22, kSSrecon )
|| meetPullInfoAddMulti( pctx, idef, idefNum ) ) {
airMopAdd( mop, err = biffGetDone( MEET ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with volumes or infos:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( !kssFinished && hestSourceUser == hopt[kssOpi].source ) {
fprintf( stderr, "\n\n%s: WARNING! Used the -%s flag, but the "
"meetPullVol specified blurring kernels\n\n\n", me,
hopt[kssOpi].flag );
}
if ( !bspFinished && hestSourceUser == hopt[bspOpi].source ) {
fprintf( stderr, "\n\n%s: WARNING! Used the -%s flag, but the "
"meetPullVol specified boundary specs\n\n\n", me,
hopt[bspOpi].flag );
}
if ( airStrlen( tracesInS ) ) {
/* don't need to initialize points if we're reading a trace,
but annoyingly we do need the rest of the pull set up,
*JUST* so that we can read off the scale-space range
associated with the constraint */
pullFlagSet( pctx, pullFlagStartSkipsPoints, AIR_TRUE );
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: about to pullStart\n", me );
}
if ( pullStart( pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble starting system:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nrrdMaybeAlloc_va( nplotA, nrrdTypeDouble, 2,
AIR_CAST( size_t, pres[0] ),
AIR_CAST( size_t, pres[1] ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble creating output:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( pullConstraintScaleRange( pctx, ssrange ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble C:\n%s", me, err );
airMopError( mop ); return 1;
}
fprintf( stderr, "!%s: ================== ssrange %g %g\n", me,
ssrange[0], ssrange[1] );
if ( vspec[0]->sbp ) {
char stmp[AIR_STRLEN_LARGE];
gageStackBlurParmSprint( stmp, vspec[0]->sbp, NULL, NULL );
fprintf( stderr, "!%s: ======== %s\n", me, stmp );
}
nplotA->axis[0].min = ssrange[0];
nplotA->axis[0].max = ssrange[1];
nplotA->axis[1].min = 1.0;
nplotA->axis[1].max = 0.0;
nrrdCopy( nplotB, nplotA );
if ( airStrlen( tracesInS ) ) {
if ( !( tracesFile = airFopen( tracesInS, stdin, "rb" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for reading\n", me, tracesInS );
airMopError( mop ); return 1;
}
if ( pullTraceMultiRead( mtrc, tracesFile ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble reading:\n%s", me, err );
airMopError( mop ); return 1;
}
airFclose( tracesFile );
goto plotting;
}
/* else not reading in traces, have to compute them */
if ( !pctx->constraint ) {
fprintf( stderr, "%s: this programs requires a constraint\n", me );
airMopError( mop ); return 1;
}
scaleVol = NULL;
{
unsigned int ii;
for ( ii=0; ii<pctx->volNum; ii++ ) {
if ( pctx->vol[ii]->ninScale ) {
scaleVol = pctx->vol[ii];
break;
}
}
}
if ( !scaleVol ) {
fprintf( stderr, "%s: this program requires scale-space\n", me );
airMopError( mop ); return 1;
}
if ( pullOutputGet( nPosOut, NULL, NULL, NULL, 0.0, pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble D:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( airStrlen( posOutS ) ) {
nrrdSave( posOutS, nPosOut, NULL );
}
{
double *pos, seedPos[4], scaleWin, scaleStep, dist=0;
unsigned int pidx, pnum, passIdx;
pullTrace *pts;
Nrrd *nsplot, *nprogA, *nprogB, *nlsplot;
char doneStr[AIR_STRLEN_SMALL];
pos = AIR_CAST( double *, nPosOut->data );
nlsplot = nrrdNew( );
airMopAdd( mop, nlsplot, ( airMopper )nrrdNuke, airMopAlways );
nsplot = nrrdNew( );
airMopAdd( mop, nsplot, ( airMopper )nrrdNuke, airMopAlways );
nprogA = nrrdNew( );
airMopAdd( mop, nprogA, ( airMopper )nrrdNuke, airMopAlways );
nprogB = nrrdNew( );
airMopAdd( mop, nprogB, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nprogA, nplotA )
|| nrrdCopy( nprogB, nplotA ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble creating nprogs:\n%s", me, err );
airMopError( mop ); return 1;
}
scaleWin = sswin*( ssrange[1]-ssrange[0] );
scaleStep = sstep*( ssrange[1]-ssrange[0] );
pnum = nPosOut->axis[1].size;
printf( "!%s: tracing initial %u points . . . ", me, pnum );
for ( pidx=0; pidx<pnum; pidx++ ) {
int added;
printf( "%s", airDoneStr( 0, pidx, pnum, doneStr ) ); fflush( stdout );
pts = pullTraceNew( );
ELL_4V_COPY( seedPos, pos + 4*pidx );
if ( pullTraceSet( pctx, pts,
AIR_TRUE /* recordStrength */,
scaleStep, scaleWin/2,
orientTestLen,
AIR_CAST( unsigned int, ( scaleWin/2 )/scaleStep ),
seedPos )
|| pullTraceMultiAdd( mtrc, pts, &added ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble on point %u:\n%s", me, pidx, err );
airMopError( mop ); return 1;
}
if ( !added ) {
/*
fprintf( stderr, "!%s: whynowhere %s, why stop %s( %s ) %s( %s )\n", me,
airEnumStr( pullTraceStop, pts->whyNowhere ),
airEnumStr( pullTraceStop, pts->whyStop[0] ),
( pts->whyStop[0] == pullTraceStopConstrFail
? airEnumStr( pullConstraintFail, pts->whyConstrFail[0] )
: "" ),
airEnumStr( pullTraceStop, pts->whyStop[1] ),
( pts->whyStop[1] == pullTraceStopConstrFail
? airEnumStr( pullConstraintFail, pts->whyConstrFail[1] )
: "" ) );
*/
pts = pullTraceNix( pts );
}
}
printf( "%s\n", airDoneStr( 0, pidx, pnum, doneStr ) );
if ( !mtrc->traceNum ) {
fprintf( stderr, "%s: %u initial points led to zero traces\n", me, pnum );
airMopError( mop ); return 1;
}
if ( pullTraceMultiPlotAdd( nprogA, mtrc, NULL,
strnUse, smoothPlot, flatWght,
0, 0, NULL, NULL ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble PlotAdd'ing ( B ):\n%s", me, err );
airMopError( mop ); return 1;
}
resamplePlot( nlsplot, nprogA );
/* savePlot( nlsplot ); */
for ( passIdx=0; passIdx<passNumMax; passIdx++ ) {
double dd;
fprintf( stderr, "!%s: pass %u/%u ==================\n",
me, passIdx, passNumMax );
nrrdSetZero( nprogB );
if ( findAndTraceMorePoints( nprogB, pctx, scaleVol,
strnUse, smoothPlot, flatWght,
scaleStep, scaleWin/2,
orientTestLen,
AIR_CAST( unsigned int, ( scaleWin/2 )/scaleStep ),
mtrc, tracePointNum )
|| resamplePlot( nsplot, nprogB ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble on pass %u:\n%s", me, passIdx, err );
airMopError( mop ); return 1;
}
/* savePlot( nsplot ); */
dd = distanceProb( nsplot, nlsplot )/tracePointNum;
if ( !passIdx ) {
dist = dd;
} else {
dist = AIR_LERP( 0.9, dist, dd );
}
fprintf( stderr, "%s: dd = %g -> dist = %g ( %s %g )\n", me, dd, dist,
dist < tpdThresh ? "<" : ">=", tpdThresh );
nrrdCopy( nlsplot, nsplot );
if ( dist < tpdThresh ) {
fprintf( stderr, "%s: converged: dist %g < thresh %g\n",
me, dist, tpdThresh );
break;
}
}
if ( dist >= tpdThresh ) {
fprintf( stderr, "%s: WARNING did NOT converge: dist %g >= thresh %g\n",
me, dist, tpdThresh );
}
if ( airStrlen( tracesOutS ) && !airStrlen( tracesInS ) ) {
tracesFile = airFopen( tracesOutS, stdout, "wb" );
if ( pullTraceMultiWrite( tracesFile, mtrc ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s", me, err );
airMopError( mop ); return 1;
}
fclose( tracesFile );
}
plotting:
if ( airStrlen( trcListOutS ) ) {
/* format:
** trcIdx isSeed X Y Z S f( stab ) strn qual
** 0 1 2 3 4 5 6 7 8 ( 9 )
*/
Nrrd *ntlo;
double *tlo;
size_t sx=9, totn=0, toti=0;
pullTrace *trc;
pullPoint *lpnt;
unsigned int ti;
for ( ti=0; ti<mtrc->traceNum; ti++ ) {
trc = mtrc->trace[ti];
fprintf( stderr, "!%s: ti %u/%u : stab ( %d-D ) %u %u\n", me, ti, mtrc->traceNum,
trc->nstab->dim, AIR_CAST( unsigned int, trc->nstab->axis[0].size ),
AIR_CAST( unsigned int, trc->nstab->axis[1].size ) );
totn += trc->nstab->axis[0].size;
}
ntlo = nrrdNew( );
lpnt = pullPointNew( pctx );
if ( nrrdMaybeAlloc_va( ntlo, nrrdTypeDouble, 2, sx, totn ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't alloc output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopAdd( mop, ntlo, ( airMopper )nrrdNuke, airMopAlways );
tlo = AIR_CAST( double *, ntlo->data );
for ( ti=0; ti<mtrc->traceNum; ti++ ) {
unsigned int vi, vn;
double *vert, *stab, *strn, qual;
trc = mtrc->trace[ti];
vn = AIR_CAST( unsigned int, trc->nstab->axis[0].size );
vert = AIR_CAST( double *, trc->nvert->data );
stab = AIR_CAST( double *, trc->nstab->data );
strn = AIR_CAST( double *, ( trc->nstrn
? trc->nstrn->data
: NULL ) );
for ( vi=0; vi<vn; vi++ ) {
tlo[sx*toti + 0] = AIR_CAST( double, ti );
tlo[sx*toti + 1] = ( vi == trc->seedIdx );
ELL_4V_COPY( tlo + sx*toti + 2, vert + 4*vi );
ELL_4V_COPY( lpnt->pos, vert + 4*vi );
if ( pctx->ispec[pullInfoQuality] ) {
pullProbe( pctx->task[0], lpnt );
qual = pullPointScalar( pctx, lpnt, pullInfoQuality, NULL, NULL );
} else {
qual = 0.0;
}
tlo[sx*toti + 6] = stab[vi];
tlo[sx*toti + 7] = strn ? strn[vi] : 0.0;
tlo[sx*toti + 8] = qual;
toti++;
}
}
if ( nrrdSave( trcListOutS, ntlo, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
}
if ( airStrlen( trcVolOutS ) ) {
/* HEY: copy and paste from above */
Nrrd *nout;
const gagePoint *pnt;
meetPullVol *mpv;
pullTrace *trc;
pullPoint *lpnt;
unsigned int size[4], idx[4], iii, ti, si;
double idxd[4], val, ( *lup )( const void *v, size_t I ),
( *ins )( void *v, size_t I, double d );
/* HEY this will segfault around here if the mask volume is
first, because of the following line of code */
if ( !( mpv = meetPullVolCopy( vspec[0] ) ) ) {
airMopAdd( mop, err = biffGetDone( MEET ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't copy volume:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopAdd( mop, mpv, ( airMopper )meetPullVolNix, airMopAlways );
/* at this point we actually have to hijack the mpv in case its
a non-scalar kind, because all we want is a scalar output */
if ( 0 != mpv->kind->baseDim ) {
unsigned int ni;
Nrrd *nslice;
nslice = nrrdNew( );
airMopAdd( mop, nslice, ( airMopper )nrrdNuke, airMopAlways );
fprintf( stderr, "!%s: slicing %u %s vols to %s\n", me,
mpv->sbp->num, mpv->kind->name, gageKindScl->name );
for ( ni=0; ni<mpv->sbp->num; ni++ ) {
if ( nrrdSlice( nslice, mpv->ninSS[ni], 0, 0 )
|| nrrdCopy( mpv->ninSS[ni], nslice ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't slice volume %u:\n%s", me, ni, err );
airMopError( mop ); return 1;
}
}
/* can we just do this? */
mpv->kind = gageKindScl;
}
size[0] = AIR_CAST( unsigned int, mpv->ninSS[0]->axis[0].size );
size[1] = AIR_CAST( unsigned int, mpv->ninSS[0]->axis[1].size );
size[2] = AIR_CAST( unsigned int, mpv->ninSS[0]->axis[2].size );
size[3] = mpv->sbp->num;
printf( "!%s: size = ( %u, %u, %u, %u )\n", me, size[0], size[1], size[2], size[3] );
lpnt = pullPointNew( pctx );
airMopAdd( mop, lpnt, ( airMopper )pullPointNix, airMopAlways );
for ( si=0; si<size[3]; si++ ) {
nrrdSetZero( mpv->ninSS[si] );
}
lup = nrrdDLookup[mpv->ninSS[0]->type];
ins = nrrdDInsert[mpv->ninSS[0]->type];
/* HEY here also assuming scale-space volume is first volume */
pnt = &( pctx->task[0]->vol[0]->gctx->point );
for ( ti=0; ti<mtrc->traceNum; ti++ ) {
unsigned int vi, vn, orn, ori;
double *vert, *stab, *strn, *orin, wght;
trc = mtrc->trace[ti];
vn = AIR_CAST( unsigned int, trc->nstab->axis[0].size );
vert = AIR_CAST( double *, trc->nvert->data );
stab = AIR_CAST( double *, trc->nstab->data );
orin = AIR_CAST( double *, trc->norin->data );
strn = AIR_CAST( double *, ( strnUse && trc->nstrn
? trc->nstrn->data
: NULL ) );
/* orn = ( orin ? 20 : 1 ); */
orn = 1;
for ( vi=0; vi<vn; vi++ ) {
for ( ori=0; ori < orn; ori++ ) {
ELL_4V_COPY( lpnt->pos, vert + 4*vi );
if ( orn > 1 ) {
/* hack to draw a dotted line along tangent */
double oscl = AIR_AFFINE( 0, ori, orn-1, -100, 100 );
lpnt->pos[0] += oscl*( orin + 3*vi )[0];
lpnt->pos[1] += oscl*( orin + 3*vi )[1];
}
if ( zeroZ && lpnt->pos[2] != 0 ) {
fprintf( stderr, "%s: zeroZ violated\n", me );
airMopError( mop ); return 1;
}
wght = stab[vi];
if ( strn ) {
wght *= strn[vi];
}
/* probe just to get the transform to idx-space from gage */
if ( pullProbe( pctx->task[0], lpnt ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't probe:\n%s", me, err );
airMopError( mop ); return 1;
}
ELL_4V_ADD2( idxd, pnt->frac, pnt->idx );
/* because of gage subtlety that gagePoint->idx is index
of upper, not lower, corner, idxd is too big by 1 */
idx[0] = airIndexClamp( -0.5, idxd[0]-1, size[0]-0.5, size[0] );
idx[1] = airIndexClamp( -0.5, idxd[1]-1, size[1]-0.5, size[1] );
idx[2] = airIndexClamp( -0.5, idxd[2]-1, size[2]-0.5, size[2] );
idx[3] = airIndexClamp( 0, idxd[3], size[3]-1, size[3] );
iii = idx[0] + size[0]*( idx[1] + size[1]*idx[2] );
val = lup( mpv->ninSS[idx[3]]->data, iii );
ins( mpv->ninSS[idx[3]]->data, iii, wght + val );
/*
printf( "!%s: ( %g, %g, %g, %g ) -> ( %g, %g, %g, %g ) -> ( %u, %u, %u, %u ) -> %u: %g\n",
me, lpnt->pos[0], lpnt->pos[1], lpnt->pos[2], lpnt->pos[3],
idxd[0], idxd[1], idxd[2], idxd[3],
idx[0], idx[1], idx[2], idx[3], iii, val );
*/
}
}
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdJoin( nout, AIR_CAST( const Nrrd *const *, mpv->ninSS ),
size[3], 3, AIR_FALSE )
|| nrrdSave( trcVolOutS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't join or save SS output:\n%s", me, err );
airMopError( mop ); return 1;
}
}
if ( pullTraceMultiPlotAdd( nplotA, mtrc, NULL,
strnUse, smoothPlot, flatWght,
0, 0, nmaskedpos, nTraceMaskIn )
/* || pullTraceMultiFilterConcaveDown( nfilt, mtrc, 0.05 ) */
/* The filter concave down idea didn't really work */
|| pullTraceMultiPlotAdd( nplotB, mtrc, NULL /* nfilt */,
AIR_TRUE, smoothPlot, flatWght,
0, 0, NULL, NULL ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble PlotAdd'ing ( C ):\n%s", me, err );
airMopError( mop ); return 1;
}
{
const Nrrd *nin[2];
nin[0] = nplotA;
nin[1] = nplotB;
if ( nrrdJoin( nplot, nin, 2, 0, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble E:\n%s", me, err );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nplot, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble F:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nmaskedpos && nmaskedpos->data && airStrlen( traceMaskPosOutS ) ) {
if ( nrrdSave( traceMaskPosOutS, nmaskedpos, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble G:\n%s", me, err );
airMopError( mop ); return 1;
}
}
}
pullFinish( pctx );
airMopOkay( mop );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../meet.h"
int
27 main( int argc, const char *argv[] ) {
const char *me;
char *err;
airArray *mop;
AIR_UNUSED( argc );
me = argv[0];
mop = airMopNew( );
if ( meetAirEnumAllCheck( ) ) {
airMopAdd( mop, err=biffGetDone( MEET ), airFree, airMopAlways );
fprintf( stderr, "%s: problem:\n%s", me, err );
airMopError( mop ); return 1;
}
/* else no problems */
meetAirEnumAllPrint( stdout );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/pull.h>
#include "../meet.h"
static const char *info =
( "For the simple task of generating N locations inside a volume. As "
"controlled by \"-m\", can be per voxel, uniform quasi-random, or "
"or uniform random." );
int
33 main( int argc, const char **argv ) {
hestOpt *hopt=NULL;
hestParm *hparm;
airArray *mop;
const char *me;
char *err, *outS;
Nrrd *nin, *npos, *nout;
gageKind *kind;
pullEnergySpec *enspR;
meetPullInfo *minf[3];
NrrdKernelSpec *k00, *k11, *k22;
pullContext *pctx=NULL;
int ret=0, verbose, method;
unsigned int num, ss;
double jitter;
mop = airMopNew( );
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
npos = nrrdNew( );
airMopAdd( mop, npos, ( airMopper )nrrdNuke, airMopAlways );
hparm->respFileEnable = AIR_TRUE;
me = argv[0];
/* these don't need to be visible on the command-line */
enspR = pullEnergySpecNew( );
airMopAdd( mop, enspR, ( airMopper )pullEnergySpecNix, airMopAlways );
pullEnergySpecParse( enspR, "cotan" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, "scalar",
"\"kind\" of volume ( \"scalar\", \"vector\", "
"\"tensor\", or \"dwi\" )",
NULL, NULL, meetHestGageKind );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verbose, "0",
"verbosity level" );
hestOptAdd( &hopt, "m", "method", airTypeEnum, 1, 1, &method, "ppv",
"way of creating point locations. Can be:\n "
"\b\bo \"ppv\": some points per-voxel ( \"-n\" is how "
"many per-voxel, can be N < -1 for every Nth voxel )\n "
"\b\bo \"halton\": use halton sequence for quasi-random "
"( \"-s\" gives initial value )\n "
"\b\bo \"random\": use uniform random positions "
"( \"-s\" gives RNG seed )",
NULL, pullInitMethod );
hestOptAdd( &hopt, "n", "#points or #ppv", airTypeUInt, 1, 1, &num, "1",
"number of points to initialize with with random and halton, "
"or, number points per voxel with ppv" );
hestOptAdd( &hopt, "s", "seed or start", airTypeUInt, 1, 1, &ss, "1",
"random number seed for random and ppv, or ( hack ), start "
"index for Halton-based sampling" );
hestOptAdd( &hopt, "jit", "jitter", airTypeDouble, 1, 1, &jitter, "1",
"amount of jittering to do with ppv" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "out.nrrd",
"filename for saving positions" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
/* other parms that we set just to keep pull happy */
enspR = pullEnergySpecNew( );
airMopAdd( mop, enspR, ( airMopper )pullEnergySpecNix, airMopAlways );
k00 = nrrdKernelSpecNew( );
airMopAdd( mop, k00, ( airMopper )nrrdKernelSpecNix, airMopAlways );
k11 = nrrdKernelSpecNew( );
airMopAdd( mop, k11, ( airMopper )nrrdKernelSpecNix, airMopAlways );
k22 = nrrdKernelSpecNew( );
airMopAdd( mop, k22, ( airMopper )nrrdKernelSpecNix, airMopAlways );
if ( pullEnergySpecParse( enspR, "cotan" ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up faux energies:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( nrrdKernelSpecParse( k00, "box" )
|| nrrdKernelSpecParse( k11, "zero" )
|| nrrdKernelSpecParse( k22, "zero" ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up faux kernels:\n%s", me, err );
airMopError( mop ); return 1;
}
#define VNAME "bingo"
pctx = pullContextNew( );
airMopAdd( mop, pctx, ( airMopper )pullContextNix, airMopAlways );
minf[0] = meetPullInfoNew( );
airMopAdd( mop, minf[0], ( airMopper )*meetPullInfoNix, airMopAlways );
minf[1] = meetPullInfoNew( );
airMopAdd( mop, minf[1], ( airMopper )*meetPullInfoNix, airMopAlways );
minf[2] = meetPullInfoNew( );
airMopAdd( mop, minf[2], ( airMopper )*meetPullInfoNix, airMopAlways );
int E = pullVerboseSet( pctx, verbose );
if ( pullInitMethodRandom == method ) {
if ( !E ) E |= pullRngSeedSet( pctx, ss );
if ( !E ) E |= pullInitRandomSet( pctx, num );
} else if ( pullInitMethodHalton == method ) {
if ( !E ) E |= pullInitHaltonSet( pctx, num, ss );
} else if ( pullInitMethodPointPerVoxel == method ) {
if ( !E ) E |= pullRngSeedSet( pctx, ss );
if ( !E ) E |= pullInitPointPerVoxelSet( pctx, num, 1, 0, 0, jitter );
} else if ( pullInitMethodGivenPos == method ) {
fprintf( stderr, "%s: this utility is for making point positions; "
"init method %s not available", me,
airEnumStr( pullInitMethod, method ) );
airMopError( mop ); return 1;
} else {
fprintf( stderr, "%s: unsupported %s %s\n", me, pullInitMethod->name,
airEnumStr( pullInitMethod, method ) );
airMopError( mop ); return 1;
}
if ( E
|| pullFlagSet( pctx, pullFlagNixAtVolumeEdgeSpaceInitRorH, AIR_TRUE )
|| pullInterEnergySet( pctx, pullInterTypeJustR,
enspR, NULL, NULL )
|| pullVolumeSingleAdd( pctx, kind, VNAME, nin,
k00, k11, k22 ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble starting system:\n%s", me, err );
airMopError( mop ); return 1;
}
/* figure out how big a voxel is, so that the bins set up don't
get overflowed. The fact that there is binning happening
at all is a sign that we shouldn't have to rely on so much
pull infrastructure just to figure out this sampling ... */
const double *spc = pctx->vol[0]->gctx->shape->spacing;
double vlen = ( spc[0] + spc[1] + spc[2] )/3;
/* "<info>[-c]:<volname>:<item>[:<zero>:<scale>]" */
if ( meetPullInfoParse( minf[0], "h:" VNAME ":val:0:1" )
|| meetPullInfoParse( minf[1], "hgvec:" VNAME ":gvec" )
/* can you see the hack on the next line */
|| meetPullInfoParse( minf[2], "sthr:" VNAME ":val:-88888888:1" )
|| meetPullInfoAddMulti( pctx, minf, 3 ) ) {
airMopAdd( mop, err = biffGetDone( MEET ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up faux info:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( pullSysParmSet( pctx, pullSysParmRadiusSpace, vlen )
|| pullStart( pctx )
|| pullOutputGet( npos, NULL, NULL, NULL, 0.0, pctx ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble starting or getting output:\n%s", me, err );
airMopError( mop ); return 1;
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
size_t cmin[2] = {0, 0};
size_t cmax[2] = {2, npos->axis[1].size-1};
if ( nrrdCrop( nout, npos, cmin, cmax )
|| nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s", me, err );
airMopError( mop ); return 1;
}
pullFinish( pctx );
airMopOkay( mop );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
const int
mitePresent = 42;
const char *
miteBiffKey = "mite";
double
miteDefRefStep = 0.01;
int
miteDefRenorm = AIR_FALSE;
int
miteDefNormalSide = 1;
double
miteDefOpacNear1 = 0.98;
double
miteDefOpacMatters = 0.05;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
const char *
_miteValStr[] = {
"( unknown miteVal )",
"Xw",
"Xi",
"Yw",
"Yi",
"Zw",
"Zi",
"Rw",
"Ri",
"Tw",
"Ti",
"V",
"N",
"NdotV",
"NdotL",
"VrefN",
"GTdotV",
"VdefT",
"VdefTdotV",
"WdotD"
};
const int
_miteValVal[] = {
miteValUnknown,
miteValXw,
miteValXi,
miteValYw,
miteValYi,
miteValZw,
miteValZi,
miteValRw,
miteValRi,
miteValTw,
miteValTi,
miteValView,
miteValNormal,
miteValNdotV,
miteValNdotL,
miteValVrefN,
miteValGTdotV,
miteValVdefT,
miteValVdefTdotV,
miteValWdotD,
};
const char *
_miteValStrEqv[] = {
"xw",
"xi",
"yw",
"yi",
"zw",
"zi",
"rw",
"ri",
"tw",
"ti",
"view", "v",
"normal", "n",
"ndotv", "vdotn",
"ndotl", "ldotn",
"vrefn",
"gtdotv",
"vdeft",
"vdeftdotv",
"wdotd",
""
};
int
_miteValValEqv[] = {
miteValXw,
miteValXi,
miteValYw,
miteValYi,
miteValZw,
miteValZi,
miteValRw,
miteValRi,
miteValTw,
miteValTi,
miteValView, miteValView,
miteValNormal, miteValNormal,
miteValNdotV, miteValNdotV,
miteValNdotL, miteValNdotL,
miteValVrefN,
miteValGTdotV,
miteValVdefT,
miteValVdefTdotV,
miteValWdotD
};
const airEnum
_miteVal = {
"miteVal",
MITE_VAL_ITEM_MAX,
_miteValStr, _miteValVal,
NULL,
_miteValStrEqv, _miteValValEqv,
AIR_FALSE
};
131 const airEnum *const
miteVal = &_miteVal;
/*
** again, this is not a true gageKind- mainly because these items may
** depend on items in different gageKinds ( scalar and vector ). So,
** the prerequisites here are all blank. Go look in miteQueryAdd( )
** to see these items' true prereqs
*/
gageItemEntry
_miteValTable[MITE_VAL_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, index, needData*/
{miteValUnknown, 0, 0, {0}, 0, 0, AIR_FALSE},
{miteValXw, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValXi, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValYw, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValYi, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValZw, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValZi, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValRw, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValRi, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValTw, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValTi, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValView, 3, 0, {0}, 0, 0, AIR_FALSE},
{miteValNormal, 3, 0, {0}, 0, 0, AIR_FALSE},
{miteValNdotV, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValNdotL, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValVrefN, 3, 0, {0}, 0, 0, AIR_FALSE},
{miteValGTdotV, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValVdefT, 3, 0, {0}, 0, 0, AIR_FALSE},
{miteValVdefTdotV, 1, 0, {0}, 0, 0, AIR_FALSE},
{miteValWdotD, 1, 0, {0}, 0, 0, AIR_FALSE}
};
gageKind
_miteValGageKind = {
AIR_FALSE,
"mite",
&_miteVal,
0,
0,
MITE_VAL_ITEM_MAX,
_miteValTable,
NULL,
NULL,
NULL,
NULL, NULL, NULL, NULL,
NULL
};
gageKind *
miteValGageKind = &_miteValGageKind;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
int
28 miteRayBegin( miteThread *mtt, miteRender *mrr, miteUser *muu,
int uIndex, int vIndex,
double rayLen,
double rayStartWorld[3], double rayStartIndex[3],
double rayDirWorld[3], double rayDirIndex[3] ) {
airPtrPtrUnion appu;
AIR_UNUSED( mrr );
AIR_UNUSED( rayStartWorld );
AIR_UNUSED( rayStartIndex );
AIR_UNUSED( rayDirIndex );
mtt->ui = uIndex;
mtt->vi = vIndex;
mtt->rayStep = ( muu->rayStep*rayLen /
( muu->hctx->cam->vspFaar - muu->hctx->cam->vspNeer ) );
if ( !uIndex ) {
fprintf( stderr, "%d/%d ", vIndex, muu->hctx->imgSize[1] );
fflush( stderr );
}
mtt->verbose = ( uIndex == muu->verbUi && vIndex == muu->verbVi );
mtt->skip = ( muu->verbUi >= 0 && muu->verbVi >= 0
&& !mtt->verbose );
if ( mtt->verbose ) {
/* create muu->ndebug */
muu->ndebug = nrrdNew( );
/* we want to store the value and index for each txf domain variable,
plus the RGBAZ computed for that sample */
muu->ndebug->axis[0].size = 2*mtt->stageNum + 5;
/* we really do want to associate ndebug with the miteUser's mop,
because the information stored in it has to persist for as long as
the user wants: mite itself doesn't call miteUserNix */
airMopAdd( muu->umop, muu->ndebug, ( airMopper )nrrdNuke, airMopAlways );
/* but the scope of the debug array allocation is within this ray */
muu->debugArr = airArrayNew( ( appu.d = &( muu->debug ), appu.v ),
NULL, sizeof( double ), 128 );
}
mtt->raySample = 0;
mtt->RR = mtt->GG = mtt->BB = 0.0;
mtt->TT = 1.0;
mtt->ZZ = AIR_NAN;
ELL_3V_SCALE( mtt->V, -1, rayDirWorld );
return 0;
}
void
74 _miteRGBACalc( mite_t *R, mite_t *G, mite_t *B, mite_t *A,
miteThread *mtt, miteRender *mrr, miteUser *muu ) {
static const char me[]="_miteRGBACalc";
mite_t tmp,
ad[3], /* ambient+diffuse light contribution */
s[3] = {0, 0, 0}, /* specular light contribution */
col[3], E, ka, kd, ks, sp, /* txf-determined rendering variables */
LdotN=0, HdotN, H[3], N[3]; /* for lighting calculation */
col[0] = mtt->range[miteRangeRed];
col[1] = mtt->range[miteRangeGreen];
col[2] = mtt->range[miteRangeBlue];
E = mtt->range[miteRangeEmissivity];
ka = mtt->range[miteRangeKa];
kd = mtt->range[miteRangeKd];
ks = mtt->range[miteRangeKs];
ELL_3V_SCALE( ad, ka, muu->lit->amb );
switch ( mrr->shadeSpec->method ) {
case miteShadeMethodNone:
/* nothing to do */
break;
case miteShadeMethodPhong:
if ( kd || ks ) {
ELL_3V_NORM( N, mtt->shadeVec0, tmp );
if ( 1 == muu->normalSide ) {
ELL_3V_SCALE( N, -1, N );
}
/* else -1==side --> N = -1*-1*N = N
or 0==side --> N = N, so there's nothing to do */
if ( kd ) {
LdotN = ELL_3V_DOT( muu->lit->dir[0], N );
if ( !muu->normalSide ) {
LdotN = AIR_ABS( LdotN );
}
if ( LdotN > 0 ) {
ELL_3V_SCALE_INCR( ad, LdotN*kd, muu->lit->col[0] );
}
}
if ( ks ) {
sp = mtt->range[miteRangeSP];
ELL_3V_ADD2( H, muu->lit->dir[0], mtt->V );
ELL_3V_NORM( H, H, tmp );
HdotN = ELL_3V_DOT( H, N );
if ( !muu->normalSide ) {
HdotN = AIR_ABS( HdotN );
}
if ( HdotN > 0 ) {
HdotN = pow( HdotN, sp );
ELL_3V_SCALE( s, HdotN*ks, muu->lit->col[0] );
}
}
}
break;
case miteShadeMethodLitTen:
fprintf( stderr, "!%s: lit-tensor not yet implemented\n", me );
break;
default:
fprintf( stderr, "!%s: PANIC, shadeMethod %d unimplemented\n",
me, mrr->shadeSpec->method );
exit( 1 );
break;
}
*R = ( E - 1 + ad[0] )*col[0] + s[0];
*G = ( E - 1 + ad[1] )*col[1] + s[1];
*B = ( E - 1 + ad[2] )*col[2] + s[2];
*A = mtt->range[miteRangeAlpha];
*A = AIR_CLAMP( 0.0, *A, 1.0 );
/*
if ( mtt->verbose ) {
fprintf( stderr, "%s: col[] = %g, %g, %g; A, E = %g, %g; Kads = %g, %g, %g\n", me,
col[0], col[1], col[2], mtt->range[miteRangeAlpha], E, ka, kd, ks );
fprintf( stderr, "%s: N = ( %g, %g, %g ), L = ( %g, %g, %g ) ---> LdotN = %g\n",
me, N[0], N[1], N[2], muu->lit->dir[0][0], muu->lit->dir[0][1],
muu->lit->dir[0][2], LdotN );
fprintf( stderr, "%s: ad[] = %g, %g, %g\n", me, ad[0], ad[1], ad[2] );
fprintf( stderr, "%s: --> R, G, B, A = %g, %g, %g, %g\n", me, *R, *G, *B, *A );
}
*/
return;
}
double
156 miteSample( miteThread *mtt, miteRender *mrr, miteUser *muu,
int num, double rayT, int inside,
double samplePosWorld[3],
double samplePosIndex[3] ) {
static const char me[]="miteSample";
mite_t R, G, B, A;
double *NN;
double NdotV, kn[3], knd[3], ref[3], len, *dbg=NULL;
if ( !inside ) {
return mtt->rayStep;
}
if ( mtt->skip ) {
/* we have one verbose pixel, but we're not on it */
return 0.0;
}
/* early ray termination */
if ( 1-mtt->TT >= muu->opacNear1 ) {
mtt->TT = 0.0;
return 0.0;
}
/* set ( fake ) view based on fake from */
if ( AIR_EXISTS( muu->fakeFrom[0] ) ) {
ELL_3V_SUB( mtt->V, samplePosWorld, muu->fakeFrom );
ELL_3V_NORM( mtt->V, mtt->V, len );
}
/* do probing at this location to determine values of everything
that might appear in the txf domain */
if ( gageProbe( mtt->gctx,
samplePosIndex[0],
samplePosIndex[1],
samplePosIndex[2] ) ) {
biffAddf( MITE, "%s: gage trouble: %s ( %d )", me,
mtt->gctx->errStr, mtt->gctx->errNum );
return AIR_NAN;
}
if ( mrr->queryMiteNonzero ) {
/* There is some optimal trade-off between slowing things down
with too many branches on all possible checks of queryMite,
and slowing things down with doing the work of setting them all.
This code has not been profiled whatsoever */
mtt->directAnsMiteVal[miteValXw][0] = samplePosWorld[0];
mtt->directAnsMiteVal[miteValXi][0] = samplePosIndex[0];
mtt->directAnsMiteVal[miteValYw][0] = samplePosWorld[1];
mtt->directAnsMiteVal[miteValYi][0] = samplePosIndex[1];
mtt->directAnsMiteVal[miteValZw][0] = samplePosWorld[2];
mtt->directAnsMiteVal[miteValZi][0] = samplePosIndex[2];
mtt->directAnsMiteVal[miteValRw][0] = ELL_3V_LEN( samplePosWorld );
mtt->directAnsMiteVal[miteValRi][0] = ELL_3V_LEN( samplePosIndex );
mtt->directAnsMiteVal[miteValTw][0] = rayT;
mtt->directAnsMiteVal[miteValTi][0] = num;
ELL_3V_COPY( mtt->directAnsMiteVal[miteValView], mtt->V );
NN = mtt->directAnsMiteVal[miteValNormal];
if ( mtt->_normal ) {
if ( 1 == muu->normalSide ) {
ELL_3V_SCALE( NN, -1, mtt->_normal );
} else {
ELL_3V_COPY( NN, mtt->_normal );
}
}
if ( ( GAGE_QUERY_ITEM_TEST( mrr->queryMite, miteValNdotV )
|| GAGE_QUERY_ITEM_TEST( mrr->queryMite, miteValNdotL )
|| GAGE_QUERY_ITEM_TEST( mrr->queryMite, miteValVrefN ) ) ) {
mtt->directAnsMiteVal[miteValNdotV][0] = ELL_3V_DOT( NN, mtt->V );
mtt->directAnsMiteVal[miteValNdotL][0] =
ELL_3V_DOT( NN, muu->lit->dir[0] );
if ( !muu->normalSide ) {
mtt->directAnsMiteVal[miteValNdotV][0] =
AIR_ABS( mtt->directAnsMiteVal[miteValNdotV][0] );
mtt->directAnsMiteVal[miteValNdotL][0] =
AIR_ABS( mtt->directAnsMiteVal[miteValNdotL][0] );
}
NdotV = mtt->directAnsMiteVal[miteValNdotV][0];
ELL_3V_SCALE_ADD2( ref, 2*NdotV, NN, -1, mtt->V );
ELL_3V_NORM( mtt->directAnsMiteVal[miteValVrefN], ref, len );
}
if ( GAGE_QUERY_ITEM_TEST( mrr->queryMite, miteValGTdotV ) ) {
ELL_3MV_MUL( kn, mtt->nPerp, mtt->V );
ELL_3V_NORM( kn, kn, len );
ELL_3MV_MUL( knd, mtt->geomTens, kn );
mtt->directAnsMiteVal[miteValGTdotV][0] = ELL_3V_DOT( knd, kn );
}
}
/* initialize txf range quantities, and apply all txfs */
if ( mtt->verbose ) {
muu->debugIdx = airArrayLenIncr( muu->debugArr, muu->ndebug->axis[0].size );
}
memcpy( mtt->range, muu->rangeInit, MITE_RANGE_NUM*sizeof( mite_t ) );
_miteStageRun( mtt, muu );
/* if there's opacity, do shading and compositing */
if ( mtt->range[miteRangeAlpha] ) {
/* fprintf( stderr, "%s: mtt->TT = %g\n", me, mtt->TT ); */
/*
if ( mtt->verbose ) {
fprintf( stderr, "%s: before compositing: RGBT = %g, %g, %g, %g\n",
me, mtt->RR, mtt->GG, mtt->BB, mtt->TT );
}
*/
_miteRGBACalc( &R, &G, &B, &A, mtt, mrr, muu );
mtt->RR += mtt->TT*A*R;
mtt->GG += mtt->TT*A*G;
mtt->BB += mtt->TT*A*B;
mtt->TT *= 1-A;
/*
if ( mtt->verbose ) {
fprintf( stderr, "%s: after compositing: RGBT = %g, %g, %g, %g\n",
me, mtt->RR, mtt->GG, mtt->BB, mtt->TT );
}
*/
/* fprintf( stderr, "%s: mtt->TT = %g\n", me, mtt->TT ); */
} else {
R = G = B = A = 0;
}
if ( mtt->verbose ) {
dbg = muu->debug + muu->debugIdx;
dbg[0 + 2*mtt->stageNum] = R;
dbg[1 + 2*mtt->stageNum] = G;
dbg[2 + 2*mtt->stageNum] = B;
dbg[3 + 2*mtt->stageNum] = A;
dbg[4 + 2*mtt->stageNum] = rayT;
}
/* set Z if it hasn't been set already */
if ( 1-mtt->TT >= muu->opacMatters && !AIR_EXISTS( mtt->ZZ ) ) {
mtt->ZZ = rayT;
}
/* this is used to index mtt->debug */
mtt->raySample += 1;
return mtt->rayStep;
}
int
301 miteRayEnd( miteThread *mtt, miteRender *mrr, miteUser *muu ) {
int idx, slen, stageIdx;
mite_t *imgData;
double A;
AIR_UNUSED( mrr );
mtt->samples += mtt->raySample;
idx = mtt->ui + ( muu->nout->axis[1].size )*mtt->vi;
imgData = ( mite_t* )muu->nout->data;
A = 1 - mtt->TT;
if ( A ) {
ELL_5V_SET( imgData + 5*idx, mtt->RR/A, mtt->GG/A, mtt->BB/A,
A, mtt->ZZ );
} else {
ELL_5V_SET( imgData + 5*idx, 0, 0, 0, 0, AIR_NAN );
}
if ( mtt->verbose ) {
/* muu->debug may be over-allocated, but that's harmless */
muu->ndebug->axis[1].size = mtt->raySample;
nrrdWrap_va( muu->ndebug, muu->debug, nrrdTypeDouble, 2,
AIR_CAST( size_t, muu->ndebug->axis[0].size ),
AIR_CAST( size_t, mtt->raySample ) );
airArrayNix( muu->debugArr );
slen = 0;
for ( stageIdx=0; stageIdx<mtt->stageNum; stageIdx++ ) {
slen += strlen( mtt->stage[stageIdx].label ) + 2;
}
slen += strlen( "R, G, B, A, Z" ) + 1;
muu->ndebug->axis[0].label = ( char * )calloc( slen, sizeof( char ) );
for ( stageIdx=0; stageIdx<mtt->stageNum; stageIdx++ ) {
strcat( muu->ndebug->axis[0].label, mtt->stage[stageIdx].label );
strcat( muu->ndebug->axis[0].label, ", , " );
}
strcat( muu->ndebug->axis[0].label, "R, G, B, A, Z" );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
miteRender *
28 _miteRenderNew( void ) {
miteRender *mrr;
mrr = ( miteRender * )calloc( 1, sizeof( miteRender ) );
if ( mrr ) {
mrr->rmop = airMopNew( );
if ( !mrr->rmop ) {
airFree( mrr );
return NULL;
}
mrr->ntxf = NULL;
mrr->ntxfNum = 0;
mrr->sclPvlIdx = -1;
mrr->vecPvlIdx = -1;
mrr->tenPvlIdx = -1;
mrr->normalSpec = gageItemSpecNew( );
airMopAdd( mrr->rmop, mrr->normalSpec,
( airMopper )gageItemSpecNix, airMopAlways );
mrr->shadeSpec = miteShadeSpecNew( );
airMopAdd( mrr->rmop, mrr->shadeSpec,
( airMopper )miteShadeSpecNix, airMopAlways );
mrr->time0 = AIR_NAN;
GAGE_QUERY_RESET( mrr->queryMite );
mrr->queryMiteNonzero = AIR_FALSE;
}
return mrr;
}
miteRender *
57 _miteRenderNix( miteRender *mrr ) {
if ( mrr ) {
airMopOkay( mrr->rmop );
airFree( mrr );
}
return NULL;
}
int
67 miteRenderBegin( miteRender **mrrP, miteUser *muu ) {
static const char me[]="miteRenderBegin";
gagePerVolume *pvl;
int E, T, pvlIdx;
gageQuery queryScl, queryVec, queryTen;
gageItemSpec isp;
unsigned int axi, thr;
if ( !( mrrP && muu ) ) {
biffAddf( MITE, "%s: got NULL pointer", me );
return 1;
}
if ( _miteUserCheck( muu ) ) {
biffAddf( MITE, "%s: problem with user-set parameters", me );
return 1;
}
if ( !( *mrrP = _miteRenderNew( ) ) ) {
biffAddf( MITE, "%s: couldn't alloc miteRender", me );
return 1;
}
if ( _miteNtxfAlphaAdjust( *mrrP, muu ) ) {
biffAddf( MITE, "%s: trouble copying and alpha-adjusting txfs", me );
return 1;
}
GAGE_QUERY_RESET( queryScl );
GAGE_QUERY_RESET( queryVec );
GAGE_QUERY_RESET( queryTen );
GAGE_QUERY_RESET( ( *mrrP )->queryMite );
for ( T=0; T<muu->ntxfNum; T++ ) {
for ( axi=1; axi<muu->ntxf[T]->dim; axi++ ) {
miteVariableParse( &isp, muu->ntxf[T]->axis[axi].label );
miteQueryAdd( queryScl, queryVec, queryTen, ( *mrrP )->queryMite, &isp );
}
}
miteVariableParse( ( *mrrP )->normalSpec, muu->normalStr );
miteQueryAdd( queryScl, queryVec, queryTen, ( *mrrP )->queryMite,
( *mrrP )->normalSpec );
miteShadeSpecParse( ( *mrrP )->shadeSpec, muu->shadeStr );
miteShadeSpecQueryAdd( queryScl, queryVec, queryTen, ( *mrrP )->queryMite,
( *mrrP )->shadeSpec );
( *mrrP )->queryMiteNonzero = GAGE_QUERY_NONZERO( ( *mrrP )->queryMite );
E = 0;
pvlIdx = 0;
if ( muu->nsin ) {
if ( !E ) E |= !( pvl = gagePerVolumeNew( muu->gctx0, muu->nsin, gageKindScl ) );
if ( !E ) E |= gageQuerySet( muu->gctx0, pvl, queryScl );
if ( !E ) E |= gagePerVolumeAttach( muu->gctx0, pvl );
if ( !E ) ( *mrrP )->sclPvlIdx = pvlIdx++;
}
if ( muu->nvin ) {
if ( !E ) E |= !( pvl = gagePerVolumeNew( muu->gctx0, muu->nvin, gageKindVec ) );
if ( !E ) E |= gageQuerySet( muu->gctx0, pvl, queryVec );
if ( !E ) E |= gagePerVolumeAttach( muu->gctx0, pvl );
if ( !E ) ( *mrrP )->vecPvlIdx = pvlIdx++;
}
if ( muu->ntin ) {
if ( !E ) E |= !( pvl = gagePerVolumeNew( muu->gctx0, muu->ntin, tenGageKind ) );
if ( !E ) E |= gageQuerySet( muu->gctx0, pvl, queryTen );
if ( !E ) E |= gagePerVolumeAttach( muu->gctx0, pvl );
if ( !E ) ( *mrrP )->tenPvlIdx = pvlIdx++;
}
if ( !E ) E |= gageKernelSet( muu->gctx0, gageKernel00,
muu->ksp[gageKernel00]->kernel,
muu->ksp[gageKernel00]->parm );
if ( !E ) E |= gageKernelSet( muu->gctx0, gageKernel11,
muu->ksp[gageKernel11]->kernel,
muu->ksp[gageKernel11]->parm );
if ( !E ) E |= gageKernelSet( muu->gctx0, gageKernel22,
muu->ksp[gageKernel22]->kernel,
muu->ksp[gageKernel22]->parm );
if ( !E ) E |= gageUpdate( muu->gctx0 );
if ( E ) {
biffMovef( MITE, GAGE, "%s: gage trouble", me );
return 1;
}
fprintf( stderr, "!%s: kernel support = %d^3 samples\n",
me, 2*muu->gctx0->radius );
if ( nrrdMaybeAlloc_va( muu->nout, mite_nt, 3,
AIR_CAST( size_t, 5 ) /* RGBAZ */ ,
AIR_CAST( size_t, muu->hctx->imgSize[0] ),
AIR_CAST( size_t, muu->hctx->imgSize[1] ) ) ) {
biffMovef( MITE, NRRD, "%s: nrrd trouble", me );
return 1;
}
muu->nout->axis[1].center = nrrdCenterCell;
muu->nout->axis[1].min = muu->hctx->cam->uRange[0];
muu->nout->axis[1].max = muu->hctx->cam->uRange[1];
muu->nout->axis[2].center = nrrdCenterCell;
muu->nout->axis[2].min = muu->hctx->cam->vRange[0];
muu->nout->axis[2].max = muu->hctx->cam->vRange[1];
for ( thr=0; thr<muu->hctx->numThreads; thr++ ) {
( *mrrP )->tt[thr] = miteThreadNew( );
if ( !( ( *mrrP )->tt[thr] ) ) {
biffAddf( MITE, "%s: couldn't allocate thread[%d]", me, thr );
return 1;
}
airMopAdd( ( *mrrP )->rmop, ( *mrrP )->tt[thr],
( airMopper )miteThreadNix, airMopAlways );
}
( *mrrP )->time0 = airTime( );
return 0;
}
int
176 miteRenderEnd( miteRender *mrr, miteUser *muu ) {
unsigned int thr;
double samples;
muu->rendTime = airTime( ) - mrr->time0;
samples = 0;
for ( thr=0; thr<muu->hctx->numThreads; thr++ ) {
samples += mrr->tt[thr]->samples;
}
muu->sampRate = samples/( 1000.0*muu->rendTime );
_miteRenderNix( mrr );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
miteShadeSpec *
28 miteShadeSpecNew( void ) {
miteShadeSpec *shpec;
shpec = ( miteShadeSpec * )calloc( 1, sizeof( miteShadeSpec ) );
if ( shpec ) {
shpec->method = miteShadeMethodUnknown;
shpec->vec0 = gageItemSpecNew( );
shpec->vec1 = gageItemSpecNew( );
shpec->scl0 = gageItemSpecNew( );
shpec->scl1 = gageItemSpecNew( );
if ( !( shpec->vec0 && shpec->vec1 &&
shpec->scl0 && shpec->scl1 ) ) {
return NULL;
}
}
return shpec;
}
miteShadeSpec *
47 miteShadeSpecNix( miteShadeSpec *shpec ) {
if ( shpec ) {
shpec->vec0 = gageItemSpecNix( shpec->vec0 );
shpec->vec1 = gageItemSpecNix( shpec->vec1 );
shpec->scl0 = gageItemSpecNix( shpec->scl0 );
shpec->scl1 = gageItemSpecNix( shpec->scl1 );
airFree( shpec );
}
return NULL;
}
/*
******** miteShadeSpecParse
**
** set up a miteShadeSpec based on a string. Valid forms are:
**
** none
** phong:<vector>
** litten:<vector>, <vector>, <scalar>, <scalar>
**
** where <vector> and <scalar> are specifications of 3-vector and scalar
** parsable by miteVariableParse
*/
int
72 miteShadeSpecParse( miteShadeSpec *shpec, char *shadeStr ) {
static const char me[]="miteShadeSpecParse";
char *buff, *qstr, *tok, *state;
airArray *mop;
int ansLength;
mop = airMopNew( );
if ( !( shpec && airStrlen( shadeStr ) ) ) {
biffAddf( MITE, "%s: got NULL pointer and/or empty string", me );
airMopError( mop ); return 1;
}
buff = airToLower( airStrdup( shadeStr ) );
if ( !buff ) {
biffAddf( MITE, "%s: couldn't strdup shading spec", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, buff, airFree, airMopAlways );
shpec->method = miteShadeMethodUnknown;
if ( !strcmp( "none", buff ) ) {
shpec->method = miteShadeMethodNone;
} else if ( buff == strstr( buff, "phong:" ) ) {
shpec->method = miteShadeMethodPhong;
qstr = buff + strlen( "phong:" );
if ( miteVariableParse( shpec->vec0, qstr ) ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as shading vector", me, qstr );
airMopError( mop ); return 1;
}
ansLength = shpec->vec0->kind->table[shpec->vec0->item].answerLength;
if ( 3 != ansLength ) {
biffAddf( MITE, "%s: \"%s\" isn't a vector ( answer length is %d, not 3 )",
me, qstr, ansLength );
airMopError( mop ); return 1;
}
shpec->method = miteShadeMethodPhong;
} else if ( buff == strstr( buff, "litten:" ) ) {
qstr = buff + strlen( "litten:" );
/* ---- first vector */
tok = airStrtok( qstr, ", ", &state );
if ( miteVariableParse( shpec->vec0, tok ) ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as first lit-tensor vector",
me, tok );
airMopError( mop ); return 1;
}
ansLength = shpec->vec0->kind->table[shpec->vec0->item].answerLength;
if ( 3 != ansLength ) {
biffAddf( MITE, "%s: \"%s\" isn't a vector ( answer length is %d, not 3 )",
me, qstr, ansLength );
airMopError( mop ); return 1;
}
/* ---- second vector */
tok = airStrtok( qstr, ", ", &state );
if ( miteVariableParse( shpec->vec1, tok ) ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as second lit-tensor vector",
me, tok );
airMopError( mop ); return 1;
}
ansLength = shpec->vec1->kind->table[shpec->vec1->item].answerLength;
if ( 3 != ansLength ) {
biffAddf( MITE, "%s: \"%s\" isn't a vector ( answer length is %d, not 3 )",
me, qstr, ansLength );
airMopError( mop ); return 1;
}
/* ---- first scalar */
tok = airStrtok( qstr, ", ", &state );
if ( miteVariableParse( shpec->scl0, tok ) ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as first lit-tensor scalar",
me, tok );
airMopError( mop ); return 1;
}
ansLength = shpec->scl0->kind->table[shpec->scl0->item].answerLength;
if ( 1 != ansLength ) {
biffAddf( MITE, "%s: \"%s\" isn't a scalar ( answer length is %d, not 1 )",
me, qstr, ansLength );
airMopError( mop ); return 1;
}
/* ---- second scalar */
tok = airStrtok( qstr, ", ", &state );
if ( miteVariableParse( shpec->scl1, tok ) ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as second lit-tensor scalar",
me, tok );
airMopError( mop ); return 1;
}
ansLength = shpec->scl1->kind->table[shpec->scl1->item].answerLength;
if ( 1 != ansLength ) {
biffAddf( MITE, "%s: \"%s\" isn't a scalar ( answer length is %d, not 1 )",
me, qstr, ansLength );
airMopError( mop ); return 1;
}
shpec->method = miteShadeMethodLitTen;
} else {
biffAddf( MITE, "%s: shading specification \"%s\" not understood",
me, shadeStr );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
void
171 miteShadeSpecPrint( char *buff, const miteShadeSpec *shpec ) {
static const char me[]="miteShadeSpecPrint";
char var[4][AIR_STRLEN_MED];
if ( buff && shpec ) {
switch( shpec->method ) {
case miteShadeMethodNone:
sprintf( buff, "none" );
break;
case miteShadeMethodPhong:
miteVariablePrint( var[0], shpec->vec0 );
sprintf( buff, "phong:%s", var[0] );
break;
case miteShadeMethodLitTen:
miteVariablePrint( var[0], shpec->vec0 );
miteVariablePrint( var[1], shpec->vec1 );
miteVariablePrint( var[2], shpec->scl0 );
miteVariablePrint( var[3], shpec->scl1 );
sprintf( buff, "litten:%s, %s, %s, %s", var[0], var[1], var[2], var[3] );
break;
default:
sprintf( buff, "%s: unknown shade method!", me );
break;
}
}
return;
}
void
200 miteShadeSpecQueryAdd( gageQuery queryScl, gageQuery queryVec,
gageQuery queryTen, gageQuery queryMite,
miteShadeSpec *shpec ) {
if ( shpec ) {
switch( shpec->method ) {
case miteShadeMethodNone:
/* no queries to add */
break;
case miteShadeMethodPhong:
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec->vec0 );
break;
case miteShadeMethodLitTen:
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec->vec0 );
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec->vec1 );
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec->scl0 );
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec->scl1 );
break;
default:
break;
}
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
miteThread *
28 miteThreadNew( ) {
static const char me[]="miteThreadNew";
miteThread *mtt;
int ii;
mtt = ( miteThread * )calloc( 1, sizeof( miteThread ) );
if ( !mtt ) {
biffAddf( MITE, "%s: couldn't calloc miteThread", me );
return NULL;
}
mtt->rmop = airMopNew( );
if ( !mtt->rmop ) {
biffAddf( MITE, "%s: couldn't calloc thread's mop", me );
airFree( mtt ); return NULL;
}
mtt->gctx = NULL;
mtt->ansScl = mtt->ansVec = mtt->ansTen = NULL;
mtt->_normal = NULL;
mtt->shadeVec0 = NULL;
mtt->shadeVec1 = NULL;
mtt->shadeScl0 = NULL;
mtt->shadeScl1 = NULL;
/* were miteVal a full-fledged gageKind, the following would
be done by gagePerVolumeNew */
mtt->ansMiteVal = AIR_CALLOC( gageKindTotalAnswerLength( miteValGageKind ),
double );
mtt->directAnsMiteVal = AIR_CALLOC( miteValGageKind->itemMax+1,
double * );
if ( !( mtt->ansMiteVal && mtt->directAnsMiteVal ) ) {
biffAddf( MITE, "%s: couldn't calloc miteVal answer arrays", me );
return NULL;
}
for ( ii=0; ii<=miteValGageKind->itemMax; ii++ ) {
mtt->directAnsMiteVal[ii] = mtt->ansMiteVal
+ gageKindAnswerOffset( miteValGageKind, ii );
}
mtt->verbose = 0;
mtt->skip = 0;
mtt->thrid = -1;
mtt->ui = mtt->vi = -1;
mtt->raySample = 0;
mtt->samples = 0;
mtt->stage = NULL;
/* mtt->range[], rayStep, V, RR, GG, BB, TT initialized in
miteRayBegin or in miteSample */
return mtt;
}
miteThread *
79 miteThreadNix( miteThread *mtt ) {
mtt->ansMiteVal = ( double * )airFree( mtt->ansMiteVal );
mtt->directAnsMiteVal = ( double ** )airFree( mtt->directAnsMiteVal );
airMopOkay( mtt->rmop );
airFree( mtt );
return NULL;
}
/*
******** miteThreadBegin( )
**
** this has some of the body of what would be miteThreadInit
*/
int
95 miteThreadBegin( miteThread **mttP, miteRender *mrr,
miteUser *muu, int whichThread ) {
static const char me[]="miteThreadBegin";
/* all the miteThreads have already been allocated */
( *mttP ) = mrr->tt[whichThread];
if ( !whichThread ) {
/* this is the first thread- it just points to the parent gageContext */
( *mttP )->gctx = muu->gctx0;
} else {
/* we have to generate a new gageContext */
( *mttP )->gctx = gageContextCopy( muu->gctx0 );
if ( !( *mttP )->gctx ) {
biffMovef( MITE, GAGE,
"%s: couldn't set up thread %d", me, whichThread );
return 1;
}
}
if ( -1 != mrr->sclPvlIdx ) {
( *mttP )->ansScl = ( *mttP )->gctx->pvl[mrr->sclPvlIdx]->answer;
( *mttP )->nPerp = ( ( *mttP )->ansScl
+ gageKindAnswerOffset( gageKindScl, gageSclNPerp ) );
( *mttP )->geomTens = ( ( *mttP )->ansScl
+ gageKindAnswerOffset( gageKindScl, gageSclGeomTens ) );
} else {
( *mttP )->ansScl = NULL;
( *mttP )->nPerp = NULL;
( *mttP )->geomTens = NULL;
}
( *mttP )->ansVec = ( -1 != mrr->vecPvlIdx
? ( *mttP )->gctx->pvl[mrr->vecPvlIdx]->answer
: NULL );
( *mttP )->ansTen = ( -1 != mrr->tenPvlIdx
? ( *mttP )->gctx->pvl[mrr->tenPvlIdx]->answer
: NULL );
( *mttP )->thrid = whichThread;
( *mttP )->raySample = 0;
( *mttP )->samples = 0;
( *mttP )->verbose = 0;
( *mttP )->skip = 0;
( *mttP )->_normal = _miteAnswerPointer( *mttP, mrr->normalSpec );
/* set up shading answers */
switch( mrr->shadeSpec->method ) {
case miteShadeMethodNone:
/* nothing to do */
break;
case miteShadeMethodPhong:
( *mttP )->shadeVec0 = _miteAnswerPointer( *mttP, mrr->shadeSpec->vec0 );
break;
case miteShadeMethodLitTen:
( *mttP )->shadeVec0 = _miteAnswerPointer( *mttP, mrr->shadeSpec->vec0 );
( *mttP )->shadeVec1 = _miteAnswerPointer( *mttP, mrr->shadeSpec->vec1 );
( *mttP )->shadeScl0 = _miteAnswerPointer( *mttP, mrr->shadeSpec->scl0 );
( *mttP )->shadeScl1 = _miteAnswerPointer( *mttP, mrr->shadeSpec->scl1 );
break;
default:
biffAddf( MITE, "%s: shade method %d not implemented!",
me, mrr->shadeSpec->method );
return 1;
break;
}
if ( _miteStageSet( *mttP, mrr ) ) {
biffAddf( MITE, "%s: trouble setting up stage array", me );
return 1;
}
return 0;
}
int
168 miteThreadEnd( miteThread *mtt, miteRender *mrr,
miteUser *muu ) {
AIR_UNUSED( mtt );
AIR_UNUSED( mrr );
AIR_UNUSED( muu );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* learned: don't confuse allocate an array of structs with an array
of pointers to structs. Don't be surprised when you bus error
because of the difference
*/
#include "mite.h"
#include "privateMite.h"
char
miteRangeChar[MITE_RANGE_NUM+1] = "ARGBEadsp";
const char *
_miteStageOpStr[] = {
"( unknown miteStageOp )",
"min",
"max",
"add",
"multiply"
};
const int
_miteStageOpVal[] = {
miteStageOpUnknown,
miteStageOpMin,
miteStageOpMax,
miteStageOpAdd,
miteStageOpMultiply
};
const char *
_miteStageOpStrEqv[] = {
"min",
"max",
"add", "+",
"multiply", "*", "x",
""
};
const int
_miteStageOpValEqv[] = {
miteStageOpMin,
miteStageOpMax,
miteStageOpAdd, miteStageOpAdd,
miteStageOpMultiply, miteStageOpMultiply, miteStageOpMultiply
};
const airEnum
_miteStageOp = {
"miteStageOp",
MITE_STAGE_OP_MAX,
_miteStageOpStr, _miteStageOpVal,
NULL,
_miteStageOpStrEqv, _miteStageOpValEqv,
AIR_FALSE
};
79 const airEnum *const
miteStageOp = &_miteStageOp;
/*
******** miteVariableParse( )
**
** takes a string ( usually the label from a nrrd axis ) and parses it
** to determine the gageItemSpec from it ( which means finding the
** kind and item ). The valid formats are:
**
** "" : NULL kind, 0 item
** <item> : miteValGageKind ( DEPRECATED )
** mite( <item> ) : miteValGageKind
** gage( <item> ) : gageKindScl ( DEPRECATED )
** gage( scalar:<item> ) : gageKindScl
** gage( vector:<item> ) : gageKindVec
** gage( tensor:<item> ) : tenGageKind
**
** Notice that "scalar", "vector", and "tensor" do NOT refer to the type
** of the quantity being measured, but rather to the type of volume in
** which quantity is measured ( i.e., the gageKind used )
*/
int
102 miteVariableParse( gageItemSpec *isp, const char *label ) {
static const char me[]="miteVariableParse";
char *buff, *endparen, *kqstr, *col, *kstr, *qstr;
airArray *mop;
if ( !( isp && label ) ) {
biffAddf( MITE, "%s: got NULL pointer", me );
return 1;
}
if ( 0 == strlen( label ) ) {
/* nothing was specified; we try to indicate that by mimicking
the return of gageItemSpecNew( ) */
isp->item = 0;
isp->kind = NULL;
return 0;
}
/* else given string was non-empty */
mop = airMopNew( );
buff = airStrdup( label );
if ( !buff ) {
biffAddf( MITE, "%s: couldn't strdup label!", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, buff, airFree, airMopAlways );
if ( strstr( buff, "gage( " ) == buff ) {
/* txf domain variable is to be measured directly by gage */
if ( !( endparen = strstr( buff, " )" ) ) ) {
biffAddf( MITE, "%s: didn't see close paren after \"gage( \"", me );
airMopError( mop ); return 1;
}
*endparen = 0;
kqstr = buff + strlen( "gage( " );
/* first see if its a ( deprecated ) gageKindScl specification */
isp->item = airEnumVal( gageScl, kqstr );
if ( 0 != isp->item ) {
isp->kind = gageKindScl;
fprintf( stderr, "\n%s: WARNING: deprecated use of txf domain "
"\"gage( %s )\" without explicit gage kind specification; "
"should use \"gage( %s:%s )\" instead\n\n",
me, kqstr, gageKindScl->name, kqstr );
} else {
/* should be of form "<kind>:<item>" */
col = strstr( kqstr, ":" );
if ( !col ) {
biffAddf( MITE, "%s: didn't see \":\" separator between gage "
"kind and item", me );
airMopError( mop ); return 1;
}
*col = 0;
kstr = kqstr;
qstr = col+1;
if ( !strcmp( gageKindScl->name, kstr ) ) {
isp->kind = gageKindScl;
} else if ( !strcmp( gageKindVec->name, kstr ) ) {
isp->kind = gageKindVec;
} else if ( !strcmp( tenGageKind->name, kstr ) ) {
isp->kind = tenGageKind;
} else {
biffAddf( MITE, "%s: don't recognized \"%s\" gage kind", me, kstr );
airMopError( mop ); return 1;
}
isp->item = airEnumVal( isp->kind->enm, qstr );
if ( 0 == isp->item ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as a %s variable",
me, qstr, isp->kind->name );
airMopError( mop ); return 1;
}
}
} else if ( strstr( buff, "mite( " ) == buff ) {
/* txf domain variable is *not* directly measured by gage */
if ( !( endparen = strstr( buff, " )" ) ) ) {
biffAddf( MITE, "%s: didn't see close paren after \"mite( \"", me );
airMopError( mop ); return 1;
}
*endparen = 0;
qstr = buff + strlen( "mite( " );
isp->item = airEnumVal( miteVal, qstr );
if ( 0 == isp->item ) {
biffAddf( MITE, "%s: couldn't parse \"%s\" as a miteVal variable",
me, qstr );
airMopError( mop ); return 1;
}
isp->kind = miteValGageKind;
} else {
/* didn't start with "gage( " or "mite( " */
isp->item = airEnumVal( miteVal, label );
if ( 0 != isp->item ) {
/* its measured by mite */
isp->kind = miteValGageKind;
fprintf( stderr, "\n%s: WARNING: deprecated use of txf domain "
"\"%s\"; should use \"mite( %s )\" instead\n\n",
me, label, label );
} else {
biffAddf( MITE, "%s: \"%s\" not a recognized variable", me, label );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
void
205 miteVariablePrint( char *buff, const gageItemSpec *isp ) {
static const char me[]="miteVariablePrint";
if ( !( isp->kind ) ) {
strcpy( buff, "" );
} else if ( gageKindScl == isp->kind
|| gageKindVec == isp->kind
|| tenGageKind == isp->kind ) {
sprintf( buff, "gage( %s:%s )", isp->kind->name,
airEnumStr( isp->kind->enm, isp->item ) );
} else if ( miteValGageKind == isp->kind ) {
sprintf( buff, "%s( %s )", isp->kind->name,
airEnumStr( isp->kind->enm, isp->item ) );
} else {
sprintf( buff, "( %s: unknown gageKind! )", me );
}
return;
}
int
225 miteNtxfCheck( const Nrrd *ntxf ) {
static const char me[]="miteNtxfCheck";
char *rangeStr, *domStr;
gageItemSpec isp;
unsigned int rii, axi;
int ilog2;
if ( nrrdCheck( ntxf ) ) {
biffMovef( MITE, NRRD, "%s: basic nrrd validity check failed", me );
return 1;
}
if ( !( nrrdTypeFloat == ntxf->type ||
nrrdTypeDouble == ntxf->type ||
nrrdTypeUChar == ntxf->type ) ) {
biffAddf( MITE, "%s: need a type %s, %s or %s nrrd ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, ntxf->type ) );
return 1;
}
if ( !( 2 <= ntxf->dim ) ) {
biffAddf( MITE, "%s: nrrd dim ( %d ) isn't at least 2 ( for a 1-D txf )",
me, ntxf->dim );
return 1;
}
rangeStr = ntxf->axis[0].label;
if ( 0 == airStrlen( rangeStr ) ) {
biffAddf( MITE, "%s: axis[0]'s label doesn't specify txf range", me );
return 1;
}
if ( airStrlen( rangeStr ) != ntxf->axis[0].size ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( MITE, "%s: axis[0]'s size %s, but label specifies %s values", me,
airSprintSize_t( stmp1, ntxf->axis[0].size ),
airSprintSize_t( stmp2, airStrlen( rangeStr ) ) );
return 1;
}
for ( rii=0; rii<airStrlen( rangeStr ); rii++ ) {
if ( !strchr( miteRangeChar, rangeStr[rii] ) ) {
biffAddf( MITE, "%s: char %d of axis[0]'s label ( \"%c\" ) isn't a valid "
"transfer function range specifier ( not in \"%s\" )",
me, rii, rangeStr[rii], miteRangeChar );
return 1;
}
}
for ( axi=1; axi<ntxf->dim; axi++ ) {
if ( 1 == ntxf->axis[axi].size ) {
biffAddf( MITE, "%s: # samples on axis %d must be > 1", me, axi );
return 1;
}
domStr = ntxf->axis[axi].label;
if ( 0 == airStrlen( domStr ) ) {
biffAddf( MITE, "%s: axis[%d] of txf didn't specify a domain variable",
me, axi );
return 1;
}
if ( miteVariableParse( &isp, domStr ) ) {
biffAddf( MITE, "%s: couldn't parse txf domain \"%s\" for axis %d\n",
me, domStr, axi );
return 1;
}
if ( !( 1 == isp.kind->table[isp.item].answerLength ||
3 == isp.kind->table[isp.item].answerLength ) ) {
biffAddf( MITE, "%s: %s ( item %d ) not a scalar or vector "
"( answerLength = %d ): "
"can't be a txf domain variable", me, domStr, isp.item,
isp.kind->table[isp.item].answerLength );
return 1;
}
if ( 3 == isp.kind->table[isp.item].answerLength ) {
/* has to be right length for one of the quantization schemes */
ilog2 = airLog2( ntxf->axis[axi].size );
if ( -1 == ilog2 ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( MITE, "%s: txf axis size for %s must be power of 2 ( not %s )",
me, domStr, airSprintSize_t( stmp, ntxf->axis[axi].size ) );
return 1;
} else {
if ( !( AIR_IN_CL( 8, ilog2, 16 ) ) ) {
biffAddf( MITE, "%s: log_2 of txf axis size for %s should be in "
"range [8, 16] ( not %d )", me, domStr, ilog2 );
return 1;
}
}
} else {
if ( !( AIR_EXISTS( ntxf->axis[axi].min ) &&
AIR_EXISTS( ntxf->axis[axi].max ) ) ) {
biffAddf( MITE, "%s: min and max of axis %d aren't both set", me, axi );
return 1;
}
if ( !( ntxf->axis[axi].min < ntxf->axis[axi].max ) ) {
biffAddf( MITE, "%s: min ( %g ) not less than max ( %g ) on axis %d",
me, ntxf->axis[axi].min, ntxf->axis[axi].max, axi );
return 1;
}
}
}
return 0;
}
/*
******** miteQueryAdd( )
**
** This looks a given gageItemSpec and sets the bits in the
** gageKindScl and tenGageKind queries that are required to calculate
** the quantity
**
** NOTE: This does NOT initialize the query{Scl, Vec, Ten}: it
** just adds on new items to the existing queries
**
** HEY: this is really unfortunate: each new gage type use for
** volume rendering in mite will have to explicitly added as
** arguments here, which is part of the reason that mite may end
** up explicitly depending on the libraries supplying those gageKinds
** ( like how mite now must depend on ten )
**
** The queryMite argument is a little odd- its not a real gage kind,
** but we use it to organize which of the miteVal quantities we take
** the time to compute in miteSample( ).
*/
void
348 miteQueryAdd( gageQuery queryScl, gageQuery queryVec,
gageQuery queryTen, gageQuery queryMite,
gageItemSpec *isp ) {
static const char me[]="miteQueryAdd";
if ( NULL == isp->kind ) {
/* nothing to add */
} else if ( gageKindScl == isp->kind ) {
GAGE_QUERY_ITEM_ON( queryScl, isp->item );
} else if ( gageKindVec == isp->kind ) {
GAGE_QUERY_ITEM_ON( queryVec, isp->item );
} else if ( tenGageKind == isp->kind ) {
GAGE_QUERY_ITEM_ON( queryTen, isp->item );
} else if ( miteValGageKind == isp->kind ) {
/* regardless of whether the mite query requires scl, vec, or ten
queries, we add it to the quantites that have to be computed
per-sample */
GAGE_QUERY_ITEM_ON( queryMite, isp->item );
/* HEY: some these have useful analogs for tensor data, but I
won't be able to express them. This means that while Phong
shading of *scalar* volumes can be implemented with transfer
functions, it is currently not possible in *tensor* volumes
( for instance, using the gradient of fractional anisotropy ) */
switch( isp->item ) {
case miteValVrefN:
case miteValNdotV:
case miteValNdotL:
/* the "N" can be a normalized vector from any of the
available kinds ( except miteValGageKind! ), and its
associated query will be handled elsewhere, so there's
nothing to add here */
break;
case miteValGTdotV:
GAGE_QUERY_ITEM_ON( queryScl, gageSclGeomTens );
break;
case miteValVdefT:
GAGE_QUERY_ITEM_ON( queryTen, tenGageTensor );
case miteValVdefTdotV:
GAGE_QUERY_ITEM_ON( queryTen, tenGageTensor );
break;
}
} else {
fprintf( stderr, "%s: PANIC: unrecognized non-NULL gageKind\n", me );
exit( 1 );
}
return;
}
int
397 _miteNtxfCopy( miteRender *mrr, miteUser *muu ) {
static const char me[]="_miteNtxfCopy";
int ni, E;
mrr->ntxf = AIR_CALLOC( muu->ntxfNum, Nrrd * );
if ( !mrr->ntxf ) {
biffAddf( MITE, "%s: couldn't calloc %d ntxf pointers", me, muu->ntxfNum );
return 1;
}
mrr->ntxfNum = muu->ntxfNum;
airMopAdd( mrr->rmop, mrr->ntxf, airFree, airMopAlways );
E = 0;
for ( ni=0; ni<mrr->ntxfNum; ni++ ) {
mrr->ntxf[ni] = nrrdNew( );
if ( !E ) airMopAdd( mrr->rmop, mrr->ntxf[ni],
( airMopper )nrrdNuke, airMopAlways );
if ( !( nrrdTypeUChar == muu->ntxf[ni]->type
|| nrrdTypeFloat == muu->ntxf[ni]->type
|| nrrdTypeDouble == muu->ntxf[ni]->type ) ) {
biffAddf( MITE,
"%s: sorry, can't handle txf of type %s ( only %s, %s, %s )",
me, airEnumStr( nrrdType, muu->ntxf[ni]->type ),
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ) );
return 1;
}
/* note that key/values need to be copied for the sake of
identifying a non-default miteStageOp */
switch( muu->ntxf[ni]->type ) {
case nrrdTypeUChar:
if ( !E ) E |= nrrdUnquantize( mrr->ntxf[ni], muu->ntxf[ni], nrrdTypeUChar );
if ( !E ) E |= nrrdKeyValueCopy( mrr->ntxf[ni], muu->ntxf[ni] );
break;
case mite_nt:
if ( !E ) E |= nrrdCopy( mrr->ntxf[ni], muu->ntxf[ni] );
break;
default: /* will be either float or double ( whatever mite_nt isn't ) */
if ( !E ) E |= nrrdConvert( mrr->ntxf[ni], muu->ntxf[ni], mite_nt );
if ( !E ) E |= nrrdKeyValueCopy( mrr->ntxf[ni], muu->ntxf[ni] );
break;
}
}
if ( E ) {
biffMovef( MITE, NRRD, "%s: troubling copying/converting all ntxfs", me );
return 1;
}
return 0;
}
int
448 _miteNtxfAlphaAdjust( miteRender *mrr, miteUser *muu ) {
static const char me[]="_miteNtxfAlphaAdjust";
int ni, ei, ri, nnum, rnum;
Nrrd *ntxf;
mite_t *data, alpha, frac;
if ( _miteNtxfCopy( mrr, muu ) ) {
biffAddf( MITE, "%s: trouble copying/converting transfer functions", me );
return 1;
}
frac = muu->rayStep/muu->refStep;
for ( ni=0; ni<mrr->ntxfNum; ni++ ) {
ntxf = mrr->ntxf[ni];
if ( !strchr( ntxf->axis[0].label, miteRangeChar[miteRangeAlpha] ) ) {
continue;
}
/* else this txf sets opacity */
data = ( mite_t * )ntxf->data;
rnum = ntxf->axis[0].size;
nnum = nrrdElementNumber( ntxf )/rnum;
for ( ei=0; ei<nnum; ei++ ) {
for ( ri=0; ri<rnum; ri++ ) {
if ( ntxf->axis[0].label[ri] == miteRangeChar[miteRangeAlpha] ) {
alpha = data[ri + rnum*ei];
data[ri + rnum*ei] = 1 - pow( AIR_MAX( 0, 1-alpha ), frac );
}
}
}
}
return 0;
}
int
481 _miteStageNum( miteRender *mrr ) {
int num, ni;
num = 0;
for ( ni=0; ni<mrr->ntxfNum; ni++ ) {
num += mrr->ntxf[ni]->dim - 1;
}
return num;
}
void
492 _miteStageInit( miteStage *stage ) {
int rii;
stage->val = NULL;
stage->size = -1;
stage->op = miteStageOpUnknown;
stage->qn = NULL;
stage->min = stage->max = AIR_NAN;
stage->data = NULL;
for ( rii=0; rii<=MITE_RANGE_NUM-1; rii++ ) {
stage->rangeIdx[rii] = -1;
}
stage->rangeNum = -1;
stage->label = NULL;
return;
}
double *
510 _miteAnswerPointer( miteThread *mtt, gageItemSpec *isp ) {
static const char me[]="_miteAnswerPointer";
double *ret;
if ( !isp->kind ) {
/* we got a NULL kind ( as happens with output of
gageItemSpecNew( ), or miteVariableParse of an
empty string ); only NULL return is sensible */
return NULL;
}
if ( gageKindScl == isp->kind ) {
ret = mtt->ansScl;
} else if ( gageKindVec == isp->kind ) {
ret = mtt->ansVec;
} else if ( tenGageKind == isp->kind ) {
ret = mtt->ansTen;
} else if ( miteValGageKind == isp->kind ) {
ret = mtt->ansMiteVal;
} else {
fprintf( stderr, "\nPANIC: %s: unknown gageKind!\n", me );
exit( 1 );
}
ret += gageKindAnswerOffset( isp->kind, isp->item );
return ret;
}
/*
** _miteStageSet
**
** ALLOCATES and initializes stage array in a miteThread
*/
int
543 _miteStageSet( miteThread *mtt, miteRender *mrr ) {
static const char me[]="_miteStageSet";
char *value;
int ni, di, stageIdx, rii, stageNum, ilog2;
Nrrd *ntxf;
miteStage *stage;
gageItemSpec isp;
char rc;
stageNum = _miteStageNum( mrr );
/* fprintf( stderr, "!%s: stageNum = %d\n", me, stageNum ); */
mtt->stage = AIR_CALLOC( stageNum, miteStage );
if ( !mtt->stage ) {
biffAddf( MITE, "%s: couldn't alloc array of %d stages", me, stageNum );
return 1;
}
airMopAdd( mtt->rmop, mtt->stage, airFree, airMopAlways );
mtt->stageNum = stageNum;
stageIdx = 0;
for ( ni=0; ni<mrr->ntxfNum; ni++ ) {
ntxf = mrr->ntxf[ni];
for ( di=ntxf->dim-1; di>=1; di-- ) {
stage = mtt->stage + stageIdx;
_miteStageInit( stage );
miteVariableParse( &isp, ntxf->axis[di].label );
stage->val = _miteAnswerPointer( mtt, &isp );
stage->label = ntxf->axis[di].label;
/*
fprintf( stderr, "!%s: ans=%p + offset[%d]=%d == %p\n", me,
mtt->ans, dom, kind->ansOffset[dom], stage->val );
*/
stage->size = ntxf->axis[di].size;
stage->min = ntxf->axis[di].min;
stage->max = ntxf->axis[di].max;
if ( di > 1 ) {
stage->data = NULL;
} else {
stage->data = ( mite_t * )ntxf->data;
value = nrrdKeyValueGet( ntxf, "miteStageOp" );
if ( value ) {
stage->op = airEnumVal( miteStageOp, value );
if ( miteStageOpUnknown == stage->op ) {
stage->op = miteStageOpMultiply;
}
} else {
stage->op = miteStageOpMultiply;
}
if ( 1 == isp.kind->table[isp.item].answerLength ) {
stage->qn = NULL;
} else if ( 3 == isp.kind->table[isp.item].answerLength ) {
char stmp[AIR_STRLEN_SMALL];
ilog2 = airLog2( ntxf->axis[di].size );
switch( ilog2 ) {
case 8: stage->qn = limnVtoQN_d[ limnQN8octa]; break;
case 9: stage->qn = limnVtoQN_d[ limnQN9octa]; break;
case 10: stage->qn = limnVtoQN_d[limnQN10octa]; break;
case 11: stage->qn = limnVtoQN_d[limnQN11octa]; break;
case 12: stage->qn = limnVtoQN_d[limnQN12octa]; break;
case 13: stage->qn = limnVtoQN_d[limnQN13octa]; break;
case 14: stage->qn = limnVtoQN_d[limnQN14octa]; break;
case 15: stage->qn = limnVtoQN_d[limnQN15octa]; break;
case 16: stage->qn = limnVtoQN_d[limnQN16octa]; break;
default:
biffAddf( MITE, "%s: txf axis %d size %s not usable for "
"vector txf domain variable %s", me, di,
airSprintSize_t( stmp, ntxf->axis[di].size ),
ntxf->axis[di].label );
return 1;
break;
}
} else {
biffAddf( MITE, "%s: %s not scalar or vector ( len = %d ): can't be "
"a txf domain variable", me,
ntxf->axis[di].label,
isp.kind->table[isp.item].answerLength );
return 1;
}
stage->rangeNum = ntxf->axis[0].size;
for ( rii=0; rii<stage->rangeNum; rii++ ) {
rc = ntxf->axis[0].label[rii];
stage->rangeIdx[rii] = strchr( miteRangeChar, rc ) - miteRangeChar;
/*
fprintf( stderr, "!%s: range: %c -> %d\n", "_miteStageSet",
ntxf->axis[0].label[rii], stage->rangeIdx[rii] );
*/
}
}
stageIdx++;
}
}
return 0;
}
void
637 _miteStageRun( miteThread *mtt, miteUser *muu ) {
static const char me[]="_miteStageRun";
int stageIdx, ri, rii;
unsigned int txfIdx, finalIdx;
miteStage *stage;
mite_t *rangeData;
double *dbg=NULL;
finalIdx = 0;
if ( mtt->verbose ) {
dbg = muu->debug + muu->debugIdx;
}
for ( stageIdx=0; stageIdx<mtt->stageNum; stageIdx++ ) {
stage = &( mtt->stage[stageIdx] );
if ( stage->qn ) {
/* its a vector-valued txf domain variable */
txfIdx = stage->qn( stage->val );
/* right now, we can't store vector-valued txf domain variables */
} else {
/* its a scalar txf domain variable */
txfIdx = airIndexClamp( stage->min, *( stage->val ),
stage->max, stage->size );
if ( mtt->verbose ) {
fprintf( stderr, "!%s: %s=%g in [%g, %g]/%u -> %u\n", me,
stage->label, *( stage->val ),
stage->min, stage->max, stage->size, txfIdx );
dbg[0 + 2*stageIdx] = *( stage->val );
}
}
finalIdx = stage->size*finalIdx + txfIdx;
if ( mtt->verbose ) {
dbg[1 + 2*stageIdx] = txfIdx;
}
if ( stage->data ) {
rangeData = stage->data + stage->rangeNum*finalIdx;
for ( rii=0; rii<stage->rangeNum; rii++ ) {
ri = stage->rangeIdx[rii];
switch( stage->op ) {
case miteStageOpMin:
mtt->range[ri] = AIR_MIN( mtt->range[ri], rangeData[rii] );
break;
case miteStageOpMax:
mtt->range[ri] = AIR_MAX( mtt->range[ri], rangeData[rii] );
break;
case miteStageOpAdd:
mtt->range[ri] += rangeData[rii];
break;
case miteStageOpMultiply:
default:
mtt->range[ri] *= rangeData[rii];
break;
}
}
finalIdx = 0;
}
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "mite.h"
#include "privateMite.h"
miteUser *
28 miteUserNew( ) {
miteUser *muu;
int i;
muu = ( miteUser * )calloc( 1, sizeof( miteUser ) );
if ( !muu )
return NULL;
muu->umop = airMopNew( );
muu->nsin = NULL;
muu->nvin = NULL;
muu->ntin = NULL;
muu->ntxf = NULL; /* managed by user ( with miter: hest ) */
muu->nout = NULL; /* managed by user ( with miter: hest ) */
muu->debug = NULL;
muu->debugArr = NULL;
muu->ndebug = NULL; /* not allocated until the debug pixel
is rendered, see miteRayBegin */
muu->ntxfNum = 0;
muu->shadeStr[0] = 0;
muu->normalStr[0] = 0;
for ( i=0; i<MITE_RANGE_NUM; i++ ) {
muu->rangeInit[i] = 1.0;
}
muu->normalSide = miteDefNormalSide;
muu->refStep = miteDefRefStep;
muu->rayStep = AIR_NAN;
muu->opacMatters = miteDefOpacMatters;
muu->opacNear1 = miteDefOpacNear1;
muu->hctx = hooverContextNew( );
ELL_3V_SET( muu->fakeFrom, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( muu->vectorD, 0, 0, 0 );
airMopAdd( muu->umop, muu->hctx, ( airMopper )hooverContextNix, airMopAlways );
for ( i=gageKernelUnknown+1; i<gageKernelLast; i++ ) {
muu->ksp[i] = NULL;
}
muu->shape = gageShapeNew( );
muu->gctx0 = gageContextNew( );
airMopAdd( muu->umop, muu->shape, ( airMopper )gageShapeNix, airMopAlways );
airMopAdd( muu->umop, muu->gctx0, ( airMopper )gageContextNix, airMopAlways );
/* gageParmSet( muu->gctx0, gageParmRequireAllSpacings, AIR_FALSE ); */
muu->lit = limnLightNew( );
airMopAdd( muu->umop, muu->lit, ( airMopper )limnLightNix, airMopAlways );
muu->normalSide = miteDefNormalSide;
muu->verbUi = muu->verbVi = -1;
muu->rendTime = 0;
muu->sampRate = 0;
return muu;
}
miteUser *
79 miteUserNix( miteUser *muu ) {
if ( muu ) {
airMopOkay( muu->umop );
airFree( muu );
}
return NULL;
}
int
89 _miteUserCheck( miteUser *muu ) {
static const char me[]="_miteUserCheck";
int T, gotOpac;
gageItemSpec isp;
gageQuery queryScl, queryVec, queryTen, queryMite;
miteShadeSpec *shpec;
airArray *mop;
unsigned int axi;
if ( !muu ) {
biffAddf( MITE, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( !( muu->ntxfNum >= 1 ) ) {
biffAddf( MITE, "%s: need at least one transfer function", me );
airMopError( mop ); return 1;
}
gotOpac = AIR_FALSE;
GAGE_QUERY_RESET( queryScl );
GAGE_QUERY_RESET( queryVec );
GAGE_QUERY_RESET( queryTen );
GAGE_QUERY_RESET( queryMite ); /* not actually used here */
/* add on all queries associated with transfer functions */
for ( T=0; T<muu->ntxfNum; T++ ) {
if ( miteNtxfCheck( muu->ntxf[T] ) ) {
biffAddf( MITE, "%s: ntxf[%d] ( %d of %d ) can't be used as a txf",
me, T, T+1, muu->ntxfNum );
airMopError( mop ); return 1;
}
/* NOTE: no error checking because miteNtxfCheck succeeded */
for ( axi=1; axi<muu->ntxf[T]->dim; axi++ ) {
miteVariableParse( &isp, muu->ntxf[T]->axis[axi].label );
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, &isp );
}
gotOpac |= !!strchr( muu->ntxf[T]->axis[0].label, 'A' );
}
if ( !gotOpac ) {
fprintf( stderr, "\n\n%s: ****************************************"
"************************\n", me );
fprintf( stderr, "%s: !!! WARNING !!! opacity ( \"A\" ) not set "
"by any transfer function\n", me );
fprintf( stderr, "%s: ****************************************"
"************************\n\n\n", me );
}
/* add on "normal"-based queries */
if ( airStrlen( muu->normalStr ) ) {
miteVariableParse( &isp, muu->normalStr );
if ( miteValGageKind == isp.kind ) {
biffAddf( MITE, "%s: normalStr \"%s\" refers to a miteVal "
"( normal must be data-intrinsic )", me, muu->normalStr );
airMopError( mop ); return 1;
}
if ( 3 != isp.kind->table[isp.item].answerLength ) {
biffAddf( MITE, "%s: %s not a vector: can't be used as normal",
me, muu->normalStr );
return 1;
}
miteQueryAdd( queryScl, queryVec, queryTen, queryMite, &isp );
}
/* add on shading-based queries */
shpec = miteShadeSpecNew( );
airMopAdd( mop, shpec, ( airMopper )miteShadeSpecNix, airMopAlways );
if ( miteShadeSpecParse( shpec, muu->shadeStr ) ) {
biffAddf( MITE, "%s: couldn't parse shading spec \"%s\"",
me, muu->shadeStr );
airMopError( mop ); return 1;
}
miteShadeSpecQueryAdd( queryScl, queryVec, queryTen, queryMite, shpec );
/* see if anyone asked for an unspecified normal */
if ( ( GAGE_QUERY_ITEM_TEST( queryMite, miteValNdotV )
|| GAGE_QUERY_ITEM_TEST( queryMite, miteValNdotL )
|| GAGE_QUERY_ITEM_TEST( queryMite, miteValVrefN ) )
&& !airStrlen( muu->normalStr ) ) {
biffAddf( MITE, "%s: txf or shading requested a miteVal's use of the "
"\"normal\", but one has not been specified in muu->normalStr",
me );
airMopError( mop ); return 1;
}
/* see if we have volumes for requested queries */
if ( GAGE_QUERY_NONZERO( queryScl ) && !( muu->nsin ) ) {
biffAddf( MITE, "%s: txf or shading require %s volume, but don't have one",
me, gageKindScl->name );
airMopError( mop ); return 1;
}
if ( GAGE_QUERY_NONZERO( queryVec ) && !( muu->nvin ) ) {
biffAddf( MITE, "%s: txf or shading require %s volume, but don't have one",
me, gageKindVec->name );
airMopError( mop ); return 1;
}
if ( GAGE_QUERY_NONZERO( queryTen ) && !( muu->ntin ) ) {
biffAddf( MITE, "%s: txf or shading require %s volume, but don't have one",
me, tenGageKind->name );
airMopError( mop ); return 1;
}
/* check appropriateness of given volumes */
if ( muu->nsin ) {
if ( gageVolumeCheck( muu->gctx0, muu->nsin, gageKindScl ) ) {
biffMovef( MITE, GAGE, "%s: trouble with input %s volume",
me, gageKindScl->name );
airMopError( mop ); return 1;
}
}
if ( muu->nvin ) {
if ( gageVolumeCheck( muu->gctx0, muu->nvin, gageKindVec ) ) {
biffMovef( MITE, GAGE, "%s: trouble with input %s volume",
me, gageKindVec->name );
airMopError( mop ); return 1;
}
}
if ( muu->ntin ) {
if ( gageVolumeCheck( muu->gctx0, muu->ntin, tenGageKind ) ) {
biffMovef( MITE, GAGE, "%s: trouble with input %s volume",
me, tenGageKind->name );
airMopError( mop ); return 1;
}
}
if ( !muu->nout ) {
biffAddf( MITE, "%s: rendered image nrrd is NULL", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "moss.h"
#include "privateMoss.h"
const char *
mossBiffKey = "moss";
int
mossDefBoundary = nrrdBoundaryBleed;
int
mossDefCenter = nrrdCenterCell;
int
mossVerbose = 0;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "moss.h"
#include "privateMoss.h"
int
28 _mossHestTransformParse ( void *ptr, char *_str, char err[AIR_STRLEN_HUGE] ) {
char me[]="_mossHestTransformParse", *str;
double **matP, tx, ty, sx, sy, angle, mat[6], shf, sha;
airArray *mop;
if ( !( ptr && _str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
matP = ( double ** )ptr;
mop = airMopNew( );
*matP = ( double * )calloc( 6, sizeof( double ) );
airMopMem( mop, matP, airMopOnError );
str = airToLower( airStrdup( _str ) );
airMopMem( mop, &str, airMopAlways );
if ( !strcmp( "identity", str ) ) {
mossMatIdentitySet( *matP );
} else if ( 1 == sscanf( str, "flip:%lf", &angle ) ) {
mossMatFlipSet( *matP, angle );
} else if ( 2 == sscanf( str, "translate:%lf, %lf", &tx, &ty ) ) {
mossMatTranslateSet( *matP, tx, ty );
} else if ( 2 == sscanf( str, "t:%lf, %lf", &tx, &ty ) ) {
mossMatTranslateSet( *matP, tx, ty );
} else if ( 1 == sscanf( str, "rotate:%lf", &angle ) ) {
mossMatRotateSet( *matP, angle );
} else if ( 1 == sscanf( str, "r:%lf", &angle ) ) {
mossMatRotateSet( *matP, angle );
} else if ( 2 == sscanf( str, "scale:%lf, %lf", &sx, &sy ) ) {
mossMatScaleSet( *matP, sx, sy );
} else if ( 2 == sscanf( str, "s:%lf, %lf", &sx, &sy ) ) {
mossMatScaleSet( *matP, sx, sy );
} else if ( 2 == sscanf( str, "shear:%lf, %lf", &shf, &sha ) ) {
mossMatShearSet( *matP, shf, sha );
} else if ( 6 == sscanf( str, "%lf, %lf, %lf, %lf, %lf, %lf",
mat+0, mat+1, mat+2, mat+3, mat+4, mat+5 ) ) {
MOSS_MAT_COPY( *matP, mat );
} else {
sprintf( err, "%s: couldn't parse \"%s\" as a transform", me, _str );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
hestCB
_mossHestTransform = {
sizeof( double* ),
"2D transform",
_mossHestTransformParse,
airFree
};
hestCB *
mossHestTransform = &_mossHestTransform;
/* ----------------------------------------------------------------- */
/*
** _mossHestOriginParse( )
**
** parse an origin specification
** p( x, y ): absolute pixel position --> val[3] = ( 0, x, y )
** u( x, y ): position in unit box [0, 1]x[0, 1] --> val[3] = ( 1, x, y )
*/
int
102 _mossHestOriginParse ( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="_mossHestOriginParse";
double **valP;
airArray *mop;
valP = ( double ** )ptr;
mop = airMopNew( );
*valP = ( double * )calloc( 3, sizeof( double ) );
airMopMem( mop, valP, airMopOnError );
if ( 2 == sscanf( str, "p:%lf, %lf", *valP + 1, *valP + 2 ) ) {
( *valP )[0] = 0;
} else if ( 2 == sscanf( str, "u:%lf, %lf", *valP + 1, *valP + 2 ) ) {
( *valP )[0] = 1;
} else {
sprintf( err, "%s: couldn't parse \"%s\" as origin", me, str );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
hestCB
_mossHestOrigin = {
sizeof( double* ),
"origin specification",
_mossHestOriginParse,
airFree
};
hestCB *
mossHestOrigin = &_mossHestOrigin;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "moss.h"
#include "privateMoss.h"
const int
mossPresent = 42;
/*
******** mossSamplerNew( )
**
*/
mossSampler *
35 mossSamplerNew ( void ) {
mossSampler *smplr;
int i;
smplr = ( mossSampler * )calloc( 1, sizeof( mossSampler ) );
if ( smplr ) {
smplr->image = NULL;
smplr->kernel = NULL;
for ( i=0; i<NRRD_KERNEL_PARMS_NUM; i++ )
smplr->kparm[i] = AIR_NAN;
smplr->ivc = NULL;
smplr->xFslw = smplr->yFslw = NULL;
smplr->xIdx = smplr->yIdx = NULL;
smplr->bg = NULL;
smplr->fdiam = smplr->ncol = 0;
smplr->boundary = mossDefBoundary;
for ( i=0; i<MOSS_FLAG_NUM; i++ )
smplr->flag[i] = AIR_FALSE;
}
return smplr;
}
int
58 mossSamplerFill ( mossSampler *smplr, int fdiam, int ncol ) {
static const char me[]="_mossSamplerFill";
if ( !( smplr ) ) {
biffAddf( MOSS, "%s: got NULL pointer", me );
return 1;
}
smplr->ivc = ( float* )calloc( fdiam*fdiam*ncol, sizeof( float ) );
smplr->xFslw = ( double* )calloc( fdiam, sizeof( double ) );
smplr->yFslw = ( double* )calloc( fdiam, sizeof( double ) );
smplr->xIdx = ( int* )calloc( fdiam, sizeof( int ) );
smplr->yIdx = ( int* )calloc( fdiam, sizeof( int ) );
if ( !( smplr->ivc && smplr->xFslw && smplr->yFslw
&& smplr->xIdx && smplr->yIdx ) ) {
biffAddf( MOSS, "%s: couldn't allocate buffers", me );
return 1;
}
smplr->fdiam = fdiam;
smplr->ncol = ncol;
return 0;
}
void
81 mossSamplerEmpty ( mossSampler *smplr ) {
if ( smplr ) {
smplr->ivc = ( float * )airFree( smplr->ivc );
smplr->xFslw = ( double * )airFree( smplr->xFslw );
smplr->yFslw = ( double * )airFree( smplr->yFslw );
smplr->xIdx = ( int * )airFree( smplr->xIdx );
smplr->yIdx = ( int * )airFree( smplr->yIdx );
smplr->fdiam = 0;
smplr->ncol = 0;
}
return;
}
mossSampler *
96 mossSamplerNix ( mossSampler *smplr ) {
if ( smplr ) {
mossSamplerEmpty( smplr );
smplr->bg = ( float * )airFree( smplr->bg );
free( smplr );
}
return NULL;
}
int
107 mossImageCheck ( Nrrd *image ) {
static const char me[]="mossImageCheck";
if ( nrrdCheck( image ) ) {
biffMovef( MOSS, NRRD, "%s: given nrrd invalid", me );
return 1;
}
if ( !( ( 2 == image->dim || 3 == image->dim )
&& nrrdTypeBlock != image->type ) ) {
biffAddf( MOSS, "%s: image has invalid dimension ( %d ) or type ( %s )", me,
image->dim, airEnumStr( nrrdType, image->type ) );
return 1;
}
return 0;
}
int
125 mossImageAlloc ( Nrrd *image, int type, int sx, int sy, int ncol ) {
static const char me[]="mossImageAlloc";
int ret;
if ( !( image && AIR_IN_OP( nrrdTypeUnknown, type, nrrdTypeBlock )
&& sx > 0 && sy > 0 && ncol > 0 ) ) {
biffAddf( MOSS, "%s: got NULL pointer or bad args", me );
return 1;
}
if ( 1 == ncol ) {
ret = nrrdMaybeAlloc_va( image, type, 2,
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ) );
} else {
ret = nrrdMaybeAlloc_va( image, type, 3,
AIR_CAST( size_t, ncol ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ) );
}
if ( ret ) {
biffMovef( MOSS, NRRD, "%s: couldn't allocate image", me );
return 1;
}
return 0;
}
int
154 _mossCenter( int center ) {
center = ( nrrdCenterUnknown == center
? mossDefCenter
: center );
center = AIR_CLAMP( nrrdCenterUnknown+1, center, nrrdCenterLast-1 );
return center;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "moss.h"
#include "privateMoss.h"
int
28 mossSamplerImageSet ( mossSampler *smplr, Nrrd *image, float *bg ) {
static const char me[]="mossSamplerImageSet";
int ci, ncol;
if ( !( smplr && image ) ) {
biffAddf( MOSS, "%s: got NULL pointer", me );
return 1;
}
if ( mossImageCheck( image ) ) {
biffAddf( MOSS, "%s: ", me );
return 1;
}
smplr->image = image;
smplr->flag[mossFlagImage] = AIR_TRUE;
ncol = MOSS_NCOL( image );
smplr->bg = ( float * )airFree( smplr->bg );
if ( bg ) {
smplr->bg = ( float* )calloc( ncol, sizeof( float ) );
for ( ci=0; ci<ncol; ci++ ) {
smplr->bg[ci] = bg[ci];
}
}
return 0;
}
int
54 mossSamplerKernelSet ( mossSampler *smplr,
const NrrdKernel *kernel, double *kparm ) {
static const char me[]="mossSamplerKernelSet";
unsigned int ki;
if ( !( smplr && kernel && kparm ) ) {
biffAddf( MOSS, "%s: got NULL pointer", me );
return 1;
}
smplr->kernel = kernel;
for ( ki=0; ki<kernel->numParm; ki++ ) {
smplr->kparm[ki] = kparm[ki];
}
smplr->flag[mossFlagKernel] = AIR_TRUE;
return 0;
}
int
72 mossSamplerUpdate ( mossSampler *smplr ) {
static const char me[]="mossSamplerUpdate";
int ncol=0, fdiam=0;
if ( !( smplr ) ) {
biffAddf( MOSS, "%s: got NULL pointer", me );
return 1;
}
if ( smplr->flag[mossFlagImage] ) {
ncol = MOSS_NCOL( smplr->image );
if ( ncol != smplr->ncol ) {
mossSamplerEmpty( smplr );
smplr->ncol = ncol;
}
}
if ( smplr->flag[mossFlagKernel] ) {
fdiam = 2*AIR_ROUNDUP( smplr->kernel->support( smplr->kparm ) );
if ( fdiam != smplr->fdiam ) {
mossSamplerEmpty( smplr );
smplr->fdiam = fdiam;
}
}
if ( !( smplr->ivc ) ) {
if ( mossSamplerFill( smplr, fdiam, ncol ) ) {
biffAddf( MOSS, "%s: ", me );
return 1;
}
}
if ( nrrdBoundaryPad == smplr->boundary && !smplr->bg ) {
biffAddf( MOSS, "%s: want %s boundary behavior, but bg vector is NULL",
me, airEnumStr( nrrdBoundary, nrrdBoundaryPad ) );
return 1;
}
return 0;
}
int
111 mossSamplerSample ( float *val, mossSampler *smplr, double xPos, double yPos ) {
static const char me[]="mossSamplerSample";
int i, xi, yi, ci, sx, sy, fdiam, frad, ncol;
double xf, yf, tmp;
float ( *lup )( const void *v, size_t I );
if ( !( val && smplr ) ) {
biffAddf( MOSS, "%s: got NULL pointer", me );
return 1;
}
if ( !( smplr->ivc ) ) {
biffAddf( MOSS, "%s: given sampler not ready ( no caches )", me );
return 1;
}
/* set {x, y}Idx, set {x, y}Fslw to sample locations */
if ( mossVerbose ) {
fprintf( stderr, "%s: pos = %g %g\n", me, xPos, yPos );
}
sx = MOSS_SX( smplr->image );
sy = MOSS_SY( smplr->image );
xi = ( int )floor( xPos ); xf = xPos - xi;
yi = ( int )floor( yPos ); yf = yPos - yi;
fdiam = smplr->fdiam;
frad = fdiam/2;
for ( i=0; i<fdiam; i++ ) {
smplr->xIdx[i] = xi + i - frad + 1;
smplr->yIdx[i] = yi + i - frad + 1;
smplr->xFslw[i] = xf - i + frad - 1;
smplr->yFslw[i] = yf - i + frad - 1;
}
if ( mossVerbose ) {
fprintf( stderr, " --> xIdx: %d %d ; xFsl %g %g\n",
smplr->xIdx[0], smplr->xIdx[1],
smplr->xFslw[0], smplr->xFslw[1] );
fprintf( stderr, " yIdx: %d %d ; yFsl %g %g\n",
smplr->yIdx[0], smplr->yIdx[1],
smplr->yFslw[0], smplr->yFslw[1] );
}
switch( smplr->boundary ) {
case nrrdBoundaryBleed:
for ( i=0; i<fdiam; i++ ) {
smplr->xIdx[i] = AIR_CLAMP( 0, smplr->xIdx[i], sx-1 );
smplr->yIdx[i] = AIR_CLAMP( 0, smplr->yIdx[i], sy-1 );
}
break;
case nrrdBoundaryWrap:
for ( i=0; i<fdiam; i++ ) {
smplr->xIdx[i] = AIR_MOD( smplr->xIdx[i], sx );
smplr->yIdx[i] = AIR_MOD( smplr->yIdx[i], sy );
}
break;
case nrrdBoundaryPad:
/* this is handled later */
break;
default:
biffAddf( MOSS, "%s: sorry, %s boundary not implemented", me,
airEnumStr( nrrdBoundary, smplr->boundary ) );
return 1;
}
if ( mossVerbose ) {
fprintf( stderr, " --> xIdx: %d %d ; xFsl %g %g\n",
smplr->xIdx[0], smplr->xIdx[1],
smplr->xFslw[0], smplr->xFslw[1] );
}
/* copy values to ivc, set {x, y}Fslw to filter sample weights */
lup = nrrdFLookup[smplr->image->type];
ncol = smplr->ncol;
if ( nrrdBoundaryPad == smplr->boundary ) {
for ( yi=0; yi<fdiam; yi++ ) {
for ( xi=0; xi<fdiam; xi++ ) {
if ( AIR_IN_CL( 0, smplr->xIdx[xi], sx-1 )
&& AIR_IN_CL( 0, smplr->yIdx[yi], sy-1 ) ) {
for ( ci=0; ci<ncol; ci++ ) {
smplr->ivc[xi + fdiam*( yi + fdiam*ci )] =
lup( smplr->image->data,
ci + ncol*( smplr->xIdx[xi] + sx*smplr->yIdx[yi] ) );
}
} else {
for ( ci=0; ci<ncol; ci++ ) {
smplr->ivc[xi + fdiam*( yi + fdiam*ci )] = smplr->bg[ci];
}
}
}
}
} else {
for ( yi=0; yi<fdiam; yi++ ) {
for ( xi=0; xi<fdiam; xi++ ) {
for ( ci=0; ci<ncol; ci++ ) {
smplr->ivc[xi + fdiam*( yi + fdiam*ci )] =
lup( smplr->image->data,
ci + ncol*( smplr->xIdx[xi] + sx*smplr->yIdx[yi] ) );
}
}
}
}
smplr->kernel->evalN_d( smplr->xFslw, smplr->xFslw, fdiam, smplr->kparm );
smplr->kernel->evalN_d( smplr->yFslw, smplr->yFslw, fdiam, smplr->kparm );
/* do convolution */
memset( val, 0, ncol*sizeof( float ) );
for ( ci=0; ci<ncol; ci++ ) {
for ( yi=0; yi<fdiam; yi++ ) {
tmp = 0;
for ( xi=0; xi<fdiam; xi++ ) {
tmp += smplr->xFslw[xi]*smplr->ivc[xi + fdiam*( yi + fdiam*ci )];
}
val[ci] += AIR_CAST( float, smplr->yFslw[yi]*tmp );
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../moss.h"
int
26 main( int argc, const char *argv[] ) {
const char *me, *info="inverts a moss transform";
hestOpt *hopt=NULL;
double *mat, inv[6];
me = argv[0];
hestOptAdd( &hopt, "t", "transform", airTypeOther, 1, 1, &mat, "identity",
"transform( s ) to apply to image",
NULL, NULL, mossHestTransform );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
fprintf( stderr, "%s: got transform:\n", me );
mossMatPrint( stderr, mat );
mossMatInvert( inv, mat );
fprintf( stderr, "\n%s: inverse:\n", me );
mossMatPrint( stderr, inv );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "moss.h"
#include "privateMoss.h"
/*
0 1 2
3 4 5
6 7 8
a c tx
b d ty
0 0 1
0 1 2
3 4 5
*/
void
43 mossMatPrint ( FILE *f, double *mat ) {
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
( float )mat[0], ( float )mat[1], ( float )mat[2] );
fprintf( f, "% 15.7f % 15.7f % 15.7f\n",
( float )mat[3], ( float )mat[4], ( float )mat[5] );
}
double *
52 mossMatRightMultiply ( double *_mat, double *_x ) {
double mat[9], x[9];
MOSS_MAT_6TO9( x, _x );
MOSS_MAT_6TO9( mat, _mat );
ell_3m_pre_mul_d( mat, x );
MOSS_MAT_9TO6( _mat, mat );
return _mat;
}
double *
63 mossMatLeftMultiply ( double *_mat, double *_x ) {
double mat[9], x[9];
MOSS_MAT_6TO9( x, _x );
MOSS_MAT_6TO9( mat, _mat );
ell_3m_post_mul_d( mat, x );
MOSS_MAT_9TO6( _mat, mat );
return _mat;
}
double *
74 mossMatInvert ( double *inv, double *mat ) {
double inv9[9], mat9[9];
MOSS_MAT_6TO9( mat9, mat );
ell_3m_inv_d( inv9, mat9 );
MOSS_MAT_9TO6( inv, inv9 );
return inv;
}
double *
84 mossMatIdentitySet ( double *mat ) {
MOSS_MAT_SET( mat, 1, 0, 0, 0, 1, 0 );
return mat;
}
double *
91 mossMatTranslateSet ( double *mat, double tx, double ty ) {
MOSS_MAT_SET( mat, 1, 0, tx, 0, 1, ty );
return mat;
}
double *
98 mossMatRotateSet ( double *mat, double angle ) {
angle *= AIR_PI/180.0;
MOSS_MAT_SET( mat, cos( angle ), -sin( angle ), 0, sin( angle ), cos( angle ), 0 );
return mat;
}
double *
106 mossMatFlipSet ( double *mat, double angle ) {
double rot[6], flip[6];
MOSS_MAT_SET( flip, -1, 0, 0, 0, 1, 0 );
mossMatIdentitySet( mat );
mossMatLeftMultiply( mat, mossMatRotateSet( rot, -angle ) );
mossMatLeftMultiply( mat, flip );
mossMatLeftMultiply( mat, mossMatRotateSet( rot, angle ) );
return mat;
}
double *
118 mossMatShearSet ( double *mat, double angleFixed, double amount ) {
double rot[6], shear[6];
MOSS_MAT_SET( shear, 1, amount, 0, 0, 1, 0 );
mossMatIdentitySet( mat );
mossMatLeftMultiply( mat, mossMatRotateSet( rot, -angleFixed ) );
mossMatLeftMultiply( mat, shear );
mossMatLeftMultiply( mat, mossMatRotateSet( rot, angleFixed ) );
return mat;
}
double *
130 mossMatScaleSet ( double *mat, double sx, double sy ) {
MOSS_MAT_SET( mat, sx, 0, 0, 0, sy, 0 );
return mat;
}
void
137 mossMatApply ( double *ox, double *oy, double *mat, double ix, double iy ) {
*ox = mat[0]*ix + mat[1]*iy + mat[2];
*oy = mat[3]*ix + mat[4]*iy + mat[5];
}
int
144 mossLinearTransform ( Nrrd *nout, Nrrd *nin, float *bg,
double *mat, mossSampler *msp,
double xMin, double xMax,
double yMin, double yMax,
int xSize, int ySize ) {
static const char me[]="mossLinearTransform";
int ncol, xi, yi, ci, ax0, xCent, yCent;
float *val, ( *ins )( void *v, size_t I, float f ), ( *clamp )( float val );
double inv[6], xInPos, xOutPos, yInPos, yOutPos;
if ( !( nout && nin && mat && msp && !mossImageCheck( nin ) ) ) {
biffAddf( MOSS, "%s: got NULL pointer or bad image", me );
return 1;
}
if ( mossSamplerImageSet( msp, nin, bg ) || mossSamplerUpdate( msp ) ) {
biffAddf( MOSS, "%s: trouble with sampler", me );
return 1;
}
if ( !( xMin != xMax && yMin != yMax && xSize > 1 && ySize > 1 ) ) {
biffAddf( MOSS, "%s: bad args: {x, y}Min == {x, y}Max or {x, y}Size <= 1", me );
return 1;
}
ax0 = MOSS_AXIS0( nin );
if ( !( AIR_EXISTS( nin->axis[ax0+0].min )
&& AIR_EXISTS( nin->axis[ax0+0].max )
&& AIR_EXISTS( nin->axis[ax0+1].min )
&& AIR_EXISTS( nin->axis[ax0+1].max ) ) ) {
biffAddf( MOSS, "%s: input axis min, max not set on axes %d and %d", me,
ax0+0, ax0+1 );
return 1;
}
ncol = MOSS_NCOL( nin );
if ( mossImageAlloc( nout, nin->type, xSize, ySize, ncol ) ) {
biffAddf( MOSS, "%s: ", me ); return 1;
}
val = ( float* )calloc( ncol, sizeof( float ) );
if ( nrrdCenterUnknown == nout->axis[ax0+0].center )
nout->axis[ax0+0].center = _mossCenter( nin->axis[ax0+0].center );
xCent = nout->axis[ax0+0].center;
if ( nrrdCenterUnknown == nout->axis[ax0+1].center )
nout->axis[ax0+1].center = _mossCenter( nin->axis[ax0+1].center );
yCent = nout->axis[ax0+1].center;
nout->axis[ax0+0].min = xMin;
nout->axis[ax0+0].max = xMax;
nout->axis[ax0+1].min = yMin;
nout->axis[ax0+1].max = yMax;
ins = nrrdFInsert[nin->type];
clamp = nrrdFClamp[nin->type];
if ( mossSamplerSample( val, msp, 0, 0 ) ) {
biffAddf( MOSS, "%s: trouble in sampler", me );
free( val ); return 1;
}
mossMatInvert( inv, mat );
for ( yi=0; yi<ySize; yi++ ) {
yOutPos = NRRD_POS( yCent, yMin, yMax, ySize, yi );
for ( xi=0; xi<xSize; xi++ ) {
/*
mossVerbose = ( ( 36 == xi && 72 == yi ) ||
( 37 == xi && 73 == yi ) ||
( 105 == xi && 175 == yi ) );
*/
xOutPos = NRRD_POS( xCent, xMin, xMax, xSize, xi );
mossMatApply( &xInPos, &yInPos, inv, xOutPos, yOutPos );
xInPos = NRRD_IDX( xCent, nin->axis[ax0+0].min, nin->axis[ax0+0].max,
nin->axis[ax0+0].size, xInPos );
yInPos = NRRD_IDX( yCent, nin->axis[ax0+1].min, nin->axis[ax0+1].max,
nin->axis[ax0+1].size, yInPos );
mossSamplerSample( val, msp, xInPos, yInPos );
for ( ci=0; ci<ncol; ci++ ) {
ins( nout->data, ci + ncol*( xi + xSize*yi ), clamp( val[ci] ) );
}
}
}
free( val );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#include "float.h"
/*
** making these typedefs here allows us to used one token for both
** constructing function names, and for specifying argument types
*/
typedef signed char CH;
typedef unsigned char UC;
typedef signed short SH;
typedef unsigned short US;
/* Microsoft apparently uses 'IN' as a keyword, so we changed 'IN' to 'JN'. */
typedef signed int JN;
typedef unsigned int UI;
typedef airLLong LL;
/* ui64 to double conversion is not implemented, sorry */
#if _MSC_VER < 1300
typedef airLLong UL;
#else
typedef airULLong UL;
#endif
typedef float FL;
typedef double DB;
#define MAP( F, A ) \
F( A, CH ) \
F( A, UC ) \
F( A, SH ) \
F( A, US ) \
F( A, JN ) \
F( A, UI ) \
F( A, LL ) \
F( A, UL ) \
F( A, FL ) \
59 F( A, DB )
/*
** _nrrdLoad<TA><TB>( <TB> *v )
**
** Dereferences v as TB*, casts it to TA, returns it.
*/
66 #define LOAD_DEF( TA, TB ) \
static TA \
_nrrdLoad##TA##TB( TB *v ) { \
return ( TA )( *v ); \
}
#define LOAD_LIST( TA, TB ) \
( TA ( * )( const void * ) )_nrrdLoad##TA##TB,
MAP( LOAD_DEF, UI )
MAP( LOAD_DEF, JN )
MAP( LOAD_DEF, FL )
MAP( LOAD_DEF, DB )
unsigned int ( *
nrrdUILoad[NRRD_TYPE_MAX+1] )( const void* ) = {
NULL, MAP( LOAD_LIST, UI ) NULL
};
int ( *
nrrdILoad[NRRD_TYPE_MAX+1] )( const void* ) = {
NULL, MAP( LOAD_LIST, JN ) NULL
};
float ( *
nrrdFLoad[NRRD_TYPE_MAX+1] )( const void* ) = {
NULL, MAP( LOAD_LIST, FL ) NULL
};
double ( *
nrrdDLoad[NRRD_TYPE_MAX+1] )( const void* ) = {
NULL, MAP( LOAD_LIST, DB ) NULL
};
/*
** _nrrdStore<TA><TB>( <TB> *v, <TA> j )
**
** Takes a TA j, and stores it in *v, thereby implicitly casting it to TB.
** Returns the result of the assignment, which may not be the same as
** the value that was passed in.
*/
#define STORE_DEF( TA, TB ) \
static TA \
_nrrdStore##TA##TB( TB *v, TA j ) { \
return ( TA )( *v = ( TB )j ); \
}
#define STORE_LIST( TA, TB ) \
( TA ( * )( void *, TA ) )_nrrdStore##TA##TB,
MAP( STORE_DEF, UI )
MAP( STORE_DEF, JN )
MAP( STORE_DEF, FL )
MAP( STORE_DEF, DB )
unsigned int ( *
nrrdUIStore[NRRD_TYPE_MAX+1] )( void *, unsigned int ) = {
NULL, MAP( STORE_LIST, UI ) NULL
};
int ( *
nrrdIStore[NRRD_TYPE_MAX+1] )( void *, int ) = {
NULL, MAP( STORE_LIST, JN ) NULL
};
float ( *
nrrdFStore[NRRD_TYPE_MAX+1] )( void *, float ) = {
NULL, MAP( STORE_LIST, FL ) NULL
};
double ( *
nrrdDStore[NRRD_TYPE_MAX+1] )( void *, double ) = {
NULL, MAP( STORE_LIST, DB ) NULL
};
/*
** _nrrdLookup<TA><TB>( <TB> *v, size_t I )
**
** Looks up element I of TB array v, and returns it cast to a TA.
*/
#define LOOKUP_DEF( TA, TB ) \
static TA \
_nrrdLookup##TA##TB( TB *v, size_t I ) { \
return ( TA )v[I]; \
}
#define LOOKUP_LIST( TA, TB ) \
( TA ( * )( const void*, size_t ) )_nrrdLookup##TA##TB,
MAP( LOOKUP_DEF, UI )
MAP( LOOKUP_DEF, JN )
MAP( LOOKUP_DEF, FL )
MAP( LOOKUP_DEF, DB )
unsigned int ( *
nrrdUILookup[NRRD_TYPE_MAX+1] )( const void *, size_t ) = {
NULL, MAP( LOOKUP_LIST, UI ) NULL
};
int ( *
nrrdILookup[NRRD_TYPE_MAX+1] )( const void *, size_t ) = {
NULL, MAP( LOOKUP_LIST, JN ) NULL
};
float ( *
nrrdFLookup[NRRD_TYPE_MAX+1] )( const void *, size_t ) = {
NULL, MAP( LOOKUP_LIST, FL ) NULL
};
double ( *
nrrdDLookup[NRRD_TYPE_MAX+1] )( const void *, size_t ) = {
NULL, MAP( LOOKUP_LIST, DB ) NULL
};
/*
** _nrrdInsert<TA><TB>( <TB> *v, size_t I, <TA> j )
**
** Given TA j, stores it in v[i] ( implicitly casting to TB ).
** Returns the result of the assignment, which may not be the same as
** the value that was passed in.
*/
#define INSERT_DEF( TA, TB ) \
static TA \
_nrrdInsert##TA##TB( TB *v, size_t I, TA j ) { \
return ( TA )( v[I] = ( TB )j ); \
}
#define INSERT_LIST( TA, TB ) \
( TA ( * )( void*, size_t, TA ) )_nrrdInsert##TA##TB,
MAP( INSERT_DEF, UI )
MAP( INSERT_DEF, JN )
MAP( INSERT_DEF, FL )
MAP( INSERT_DEF, DB )
unsigned int ( *
nrrdUIInsert[NRRD_TYPE_MAX+1] )( void *, size_t, unsigned int ) = {
NULL, MAP( INSERT_LIST, UI ) NULL
};
int ( *
nrrdIInsert[NRRD_TYPE_MAX+1] )( void *, size_t, int ) = {
NULL, MAP( INSERT_LIST, JN ) NULL
};
float ( *
nrrdFInsert[NRRD_TYPE_MAX+1] )( void *, size_t, float ) = {
NULL, MAP( INSERT_LIST, FL ) NULL
};
double ( *
nrrdDInsert[NRRD_TYPE_MAX+1] )( void *, size_t, double ) = {
NULL, MAP( INSERT_LIST, DB ) NULL
};
/*
******** nrrdSprint
**
** Dereferences pointer v and sprintf( )s that value into given string s,
** returns the result of sprintf( )
**
** There is obviously no provision for ensuring that the sprint'ing
** doesn't overflow the buffer, which is unfortunate...
*/
static int _nrrdSprintCH( char *s, const CH *v ) { return sprintf( s, "%d", *v ); }
static int _nrrdSprintUC( char *s, const UC *v ) { return sprintf( s, "%u", *v ); }
static int _nrrdSprintSH( char *s, const SH *v ) { return sprintf( s, "%d", *v ); }
static int _nrrdSprintUS( char *s, const US *v ) { return sprintf( s, "%u", *v ); }
static int _nrrdSprintIN( char *s, const JN *v ) { return sprintf( s, "%d", *v ); }
static int _nrrdSprintUI( char *s, const UI *v ) { return sprintf( s, "%u", *v ); }
static int _nrrdSprintLL( char *s, const LL *v ) {
return sprintf( s, AIR_LLONG_FMT, *v );
}
static int _nrrdSprintUL( char *s, const UL *v ) {
return sprintf( s, AIR_ULLONG_FMT, *v );
}
/* HEY: sizeof( float ) and sizeof( double ) assumed here, since we're
basing "8" and "17" on 6 == FLT_DIG and 15 == DBL_DIG, which are
digits of precision for floats and doubles, respectively */
static int _nrrdSprintFL( char *s, const FL *v ) {
return airSinglePrintf( NULL, s, "%.8g", ( double )( *v ) ); }
static int _nrrdSprintDB( char *s, const DB *v ) {
return airSinglePrintf( NULL, s, "%.17g", *v ); }
int ( *
nrrdSprint[NRRD_TYPE_MAX+1] )( char *, const void * ) = {
NULL,
( int ( * )( char *, const void * ) )_nrrdSprintCH,
( int ( * )( char *, const void * ) )_nrrdSprintUC,
( int ( * )( char *, const void * ) )_nrrdSprintSH,
( int ( * )( char *, const void * ) )_nrrdSprintUS,
( int ( * )( char *, const void * ) )_nrrdSprintIN,
( int ( * )( char *, const void * ) )_nrrdSprintUI,
( int ( * )( char *, const void * ) )_nrrdSprintLL,
( int ( * )( char *, const void * ) )_nrrdSprintUL,
( int ( * )( char *, const void * ) )_nrrdSprintFL,
( int ( * )( char *, const void * ) )_nrrdSprintDB,
NULL};
/* ---- BEGIN non-NrrdIO */
/*
******** nrrdFprint
**
** Dereferences pointer v and fprintf( )s that value into given file f;
** returns the result of fprintf( )
*/
static int _nrrdFprintCH( FILE *f, const CH *v ) { return fprintf( f, "%d", *v ); }
static int _nrrdFprintUC( FILE *f, const UC *v ) { return fprintf( f, "%u", *v ); }
static int _nrrdFprintSH( FILE *f, const SH *v ) { return fprintf( f, "%d", *v ); }
static int _nrrdFprintUS( FILE *f, const US *v ) { return fprintf( f, "%u", *v ); }
static int _nrrdFprintIN( FILE *f, const JN *v ) { return fprintf( f, "%d", *v ); }
static int _nrrdFprintUI( FILE *f, const UI *v ) { return fprintf( f, "%u", *v ); }
static int _nrrdFprintLL( FILE *f, const LL *v ) {
return fprintf( f, AIR_LLONG_FMT, *v );
}
static int _nrrdFprintUL( FILE *f, const UL *v ) {
return fprintf( f, AIR_ULLONG_FMT, *v );
}
static int _nrrdFprintFL( FILE *f, const FL *v ) {
return airSinglePrintf( f, NULL, "%.8g", ( double )( *v ) ); }
static int _nrrdFprintDB( FILE *f, const DB *v ) {
return airSinglePrintf( f, NULL, "%.17g", *v ); }
int ( *
nrrdFprint[NRRD_TYPE_MAX+1] )( FILE *, const void * ) = {
NULL,
( int ( * )( FILE *, const void * ) )_nrrdFprintCH,
( int ( * )( FILE *, const void * ) )_nrrdFprintUC,
( int ( * )( FILE *, const void * ) )_nrrdFprintSH,
( int ( * )( FILE *, const void * ) )_nrrdFprintUS,
( int ( * )( FILE *, const void * ) )_nrrdFprintIN,
( int ( * )( FILE *, const void * ) )_nrrdFprintUI,
( int ( * )( FILE *, const void * ) )_nrrdFprintLL,
( int ( * )( FILE *, const void * ) )_nrrdFprintUL,
( int ( * )( FILE *, const void * ) )_nrrdFprintFL,
( int ( * )( FILE *, const void * ) )_nrrdFprintDB,
NULL};
/* about here is where Gordon admits he might have some use for C++ */
#define _MMEF_ARGS( type ) type *minP, type *maxP, int *hneP, const Nrrd *nrrd
#define _MMEF_FIXED( type ) \
size_t I, N; \
type a, b, min, max, *v; \
\
if ( !( minP && maxP ) ) \
return; \
\
/* all integral values exist */ \
*hneP = nrrdHasNonExistFalse; \
\
/* set the local data pointer */ \
v = ( type* )( nrrd->data ); \
\
/* get initial values */ \
N = nrrdElementNumber( nrrd ); \
min = max = v[0]; \
\
/* run through array in pairs; by doing a compare on successive \
elements, we can do three compares per pair instead of the naive \
four. In one very unexhaustive test on irix6.64, this resulted \
in a 20% decrease in running time. I learned this trick from \
Numerical Recipes in C, long time ago, but I can't find it \
anywhere in the book now ... */ \
if ( N>1 ) /* size_t is unsigned, so N-2 may overflow */ \
for ( I=0; I<=N-2; I+=2 ) { \
a = v[0 + I]; \
b = v[1 + I]; \
if ( a < b ) { \
min = AIR_MIN( a, min ); \
max = AIR_MAX( b, max ); \
} else { \
max = AIR_MAX( a, max ); \
min = AIR_MIN( b, min ); \
} \
} \
\
/* get the very last one ( may be redundant ) */ \
a = v[N-1]; \
min = AIR_MIN( a, min ); \
max = AIR_MAX( a, max ); \
\
/* record results */ \
*minP = min; \
*maxP = max;
#define _MMEF_FLOAT( type ) \
size_t I, N; \
type a, min, max, *v; \
\
if ( !( minP && maxP ) ) \
return; \
\
/* this may be over-written below */ \
*hneP = nrrdHasNonExistFalse; \
\
/* set the local data pointer */ \
N = nrrdElementNumber( nrrd ); \
v = ( type* )( nrrd->data ); \
\
/* we have to explicitly search for the first non-NaN value */ \
max = min = AIR_NAN; \
for ( I=0; I<N; I++ ) { \
a = v[I]; \
if ( AIR_EXISTS( a ) ) { \
min = max = a; \
break; \
} else { \
*hneP = nrrdHasNonExistTrue; \
} \
} \
if ( I == N ) { \
/* oh dear, there were NO existent values */ \
min = max = AIR_NAN; \
*hneP = nrrdHasNonExistOnly; \
} else { \
/* there was at least one existent value; we continue searching, \
still checking AIR_EXISTS at each value */ \
for ( I=I+1; I<N; I++ ) { \
a = v[I]; \
if ( AIR_EXISTS( a ) ) { \
if ( a < min ) { \
min = a; \
} else { \
if ( a > max ) { \
max = a; \
} \
} \
} else { \
*hneP = nrrdHasNonExistTrue; \
} \
} \
} \
*minP = min; \
*maxP = max;
static void _nrrdMinMaxExactFindCH ( _MMEF_ARGS( CH ) ) {_MMEF_FIXED( CH )}
static void _nrrdMinMaxExactFindUC ( _MMEF_ARGS( UC ) ) {_MMEF_FIXED( UC )}
static void _nrrdMinMaxExactFindSH ( _MMEF_ARGS( SH ) ) {_MMEF_FIXED( SH )}
static void _nrrdMinMaxExactFindUS ( _MMEF_ARGS( US ) ) {_MMEF_FIXED( US )}
static void _nrrdMinMaxExactFindIN ( _MMEF_ARGS( JN ) ) {_MMEF_FIXED( JN )}
static void _nrrdMinMaxExactFindUI ( _MMEF_ARGS( UI ) ) {_MMEF_FIXED( UI )}
static void _nrrdMinMaxExactFindLL ( _MMEF_ARGS( LL ) ) {_MMEF_FIXED( LL )}
static void _nrrdMinMaxExactFindUL ( _MMEF_ARGS( UL ) ) {_MMEF_FIXED( UL )}
static void _nrrdMinMaxExactFindFL ( _MMEF_ARGS( FL ) ) {_MMEF_FLOAT( FL )}
static void _nrrdMinMaxExactFindDB ( _MMEF_ARGS( DB ) ) {_MMEF_FLOAT( DB )}
/*
******** nrrdMinMaxExactFind[]
**
** the role of these is to allow finding the EXACT min and max of a nrrd,
** so that one does not have to rely on the potentially lossy storage
** of the min and max values in range->min and range->max, which are doubles.
**
** These also sets *hneP, using a value from the nrrdHasNonExist* enum
*/
void ( *
nrrdMinMaxExactFind[NRRD_TYPE_MAX+1] )( void *minP, void *maxP,
int *hneP, const Nrrd * ) = {
NULL,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindCH,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindUC,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindSH,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindUS,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindIN,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindUI,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindLL,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindUL,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindFL,
( void ( * )( void *, void *, int *, const Nrrd * ) )_nrrdMinMaxExactFindDB,
NULL
};
/*
******** nrrdValCompare[]
**
** the sort of compare you'd give to qsort( ) to sort in ascending order:
** return < 0 if A < B,
** 0 if A == B,
** > 0 if A > B
** The non-trivial part of this is that for floating-point values, we
** dictate that all non-existent values are smaller than all existent
** values, regardless of their actual values ( so +infinity < -42 ). This
** is to make sure that we have comparison that won't confuse qsort( ),
** which underlies _nrrdMeasureMedian( ), and to make it easier to separate
** existent from non-existent values.
*/
#define _VC_ARGS( type ) const type *A, const type *B
#define _VC_FIXED ( *A < *B ? -1 : ( *A > *B ? 1 : 0 ) )
#define _VC_FLOAT \
int ex, ret; \
\
ex = AIR_EXISTS( *A ) + AIR_EXISTS( *B ); \
switch ( ex ) { \
case 2: ret = _VC_FIXED; break; \
case 1: ret = AIR_EXISTS( *A ) ? 1 : -1; break; \
case 0: default: ret = 0; \
}
static int _nrrdValCompareCH ( _VC_ARGS( CH ) ) {return _VC_FIXED;}
static int _nrrdValCompareUC ( _VC_ARGS( UC ) ) {return _VC_FIXED;}
static int _nrrdValCompareSH ( _VC_ARGS( SH ) ) {return _VC_FIXED;}
static int _nrrdValCompareUS ( _VC_ARGS( US ) ) {return _VC_FIXED;}
static int _nrrdValCompareIN ( _VC_ARGS( JN ) ) {return _VC_FIXED;}
static int _nrrdValCompareUI ( _VC_ARGS( UI ) ) {return _VC_FIXED;}
static int _nrrdValCompareLL ( _VC_ARGS( LL ) ) {return _VC_FIXED;}
static int _nrrdValCompareUL ( _VC_ARGS( UL ) ) {return _VC_FIXED;}
static int _nrrdValCompareFL ( _VC_ARGS( FL ) ) {_VC_FLOAT; return ret;}
static int _nrrdValCompareDB ( _VC_ARGS( DB ) ) {_VC_FLOAT; return ret;}
int ( *
nrrdValCompare[NRRD_TYPE_MAX+1] )( const void *, const void * ) = {
NULL,
( int ( * )( const void *, const void * ) )_nrrdValCompareCH,
( int ( * )( const void *, const void * ) )_nrrdValCompareUC,
( int ( * )( const void *, const void * ) )_nrrdValCompareSH,
( int ( * )( const void *, const void * ) )_nrrdValCompareUS,
( int ( * )( const void *, const void * ) )_nrrdValCompareIN,
( int ( * )( const void *, const void * ) )_nrrdValCompareUI,
( int ( * )( const void *, const void * ) )_nrrdValCompareLL,
( int ( * )( const void *, const void * ) )_nrrdValCompareUL,
( int ( * )( const void *, const void * ) )_nrrdValCompareFL,
( int ( * )( const void *, const void * ) )_nrrdValCompareDB,
NULL
};
/*
** ...Inv: for descending order
*/
static int _nrrdValCompareInvCH ( _VC_ARGS( CH ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvUC ( _VC_ARGS( UC ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvSH ( _VC_ARGS( SH ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvUS ( _VC_ARGS( US ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvIN ( _VC_ARGS( JN ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvUI ( _VC_ARGS( UI ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvLL ( _VC_ARGS( LL ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvUL ( _VC_ARGS( UL ) ) {return -_VC_FIXED;}
static int _nrrdValCompareInvFL ( _VC_ARGS( FL ) ) {_VC_FLOAT; return -ret;}
static int _nrrdValCompareInvDB ( _VC_ARGS( DB ) ) {_VC_FLOAT; return -ret;}
int ( *
nrrdValCompareInv[NRRD_TYPE_MAX+1] )( const void *, const void * ) = {
NULL,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvCH,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvUC,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvSH,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvUS,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvIN,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvUI,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvLL,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvUL,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvFL,
( int ( * )( const void *, const void * ) )_nrrdValCompareInvDB,
NULL
};
/*
** nrrdArrayCompare
**
** something like strcmp( ) for arrays of numeric values, except
** that the arrays have to be equal length, and it has to do
** error checking.
**
** See comment about logic of return value above nrrdCompare( )
**
** This is a very rare kind of nrrd function that operates on
** a bare array and not a Nrrd itself
*/
int nrrdArrayCompare( int type, const void *_valA, const void *_valB,
size_t valNum, double epsilon, int *differ,
char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdArrayCompare";
const unsigned char *valA, *valB;
int ( *compare )( const void *, const void * );
size_t ii, sze;
char stmp[AIR_STRLEN_SMALL];
if ( !( _valA && _valB && differ ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, or %p )", me,
_valA, _valB, AIR_VOIDP( differ ) );
return 1;
}
if ( !valNum ) {
biffAddf( NRRD, "%s: can't work with 0-length arrays", me );
return 1;
}
if ( !AIR_EXISTS( epsilon ) ) {
biffAddf( NRRD, "%s: non-existent epsilon %g", me, epsilon );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: invalid nrrd type %d", me, type );
return 1;
}
if ( nrrdTypeBlock == type ) {
biffAddf( NRRD, "%s: can't use type %s", me, airEnumStr( nrrdType, type ) );
return 1;
}
if ( explain ) {
strcpy( explain, "" );
}
if ( type == nrrdTypeLLong || type == nrrdTypeULLong ) {
fprintf( stderr, "%s: WARNING: possible erroneous comparison of "
"%s values with %s-based comparison\n", me,
airEnumStr( nrrdType, type ),
airEnumStr( nrrdType, nrrdTypeDouble ) );
}
sze = nrrdTypeSize[type];
compare = nrrdValCompare[type];
valA = AIR_CAST( const unsigned char *, _valA );
valB = AIR_CAST( const unsigned char *, _valB );
for ( ii=0; ii<valNum; ii++ ) {
*differ = compare( valA + ii*sze, valB + ii*sze );
if ( *differ ) {
double aa, bb;
/* same loss of precision as warned about above */
aa = nrrdDLookup[type]( valA, ii );
bb = nrrdDLookup[type]( valB, ii );
if ( 0 == epsilon
|| fabs( aa - bb ) > epsilon ) {
if ( explain ) {
airSprintSize_t( stmp, ii );
if ( 0 == epsilon ) {
sprintf( explain, "valA[%s]=%.17g %s valB[%s]=%.17g "
"by %g",
stmp, aa, *differ < 0 ? "<" : ">", stmp, bb,
fabs( aa - bb ) );
} else {
sprintf( explain, "valA[%s]=%.17g %s valB[%s]=%.17g "
"by %g, more than eps %g",
stmp, aa, *differ < 0 ? "<" : ">", stmp, bb,
fabs( aa - bb ), epsilon );
}
}
break;
} else {
/* we did detect a difference, but it was not in excess of
epsilon, so we reset *differ to 0 */
*differ = 0;
}
}
}
return 0;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** learned: even when using doubles, because of limited floating point
** precision, you can get different results between quantizing
** unrescaled ( value directly from nrrd, map domain set to nrrd range,
** as with early behavior of unu rmap ) and rescaled ( value from nrrd
** scaled to fit in existing map domain, as with unu imap -r ) value,
** to the exact same index space.
*/
/*
** I won't try to support mapping individual values through a
** colormap, as with a function evaluation on a single passed value.
** That will be handled in an upcoming library...
*/
/*
** this identifies the different kinds of 1D maps, useful for the
** functions in this file only
*/
enum {
kindLut=0,
kindRmap=1,
kindImap=2
};
double
53 _nrrdApplyDomainMin( const Nrrd *nmap, int ramps, int mapAxis ) {
double ret;
AIR_UNUSED( ramps );
ret = nmap->axis[mapAxis].min;
ret = AIR_EXISTS( ret ) ? ret : 0;
return ret;
}
double
63 _nrrdApplyDomainMax( const Nrrd *nmap, int ramps, int mapAxis ) {
double ret;
ret = nmap->axis[mapAxis].max;
if ( !AIR_EXISTS( ret ) ) {
ret = AIR_CAST( double, nmap->axis[mapAxis].size );
ret = ramps ? ret-1 : ret;
}
return ret;
}
/*
** _nrrdApply1DSetUp( )
**
** some error checking and initializing needed for 1D LUTS, regular,
** and irregular maps. The intent is that if this succeeds, then
** there is no need for any further error checking.
**
** The only thing this function DOES is allocate the output nrrd, and
** set meta information. The rest is just error checking.
**
** The given NrrdRange has to be fleshed out by the caller: it can't
** be NULL, and both range->min and range->max must exist.
*/
int
88 _nrrdApply1DSetUp( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
const Nrrd *nmap, int kind, int typeOut,
int rescale, int multi ) {
static const char me[]="_nrrdApply1DSetUp";
char *mapcnt;
char nounStr[][AIR_STRLEN_SMALL]={"lut",
"regular map",
"irregular map"};
char mnounStr[][AIR_STRLEN_SMALL]={"multi lut",
"multi regular map",
"multi irregular map"};
/* wishful thinking */
char verbStr[][AIR_STRLEN_SMALL]={"lut",
"rmap",
"imap"};
char mverbStr[][AIR_STRLEN_SMALL]={"mlut",
"mrmap",
"mimap"}; /* wishful thinking */
int mapAxis, copyMapAxis0=AIR_FALSE, axisMap[NRRD_DIM_MAX];
unsigned int ax, dim, entLen;
size_t size[NRRD_DIM_MAX];
double domMin, domMax;
if ( nout == nin ) {
biffAddf( NRRD, "%s: due to laziness, nout==nin always disallowed", me );
return 1;
}
if ( airEnumValCheck( nrrdType, typeOut ) ) {
biffAddf( NRRD, "%s: invalid requested output type %d", me, typeOut );
return 1;
}
if ( nrrdTypeBlock == nin->type || nrrdTypeBlock == typeOut ) {
biffAddf( NRRD, "%s: input or requested output type is %s, need scalar",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( rescale ) {
if ( !range ) {
biffAddf( NRRD, "%s: want rescaling but didn't get a range", me );
return 1;
}
if ( !( AIR_EXISTS( range->min ) && AIR_EXISTS( range->max ) ) ) {
biffAddf( NRRD, "%s: want rescaling but not both "
"range->{min, max} %g %g exist", me, range->min, range->max );
return 1;
}
/* HEY: consider adding an error check for range->min == range->max
here; the code below now guards
AIR_AFFINE( range->min, val, range->max, domMin, domMax )
against producing NaNs, but maybe that's too clever */
}
if ( kindLut == kind || kindRmap == kind ) {
if ( !multi ) {
mapAxis = nmap->dim - 1;
if ( !( 0 == mapAxis || 1 == mapAxis ) ) {
biffAddf( NRRD, "%s: dimension of %s should be 1 or 2, not %d",
me, nounStr[kind], nmap->dim );
return 1;
}
copyMapAxis0 = ( 1 == mapAxis );
} else {
mapAxis = nmap->dim - nin->dim - 1;
if ( !( 0 == mapAxis || 1 == mapAxis ) ) {
biffAddf( NRRD, "%s: dimension of %s should be %d or %d, not %d",
me, mnounStr[kind],
nin->dim + 1, nin->dim + 2, nmap->dim );
return 1;
}
copyMapAxis0 = ( 1 == mapAxis );
/* need to make sure the relevant sizes match */
for ( ax=0; ax<nin->dim; ax++ ) {
unsigned int taxi = mapAxis + 1 + ax;
if ( taxi > NRRD_DIM_MAX-1 ) {
biffAddf( NRRD, "%s: test axis index %u exceeds NRRD_DIM_MAX-1 %u",
me, taxi, NRRD_DIM_MAX-1 );
return 1;
}
if ( nin->axis[ax].size != nmap->axis[taxi].size ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: input and mmap don't have compatible sizes: "
"nin->axis[%d].size ( %s ) "
"!= nmap->axis[%d].size ( %s ): ", me,
ax, airSprintSize_t( stmp1, nin->axis[ax].size ),
mapAxis + 1 + ax,
airSprintSize_t( stmp2, nmap->axis[taxi].size ) );
return 1;
}
}
}
domMin = _nrrdApplyDomainMin( nmap, AIR_FALSE, mapAxis );
domMax = _nrrdApplyDomainMax( nmap, AIR_FALSE, mapAxis );
if ( !( domMin < domMax ) ) {
biffAddf( NRRD, "%s: ( axis %d ) domain min ( %g ) not less than max ( %g )",
me, mapAxis, domMin, domMax );
return 1;
}
if ( nrrdHasNonExist( nmap ) ) {
biffAddf( NRRD, "%s: %s nrrd has non-existent values",
me, multi ? mnounStr[kind] : nounStr[kind] );
return 1;
}
entLen = mapAxis ? AIR_CAST( unsigned int, nmap->axis[0].size ) : 1;
} else {
if ( multi ) {
biffAddf( NRRD, "%s: sorry, multi irregular maps not implemented", me );
return 1;
}
/* its an irregular map */
if ( nrrd1DIrregMapCheck( nmap ) ) {
biffAddf( NRRD, "%s: problem with irregular map", me );
return 1;
}
/* mapAxis has no meaning for irregular maps, but we'll pretend ... */
mapAxis = nmap->axis[0].size == 2 ? 0 : 1;
copyMapAxis0 = AIR_TRUE;
entLen = AIR_CAST( unsigned int, nmap->axis[0].size-1 );
}
if ( mapAxis + nin->dim > NRRD_DIM_MAX ) {
biffAddf( NRRD, "%s: input nrrd dim %d through non-scalar %s exceeds "
"NRRD_DIM_MAX %d",
me, nin->dim,
multi ? mnounStr[kind] : nounStr[kind], NRRD_DIM_MAX );
return 1;
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size+mapAxis );
if ( mapAxis ) {
size[0] = entLen;
axisMap[0] = -1;
}
for ( dim=0; dim<nin->dim; dim++ ) {
axisMap[dim+mapAxis] = dim;
}
/*
fprintf( stderr, "##%s: pre maybe alloc: nout->data = %p\n", me, nout->data );
for ( dim=0; dim<mapAxis + nin->dim; dim++ ) {
fprintf( stderr, " size[%d] = %d\n", d, ( int )size[d] );
}
fprintf( stderr, " nout->dim = %d; nout->type = %d = %s; sizes = %d, %d\n",
nout->dim, nout->type,
airEnumStr( nrrdType, nout->type ) );
fprintf( stderr, " typeOut = %d = %s\n", typeOut,
airEnumStr( nrrdType, typeOut ) );
*/
if ( nrrdMaybeAlloc_nva( nout, typeOut, mapAxis + nin->dim, size ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
/*
fprintf( stderr, " nout->dim = %d; nout->type = %d = %s\n",
nout->dim, nout->type,
airEnumStr( nrrdType, nout->type ),
nout->axis[0].size, nout->axis[1].size );
for ( d=0; d<nout->dim; d++ ) {
fprintf( stderr, " size[%d] = %d\n", d, ( int )nout->axis[d].size );
}
fprintf( stderr, "##%s: post maybe alloc: nout->data = %p\n", me, nout->data );
*/
if ( nrrdAxisInfoCopy( nout, nin, axisMap, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s: trouble copying axis info", me );
return 1;
}
if ( copyMapAxis0 ) {
_nrrdAxisInfoCopy( nout->axis + 0, nmap->axis + 0,
NRRD_AXIS_INFO_SIZE_BIT );
}
mapcnt = _nrrdContentGet( nmap );
if ( nrrdContentSet_va( nout, multi ? mverbStr[kind] : verbStr[kind],
nin, "%s", mapcnt ) ) {
biffAddf( NRRD, "%s:", me );
free( mapcnt ); return 1;
}
free( mapcnt );
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
** _nrrdApply1DLutOrRegMap( )
**
** the guts of nrrdApply1DLut and nrrdApply1DRegMap
**
** yikes, does NOT use biff, since we're only supposed to be called
** after copious error checking.
**
** FOR INSTANCE, this allows nout == nin, which could be a big
** problem if mapAxis == 1.
**
** we don't need a typeOut arg because nout has already been allocated
** as some specific type; we'll look at that.
**
** NOTE: non-existent values get passed through regular maps and luts
** "unchanged". However, if the output type is integral, the results
** are probaby undefined. HEY: there is currently no warning message
** or error handling based on nrrdStateDisallowIntegerNonExist, but
** there really should be.
*/
int
297 _nrrdApply1DLutOrRegMap( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
const Nrrd *nmap, int ramps, int rescale, int multi ) {
/* static const char me[]="_nrrdApply1DLutOrRegMap"; */
char *inData, *outData, *mapData, *entData0, *entData1;
size_t N, I;
double ( *inLoad )( const void *v ), ( *mapLup )( const void *v, size_t I ),
( *outInsert )( void *v, size_t I, double d ),
val, mapIdxFrac, domMin, domMax;
unsigned int i, mapAxis, mapLen, mapIdx, entSize, entLen, inSize, outSize;
if ( !multi ) {
mapAxis = nmap->dim - 1; /* axis of nmap containing entries */
} else {
mapAxis = nmap->dim - nin->dim - 1;
}
mapData = ( char * )nmap->data; /* map data, as char* */
/* low end of map domain */
domMin = _nrrdApplyDomainMin( nmap, ramps, mapAxis );
/* high end of map domain */
domMax = _nrrdApplyDomainMax( nmap, ramps, mapAxis );
mapLen = AIR_CAST( unsigned int, nmap->axis[mapAxis].size ); /* number of entries in map */
mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */
inData = ( char * )nin->data; /* input data, as char* */
inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */
inSize = AIR_CAST( unsigned int, nrrdElementSize( nin ) ); /* size of one input value */
outData = ( char * )nout->data; /* output data, as char* */
outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */
entLen = ( mapAxis /* number of elements in one entry */
? AIR_CAST( unsigned int, nmap->axis[0].size )
: 1 );
outSize = entLen*AIR_CAST( unsigned int, nrrdElementSize( nout ) ); /* size of entry in output */
entSize = entLen*AIR_CAST( unsigned int, nrrdElementSize( nmap ) ); /* size of entry in map */
N = nrrdElementNumber( nin ); /* the number of values to be mapped */
if ( ramps ) {
/* regular map */
for ( I=0; I<N; I++ ) {
/* ...
if ( !( I % 100 ) ) fprintf( stderr, "I = %d\n", ( int )I );
... */
val = inLoad( inData );
/* ...
fprintf( stderr, "##%s: val = \na% 31.15f --> ", me, val );
... */
if ( rescale ) {
val = ( range->min != range->max
? AIR_AFFINE( range->min, val, range->max, domMin, domMax )
: domMin );
/* ...
fprintf( stderr, "\nb% 31.15f ( min, max = %g, %g )--> ", val,
range->min, range->max );
... */
}
/* ...
fprintf( stderr, "\nc% 31.15f --> clamp( %g, %g ), %d --> ",
val, domMin, domMax, mapLen );
... */
if ( AIR_EXISTS( val ) ) {
val = AIR_CLAMP( domMin, val, domMax );
mapIdxFrac = AIR_AFFINE( domMin, val, domMax, 0, mapLen-1 );
/* ...
fprintf( stderr, "mapIdxFrac = \nd% 31.15f --> ",
mapIdxFrac );
... */
mapIdx = ( unsigned int )mapIdxFrac;
mapIdx -= mapIdx == mapLen-1;
mapIdxFrac -= mapIdx;
/* ...
fprintf( stderr, "%s: ( %d, \ne% 31.15f ) --> \n",
me, mapIdx, mapIdxFrac );
... */
entData0 = mapData + mapIdx*entSize;
entData1 = mapData + ( mapIdx+1 )*entSize;
for ( i=0; i<entLen; i++ ) {
val = ( ( 1-mapIdxFrac )*mapLup( entData0, i ) +
mapIdxFrac*mapLup( entData1, i ) );
outInsert( outData, i, val );
/* ...
fprintf( stderr, "f% 31.15f\n", val );
... */
}
} else {
/* copy non-existent values from input to output */
for ( i=0; i<entLen; i++ ) {
outInsert( outData, i, val );
}
}
inData += inSize;
outData += outSize;
if ( multi ) {
mapData += mapLen*entSize;
}
}
} else {
/* lookup table */
for ( I=0; I<N; I++ ) {
val = inLoad( inData );
if ( rescale ) {
val = ( range->min != range->max
? AIR_AFFINE( range->min, val, range->max, domMin, domMax )
: domMin );
}
if ( AIR_EXISTS( val ) ) {
mapIdx = airIndexClamp( domMin, val, domMax, mapLen );
entData0 = mapData + mapIdx*entSize;
for ( i=0; i<entLen; i++ ) {
outInsert( outData, i, mapLup( entData0, i ) );
}
} else {
/* copy non-existent values from input to output */
for ( i=0; i<entLen; i++ ) {
outInsert( outData, i, val );
}
}
inData += inSize;
outData += outSize;
if ( multi ) {
mapData += mapLen*entSize;
}
}
}
return 0;
}
/*
******** nrrdApply1DLut
**
** A "lut" is a simple lookup table: the data points are evenly spaced,
** with cell-centering assumed, and there is no interpolation except
** nearest neighbor. The axis min and max are used to determine the
** range of values that can be mapped with the lut.
**
** Of the three kinds of 1-D maps, only luts can have output type block.
**
** If the lut nrrd is 1-D, then the output is a scalar nrrd with the
** same dimension as the input. If the lut nrrd is 2-D, then each
** value in the input is mapped to a vector of values from the lut,
** which is always a scanline along axis 0.
**
** This allows lut length to be simply 1.
*/
int
440 nrrdApply1DLut( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range, const Nrrd *nlut,
int typeOut, int rescale ) {
static const char me[]="nrrdApply1DLut";
NrrdRange *range;
airArray *mop;
if ( !( nout && nlut && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply1DSetUp( nout, nin, range, nlut, kindLut, typeOut,
rescale, AIR_FALSE /* multi */ )
|| _nrrdApply1DLutOrRegMap( nout, nin, range, nlut, AIR_FALSE /* ramps */,
rescale, AIR_FALSE /* multi */ ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
471 nrrdApplyMulti1DLut( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range, const Nrrd *nmlut,
int typeOut, int rescale ) {
static const char me[]="nrrdApplyMulti1DLut";
NrrdRange *range;
airArray *mop;
if ( !( nout && nmlut && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply1DSetUp( nout, nin, range, nmlut, kindLut, typeOut,
rescale, AIR_TRUE /* multi */ )
|| _nrrdApply1DLutOrRegMap( nout, nin, range, nmlut,
AIR_FALSE /* ramps */,
rescale, AIR_TRUE /* multi */ ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdApply1DRegMap
**
** A regular map has data points evenly spaced, with node-centering and
** and linear interpolation assumed. As with luts, the axis min and
** max determine the range of values that are mapped. This function
** is used in nrrdHistoEq( ) and is the basis of the very popular "unu rmap".
**
** If the lut nrrd is 1-D, then the output is a scalar nrrd with the
** same dimension as the input. If the lut nrrd is 2-D, then each
** value in the input is mapped to a linear weighting of vectors
** from the map; the vectors are the scanlines along axis 0.
**
** NB: this function makes NO provisions for non-existent input values.
** There won't be any memory errors, but the results are undefined.
**
** This allows map length to be simply 1.
*/
int
521 nrrdApply1DRegMap( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range, const Nrrd *nmap,
int typeOut, int rescale ) {
static const char me[]="nrrdApply1DRegMap";
NrrdRange *range;
airArray *mop;
if ( !( nout && nmap && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply1DSetUp( nout, nin, range, nmap, kindRmap, typeOut,
rescale, AIR_FALSE /* multi */ )
|| _nrrdApply1DLutOrRegMap( nout, nin, range, nmap, AIR_TRUE /* ramps */,
rescale, AIR_FALSE /* multi */ ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
552 nrrdApplyMulti1DRegMap( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range, const Nrrd *nmmap,
int typeOut, int rescale ) {
static const char me[]="nrrdApplyMulti1DRegMap";
NrrdRange *range;
airArray *mop;
if ( !( nout && nmmap && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply1DSetUp( nout, nin, range, nmmap, kindRmap, typeOut,
rescale, AIR_TRUE /* multi */ )
|| _nrrdApply1DLutOrRegMap( nout, nin, range, nmmap, AIR_TRUE /* ramps */,
rescale, AIR_TRUE /* multi */ ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** nrrd1DIrregMapCheck( )
**
** return zero only for the valid forms of 1D irregular map.
** imap must be 2D, both sizes >= 2, non-block-type, no non-existent
** values in range. If the first point's position is non-existent,
** than the first three points positions must be -inf, NaN, and +inf,
** and none of the other points locations can be non-existent, and
** they must increase monotonically. There must be at least two
** points with existent positions.
*/
int
594 nrrd1DIrregMapCheck( const Nrrd *nmap ) {
static const char me[]="nrrd1DIrregMapCheck";
double ( *mapLup )( const void *v, size_t I );
int i, entLen, mapLen, baseI;
size_t min[2], max[2];
Nrrd *nrange;
if ( !nmap ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nmap ) ) {
biffAddf( NRRD, "%s: ", me );
return 1;
}
if ( nrrdTypeBlock == nmap->type ) {
biffAddf( NRRD, "%s: map is %s type, need scalar",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( 2 != nmap->dim ) {
biffAddf( NRRD, "%s: map needs to have dimension 2, not %d",
me, nmap->dim );
return 1;
}
entLen = AIR_CAST( unsigned int, nmap->axis[0].size );
mapLen = AIR_CAST( unsigned int, nmap->axis[1].size );
if ( !( entLen >= 2 && mapLen >= 2 ) ) {
biffAddf( NRRD, "%s: both map's axes sizes should be >= 2 ( not %d, %d )",
me, entLen, mapLen );
return 1;
}
min[0] = 1; max[0] = nmap->axis[0].size-1;
min[1] = 0; max[1] = nmap->axis[1].size-1;
if ( nrrdCrop( nrange=nrrdNew( ), nmap, min, max ) ) {
biffAddf( NRRD, "%s: couldn't crop to isolate range of map", me );
nrrdNuke( nrange ); return 1;
}
if ( nrrdHasNonExist( nrange ) ) {
biffAddf( NRRD, "%s: map has non-existent values in its range", me );
nrrdNuke( nrange ); return 1;
}
nrrdNuke( nrange );
mapLup = nrrdDLookup[nmap->type];
if ( AIR_EXISTS( mapLup( nmap->data, 0 ) ) ) {
baseI = 0;
} else {
baseI = 3;
if ( !( mapLen >= 5 ) ) {
biffAddf( NRRD, "%s: length of map w/ non-existent locations must "
"be >= 5 ( not %d )", me, mapLen );
return 1;
}
if ( !( airFP_NEG_INF == airFPClass_d( mapLup( nmap->data, 0*entLen ) ) &&
airFP_QNAN == airFPClass_d( mapLup( nmap->data, 1*entLen ) ) &&
airFP_POS_INF == airFPClass_d( mapLup( nmap->data, 2*entLen ) ) ) ) {
biffAddf( NRRD, "%s: 1st entry's position non-existent, but position "
"of 1st three entries ( %g:%d, %g:%d, %g:%d ) not "
"-inf, NaN, and +inf", me,
mapLup( nmap->data, 0*entLen ),
airFPClass_d( mapLup( nmap->data, 0*entLen ) ),
mapLup( nmap->data, 1*entLen ),
airFPClass_d( mapLup( nmap->data, 1*entLen ) ),
mapLup( nmap->data, 2*entLen ),
airFPClass_d( mapLup( nmap->data, 2*entLen ) ) );
return 1;
}
}
for ( i=baseI; i<mapLen; i++ ) {
if ( !AIR_EXISTS( mapLup( nmap->data, i*entLen ) ) ) {
biffAddf( NRRD, "%s: entry %d has non-existent position", me, i );
return 1;
}
}
for ( i=baseI; i<mapLen-1; i++ ) {
if ( !( mapLup( nmap->data, i*entLen ) < mapLup( nmap->data, ( i+1 )*entLen ) ) ) {
biffAddf( NRRD, "%s: map entry %d pos ( %g ) not < entry %d pos ( %g )",
me, i, mapLup( nmap->data, i*entLen ),
i+1, mapLup( nmap->data, ( i+1 )*entLen ) );
return 1;
}
}
return 0;
}
/*
******** nrrd1DIrregAclCheck( )
**
** returns zero only on valid accelerators for 1D irregular mappings
*/
int
685 nrrd1DIrregAclCheck( const Nrrd *nacl ) {
static const char me[]="nrrd1DIrregAclCheck";
if ( !nacl ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nacl ) ) {
biffAddf( NRRD, "%s: ", me );
return 1;
}
if ( nrrdTypeUShort != nacl->type ) {
biffAddf( NRRD, "%s: type should be %s, not %s", me,
airEnumStr( nrrdType, nrrdTypeUShort ),
airEnumStr( nrrdType, nacl->type ) );
return 1;
}
if ( 2 != nacl->dim ) {
biffAddf( NRRD, "%s: dimension should be 2, not %d", me, nacl->dim );
return 1;
}
if ( !( nacl->axis[0].size == 2 && nacl->axis[1].size >= 2 ) ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: sizes ( %s, %s ) not ( 2, >=2 )", me,
airSprintSize_t( stmp1, nacl->axis[0].size ),
airSprintSize_t( stmp2, nacl->axis[1].size ) );
return 1;
}
return 0;
}
/*
** _nrrd1DIrregMapDomain( )
**
** ALLOCATES an array of doubles storing the existent control point
** locations, and sets its length in *poslenP. If there are the three
** points with non-existent locations, these are ignored.
**
** Assumes that nrrd1DIrregMapCheck has been called on "nmap".
*/
double *
727 _nrrd1DIrregMapDomain( int *posLenP, int *baseIP, const Nrrd *nmap ) {
static const char me[]="_nrrd1DIrregMapDomain";
int i, entLen, baseI, posLen;
double *pos, ( *mapLup )( const void *v, size_t I );
mapLup = nrrdDLookup[nmap->type];
baseI = AIR_EXISTS( mapLup( nmap->data, 0 ) ) ? 0 : 3;
if ( baseIP ) {
*baseIP = baseI;
}
entLen = AIR_CAST( unsigned int, nmap->axis[0].size );
posLen = AIR_CAST( unsigned int, nmap->axis[1].size ) - baseI;
if ( posLenP ) {
*posLenP = posLen;
}
pos = ( double* )malloc( posLen * sizeof( double ) );
if ( !pos ) {
biffAddf( NRRD, "%s: couldn't allocate %d doubles\n", me, posLen );
return NULL;
}
for ( i=0; i<posLen; i++ ) {
pos[i] = mapLup( nmap->data, ( baseI+i )*entLen );
}
return pos;
}
/*
** _nrrd1DIrregFindInterval( )
**
** The hard part of doing 1D irregular mapping: given an array of
** control point locations, and a value, find which interval the value
** lies in. The lowest and highest possible indices are given in
** "loI" and "hiI". Results are undefined if these do not in fact
** bound the location of correct interval, or if loI > hiI, or if the
** query positon "p" is not in the domain vector "pos". Intervals are
** identified by the integral index of the LOWER of the two control
** points spanning the interval.
**
** This imposes the same structure of half-open intervals that
** is done by airIndex( ). That is, a value p is in interval i
** if pos[i] <= p < pos[i+1] for all but the last interval, and
** pos[i] <= p <= pos[i+1] for the last interval ( in which case
** i == hiI )
*/
int
772 _nrrd1DIrregFindInterval( const double *pos, double p, int loI, int hiI ) {
int midI;
/*
fprintf( stderr, "##%s( %g ): hi: %d/%g-%g | %d/%g-%g\n",
"_nrrd1DIrregFindInterval", p,
loI, pos[loI], pos[loI+1],
hiI, pos[hiI], pos[hiI+1] );
*/
while ( loI < hiI ) {
midI = ( loI + hiI )/2;
if ( pos[midI] <= p && ( ( midI < hiI && p < pos[midI+1] ) ||
( midI == hiI && p <= pos[midI+1] ) ) ) {
/* p is between ( pos[midI], pos[midI+1] ): we're done */
loI = hiI = midI;
} else if ( pos[midI] > p ) {
/* p is below interval midI: midI-1 is valid upper bound */
hiI = midI-1;
} else {
/* p is above interval midI: midI+1 is valid lower bound */
loI = midI+1;
}
/*
fprintf( stderr, "##%s( %g ): %d/%g-%g | %d/%g-%g | %d/%g-%g\n",
"_nrrd1DIrregFindInterval", p,
loI, pos[loI], pos[loI+1],
midI, pos[midI], pos[midI+1],
hiI, pos[hiI], pos[hiI+1] );
*/
}
return loI;
}
/*
******** nrrd1DIrregAclGenerate( )
**
** Generates the "acl" that is used to speed up the action of
** nrrdApply1DIrregMap( ). Basically, the domain of the map
** is quantized into "acllen" bins, and for each bin, the
** lowest and highest possible map interval is stored. This
** either obviates or speeds up the task of finding which
** interval contains a given value.
**
** Assumes that nrrd1DIrregMapCheck has been called on "nmap".
*/
int
818 nrrd1DIrregAclGenerate( Nrrd *nacl, const Nrrd *nmap, size_t aclLen ) {
static const char me[]="nrrd1DIrregAclGenerate";
int posLen;
unsigned int ii;
unsigned short *acl;
double lo, hi, min, max, *pos;
if ( !( nacl && nmap ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( aclLen >= 2 ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: given acl length ( %s ) is too small", me,
airSprintSize_t( stmp, aclLen ) );
return 1;
}
if ( nrrdMaybeAlloc_va( nacl, nrrdTypeUShort, 2,
AIR_CAST( size_t, 2 ), AIR_CAST( size_t, aclLen ) ) ) {
biffAddf( NRRD, "%s: ", me );
return 1;
}
acl = ( unsigned short * )nacl->data;
pos = _nrrd1DIrregMapDomain( &posLen, NULL, nmap );
if ( !pos ) {
biffAddf( NRRD, "%s: couldn't determine domain", me );
return 1;
}
nacl->axis[1].min = min = pos[0];
nacl->axis[1].max = max = pos[posLen-1];
for ( ii=0; ii<=aclLen-1; ii++ ) {
lo = AIR_AFFINE( 0, ii, aclLen, min, max );
hi = AIR_AFFINE( 0, ii+1, aclLen, min, max );
acl[0 + 2*ii] = _nrrd1DIrregFindInterval( pos, lo, 0, posLen-2 );
acl[1 + 2*ii] = _nrrd1DIrregFindInterval( pos, hi, 0, posLen-2 );
}
free( pos );
return 0;
}
/*
******** nrrdApply1DIrregMap( )
**
** Linear interpolation between irregularly spaced control points.
** Obviously, the location of the control point has to be given
** explicitly. The map nrrd must have dimension 2, and each
** control point is represented by a scanline along axis 0. The
** first value is the position of the control point, and the remaining
** value( s ) are linearly weighted according to the position of the
** input value among the control point locations.
**
** To allow "coloring" of non-existent values -inf, NaN, and +inf, if
** the very first value of the map ( the location of the first control
** point ) is non-existent, then the first three control point locations
** must be -inf, NaN, and +inf, in that order, and the information
** about these points will be used for corresponding input values.
** Doing this makes everything slower, however, because airFPClass_f( )
** is called on every single value.
**
** This assumes that nrrd1DIrregMapCheck has been called on "nmap",
** and that nrrd1DIrregAclCheck has been called on "nacl" ( if it is
** non-NULL ).
*/
int
883 nrrdApply1DIrregMap( Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
const Nrrd *nmap, const Nrrd *nacl,
int typeOut, int rescale ) {
static const char me[]="nrrdApply1DIrregMap";
size_t N, I;
int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx,
entSize, colSize, inSize, lo, hi, baseI;
double val, *pos, domMin, domMax, mapIdxFrac,
( *mapLup )( const void *v, size_t I ),
( *inLoad )( const void *v ), ( *outInsert )( void *v, size_t I, double d );
char *inData, *outData, *entData0, *entData1;
NrrdRange *range;
airArray *mop;
/*
fprintf( stderr, "!%s: rescale = %d\n", me, rescale );
*/
if ( !( nout && nmap && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply1DSetUp( nout, nin, range, nmap,
kindImap, typeOut, rescale, AIR_FALSE /* multi */ ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nacl && nrrd1DIrregAclCheck( nacl ) ) {
biffAddf( NRRD, "%s: given acl isn't valid", me );
airMopError( mop ); return 1;
}
if ( nacl ) {
acl = ( int * )nacl->data;
aclLen = AIR_CAST( unsigned int, nacl->axis[1].size );
} else {
acl = NULL;
aclLen = 0;
}
pos = _nrrd1DIrregMapDomain( &posLen, &baseI, nmap );
if ( !pos ) {
biffAddf( NRRD, "%s: couldn't determine domain", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, pos, airFree, airMopAlways );
mapLup = nrrdDLookup[nmap->type];
inData = ( char * )nin->data;
inLoad = nrrdDLoad[nin->type];
inSize = AIR_CAST( unsigned int, nrrdElementSize( nin ) );
mapLup = nrrdDLookup[nmap->type];
entLen = AIR_CAST( unsigned int, nmap->axis[0].size ); /* entLen is really 1 + entry length */
entSize = entLen*AIR_CAST( unsigned int, nrrdElementSize( nmap ) );
colSize = ( entLen-1 )*AIR_CAST( unsigned int, nrrdTypeSize[typeOut] );
outData = ( char * )nout->data;
outInsert = nrrdDInsert[nout->type];
domMin = pos[0];
domMax = pos[posLen-1];
/*
fprintf( stderr, "!%s: domMin, domMax = %g, %g\n", me, domMin, domMax );
*/
N = nrrdElementNumber( nin );
for ( I=0;
I<N;
I++, inData += inSize, outData += colSize ) {
val = inLoad( inData );
/*
fprintf( stderr, "!%s: ( %d ) val = % 31.15f\n", me, ( int )I, val );
*/
if ( !AIR_EXISTS( val ) ) {
/* got a non-existent value */
if ( baseI ) {
/* and we know how to deal with them */
switch ( airFPClass_d( val ) ) {
case airFP_NEG_INF:
mapIdx = 0;
break;
case airFP_SNAN:
case airFP_QNAN:
mapIdx = 1;
break;
case airFP_POS_INF:
mapIdx = 2;
break;
default:
mapIdx = 0;
fprintf( stderr, "%s: PANIC: non-existent value/class %g/%d "
"not handled\n",
me, val, airFPClass_d( val ) );
exit( 1 );
}
entData0 = ( char* )( nmap->data ) + mapIdx*entSize;
for ( i=1; i<entLen; i++ ) {
outInsert( outData, i-1, mapLup( entData0, i ) );
}
continue; /* we're done! ( with this value ) */
} else {
/* we don't know how to properly deal with this non-existent value:
we use the first entry, and then fall through to code below */
mapIdx = 0;
mapIdxFrac = 0.0;
}
} else {
/* we have an existent value */
if ( rescale ) {
val = ( range->min != range->max
? AIR_AFFINE( range->min, val, range->max, domMin, domMax )
: domMin );
}
val = AIR_CLAMP( domMin, val, domMax );
if ( acl ) {
aclIdx = airIndex( domMin, val, domMax, aclLen );
lo = acl[0 + 2*aclIdx];
hi = acl[1 + 2*aclIdx];
} else {
lo = 0;
hi = posLen-2;
}
if ( lo < hi ) {
mapIdx = _nrrd1DIrregFindInterval( pos, val, lo, hi );
} else {
/* acl did its job ==> lo == hi */
mapIdx = lo;
}
/*
fprintf( stderr, "!%s: --> val = %g; lo, hi = %d, %d, mapIdx = %d\n",
me, val, lo, hi, mapIdx );
*/
}
mapIdxFrac = AIR_AFFINE( pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0 );
/*
fprintf( stderr, "!%s: mapIdxFrac = %g\n", me, mapIdxFrac );
*/
entData0 = ( char* )( nmap->data ) + ( baseI+mapIdx )*entSize;
entData1 = ( char* )( nmap->data ) + ( baseI+mapIdx+1 )*entSize;
for ( i=1; i<entLen; i++ ) {
val = ( ( 1-mapIdxFrac )*mapLup( entData0, i ) +
mapIdxFrac*mapLup( entData1, i ) );
/*
fprintf( stderr, "!%s: %g * %g + %g * %g = %g\n", me,
1-mapIdxFrac, mapLup( entData0, i ),
mapIdxFrac, mapLup( entData1, i ), val );
*/
outInsert( outData, i-1, val );
}
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdApply1DSubstitution
**
** A "subst" is a substitution table, i.e. a list of pairs that
** describes what values should be substituted with what ( substitution
** rules ). So, nsubst must be a scalar 2xN array. The output is a
** copy of the input with values substituted using this table.
**
** Unlike with lookup tables and maps ( irregular and regular ), we
** aren't at liberty to change the dimensionality of the data ( can't
** substitute a grayscale with a color ). The ability to apply
** substitutions to non-scalar data will be in a different function.
** Also unlike lookup tables and maps, the output type is the SAME as
** the input type; the output does NOT inherit the type of the
** substitution
*/
int
1058 nrrdApply1DSubstitution( Nrrd *nout, const Nrrd *nin, const Nrrd *_nsubst ) {
static const char me[]="nrrdApply1DSubstitution";
double ( *lup )( const void *, size_t );
double ( *ins )( void *, size_t, double );
Nrrd *nsubst;
double val, *subst;
size_t ii, jj, num, asize0, asize1;
int changed;
airArray *mop;
if ( !( nout && _nsubst && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type || nrrdTypeBlock == _nsubst->type ) {
biffAddf( NRRD, "%s: input or substitution type is %s, need scalar",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( 2 != _nsubst->dim ) {
biffAddf( NRRD, "%s: substitution table has to be 2-D, not %d-D",
me, _nsubst->dim );
return 1;
}
/* nrrdAxisInfoGet_va( _nsubst, nrrdAxisInfoSize, &asize0, &asize1 ); */
asize0 = _nsubst->axis[0].size;
asize1 = _nsubst->axis[1].size;
if ( 2 != asize0 ) {
biffAddf( NRRD, "%s: substitution table has to be 2xN, not %uxN",
me, AIR_CAST( unsigned int, asize0 ) );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: couldn't initialize by copy to output", me );
return 1;
}
}
mop = airMopNew( );
nsubst = nrrdNew( );
airMopAdd( mop, nsubst, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nsubst, _nsubst, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: couldn't create double copy of substitution table",
me );
airMopError( mop ); return 1;
}
lup = nrrdDLookup[nout->type];
ins = nrrdDInsert[nout->type];
subst = ( double * )nsubst->data;
num = nrrdElementNumber( nout );
for ( ii=0; ii<num; ii++ ) {
val = lup( nout->data, ii );
changed = AIR_FALSE;
for ( jj=0; jj<asize1; jj++ ) {
if ( val == subst[jj*2+0] ) {
val = subst[jj*2+1];
changed = AIR_TRUE;
}
}
if ( changed ) {
ins( nout->data, ii, val );
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
enum {
kindLut=0,
kindRmap=1
};
/*
** _nrrdApply2DSetUp( )
**
** some error checking and initializing needed for 2D LUTS and regular
** maps. The intent is that if this succeeds, then there is no need
** for any further error checking.
**
** The only thing this function DOES is allocate the output nrrd, and
** set meta information. The rest is just error checking.
**
** The given NrrdRange has to be fleshed out by the caller: it can't
** be NULL, and both range->min and range->max must exist.
*/
int
46 _nrrdApply2DSetUp( Nrrd *nout, const Nrrd *nin,
const NrrdRange *range0, const NrrdRange *range1,
const Nrrd *nmap, int kind, int typeOut,
int rescale0, int rescale1 ) {
static const char me[]="_nrrdApply2DSetUp";
char *mapcnt;
char nounStr[][AIR_STRLEN_SMALL]={"2D lut",
"2D regular map"};
char verbStr[][AIR_STRLEN_SMALL]={"lut2",
"rmap2"};
int mapAxis, copyMapAxis0=AIR_FALSE, axisMap[NRRD_DIM_MAX];
unsigned int dim, entLen;
size_t size[NRRD_DIM_MAX];
double domMin, domMax;
if ( nout == nin ) {
biffAddf( NRRD, "%s: due to laziness, nout==nin always disallowed", me );
return 1;
}
if ( airEnumValCheck( nrrdType, typeOut ) ) {
biffAddf( NRRD, "%s: invalid requested output type %d", me, typeOut );
return 1;
}
if ( nrrdTypeBlock == nin->type || nrrdTypeBlock == typeOut ) {
biffAddf( NRRD, "%s: input or requested output type is %s, need scalar",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( 2 == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: input axis[0] must have size 2 ( not %s )", me,
airSprintSize_t( stmp, nin->axis[0].size ) );
return 1;
}
if ( !( nin->dim > 1 ) ) {
biffAddf( NRRD, "%s: input dimension must be > 1 ( not %u )", me, nin->dim );
return 1;
}
if ( rescale0 && !( range0
&& AIR_EXISTS( range0->min )
&& AIR_EXISTS( range0->max ) ) ) {
biffAddf( NRRD, "%s: want axis 0 rescaling but didn't get range, or "
"not both range->{min, max} exist", me );
return 1;
}
if ( rescale1 && !( range1
&& AIR_EXISTS( range1->min )
&& AIR_EXISTS( range1->max ) ) ) {
biffAddf( NRRD, "%s: want axis 1 rescaling but didn't get range, or "
"not both range->{min, max} exist", me );
return 1;
}
mapAxis = nmap->dim - 2;
if ( !( 0 == mapAxis || 1 == mapAxis ) ) {
biffAddf( NRRD, "%s: dimension of %s should be 2 or 3, not %d",
me, nounStr[kind], nmap->dim );
return 1;
}
copyMapAxis0 = ( 1 == mapAxis );
domMin = _nrrdApplyDomainMin( nmap, AIR_FALSE, mapAxis );
domMax = _nrrdApplyDomainMax( nmap, AIR_FALSE, mapAxis );
if ( !( domMin < domMax ) ) {
biffAddf( NRRD, "%s: ( axis %d ) domain min ( %g ) not less than max ( %g )", me,
mapAxis, domMin, domMax );
return 1;
}
domMin = _nrrdApplyDomainMin( nmap, AIR_FALSE, mapAxis+1 );
domMax = _nrrdApplyDomainMax( nmap, AIR_FALSE, mapAxis+1 );
if ( !( domMin < domMax ) ) {
biffAddf( NRRD, "%s: ( axis %d ) domain min ( %g ) not less than max ( %g )", me,
mapAxis+1, domMin, domMax );
return 1;
}
if ( nrrdHasNonExist( nmap ) ) {
biffAddf( NRRD, "%s: %s nrrd has non-existent values",
me, nounStr[kind] );
return 1;
}
entLen = mapAxis ? AIR_CAST( unsigned int, nmap->axis[0].size ) : 1;
if ( mapAxis + nin->dim - 1 > NRRD_DIM_MAX ) {
biffAddf( NRRD, "%s: input nrrd dim %d through non-scalar %s exceeds "
"NRRD_DIM_MAX %d",
me, nin->dim, nounStr[kind], NRRD_DIM_MAX );
return 1;
}
if ( mapAxis ) {
size[0] = entLen;
axisMap[0] = -1;
}
for ( dim=1; dim<nin->dim; dim++ ) {
size[dim-1+mapAxis] = nin->axis[dim].size;
axisMap[dim-1+mapAxis] = dim;
}
/*
fprintf( stderr, "##%s: pre maybe alloc: nout->data = %p\n", me, nout->data );
for ( dim=0; dim<mapAxis + nin->dim - 1; dim++ ) {
fprintf( stderr, " size[%d] = %d\n", dim, ( int )size[dim] );
}
fprintf( stderr, " nout->dim = %u; nout->type = %d = %s; sizes = %u, %u\n",
nout->dim, nout->type,
airEnumStr( nrrdType, nout->type ),
AIR_CAST( unsigned int, nout->axis[0].size ),
AIR_CAST( unsigned int, nout->axis[1].size ) );
fprintf( stderr, " typeOut = %d = %s\n", typeOut,
airEnumStr( nrrdType, typeOut ) );
*/
if ( nrrdMaybeAlloc_nva( nout, typeOut, nin->dim - 1 + mapAxis, size ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
/*
fprintf( stderr, " nout->dim = %d; nout->type = %d = %s\n",
nout->dim, nout->type,
airEnumStr( nrrdType, nout->type ) );
for ( dim=0; dim<nout->dim; dim++ ) {
fprintf( stderr, " size[%d] = %d\n", dim, ( int )nout->axis[dim].size );
}
fprintf( stderr, "##%s: post maybe alloc: nout->data = %p\n", me, nout->data );
*/
if ( nrrdAxisInfoCopy( nout, nin, axisMap, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s: trouble copying axis info", me );
return 1;
}
if ( copyMapAxis0 ) {
_nrrdAxisInfoCopy( nout->axis + 0, nmap->axis + 0,
NRRD_AXIS_INFO_SIZE_BIT );
}
mapcnt = _nrrdContentGet( nmap );
if ( nrrdContentSet_va( nout, verbStr[kind], nin, "%s", mapcnt ) ) {
biffAddf( NRRD, "%s:", me );
free( mapcnt ); return 1;
}
free( mapcnt );
nrrdBasicInfoInit( nout, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT ) );
return 0;
}
/*
** _nrrdApply2DLutOrRegMap( )
**
** the guts of nrrdApply2DLut and nrrdApply2DRegMap
**
** yikes, does NOT use biff, since we're only supposed to be called
** after copious error checking.
**
** FOR INSTANCE, this allows nout == nin, which could be a big
** problem if mapAxis == 1.
**
** we don't need a typeOut arg because nout has already been allocated
** as some specific type; we'll look at that.
**
** NOTE: non-existent values get passed through regular maps and luts
** "unchanged". However, if the output type is integral, the results
** are probaby undefined. HEY: there is currently no warning message
** or error handling based on nrrdStateDisallowIntegerNonExist, but
** there really should be.
*/
int
209 _nrrdApply2DLutOrRegMap( Nrrd *nout, const Nrrd *nin,
const NrrdRange *range0, const NrrdRange *range1,
const Nrrd *nmap, int ramps,
int rescale0, int rescale1 ) {
static const char me[]="_nrrdApply2DLutOrRegMap";
char *inData, *outData, *mapData, *entData;
size_t N, I;
double ( *inLoad )( const void *v ), ( *mapLup )( const void *v, size_t I ),
( *outInsert )( void *v, size_t I, double d ),
val0, val1, domMin0, domMax0, domMin1, domMax1;
unsigned int i, mapAxis, mapLen0, mapLen1, mapIdx0, mapIdx1,
entSize, entLen, inSize, outSize;
mapAxis = nmap->dim - 2; /* axis of nmap containing entries */
mapData = ( char * )nmap->data; /* map data, as char* */
/* low end of map domain */
domMin0 = _nrrdApplyDomainMin( nmap, ramps, mapAxis + 0 );
domMin1 = _nrrdApplyDomainMin( nmap, ramps, mapAxis + 1 );
/* high end of map domain */
domMax0 = _nrrdApplyDomainMax( nmap, ramps, mapAxis + 0 );
domMax1 = _nrrdApplyDomainMax( nmap, ramps, mapAxis + 1 );
mapLen0 = AIR_CAST( unsigned int, nmap->axis[mapAxis+0].size ); /* number of entries in map axis 0 */
mapLen1 = AIR_CAST( unsigned int, nmap->axis[mapAxis+1].size ); /* number of entries in map axis 1 */
mapLup = nrrdDLookup[nmap->type]; /* how to get doubles out of map */
inData = ( char * )nin->data; /* input data, as char* */
inLoad = nrrdDLoad[nin->type]; /* how to get doubles out of nin */
inSize = AIR_CAST( unsigned int, nrrdElementSize( nin ) ); /* size of one input value */
outData = ( char * )nout->data; /* output data, as char* */
outInsert = nrrdDInsert[nout->type]; /* putting doubles into output */
entLen = ( mapAxis /* number of elements in one entry */
? AIR_CAST( unsigned int, nmap->axis[0].size )
: 1 );
outSize = entLen*AIR_CAST( unsigned int, nrrdElementSize( nout ) ); /* size of entry in output */
entSize = entLen*AIR_CAST( unsigned int, nrrdElementSize( nmap ) ); /* size of entry in map */
/*
fprintf( stderr, "!%s: entLen = %u, mapLen = %u, %u\n", me,
entLen, mapLen0, mapLen1 );
*/
N = nrrdElementNumber( nin )/2; /* number of value pairs to be mapped */
/* _VV = 1; */
if ( ramps ) {
fprintf( stderr, "%s: PANIC: unimplemented\n", me );
exit( 1 );
} else {
/* lookup table */
for ( I=0; I<N; I++ ) {
val0 = inLoad( inData + 0*inSize );
val1 = inLoad( inData + 1*inSize );
if ( rescale0 ) {
val0 = AIR_AFFINE( range0->min, val0, range0->max, domMin0, domMax0 );
}
if ( rescale1 ) {
val1 = AIR_AFFINE( range1->min, val1, range1->max, domMin1, domMax1 );
}
if ( AIR_EXISTS( val0 ) && AIR_EXISTS( val1 ) ) {
mapIdx0 = airIndexClamp( domMin0, val0, domMax0, mapLen0 );
mapIdx1 = airIndexClamp( domMin1, val1, domMax1, mapLen1 );
entData = mapData + entSize*( mapIdx0 + mapLen0*mapIdx1 );
for ( i=0; i<entLen; i++ ) {
outInsert( outData, i, mapLup( entData, i ) );
}
} else {
/* copy non-existent values from input to output */
for ( i=0; i<entLen; i++ ) {
outInsert( outData, i, val0 + val1 ); /* HEY this is weird */
}
}
inData += 2*inSize;
outData += outSize;
}
}
return 0;
}
/*
******** nrrdApply2DLut
**
** A "lut" is a simple lookup table: the data points are evenly spaced,
** with cell-centering assumed, and there is no interpolation except
** nearest neighbor. The axis min and max are used to determine the
** range of values that can be mapped with the lut.
**
** Of the three kinds of 1-D maps, only luts can have output type block.
**
** If the lut nrrd is 1-D, then the output is a scalar nrrd with the
** same dimension as the input. If the lut nrrd is 2-D, then each
** value in the input is mapped to a vector of values from the lut,
** which is always a scanline along axis 0.
**
** This allows lut length to be simply 1.
*/
int
304 nrrdApply2DLut( Nrrd *nout, const Nrrd *nin, unsigned int domainAxis,
const NrrdRange *_range0, const NrrdRange *_range1,
const Nrrd *nlut,
int typeOut, int rescale0, int rescale1 ) {
static const char me[]="nrrdApply2DLut";
NrrdRange *range0, *range1;
airArray *mop;
Nrrd *nin0, *nin1;
if ( !( nout && nlut && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, %p )", me,
AIR_CAST( void*, nout ), AIR_CVOIDP( nlut ), AIR_CVOIDP( nin ) );
return 1;
}
if ( 0 != domainAxis ) {
biffAddf( NRRD, "%s: sorry, domainAxis must currently be 0 ( not %u )", me,
domainAxis );
return 1;
}
mop = airMopNew( );
if ( _range0 ) {
range0 = nrrdRangeCopy( _range0 );
airMopAdd( mop, nin0 = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( nin0, nin, 0, 0 ) ) {
biffAddf( NRRD, "%s: trouble learning range 0", me );
airMopError( mop ); return 1;
}
nrrdRangeSafeSet( range0, nin0, nrrdBlind8BitRangeState );
} else {
range0 = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
if ( _range1 ) {
range1 = nrrdRangeCopy( _range1 );
airMopAdd( mop, nin1 = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( nin1, nin, 0, 1 ) ) {
biffAddf( NRRD, "%s: trouble learning range 1", me );
airMopError( mop ); return 1;
}
nrrdRangeSafeSet( range1, nin1, nrrdBlind8BitRangeState );
} else {
range1 = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range0, ( airMopper )nrrdRangeNix, airMopAlways );
airMopAdd( mop, range1, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply2DSetUp( nout, nin, range0, range1,
nlut, kindLut, typeOut, rescale0, rescale1 )
|| _nrrdApply2DLutOrRegMap( nout, nin, range0, range1,
nlut, AIR_FALSE, rescale0, rescale1 ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdApply2DRegMap
**
no, this doesn't actually exist yet....
int
nrrdApply2DRegMap( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range0, const NrrdRange *_range1,
const Nrrd *nmap, int typeOut, int rescale ) {
static const char me[]="nrrdApply2DRegMap";
NrrdRange *range;
airArray *mop;
if ( !( nout && nmap && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( _nrrdApply2DSetUp( nout, nin, range, nmap, kindRmap, typeOut,
rescale, AIR_FALSE )
|| _nrrdApply2DLutOrRegMap( nout, nin, range, nmap, AIR_TRUE,
rescale, AIR_FALSE ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
*/
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdArithGamma( )
**
** map the values in a nrrd through a power function; essentially:
** val = pow( val, 1/gamma ), but this is after the val has been normalized
** to be in the range of 0.0 to 1.0 ( assuming that the given min and
** max really are the full range of the values in the nrrd ). Thus,
** the given min and max values are fixed points of this
** transformation. Using a negative gamma means that after the pow( )
** function has been applied, the value is inverted with respect to
** min and max ( like in xv ).
*/
int
40 nrrdArithGamma( Nrrd *nout, const Nrrd *nin,
const NrrdRange *_range, double Gamma ) {
static const char me[]="nrrdArithGamma", func[]="gamma";
double val, min, max;
size_t I, num;
NrrdRange *range;
airArray *mop;
double ( *lup )( const void *, size_t );
double ( *ins )( void *, size_t, double );
if ( !( nout && nin ) ) {
/* _range can be NULL */
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( Gamma ) ) ) {
biffAddf( NRRD, "%s: gamma doesn't exist", me );
return 1;
}
if ( !( nrrdTypeBlock != nin->type && nrrdTypeBlock != nout->type ) ) {
biffAddf( NRRD, "%s: can't deal with %s type", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: couldn't initialize by copy to output", me );
return 1;
}
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeTrue );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
min = range->min;
max = range->max;
if ( min == max ) {
/* this is stupid. We want min < max to avoid making NaNs */
max += 1;
}
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nout->type];
Gamma = 1/Gamma;
num = nrrdElementNumber( nin );
if ( Gamma < 0.0 ) {
Gamma = -Gamma;
for ( I=0; I<num; I++ ) {
val = lup( nin->data, I );
val = AIR_AFFINE( min, val, max, 0.0, 1.0 );
val = pow( val, Gamma );
val = AIR_AFFINE( 1.0, val, 0.0, min, max );
ins( nout->data, I, val );
}
} else {
for ( I=0; I<num; I++ ) {
val = lup( nin->data, I );
val = AIR_AFFINE( min, val, max, 0.0, 1.0 );
val = pow( val, Gamma );
val = AIR_AFFINE( 0.0, val, 1.0, min, max );
ins( nout->data, I, val );
}
}
if ( nrrdContentSet_va( nout, func, nin, "%g, %g, %g", min, max, Gamma ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
/* basic info handled by nrrdCopy above */
airMopOkay( mop );
return 0;
}
/* ---------------------------- unary -------------- */
121 static double _nrrdUnaryOpNegative( double a ) {return -a;}
122 static double _nrrdUnaryOpReciprocal( double a ) {return 1.0/a;}
123 static double _nrrdUnaryOpSin( double a ) {return sin( a );}
124 static double _nrrdUnaryOpCos( double a ) {return cos( a );}
125 static double _nrrdUnaryOpTan( double a ) {return tan( a );}
126 static double _nrrdUnaryOpAsin( double a ) {return asin( a );}
127 static double _nrrdUnaryOpAcos( double a ) {return acos( a );}
128 static double _nrrdUnaryOpAtan( double a ) {return atan( a );}
129 static double _nrrdUnaryOpExp( double a ) {return exp( a );}
130 static double _nrrdUnaryOpLog( double a ) {return log( a );}
131 static double _nrrdUnaryOpLog2( double a ) {return log( a )/0.69314718;}
132 static double _nrrdUnaryOpLog10( double a ) {return log10( a );}
/* This code for log1p and expm1 comes from
http://www.plunk.org/~hatch/rightway.php which in turn references
http://www.cs.berkeley.edu/~wkahan/Math128/Sumnfp.pdf from the
great Kahan of IEEE 754 fame, but sadly that URL no longer works
( though the Math128 directory is still there, as are other documents ) */
138 static double _nrrdUnaryOpLog1p( double a ) {
double b;
b = 1.0 + a;
if ( b == 1.0 ) {
/* "a" was so close to zero that we had underflow when adding to 1,
resulting in something that is exactly equal to 1. So, use the
first term of Taylor expansion of log( x+1 ) around 0 == x */
return a;
}
/* else "a" was not so near zero; but GLK doesn't fully grasp
the design of this expression */
return log( b )*a/( b-1 );
}
152 static double _nrrdUnaryOpExpm1( double x ) {
double u;
u = exp( x );
if ( u == 1.0 ) {
/* "x" was so close to 0.0 that exp return exactly 1; subtracting
1 from this will give a constant for a range of x's. Instead,
use the Taylor expansion of exp( x )-1 around 0 == x */
return x;
} else if ( u-1.0 == -1.0 ) {
/* "x" was close enough to -inf that exp returned something so close
to 0 that subtracting 1 resulted in exactly -1; return that */
return -1.0;
}
/* else "x" was neither near 0.0 or -inf, but GLK doesn't fully grasp
the design of this expression */
return ( u-1.0 )*x/log( u );
}
170 static double _nrrdUnaryOpSqrt( double a ) {return sqrt( a );}
171 static double _nrrdUnaryOpCbrt( double a ) {return airCbrt( a );}
172 static double _nrrdUnaryOpErf( double a ) {return airErf( a );}
173 static double _nrrdUnaryOpNerf( double a ) {return ( 1+airErf( a ) )/2;}
174 static double _nrrdUnaryOpCeil( double a ) {return ceil( a );}
175 static double _nrrdUnaryOpFloor( double a ) {return floor( a );}
176 static double _nrrdUnaryOpRoundUp( double a ) {return AIR_ROUNDUP( a );}
177 static double _nrrdUnaryOpRoundDown( double a ) {return AIR_ROUNDDOWN( a );}
178 static double _nrrdUnaryOpAbs( double a ) {return AIR_ABS( a );}
179 static double _nrrdUnaryOpSgn( double a ) {
return ( a < 0.0 ? -1 : ( a > 0.0 ? 1 : 0 ) );}
181 static double _nrrdUnaryOpExists( double a ) {return AIR_EXISTS( a );}
182 static double _nrrdUnaryOpRand( double a ) {
AIR_UNUSED( a );
return airDrandMT( );
}
186 static double _nrrdUnaryOpNormalRand( double a ) {
double v;
AIR_UNUSED( a );
airNormalRand( &v, NULL );
return v;
}
192 static double _nrrdUnaryOpIf( double a ) { return ( a ? 1 : 0 ); }
193 static double _nrrdUnaryOpZero( double a ) {
AIR_UNUSED( a );
return 0.0;
}
197 static double _nrrdUnaryOpOne( double a ) {
AIR_UNUSED( a );
return 1.0;
}
201 static double _nrrdUnaryOpTauOfSigma( double s ) { return airTauOfSigma( s ); }
202 static double _nrrdUnaryOpSigmaOfTau( double t ) { return airSigmaOfTau( t ); }
204 double ( *_nrrdUnaryOp[NRRD_UNARY_OP_MAX+1] )( double ) = {
NULL,
_nrrdUnaryOpNegative,
_nrrdUnaryOpReciprocal,
_nrrdUnaryOpSin,
_nrrdUnaryOpCos,
_nrrdUnaryOpTan,
_nrrdUnaryOpAsin,
_nrrdUnaryOpAcos,
_nrrdUnaryOpAtan,
_nrrdUnaryOpExp,
_nrrdUnaryOpLog,
_nrrdUnaryOpLog2,
_nrrdUnaryOpLog10,
_nrrdUnaryOpLog1p,
_nrrdUnaryOpExpm1,
_nrrdUnaryOpSqrt,
_nrrdUnaryOpCbrt,
_nrrdUnaryOpErf,
_nrrdUnaryOpNerf,
_nrrdUnaryOpCeil,
_nrrdUnaryOpFloor,
_nrrdUnaryOpRoundUp,
_nrrdUnaryOpRoundDown,
_nrrdUnaryOpAbs,
_nrrdUnaryOpSgn,
_nrrdUnaryOpExists,
_nrrdUnaryOpRand,
_nrrdUnaryOpNormalRand,
_nrrdUnaryOpIf,
_nrrdUnaryOpZero,
_nrrdUnaryOpOne,
_nrrdUnaryOpTauOfSigma,
_nrrdUnaryOpSigmaOfTau
};
int
241 nrrdArithUnaryOp( Nrrd *nout, int op, const Nrrd *nin ) {
static const char me[]="nrrdArithUnaryOp";
size_t N, I;
int size[NRRD_DIM_MAX];
double ( *insert )( void *v, size_t I, double d ),
( *lookup )( const void *v, size_t I ), ( *uop )( double ), val;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can't operate on type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( airEnumValCheck( nrrdUnaryOp, op ) ) {
biffAddf( NRRD, "%s: unary op %d invalid", me, op );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
uop = _nrrdUnaryOp[op];
N = nrrdElementNumber( nin );
lookup = nrrdDLookup[nin->type];
insert = nrrdDInsert[nin->type];
for ( I=0; I<N; I++ ) {
val = lookup( nin->data, I );
insert( nout->data, I, uop( val ) );
}
if ( nrrdContentSet_va( nout, airEnumStr( nrrdUnaryOp, op ), nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
nrrdBasicInfoInit( nout,
NRRD_BASIC_INFO_ALL ^ ( NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT ) );
return 0;
}
/* ---------------------------- binary -------------- */
289 static double _nrrdBinaryOpAdd( double a, double b ) {return a + b;}
290 static double _nrrdBinaryOpSubtract( double a, double b ) {return a - b;}
291 static double _nrrdBinaryOpMultiply( double a, double b ) {return a * b;}
292 static double _nrrdBinaryOpDivide( double a, double b ) {return a / b;}
293 static double _nrrdBinaryOpPow( double a, double b ) {return pow( a, b );}
294 static double _nrrdBinaryOpSgnPow( double a, double b ) {return airSgnPow( a, b );}
295 static double _nrrdBinaryOpFlippedSgnPow( double a, double b ) {return airFlippedSgnPow( a, b );}
296 static double _nrrdBinaryOpMod( double a, double b ) {
return AIR_MOD( ( int )a, ( int )b );}
298 static double _nrrdBinaryOpFmod( double a, double b ) {return fmod( a, b );}
299 static double _nrrdBinaryOpAtan2( double a, double b ) {return atan2( a, b );}
300 static double _nrrdBinaryOpMin( double a, double b ) {return AIR_MIN( a, b );}
301 static double _nrrdBinaryOpMax( double a, double b ) {return AIR_MAX( a, b );}
302 static double _nrrdBinaryOpLT( double a, double b ) {return ( a < b );}
303 static double _nrrdBinaryOpLTE( double a, double b ) {return ( a <= b );}
304 static double _nrrdBinaryOpGT( double a, double b ) {return ( a > b );}
305 static double _nrrdBinaryOpGTE( double a, double b ) {return ( a >= b );}
306 static double _nrrdBinaryOpCompare( double a, double b ) {
return ( a < b ? -1 : ( a > b ? 1 : 0 ) );}
308 static double _nrrdBinaryOpEqual( double a, double b ) {return ( a == b );}
309 static double _nrrdBinaryOpNotEqual( double a, double b ) {return ( a != b );}
310 static double _nrrdBinaryOpExists( double a, double b ) {return ( AIR_EXISTS( a )
? a : b );}
312 static double _nrrdBinaryOpIf( double a, double b ) {return ( a ? a : b );}
313 static double _nrrdBinaryOpNormalRandScaleAdd( double a, double b ) {
double v;
airNormalRand( &v, NULL );
return a + b*v;
}
318 static double _nrrdBinaryOpRicianRand( double a, double b ) {
double vr, vi, rr, ri;
airNormalRand( &rr, &ri );
vr = a + b*rr;
vi = b*ri;
return sqrt( vr*vr + vi*vi );
}
327 double ( *_nrrdBinaryOp[NRRD_BINARY_OP_MAX+1] )( double, double ) = {
NULL,
_nrrdBinaryOpAdd,
_nrrdBinaryOpSubtract,
_nrrdBinaryOpMultiply,
_nrrdBinaryOpDivide,
_nrrdBinaryOpPow,
_nrrdBinaryOpSgnPow,
_nrrdBinaryOpFlippedSgnPow,
_nrrdBinaryOpMod,
_nrrdBinaryOpFmod,
_nrrdBinaryOpAtan2,
_nrrdBinaryOpMin,
_nrrdBinaryOpMax,
_nrrdBinaryOpLT,
_nrrdBinaryOpLTE,
_nrrdBinaryOpGT,
_nrrdBinaryOpGTE,
_nrrdBinaryOpCompare,
_nrrdBinaryOpEqual,
_nrrdBinaryOpNotEqual,
_nrrdBinaryOpExists,
_nrrdBinaryOpIf,
_nrrdBinaryOpNormalRandScaleAdd,
_nrrdBinaryOpRicianRand,
/* for these, the clamping is actually done by the caller */
_nrrdBinaryOpAdd, /* for nrrdBinaryOpAddClamp */
_nrrdBinaryOpSubtract, /* for nrrdBinaryOpSubtractClamp */
_nrrdBinaryOpMultiply, /* for nrrdBinaryOpMultiplyClamp */
};
/*
******** nrrdArithBinaryOp
**
** this is a simplified version of nrrdArithIterBinaryOp, written after
** that, in a hurry, to operate directly on two nrrds, instead with
** the NrrdIter nonsense
*/
int
366 nrrdArithBinaryOp( Nrrd *nout, int op, const Nrrd *ninA, const Nrrd *ninB ) {
static const char me[]="nrrdArithBinaryOp";
char *contA, *contB;
size_t N, I, size[NRRD_DIM_MAX];
double ( *ins )( void *v, size_t I, double d ), ( *clmp )( double d ),
( *lupA )( const void *v, size_t I ), ( *lupB )( const void *v, size_t I ),
( *bop )( double a, double b ), valA, valB;
if ( !( nout && !nrrdCheck( ninA ) && !nrrdCheck( ninB ) ) ) {
biffAddf( NRRD, "%s: NULL pointer or invalid args", me );
return 1;
}
if ( nrrdTypeBlock == ninA->type || nrrdTypeBlock == ninB->type ) {
biffAddf( NRRD, "%s: can't operate on type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !nrrdSameSize( ninA, ninB, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: size mismatch between arguments", me );
return 1;
}
if ( airEnumValCheck( nrrdBinaryOp, op ) ) {
biffAddf( NRRD, "%s: binary op %d invalid", me, op );
return 1;
}
nrrdAxisInfoGet_nva( ninA, nrrdAxisInfoSize, size );
if ( !( nout == ninA || nout == ninB ) ) {
if ( _nrrdMaybeAllocMaybeZero_nva( nout, ninA->type, ninA->dim, size,
AIR_FALSE /* zero when no realloc */ ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
if ( nrrdAxisInfoCopy( nout, ninA, NULL, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
nrrdBasicInfoCopy( nout, ninA, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) );
}
nrrdBasicInfoInit( nout,
NRRD_BASIC_INFO_ALL ^ ( NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT ) );
bop = _nrrdBinaryOp[op];
N = nrrdElementNumber( ninA );
lupA = nrrdDLookup[ninA->type];
lupB = nrrdDLookup[ninB->type];
ins = nrrdDInsert[nout->type];
if ( nrrdBinaryOpAddClamp == op
|| nrrdBinaryOpSubtractClamp == op
|| nrrdBinaryOpMultiplyClamp == op ) {
clmp = nrrdDClamp[nout->type];
} else {
clmp = NULL;
}
for ( I=0; I<N; I++ ) {
double tmp;
/* HEY: there is a loss of precision issue here with 64-bit ints */
valA = lupA( ninA->data, I );
valB = lupB( ninB->data, I );
tmp = bop( valA, valB );
if ( clmp ) {
tmp = clmp( tmp );
}
ins( nout->data, I, tmp );
}
contA = _nrrdContentGet( ninA );
contB = _nrrdContentGet( ninB );
if ( _nrrdContentSet_va( nout, airEnumStr( nrrdBinaryOp, op ),
contA, "%s", contB ) ) {
biffAddf( NRRD, "%s:", me );
free( contA ); free( contB ); return 1;
}
free( contA );
free( contB );
return 0;
}
int
453 nrrdArithIterBinaryOpSelect( Nrrd *nout, int op,
NrrdIter *inA, NrrdIter *inB,
unsigned int which ) {
static const char me[]="nrrdArithIterBinaryOpSelect";
char *contA, *contB;
size_t N, I, size[NRRD_DIM_MAX];
int type;
double ( *insert )( void *v, size_t I, double d ), ( *clmp )( double d ),
( *bop )( double a, double b ), valA, valB;
const Nrrd *nin;
if ( !( nout && inA && inB ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdBinaryOp, op ) ) {
biffAddf( NRRD, "%s: binary op %d invalid", me, op );
return 1;
}
if ( !( 0 == which || 1 == which ) ) {
biffAddf( NRRD, "%s: which %u not 0 or 1", me, which );
return 1;
}
nin = ( 0 == which
? _NRRD_ITER_NRRD( inA )
: _NRRD_ITER_NRRD( inB ) );
if ( !nin ) {
biffAddf( NRRD, "%s: selected input %u is a fixed value", me, which );
return 1;
}
type = nin->type;
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( _nrrdMaybeAllocMaybeZero_nva( nout, type, nin->dim, size,
AIR_FALSE /* zero when no realloc */ ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
nrrdBasicInfoCopy( nout, nin, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) );
nrrdBasicInfoInit( nout,
NRRD_BASIC_INFO_ALL ^ ( NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT ) );
bop = _nrrdBinaryOp[op];
/*
fprintf( stderr, "%s: inA->left = %d, inB->left = %d\n", me,
( int )( inA->left ), ( int )( inB->left ) );
*/
N = nrrdElementNumber( nin );
insert = nrrdDInsert[type];
if ( nrrdBinaryOpAddClamp == op
|| nrrdBinaryOpSubtractClamp == op
|| nrrdBinaryOpMultiplyClamp == op ) {
clmp = nrrdDClamp[nout->type];
} else {
clmp = NULL;
}
for ( I=0; I<N; I++ ) {
double tmp;
/* HEY: there is a loss of precision issue here with 64-bit ints */
valA = nrrdIterValue( inA );
valB = nrrdIterValue( inB );
tmp = bop( valA, valB );
if ( clmp ) {
tmp = clmp( tmp );
}
insert( nout->data, I, tmp );
}
contA = nrrdIterContent( inA );
contB = nrrdIterContent( inB );
if ( _nrrdContentSet_va( nout, airEnumStr( nrrdBinaryOp, op ),
contA, "%s", contB ) ) {
biffAddf( NRRD, "%s:", me );
free( contA ); free( contB ); return 1;
}
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
free( contA );
free( contB );
return 0;
}
int
544 nrrdArithIterBinaryOp( Nrrd *nout, int op, NrrdIter *inA, NrrdIter *inB ) {
static const char me[]="nrrdArithIterBinaryOp";
unsigned int which;
if ( !( nout && inA && inB ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
which = ( _NRRD_ITER_NRRD( inA )
? 0
: ( _NRRD_ITER_NRRD( inB )
? 1
: 2 ) );
if ( 2 == which ) {
biffAddf( NRRD, "%s: can't operate on two fixed values", me );
return 1;
}
if ( nrrdArithIterBinaryOpSelect( nout, op, inA, inB, which ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/* ---------------------------- ternary -------------- */
570 static double _nrrdTernaryOpAdd( double a, double b, double c ) {
return a + b + c;
}
573 static double _nrrdTernaryOpMultiply( double a, double b, double c ) {
return a * b * c;
}
576 static double _nrrdTernaryOpMin( double a, double b, double c ) {
b = AIR_MIN( b, c );
return AIR_MIN( a, b );
}
/*
** minsmooth( x, w, M ) is like min( x, M ), but starting at value M-w, values
** are lowered ( via erf ), so that the output is asymptotic to M
*/
584 static double _nrrdTernaryOpMinSmooth( double x, double width, double max ) {
double tran;
tran = max - width;
return ( tran < max /* using the function as intended */
? ( x < tran
? x
: airErf( ( x-tran )*0.886226925452758/( max - tran ) )*( max - tran ) + tran )
: AIR_MIN( x, max ) ); /* transition in wrong place; revert to simple max( ) */
}
593 static double _nrrdTernaryOpMax( double a, double b, double c ) {
b = AIR_MAX( b, c );
return AIR_MAX( a, b );
}
/*
** maxsmooth( m, w, x ) is like max( m, x ), but starting at value m+w, values
** are raised ( via erf ), so that the output is asymptotic to m
*/
601 static double _nrrdTernaryOpMaxSmooth( double min, double width, double x ) {
double tran;
tran = min + width;
return ( min < tran /* using the function as intended */
? ( tran < x
? x
: airErf( ( x-tran )*0.886226925452758/( min - tran ) )*( min - tran ) + tran )
: AIR_MAX( x, min ) ); /* transition in wrong place; revert to simple max( ) */
}
610 static double _nrrdTernaryOpLTSmooth( double a, double w, double b ) {
return AIR_AFFINE( -1.0, airErf( ( b-a )/w ), 1.0, 0.0, 1.0 );
}
613 static double _nrrdTernaryOpGTSmooth( double a, double w, double b ) {
return AIR_AFFINE( -1.0, airErf( ( a-b )/w ), 1.0, 0.0, 1.0 );
}
616 static double _nrrdTernaryOpClamp( double a, double b, double c ) {
return AIR_CLAMP( a, b, c );
}
619 static double _nrrdTernaryOpIfElse( double a, double b, double c ) {
return ( a ? b : c );
}
622 static double _nrrdTernaryOpLerp( double a, double b, double c ) {
/* we do something more than the simple lerp here because
we want to facilitate usage as something which can get around
non-existent values ( b and c as NaN or Inf ) without
getting polluted by them. */
if ( 0.0 == a ) {
return b;
} else if ( 1.0 == a ) {
return c;
} else {
return AIR_LERP( a, b, c );
}
}
636 static double _nrrdTernaryOpExists( double a, double b, double c ) {
return ( AIR_EXISTS( a ) ? b : c );
}
639 static double _nrrdTernaryOpInOpen( double a, double b, double c ) {
return ( AIR_IN_OP( a, b, c ) );
}
642 static double _nrrdTernaryOpInClosed( double a, double b, double c ) {
return ( AIR_IN_CL( a, b, c ) );
}
645 static double _nrrdTernaryOpGaussian( double x, double mu, double sig ) {
return airGaussian( x, mu, sig );
}
648 static double _nrrdTernaryOpRician( double x, double mu, double sig ) {
return airRician( x, mu, sig );
}
651 double ( *_nrrdTernaryOp[NRRD_TERNARY_OP_MAX+1] )( double, double, double ) = {
NULL,
_nrrdTernaryOpAdd,
_nrrdTernaryOpMultiply,
_nrrdTernaryOpMin,
_nrrdTernaryOpMinSmooth,
_nrrdTernaryOpMax,
_nrrdTernaryOpMaxSmooth,
_nrrdTernaryOpLTSmooth,
_nrrdTernaryOpGTSmooth,
_nrrdTernaryOpClamp,
_nrrdTernaryOpIfElse,
_nrrdTernaryOpLerp,
_nrrdTernaryOpExists,
_nrrdTernaryOpInOpen,
_nrrdTernaryOpInClosed,
_nrrdTernaryOpGaussian,
_nrrdTernaryOpRician
};
/*
******** nrrdArithTerneryOp
**
** HEY: UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED UNTESTED
**
** this is a simplified version of nrrdArithIterTernaryOp, written after
** that, in a hurry, to operate directly on three nrrds, instead with
** the NrrdIter nonsense
*/
int
681 nrrdArithTernaryOp( Nrrd *nout, int op, const Nrrd *ninA,
const Nrrd *ninB, const Nrrd *ninC ) {
static const char me[]="nrrdArithTernaryOp";
char *contA, *contB, *contC;
size_t N, I, size[NRRD_DIM_MAX];
double ( *ins )( void *v, size_t I, double d ),
( *lupA )( const void *v, size_t I ), ( *lupB )( const void *v, size_t I ),
( *lupC )( const void *v, size_t I ),
( *top )( double a, double b, double c ), valA, valB, valC;
if ( !( nout && !nrrdCheck( ninA ) && !nrrdCheck( ninB ) && !nrrdCheck( ninC ) ) ) {
biffAddf( NRRD, "%s: NULL pointer or invalid args", me );
return 1;
}
if ( !( nrrdSameSize( ninA, ninB, AIR_TRUE ) &&
nrrdSameSize( ninA, ninC, AIR_TRUE ) ) ) {
biffAddf( NRRD, "%s: size mismatch between arguments", me );
return 1;
}
if ( airEnumValCheck( nrrdTernaryOp, op ) ) {
biffAddf( NRRD, "%s: ternary op %d invalid", me, op );
return 1;
}
nrrdAxisInfoGet_nva( ninA, nrrdAxisInfoSize, size );
if ( !( nout == ninA || nout == ninB || nout == ninC ) ) {
if ( _nrrdMaybeAllocMaybeZero_nva( nout, ninA->type, ninA->dim, size,
AIR_FALSE /* zero when no realloc */ ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
if ( nrrdAxisInfoCopy( nout, ninA, NULL, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
nrrdBasicInfoCopy( nout, ninA, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) );
}
nrrdBasicInfoInit( nout,
NRRD_BASIC_INFO_ALL ^ ( NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT ) );
top = _nrrdTernaryOp[op];
N = nrrdElementNumber( ninA );
lupA = nrrdDLookup[ninA->type];
lupB = nrrdDLookup[ninB->type];
lupC = nrrdDLookup[ninC->type];
ins = nrrdDInsert[nout->type];
for ( I=0; I<N; I++ ) {
/* HEY: there is a loss of precision issue here with 64-bit ints */
valA = lupA( ninA->data, I );
valB = lupB( ninB->data, I );
valC = lupC( ninC->data, I );
ins( nout->data, I, top( valA, valB, valC ) );
}
contA = _nrrdContentGet( ninA );
contB = _nrrdContentGet( ninB );
contC = _nrrdContentGet( ninC );
if ( _nrrdContentSet_va( nout, airEnumStr( nrrdTernaryOp, op ),
contA, "%s, %s", contB, contC ) ) {
biffAddf( NRRD, "%s:", me );
free( contA ); free( contB ); free( contC ); return 1;
}
free( contA );
free( contB );
free( contC );
return 0;
}
int
759 nrrdArithIterTernaryOpSelect( Nrrd *nout, int op,
NrrdIter *inA, NrrdIter *inB, NrrdIter *inC,
unsigned int which ) {
static const char me[]="nrrdArithIterTernaryOpSelect";
char *contA, *contB, *contC;
size_t N, I, size[NRRD_DIM_MAX];
int type;
double ( *insert )( void *v, size_t I, double d ),
( *top )( double a, double b, double c ), valA, valB, valC;
const Nrrd *nin;
if ( !( nout && inA && inB && inC ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdTernaryOp, op ) ) {
biffAddf( NRRD, "%s: ternary op %d invalid", me, op );
return 1;
}
if ( !( 0 == which || 1 == which || 2 == which ) ) {
biffAddf( NRRD, "%s: which %u not valid, want 0, 1, or 2", me, which );
return 1;
}
nin = ( 0 == which
? _NRRD_ITER_NRRD( inA )
: ( 1 == which
? _NRRD_ITER_NRRD( inB )
: _NRRD_ITER_NRRD( inC ) ) );
if ( !nin ) {
biffAddf( NRRD, "%s: selected input %u is a fixed value", me, which );
return 1;
}
type = nin->type;
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( _nrrdMaybeAllocMaybeZero_nva( nout, type, nin->dim, size,
AIR_FALSE /* zero when no realloc */ ) ) {
biffAddf( NRRD, "%s: couldn't allocate output nrrd", me );
return 1;
}
nrrdBasicInfoCopy( nout, nin, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) );
nrrdBasicInfoInit( nout,
NRRD_BASIC_INFO_ALL ^ ( NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT ) );
top = _nrrdTernaryOp[op];
/*
fprintf( stderr, "%!s: inA->left = %d, inB->left = %d\n", me,
( int )( inA->left ), ( int )( inB->left ) );
*/
N = nrrdElementNumber( nin );
insert = nrrdDInsert[type];
for ( I=0; I<N; I++ ) {
/* HEY: there is a loss of precision issue here with 64-bit ints */
valA = nrrdIterValue( inA );
valB = nrrdIterValue( inB );
valC = nrrdIterValue( inC );
/*
if ( !( I % 1000 ) ) {
fprintf( stderr, "!%s: %d: top( %g, %g, %g ) = %g\n", me, ( int )I,
valA, valB, valC,
top( valA, valB, valC ) );
}
*/
insert( nout->data, I, top( valA, valB, valC ) );
}
contA = nrrdIterContent( inA );
contB = nrrdIterContent( inB );
contC = nrrdIterContent( inC );
if ( _nrrdContentSet_va( nout, airEnumStr( nrrdTernaryOp, op ),
contA, "%s, %s", contB, contC ) ) {
biffAddf( NRRD, "%s:", me );
free( contA ); free( contB ); free( contC ); return 1;
}
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
free( contA );
free( contB );
free( contC );
return 0;
}
int
849 nrrdArithIterTernaryOp( Nrrd *nout, int op,
NrrdIter *inA, NrrdIter *inB, NrrdIter *inC ) {
static const char me[]="nrrdArithIterTernaryOp";
unsigned int which;
if ( !( nout && inA && inB && inC ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
which = ( _NRRD_ITER_NRRD( inA )
? 0
: ( _NRRD_ITER_NRRD( inB )
? 1
: ( _NRRD_ITER_NRRD( inC )
? 2
: 3 ) ) );
if ( 3 == which ) {
biffAddf( NRRD, "%s: can't operate on 3 fixed values", me );
return 1;
}
if ( nrrdArithIterTernaryOpSelect( nout, op, inA, inB, inC, which ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
int
877 nrrdArithAffine( Nrrd *nout, double minIn,
const Nrrd *nin, double maxIn,
double minOut, double maxOut, int clamp ) {
static const char me[]="nrrdArithAffine";
size_t I, N;
double ( *ins )( void *v, size_t I, double d ),
( *lup )( const void *v, size_t I ), mmin, mmax;
if ( !nout || nrrdCheck( nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer or invalid input", me );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: couldn't initialize output", me );
return 1;
}
}
N = nrrdElementNumber( nin );
ins = nrrdDInsert[nout->type];
lup = nrrdDLookup[nin->type];
mmin = AIR_MIN( minOut, maxOut );
mmax = AIR_MAX( minOut, maxOut );
for ( I=0; I<N; I++ ) {
double val;
val = lup( nin->data, I );
val = AIR_AFFINE( minIn, val, maxIn, minOut, maxOut );
if ( clamp ) {
val = AIR_CLAMP( mmin, val, mmax );
}
ins( nout->data, I, val );
}
/* HEY: it would be much better if the ordering here was the same as in
AIR_AFFINE, but that's not easy with the way the content functions are
now set up */
if ( nrrdContentSet_va( nout, "affine", nin,
"%g, %g, %g, %g", minIn, maxIn,
minOut, maxOut ) ) {
biffAddf( NRRD, "%s:", me );
}
return 0;
}
int
921 nrrdArithIterAffine( Nrrd *nout, NrrdIter *minIn,
NrrdIter *in, NrrdIter *maxIn,
NrrdIter *minOut, NrrdIter *maxOut, int clamp ) {
static const char me[]="nrrdArithInterAffine";
double ( *ins )( void *v, size_t I, double d ),
mini, vin, maxi, mino, maxo, vout;
const Nrrd *nin;
char *contA, *contB, *contC, *contD, *contE;
size_t I, N;
if ( !( nout && minIn && in && maxIn && minOut && maxOut ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
nin = ( _NRRD_ITER_NRRD( in )
? _NRRD_ITER_NRRD( in )
: ( _NRRD_ITER_NRRD( minIn )
? _NRRD_ITER_NRRD( minIn )
: ( _NRRD_ITER_NRRD( maxIn )
? _NRRD_ITER_NRRD( maxIn )
: ( _NRRD_ITER_NRRD( minOut )
? _NRRD_ITER_NRRD( minOut )
: _NRRD_ITER_NRRD( maxOut ) ) ) ) );
if ( !nin ) {
biffAddf( NRRD, "%s: can't operate solely on fixed values", me );
return 1;
}
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: couldn't initialize output", me );
return 1;
}
N = nrrdElementNumber( nin );
ins = nrrdDInsert[nout->type];
for ( I=0; I<N; I++ ) {
mini = nrrdIterValue( minIn );
vin = nrrdIterValue( in );
maxi = nrrdIterValue( maxIn );
mino = nrrdIterValue( minOut );
maxo = nrrdIterValue( maxOut );
vout = AIR_AFFINE( mini, vin, maxi, mino, maxo );
if ( clamp ) {
double mmin = AIR_MIN( mino, maxo );
double mmax = AIR_MAX( mino, maxo );
vout = AIR_CLAMP( mmin, vout, mmax );
}
ins( nout->data, I, vout );
}
contA = nrrdIterContent( in );
contB = nrrdIterContent( minIn );
contC = nrrdIterContent( maxIn );
contD = nrrdIterContent( maxOut );
contE = nrrdIterContent( maxOut );
/* HEY: same annoyance about order of arguments as in function above */
if ( _nrrdContentSet_va( nout, "affine", contA, "%s, %s, %s, %s",
contB, contC, contD, contE ) ) {
biffAddf( NRRD, "%s:", me );
free( contA ); free( contB ); free( contC ); free( contD ); free( contE ); return 1;
}
free( contA ); free( contB ); free( contC ); free( contD ); free( contE );
return 0;
}
unsigned int
985 nrrdCRC32( const Nrrd *nin, int endian ) {
size_t nn;
/* NULL nrrd or data */
if ( !nin
|| !( nin->data )
|| !( nn = nrrdElementSize( nin )*nrrdElementNumber( nin ) )
|| airEnumValCheck( airEndian, endian ) ) {
return 0;
}
return airCRC32( AIR_CAST( const unsigned char *, nin->data ),
nn, nrrdElementSize( nin ),
endian == airMyEndian( ) ? AIR_FALSE : AIR_TRUE );
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/* learned: /usr/bin/c++ on mac ( at least ) won't actually put a
const int blah[] array in an object file if it hasn't been declared
as "extern" */
const char
nrrdTypePrintfStr[NRRD_TYPE_MAX+1][AIR_STRLEN_SMALL] = {
"%*d", /* nrrdTypeUnknown: what else? the effect will be
"skip" for sscanf, and "minimum precision" for printf */
"%d", /* nrrdTypeChar: char */
"%u", /* nrrdTypeUChar: unsigned char */
"%hd", /* nrrdTypeShort: short */
"%hu", /* nrrdTypeUShort: unsigned short */
"%d", /* nrrdTypeInt: int */
"%u", /* nrrdTypeUInt: unsigned int */
AIR_LLONG_FMT, /* nrrdTypeLLong: long long */
AIR_ULLONG_FMT, /* nrrdTypeULLong: unsigned long long */
"%f", /* nrrdTypeFloat: float */
"%lf", /* nrrdTypeDouble: double */
"%*d" /* nrrdTypeBlock: what else? */
};
/*
** the setting of NRRD_TYPE_BIGGEST has to be in accordance with this
*/
const size_t
nrrdTypeSize[NRRD_TYPE_MAX+1] = {
0, /* nrrdTypeUnknown: unknown */
1, /* nrrdTypeChar: char */
1, /* nrrdTypeUChar: unsigned char */
2, /* nrrdTypeShort: short */
2, /* nrrdTypeUShort: unsigned short */
4, /* nrrdTypeInt: int */
4, /* nrrdTypeUInt: unsigned int */
8, /* nrrdTypeLLong: long long */
8, /* nrrdTypeULLong: unsigned long long */
4, /* nrrdTypeFloat: float */
8, /* nrrdTypeDouble: double */
0 /* nrrdTypeBlock: effectively unknown; user has to set explicitly */
};
const int
nrrdTypeIsIntegral[NRRD_TYPE_MAX+1] = {
0, /* nrrdTypeUnknown: unknown */
1, /* nrrdTypeChar: char */
1, /* nrrdTypeUChar: unsigned char */
1, /* nrrdTypeShort: short */
1, /* nrrdTypeUShort: unsigned short */
1, /* nrrdTypeInt: int */
1, /* nrrdTypeUInt: unsigned int */
1, /* nrrdTypeLLong: long long */
1, /* nrrdTypeULLong: unsigned long long */
0, /* nrrdTypeFloat: float */
0, /* nrrdTypeDouble: double */
1 /* nrrdTypeBlock: for some reason we pretend that blocks are integers */
};
const int
nrrdTypeIsUnsigned[NRRD_TYPE_MAX+1] = {
0, /* nrrdTypeUnknown: unknown */
0, /* nrrdTypeChar: char */
1, /* nrrdTypeUChar: unsigned char */
0, /* nrrdTypeShort: short */
1, /* nrrdTypeUShort: unsigned short */
0, /* nrrdTypeInt: int */
1, /* nrrdTypeUInt: unsigned int */
0, /* nrrdTypeLLong: long long */
1, /* nrrdTypeULLong: unsigned long long */
0, /* nrrdTypeFloat: float */
0, /* nrrdTypeDouble: double */
0 /* nrrdTypeBlock: for some reason we pretend that blocks are signed */
};
/*
******** nrrdTypeMin[]
******** nrrdTypeMax[]
**
** only intended for small ( <= 32 bits ) integral types,
** so that we know how to "unquantize" integral values.
** A 64-bit double can correctly store the 32-bit integral
** mins and maxs, but gets the last few places wrong in the
** 64-bit mins and max.
*/
const double
nrrdTypeMin[NRRD_TYPE_MAX+1] = {
0, /* nrrdTypeUnknown: unknown */
SCHAR_MIN, /* nrrdTypeChar: char */
0, /* nrrdTypeUChar: unsigned char */
SHRT_MIN, /* nrrdTypeShort: short */
0, /* nrrdTypeUShort: unsigned short */
INT_MIN, /* nrrdTypeInt: int */
0, /* nrrdTypeUInt: unsigned int */
( double )NRRD_LLONG_MIN, /* nrrdTypeLLong: long long */
0, /* nrrdTypeULLong: unsigned long long */
0, /* nrrdTypeFloat: float */
0, /* nrrdTypeDouble: double */
0 /* nrrdTypeBlock: punt */
},
nrrdTypeMax[NRRD_TYPE_MAX+1] = {
0, /* nrrdTypeUnknown: unknown */
126 SCHAR_MAX, /* nrrdTypeChar: char */
UCHAR_MAX, /* nrrdTypeUChar: unsigned char */
SHRT_MAX, /* nrrdTypeShort: short */
USHRT_MAX, /* nrrdTypeUShort: unsigned short */
INT_MAX, /* nrrdTypeInt: int */
UINT_MAX, /* nrrdTypeUInt: unsigned int */
( double )NRRD_LLONG_MAX, /* nrrdTypeLLong: long long */
( double )NRRD_ULLONG_MAX, /* nrrdTypeULLong: unsigned long long */
0, /* nrrdTypeFloat: float */
0, /* nrrdTypeDouble: double */
0 /* nrrdTypeBlock: punt */
};
/*
** _nrrdFieldValidInImage[]
**
** these fields are valid embedded in PNM and PNG comments
** This does NOT include the fields who's values are constrained
** by the image format ( and in the case of PNM, magic ) itself.
*/
const int
_nrrdFieldValidInImage[NRRD_FIELD_MAX+1] = {
0, /* nrrdField_unknown */
1, /* nrrdField_comment */
1, /* nrrdField_content */
0, /* nrrdField_number */
0, /* nrrdField_type */
0, /* nrrdField_block_size */
0, /* nrrdField_dimension */
1, /* nrrdField_space */
1, /* nrrdField_space_dimension */
0, /* nrrdField_sizes */
1, /* nrrdField_spacings */
1, /* nrrdField_thicknesses */
1, /* nrrdField_axis_mins */
1, /* nrrdField_axis_maxs */
1, /* nrrdField_space_directions */
1, /* nrrdField_centers */
1, /* nrrdField_kinds */
1, /* nrrdField_labels */
1, /* nrrdField_units */
0, /* nrrdField_min */
0, /* nrrdField_max */
1, /* nrrdField_old_min */
1, /* nrrdField_old_max */
0, /* nrrdField_endian */
0, /* nrrdField_encoding */
0, /* nrrdField_line_skip */
0, /* nrrdField_byte_skip */
1, /* nrrdField_keyvalue */
1, /* nrrdField_sample_units */
1, /* nrrdField_space_units */
1, /* nrrdField_space_origin */
1, /* nrrdField_measurement_frame */
0 /* nrrdField_data_file */
};
/*
** _nrrdFieldOnePerAxis
**
** whether or not you need one value per axis, like labels and spacings
*/
const int
_nrrdFieldOnePerAxis[NRRD_FIELD_MAX+1] = {
0, /* nrrdField_unknown */
0, /* nrrdField_comment */
0, /* nrrdField_content */
0, /* nrrdField_number */
0, /* nrrdField_type */
0, /* nrrdField_block_size */
0, /* nrrdField_dimension */
0, /* nrrdField_space */
0, /* nrrdField_space_dimension */
1, /* nrrdField_sizes */
1, /* nrrdField_spacings */
1, /* nrrdField_thicknesses */
1, /* nrrdField_axis_mins */
1, /* nrrdField_axis_maxs */
1, /* nrrdField_space_directions */
1, /* nrrdField_centers */
1, /* nrrdField_kinds */
1, /* nrrdField_labels */
1, /* nrrdField_units */
0, /* nrrdField_min */
0, /* nrrdField_max */
0, /* nrrdField_old_min */
0, /* nrrdField_old_max */
0, /* nrrdField_endian */
0, /* nrrdField_encoding */
0, /* nrrdField_line_skip */
0, /* nrrdField_byte_skip */
0, /* nrrdField_keyvalue */
0, /* nrrdField_sample_units */
0, /* nrrdField_space_units */
0, /* nrrdField_space_origin */
0, /* nrrdField_measurement_frame */
0 /* nrrdField_data_file */
};
/*
** _nrrdFieldValidInText[]
**
** these fields are valid embedded in plain text comments
** This does NOT include the fields who's values are constrained
** the plain text format itself.
*/
const int
_nrrdFieldValidInText[NRRD_FIELD_MAX+1] = {
0, /* nrrdField_unknown */
1, /* nrrdField_comment */
1, /* nrrdField_content */
0, /* nrrdField_number */
0, /* nrrdField_type: decided AGAINST plain text holding general type
( but I forget why ... ) */
0, /* nrrdField_block_size */
1, /* nrrdField_dimension: but can only be 1 or 2 */
0, /* nrrdField_space */
0, /* nrrdField_space_dimension */
0, /* nrrdField_sizes */
1, /* nrrdField_spacings */
1, /* nrrdField_thicknesses */
1, /* nrrdField_axis_mins */
1, /* nrrdField_axis_maxs */
1, /* nrrdField_space_directions */
1, /* nrrdField_centers */
1, /* nrrdField_kinds */
1, /* nrrdField_labels */
1, /* nrrdField_units */
0, /* nrrdField_min */
0, /* nrrdField_max */
1, /* nrrdField_old_min */
1, /* nrrdField_old_max */
0, /* nrrdField_endian */
0, /* nrrdField_encoding */
0, /* nrrdField_line_skip */
0, /* nrrdField_byte_skip */
1, /* nrrdField_keyvalue */
0, /* nrrdField_sample_units */
0, /* nrrdField_space_units */
0, /* nrrdField_space_origin */
0, /* nrrdField_measurement_frame */
0 /* nrrdField_data_file */
};
/*
** _nrrdFieldRequired[]
**
** regardless of whether its a nrrd, PNM, or plain text, these things
** need to be conveyed, either explicity or implicitly
*/
const int
_nrrdFieldRequired[NRRD_FIELD_MAX+1] = {
0, /* "Ernesto \"Che\" Guevara" */
0, /* "#" */
0, /* nrrdField_content */
0, /* nrrdField_number */
1, /* nrrdField_type */
0, /* nrrdField_block size */
1, /* nrrdField_dimension */
0, /* nrrdField_space */
0, /* nrrdField_space_dimension */
1, /* nrrdField_sizes */
0, /* nrrdField_spacings */
0, /* nrrdField_thicknesses */
0, /* nrrdField_axis mins */
0, /* nrrdField_axis maxs */
0, /* nrrdField_space_directions */
0, /* nrrdField_centers */
0, /* nrrdField_kinds */
0, /* nrrdField_labels */
0, /* nrrdField_units */
0, /* nrrdField_min */
0, /* nrrdField_max */
0, /* nrrdField_old min */
0, /* nrrdField_old max */
0, /* nrrdField_endian */
1, /* nrrdField_encoding */
0, /* nrrdField_line_skip */
0, /* nrrdField_byte_skip */
0, /* nrrdField_keyvalue */
0, /* nrrdField_sample_units */
0, /* nrrdField_space_units */
0, /* nrrdField_space_origin */
0, /* nrrdField_measurement_frame */
0 /* nrrdField_data file */
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/* ------------------------------------------------------------ */
void
30 _nrrdAxisInfoInit( NrrdAxisInfo *axis ) {
int dd;
if ( axis ) {
axis->size = 0;
axis->spacing = axis->thickness = AIR_NAN;
axis->min = axis->max = AIR_NAN;
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
axis->spaceDirection[dd] = AIR_NAN;
}
axis->center = nrrdCenterUnknown;
axis->kind = nrrdKindUnknown;
axis->label = ( char * )airFree( axis->label );
axis->units = ( char * )airFree( axis->units );
}
}
void
48 _nrrdAxisInfoNewInit( NrrdAxisInfo *axis ) {
if ( axis ) {
axis->label = NULL;
axis->units = NULL;
_nrrdAxisInfoInit( axis );
}
}
/* ------------------------------------------------------------ */
/*
******** nrrdKindIsDomain
**
** returns non-zero for kinds ( from nrrdKind* enum ) that are domain
** axes, or independent variable axes, or resample-able axes, all
** different ways of describing the same thing
*/
int
67 nrrdKindIsDomain( int kind ) {
return ( nrrdKindDomain == kind
|| nrrdKindSpace == kind
|| nrrdKindTime == kind );
}
/*
******** nrrdKindSize
**
** returns suggested size ( length ) of an axis with the given kind, or,
** 0 if either ( 1 ) there is no suggested size because the axis is the
** kind of an independent or domain variable or ( 2 ) the kind is invalid
*/
unsigned int
82 nrrdKindSize( int kind ) {
static const char me[]="nrrdKindSize";
unsigned int ret;
if ( !( AIR_IN_OP( nrrdKindUnknown, kind, nrrdKindLast ) ) ) {
/* they gave us invalid or unknown kind */
return 0;
}
switch ( kind ) {
case nrrdKindDomain:
case nrrdKindSpace:
case nrrdKindTime:
case nrrdKindList:
case nrrdKindPoint:
case nrrdKindVector:
case nrrdKindCovariantVector:
case nrrdKindNormal:
ret = 0;
break;
case nrrdKindStub:
case nrrdKindScalar:
ret = 1;
break;
case nrrdKindComplex:
case nrrdKind2Vector:
ret = 2;
break;
case nrrdKind3Color:
case nrrdKindRGBColor:
case nrrdKindHSVColor:
case nrrdKindXYZColor:
ret = 3;
break;
case nrrdKind4Color:
case nrrdKindRGBAColor:
ret = 4;
break;
case nrrdKind3Vector:
case nrrdKind3Normal:
ret = 3;
break;
case nrrdKind4Vector:
case nrrdKindQuaternion:
ret = 4;
break;
case nrrdKind2DSymMatrix:
ret = 3;
break;
case nrrdKind2DMaskedSymMatrix:
ret = 4;
break;
case nrrdKind2DMatrix:
ret = 4;
break;
case nrrdKind2DMaskedMatrix:
ret = 5;
break;
case nrrdKind3DSymMatrix:
ret = 6;
break;
case nrrdKind3DMaskedSymMatrix:
ret = 7;
break;
case nrrdKind3DMatrix:
ret = 9;
break;
case nrrdKind3DMaskedMatrix:
ret = 10;
break;
default:
fprintf( stderr, "%s: PANIC: nrrdKind %d not implemented!\n", me, kind );
ret = UINT_MAX;
}
return ret;
}
/*
** _nrrdKindAltered:
**
** implements logic for how kind should be updated when samples
** along the axis are altered
*/
int
167 _nrrdKindAltered( int kindIn, int resampling ) {
int kindOut;
if ( nrrdStateKindNoop ) {
kindOut = nrrdKindUnknown;
/* HEY: setting the kindOut to unknown is arguably not a no-op.
It is more like pointedly and stubbornly simplistic. So maybe
nrrdStateKindNoop could be renamed .. */
} else {
if ( nrrdKindIsDomain( kindIn )
|| ( 0 == nrrdKindSize( kindIn ) && !resampling ) ) {
kindOut = kindIn;
} else {
kindOut = nrrdKindUnknown;
}
}
return kindOut;
}
/*
** _nrrdAxisInfoCopy
**
** HEY: we have a void return even though this function potentially
** involves calling airStrdup!!
*/
void
193 _nrrdAxisInfoCopy( NrrdAxisInfo *dest, const NrrdAxisInfo *src, int bitflag ) {
int ii;
if ( !( NRRD_AXIS_INFO_SIZE_BIT & bitflag ) ) {
dest->size = src->size;
}
if ( !( NRRD_AXIS_INFO_SPACING_BIT & bitflag ) ) {
dest->spacing = src->spacing;
}
if ( !( NRRD_AXIS_INFO_THICKNESS_BIT & bitflag ) ) {
dest->thickness = src->thickness;
}
if ( !( NRRD_AXIS_INFO_MIN_BIT & bitflag ) ) {
dest->min = src->min;
}
if ( !( NRRD_AXIS_INFO_MAX_BIT & bitflag ) ) {
dest->max = src->max;
}
if ( !( NRRD_AXIS_INFO_SPACEDIRECTION_BIT & bitflag ) ) {
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
dest->spaceDirection[ii] = src->spaceDirection[ii];
}
}
if ( !( NRRD_AXIS_INFO_CENTER_BIT & bitflag ) ) {
dest->center = src->center;
}
if ( !( NRRD_AXIS_INFO_KIND_BIT & bitflag ) ) {
dest->kind = src->kind;
}
if ( !( NRRD_AXIS_INFO_LABEL_BIT & bitflag ) ) {
if ( dest->label != src->label ) {
dest->label = ( char * )airFree( dest->label );
dest->label = ( char * )airStrdup( src->label );
}
}
if ( !( NRRD_AXIS_INFO_UNITS_BIT & bitflag ) ) {
if ( dest->units != src->units ) {
dest->units = ( char * )airFree( dest->units );
dest->units = ( char * )airStrdup( src->units );
}
}
return;
}
/*
******** nrrdAxisInfoCopy( )
**
** For copying all the per-axis peripheral information. Takes a
** permutation "map"; map[d] tells from which axis in input should the
** output axis d copy its information. The length of this permutation
** array is nout->dim. If map is NULL, the identity permutation is
** assumed. If map[i]==-1 for any i in [0, dim-1], then nothing is
** copied into axis i of output. The "bitflag" field controls which
** per-axis fields will NOT be copied; if bitflag==0, then all fields
** are copied. The value of bitflag should be |'s of NRRD_AXIS_INFO_*
** defines.
**
** Decided to Not use Biff, since many times map will be NULL, in
** which case the only error is getting a NULL nrrd, or an invalid map
** permutation, which will probably be unlikely given the contexts in
** which this is called. For the paranoid, the integer return value
** indicates error.
**
** Sun Feb 27 21:12:57 EST 2005: decided to allow nout==nin, so now
** use a local array of NrrdAxisInfo as buffer.
*/
int
261 nrrdAxisInfoCopy( Nrrd *nout, const Nrrd *nin, const int *axmap, int bitflag ) {
NrrdAxisInfo axisBuffer[NRRD_DIM_MAX];
const NrrdAxisInfo *axis;
unsigned int from, axi;
if ( !( nout && nin ) ) {
return 1;
}
if ( axmap ) {
for ( axi=0; axi<nout->dim; axi++ ) {
if ( -1 == axmap[axi] ) {
continue;
}
if ( !AIR_IN_CL( 0, axmap[axi], ( int )nin->dim-1 ) ) {
return 3;
}
}
}
if ( nout == nin ) {
/* copy axis info to local buffer */
for ( axi=0; axi<nin->dim; axi++ ) {
_nrrdAxisInfoNewInit( axisBuffer + axi );
_nrrdAxisInfoCopy( axisBuffer + axi, nin->axis + axi, bitflag );
}
axis = axisBuffer;
} else {
axis = nin->axis;
}
for ( axi=0; axi<nout->dim; axi++ ) {
if ( axmap && -1 == axmap[axi] ) {
/* for this axis, we don't touch a thing */
continue;
}
from = axmap ? ( unsigned int )axmap[axi] : axi;
_nrrdAxisInfoCopy( nout->axis + axi, axis + from, bitflag );
}
if ( nout == nin ) {
/* free dynamically allocated stuff */
for ( axi=0; axi<nin->dim; axi++ ) {
_nrrdAxisInfoInit( axisBuffer + axi );
}
}
return 0;
}
/*
******** nrrdAxisInfoSet_nva( )
**
** Simple means of setting fields of the axis array in the nrrd.
**
** type to pass for third argument:
** nrrdAxisInfoSize: size_t*
** nrrdAxisInfoSpacing: double*
** nrrdAxisInfoThickness: double*
** nrrdAxisInfoMin: double*
** nrrdAxisInfoMax: double*
** nrrdAxisInfoSpaceDirection: double ( *var )[NRRD_SPACE_DIM_MAX]
** nrrdAxisInfoCenter: int*
** nrrdAxisInfoKind: int*
** nrrdAxisInfoLabel: char**
** nrrdAxisInfoUnits: char**
**
** Note that in the case of nrrdAxisInfoSpaceDirection, we only access
** spaceDim elements of info.V[ai] ( so caller can allocate it for less
** than NRRD_SPACE_DIM_MAX if they know what they're doing )
*/
void
328 nrrdAxisInfoSet_nva( Nrrd *nrrd, int axInfo, const void *_info ) {
_nrrdAxisInfoSetPtrs info;
int exists;
unsigned int ai, si, minsi;
if ( !( nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& AIR_IN_OP( nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast )
&& _info ) ) {
return;
}
info.P = _info;
for ( ai=0; ai<nrrd->dim; ai++ ) {
switch ( axInfo ) {
case nrrdAxisInfoSize:
nrrd->axis[ai].size = info.ST[ai];
break;
case nrrdAxisInfoSpacing:
nrrd->axis[ai].spacing = info.D[ai];
break;
case nrrdAxisInfoThickness:
nrrd->axis[ai].thickness = info.D[ai];
break;
case nrrdAxisInfoMin:
nrrd->axis[ai].min = info.D[ai];
break;
case nrrdAxisInfoMax:
nrrd->axis[ai].max = info.D[ai];
break;
case nrrdAxisInfoSpaceDirection:
/* we won't allow setting an invalid direction */
exists = AIR_EXISTS( info.V[ai][0] );
minsi = nrrd->spaceDim;
for ( si=0; si<nrrd->spaceDim; si++ ) {
nrrd->axis[ai].spaceDirection[si] = info.V[ai][si];
if ( exists ^ AIR_EXISTS( info.V[ai][si] ) ) {
minsi = 0;
break;
}
}
for ( si=minsi; si<NRRD_SPACE_DIM_MAX; si++ ) {
nrrd->axis[ai].spaceDirection[si] = AIR_NAN;
}
break;
case nrrdAxisInfoCenter:
nrrd->axis[ai].center = info.I[ai];
break;
case nrrdAxisInfoKind:
nrrd->axis[ai].kind = info.I[ai];
break;
case nrrdAxisInfoLabel:
nrrd->axis[ai].label = ( char * )airFree( nrrd->axis[ai].label );
nrrd->axis[ai].label = ( char * )airStrdup( info.CP[ai] );
break;
case nrrdAxisInfoUnits:
nrrd->axis[ai].units = ( char * )airFree( nrrd->axis[ai].units );
nrrd->axis[ai].units = ( char * )airStrdup( info.CP[ai] );
break;
}
}
if ( nrrdAxisInfoSpaceDirection == axInfo ) {
for ( ai=nrrd->dim; ai<NRRD_DIM_MAX; ai++ ) {
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
nrrd->axis[ai].spaceDirection[si] = AIR_NAN;
}
}
}
return;
}
/*
******** nrrdAxisInfoSet_va( )
**
** var args front-end for nrrdAxisInfoSet_nva
**
** types to pass, one for each dimension:
** nrrdAxisInfoSize: size_t
** nrrdAxisInfoSpacing: double
** nrrdAxisInfoThickness: double
** nrrdAxisInfoMin: double
** nrrdAxisInfoMax: double
** nrrdAxisInfoSpaceDirection: double*
** nrrdAxisInfoCenter: int
** nrrdAxisInfoKind: int
** nrrdAxisInfoLabel: char*
** nrrdAxisInfoUnits: char*
*/
void
417 nrrdAxisInfoSet_va( Nrrd *nrrd, int axInfo, ... ) {
NRRD_TYPE_BIGGEST *buffer[NRRD_DIM_MAX];
_nrrdAxisInfoSetPtrs info;
unsigned int ai, si;
va_list ap;
double *dp, svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
if ( !( nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& AIR_IN_OP( nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast ) ) ) {
return;
}
info.P = buffer;
va_start( ap, axInfo );
for ( ai=0; ai<nrrd->dim; ai++ ) {
switch ( axInfo ) {
case nrrdAxisInfoSize:
info.ST[ai] = va_arg( ap, size_t );
/*
printf( "!%s: got int[%d] = %d\n", "nrrdAxisInfoSet", d, info.I[ai] );
*/
break;
case nrrdAxisInfoSpaceDirection:
dp = va_arg( ap, double* ); /* punting on using info enum */
/*
printf( "!%s: got dp = %lu\n", "nrrdAxisInfoSet",
( unsigned long )( dp ) );
*/
for ( si=0; si<nrrd->spaceDim; si++ ) {
/* nrrd->axis[ai].spaceDirection[si] = dp[si]; */
svec[ai][si] = dp[si];
}
for ( si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++ ) {
/* nrrd->axis[ai].spaceDirection[si] = AIR_NAN; */
svec[ai][si] = dp[si];
}
break;
case nrrdAxisInfoCenter:
case nrrdAxisInfoKind:
info.I[ai] = va_arg( ap, int );
/*
printf( "!%s: got int[%d] = %d\n",
"nrrdAxisInfoSet", d, info.I[ai] );
*/
break;
case nrrdAxisInfoSpacing:
case nrrdAxisInfoThickness:
case nrrdAxisInfoMin:
case nrrdAxisInfoMax:
info.D[ai] = va_arg( ap, double );
/*
printf( "!%s: got double[%d] = %g\n",
"nrrdAxisInfoSet", d, info.D[ai] );
*/
break;
case nrrdAxisInfoLabel:
/* we DO NOT do the airStrdup( ) here because this pointer value is
just going to be handed to nrrdAxisInfoSet_nva( ), which WILL do the
airStrdup( ); we're not violating the rules for axis labels */
info.CP[ai] = va_arg( ap, char * );
/*
printf( "!%s: got char*[%d] = |%s|\n",
"nrrdAxisInfoSet", d, info.CP[ai] );
*/
break;
case nrrdAxisInfoUnits:
/* see not above */
info.CP[ai] = va_arg( ap, char * );
break;
}
}
va_end( ap );
if ( nrrdAxisInfoSpaceDirection != axInfo ) {
/* now set the quantities which we've gotten from the var args */
nrrdAxisInfoSet_nva( nrrd, axInfo, info.P );
} else {
nrrdAxisInfoSet_nva( nrrd, axInfo, svec );
}
return;
}
/*
******** nrrdAxisInfoGet_nva( )
**
** get any of the axis fields into an array
**
** Note that getting axes labels involves implicitly allocating space
** for them, due to the action of airStrdup( ). The user is
** responsible for free( )ing these strings when done with them.
**
** type to pass for third argument:
** nrrdAxisInfoSize: size_t*
** nrrdAxisInfoSpacing: double*
** nrrdAxisInfoThickness: double*
** nrrdAxisInfoMin: double*
** nrrdAxisInfoMax: double*
** nrrdAxisInfoSpaceDirection: double ( *var )[NRRD_SPACE_DIM_MAX]
** nrrdAxisInfoCenter: int*
** nrrdAxisInfoKind: int*
** nrrdAxisInfoLabel: char**
** nrrdAxisInfoUnits: char**
*/
void
523 nrrdAxisInfoGet_nva( const Nrrd *nrrd, int axInfo, void *_info ) {
_nrrdAxisInfoGetPtrs info;
unsigned int ai, si;
if ( !( nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& AIR_IN_OP( nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast ) ) ) {
return;
}
info.P = _info;
for ( ai=0; ai<nrrd->dim; ai++ ) {
switch ( axInfo ) {
case nrrdAxisInfoSize:
info.ST[ai] = nrrd->axis[ai].size;
break;
case nrrdAxisInfoSpacing:
info.D[ai] = nrrd->axis[ai].spacing;
break;
case nrrdAxisInfoThickness:
info.D[ai] = nrrd->axis[ai].thickness;
break;
case nrrdAxisInfoMin:
info.D[ai] = nrrd->axis[ai].min;
break;
case nrrdAxisInfoMax:
info.D[ai] = nrrd->axis[ai].max;
break;
case nrrdAxisInfoSpaceDirection:
for ( si=0; si<nrrd->spaceDim; si++ ) {
info.V[ai][si] = nrrd->axis[ai].spaceDirection[si];
}
for ( si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++ ) {
info.V[ai][si] = AIR_NAN;
}
break;
case nrrdAxisInfoCenter:
info.I[ai] = nrrd->axis[ai].center;
break;
case nrrdAxisInfoKind:
info.I[ai] = nrrd->axis[ai].kind;
break;
case nrrdAxisInfoLabel:
/* note airStrdup( )! */
info.CP[ai] = airStrdup( nrrd->axis[ai].label );
break;
case nrrdAxisInfoUnits:
/* note airStrdup( )! */
info.CP[ai] = airStrdup( nrrd->axis[ai].units );
break;
}
}
if ( nrrdAxisInfoSpaceDirection == axInfo ) {
for ( ai=nrrd->dim; ai<NRRD_DIM_MAX; ai++ ) {
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
info.V[ai][si] = AIR_NAN;
}
}
}
return;
}
/*
** types to pass, one for each dimension:
** nrrdAxisInfoSize: size_t*
** nrrdAxisInfoSpacing: double*
** nrrdAxisInfoThickness: double*
** nrrdAxisInfoMin: double*
** nrrdAxisInfoMax: double*
** nrrdAxisInfoSpaceDirection: double*
** nrrdAxisInfoCenter: int*
** nrrdAxisInfoKind: int*
** nrrdAxisInfoLabel: char**
** nrrdAxisInfoUnits: char**
*/
void
599 nrrdAxisInfoGet_va( const Nrrd *nrrd, int axInfo, ... ) {
void *buffer[NRRD_DIM_MAX], *ptr;
_nrrdAxisInfoGetPtrs info;
unsigned int ai, si;
va_list ap;
double svec[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
if ( !( nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& AIR_IN_OP( nrrdAxisInfoUnknown, axInfo, nrrdAxisInfoLast ) ) ) {
return;
}
if ( nrrdAxisInfoSpaceDirection != axInfo ) {
info.P = buffer;
nrrdAxisInfoGet_nva( nrrd, axInfo, info.P );
} else {
nrrdAxisInfoGet_nva( nrrd, axInfo, svec );
}
va_start( ap, axInfo );
for ( ai=0; ai<nrrd->dim; ai++ ) {
ptr = va_arg( ap, void* );
/*
printf( "!%s( %d ): ptr = %lu\n",
"nrrdAxisInfoGet", d, ( unsigned long )ptr );
*/
switch ( axInfo ) {
case nrrdAxisInfoSize:
*( ( size_t* )ptr ) = info.ST[ai];
break;
case nrrdAxisInfoSpacing:
case nrrdAxisInfoThickness:
case nrrdAxisInfoMin:
case nrrdAxisInfoMax:
*( ( double* )ptr ) = info.D[ai];
/* printf( "!%s: got double[%d] = %lg\n", "nrrdAxisInfoGet", d,
*( ( double* )ptr ) ); */
break;
case nrrdAxisInfoSpaceDirection:
for ( si=0; si<nrrd->spaceDim; si++ ) {
( ( double* )ptr )[si] = svec[ai][si];
}
for ( si=nrrd->spaceDim; si<NRRD_SPACE_DIM_MAX; si++ ) {
( ( double* )ptr )[si] = AIR_NAN;
}
break;
case nrrdAxisInfoCenter:
case nrrdAxisInfoKind:
*( ( int* )ptr ) = info.I[ai];
/* printf( "!%s: got int[%d] = %d\n",
"nrrdAxisInfoGet", d, *( ( int* )ptr ) ); */
break;
case nrrdAxisInfoLabel:
case nrrdAxisInfoUnits:
/* we DO NOT do the airStrdup( ) here because this pointer value just
came from nrrdAxisInfoGet_nva( ), which already did the airStrdup( ) */
*( ( char** )ptr ) = info.CP[ai];
/* printf( "!%s: got char*[%d] = |%s|\n", "nrrdAxisInfoSet", d,
*( ( char** )ptr ) ); */
break;
}
}
va_end( ap );
return;
}
/*
** _nrrdCenter( )
**
** for nrrdCenterCell and nrrdCenterNode, return will be the same
** as input. Converts nrrdCenterUnknown into nrrdDefaultCenter,
** and then clamps to ( nrrdCenterUnknown+1, nrrdCenterLast-1 ).
**
** Thus, this ALWAYS returns nrrdCenterNode or nrrdCenterCell
** ( as long as those are the only two centering schemes ).
*/
int
678 _nrrdCenter( int center ) {
center = ( nrrdCenterUnknown == center
? nrrdDefaultCenter
: center );
center = AIR_CLAMP( nrrdCenterUnknown+1, center, nrrdCenterLast-1 );
return center;
}
int
688 _nrrdCenter2( int center, int defCenter ) {
center = ( nrrdCenterUnknown == center
? defCenter
: center );
center = AIR_CLAMP( nrrdCenterUnknown+1, center, nrrdCenterLast-1 );
return center;
}
/*
******** nrrdAxisInfoPos( )
**
** given a nrrd, an axis, and a ( floating point ) index space position,
** return the position implied the axis's min, max, and center
** Does the opposite of nrrdAxisIdx( ).
**
** does not use biff
*/
double
708 nrrdAxisInfoPos( const Nrrd *nrrd, unsigned int ax, double idx ) {
int center;
size_t size;
double min, max;
if ( !( nrrd && ax <= nrrd->dim-1 ) ) {
return AIR_NAN;
}
center = _nrrdCenter( nrrd->axis[ax].center );
min = nrrd->axis[ax].min;
max = nrrd->axis[ax].max;
size = nrrd->axis[ax].size;
return NRRD_POS( center, min, max, size, idx );
}
/*
******** nrrdAxisInfoIdx( )
**
** given a nrrd, an axis, and a ( floating point ) world space position,
** return the index implied the axis's min, max, and center.
** Does the opposite of nrrdAxisPos( ).
**
** does not use biff
*/
double
734 nrrdAxisInfoIdx( const Nrrd *nrrd, unsigned int ax, double pos ) {
int center;
size_t size;
double min, max;
if ( !( nrrd && ax <= nrrd->dim-1 ) ) {
return AIR_NAN;
}
center = _nrrdCenter( nrrd->axis[ax].center );
min = nrrd->axis[ax].min;
max = nrrd->axis[ax].max;
size = nrrd->axis[ax].size;
return NRRD_IDX( center, min, max, size, pos );
}
/*
******** nrrdAxisInfoPosRange( )
**
** given a nrrd, an axis, and two ( floating point ) index space positions,
** return the range of positions implied the axis's min, max, and center
** The opposite of nrrdAxisIdxRange( )
*/
void
758 nrrdAxisInfoPosRange( double *loP, double *hiP,
const Nrrd *nrrd, unsigned int ax,
double loIdx, double hiIdx ) {
int center, flip = 0;
size_t size;
double min, max, tmp;
if ( !( loP && hiP && nrrd && ax <= nrrd->dim-1 ) ) {
if ( loP ) *loP = AIR_NAN;
if ( hiP ) *hiP = AIR_NAN;
return;
}
center = _nrrdCenter( nrrd->axis[ax].center );
min = nrrd->axis[ax].min;
max = nrrd->axis[ax].max;
size = nrrd->axis[ax].size;
if ( loIdx > hiIdx ) {
flip = 1;
tmp = loIdx; loIdx = hiIdx; hiIdx = tmp;
}
if ( nrrdCenterCell == center ) {
*loP = AIR_AFFINE( 0, loIdx, size, min, max );
*hiP = AIR_AFFINE( 0, hiIdx+1, size, min, max );
} else {
*loP = AIR_AFFINE( 0, loIdx, size-1, min, max );
*hiP = AIR_AFFINE( 0, hiIdx, size-1, min, max );
}
if ( flip ) {
tmp = *loP; *loP = *hiP; *hiP = tmp;
}
return;
}
/*
******** nrrdAxisInfoIdxRange( )
**
** given a nrrd, an axis, and two ( floating point ) world space positions,
** return the range of index space implied the axis's min, max, and center
** The opposite of nrrdAxisPosRange( ).
**
** Actually- there are situations where sending an interval through
** nrrdAxisIdxRange -> nrrdAxisPosRange -> nrrdAxisIdxRange
** such as in cell centering, when the range of positions given does
** not even span one sample. Such as:
** axis->size = 4, axis->min = -4, axis->max = 4, loPos = 0, hiPos = 1
** --> nrrdAxisIdxRange == ( 2, 1.5 ) --> nrrdAxisPosRange == ( 2, -1 )
** The basic problem is that because of the 0.5 offset inherent in
** cell centering, there are situations where ( in terms of the arguments
** to nrrdAxisIdxRange( ) ) loPos < hiPos, but *loP > *hiP.
*/
void
811 nrrdAxisInfoIdxRange( double *loP, double *hiP,
const Nrrd *nrrd, unsigned int ax,
double loPos, double hiPos ) {
int center, flip = 0;
size_t size;
double min, max, tmp;
if ( !( loP && hiP && nrrd && ax <= nrrd->dim-1 ) ) {
*loP = *hiP = AIR_NAN;
return;
}
center = _nrrdCenter( nrrd->axis[ax].center );
min = nrrd->axis[ax].min;
max = nrrd->axis[ax].max;
size = nrrd->axis[ax].size;
if ( loPos > hiPos ) {
flip = 1;
tmp = loPos; loPos = hiPos; hiPos = tmp;
}
if ( nrrdCenterCell == center ) {
if ( min < max ) {
*loP = AIR_AFFINE( min, loPos, max, 0, size );
*hiP = AIR_AFFINE( min, hiPos, max, -1, size-1 );
} else {
*loP = AIR_AFFINE( min, loPos, max, -1, size-1 );
*hiP = AIR_AFFINE( min, hiPos, max, 0, size );
}
} else {
*loP = AIR_AFFINE( min, loPos, max, 0, size-1 );
*hiP = AIR_AFFINE( min, hiPos, max, 0, size-1 );
}
if ( flip ) {
tmp = *loP; *loP = *hiP; *hiP = tmp;
}
return;
}
void
851 nrrdAxisInfoSpacingSet( Nrrd *nrrd, unsigned int ax ) {
int sign;
double min, max, tmp;
if ( !( nrrd && ax <= nrrd->dim-1 ) ) {
return;
}
min = nrrd->axis[ax].min;
max = nrrd->axis[ax].max;
if ( !( AIR_EXISTS( min ) && AIR_EXISTS( max ) ) ) {
/* there's no actual basis on which to set the spacing information,
but we have to set it something, so here goes .. */
nrrd->axis[ax].spacing = nrrdDefaultSpacing;
return;
}
if ( min > max ) {
tmp = min; min = max; max = tmp;
sign = -1;
} else {
sign = 1;
}
/* the skinny */
nrrd->axis[ax].spacing = NRRD_SPACING( _nrrdCenter( nrrd->axis[ax].center ),
min, max, nrrd->axis[ax].size );
nrrd->axis[ax].spacing *= sign;
return;
}
void
884 nrrdAxisInfoMinMaxSet( Nrrd *nrrd, unsigned int ax, int defCenter ) {
int center;
double spacing;
if ( !( nrrd && ax <= nrrd->dim-1 ) ) {
return;
}
center = _nrrdCenter2( nrrd->axis[ax].center, defCenter );
spacing = nrrd->axis[ax].spacing;
if ( !AIR_EXISTS( spacing ) )
spacing = nrrdDefaultSpacing;
if ( nrrdCenterCell == center ) {
nrrd->axis[ax].min = 0;
nrrd->axis[ax].max = spacing*AIR_CAST( double, nrrd->axis[ax].size );
} else {
nrrd->axis[ax].min = 0;
nrrd->axis[ax].max = spacing*AIR_CAST( double, nrrd->axis[ax].size - 1 );
}
return;
}
/* ---- BEGIN non-NrrdIO */
/*
** not using the value comparators in accessors.c because of their
** slightly strange behavior WRT infinity ( +inf < -42 ). This code
** may eventually warrant wider availability, for now its here but
** accessible to nrrd files via privateNrrd.h
*/
int
915 _nrrdDblcmp( double aa, double bb ) {
int nna, nnb, ret;
nna = AIR_EXISTS( aa ) || !airIsNaN( aa );
nnb = AIR_EXISTS( bb ) || !airIsNaN( bb );
if ( nna && nnb ) {
/* both either exist or are an infinity */
ret = ( aa < bb
? -1
: ( aa > bb
? 1
: 0 ) );
} else {
/* one or the other is NaN */
ret = ( nna < nnb
? -1
: ( nna > nnb
? 1
: 0 ) );
}
return ret;
}
/*
******** nrrdAxisInfoCompare
**
** compares all fields in the NrrdAxisInfoCompare
**
** See comment about logic of return value above nrrdCompare( )
**
** NOTE: the structure of this code is very similar to that of
** nrrdCompare, and any improvements here should be reflected there
*/
int
949 nrrdAxisInfoCompare( const NrrdAxisInfo *axisA, const NrrdAxisInfo *axisB,
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdAxisInfoCompare";
unsigned int saxi;
if ( !( axisA && axisB && differ ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, or %p )", me,
AIR_CVOIDP( axisA ), AIR_CVOIDP( axisB ), AIR_VOIDP( differ ) );
return 1;
}
if ( explain ) {
strcpy( explain, "" );
}
if ( axisA->size != axisB->size ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
*differ = axisA->size < axisB->size ? -1 : 1;
if ( explain ) {
sprintf( explain, "axisA->size=%s %s axisB->size=%s",
airSprintSize_t( stmp1, axisA->size ),
*differ < 0 ? "<" : ">",
airSprintSize_t( stmp2, axisB->size ) );
}
return 0;
}
#define DOUBLE_COMPARE( VAL, STR ) \
*differ = _nrrdDblcmp( axisA->VAL, axisB->VAL ); \
if ( *differ ) { \
if ( explain ) { \
sprintf( explain, "axisA->%s %.17g %s axisB->%s %.17g", \
STR, axisA->VAL, *differ < 0 ? "<" : ">", \
STR, axisB->VAL ); \
} \
return 0; \
}
DOUBLE_COMPARE( spacing, "spacing" );
DOUBLE_COMPARE( thickness, "thickness" );
DOUBLE_COMPARE( min, "min" );
DOUBLE_COMPARE( max, "max" );
for ( saxi=0; saxi<NRRD_SPACE_DIM_MAX; saxi++ ) {
char stmp[AIR_STRLEN_SMALL];
sprintf( stmp, "spaceDirection[%u]", saxi );
DOUBLE_COMPARE( spaceDirection[saxi], stmp );
}
#undef DOUBLE_COMPARE
if ( axisA->center != axisB->center ) {
*differ = axisA->center < axisB->center ? -1 : 1;
if ( explain ) {
sprintf( explain, "axisA->center %s %s axisB->center %s",
airEnumStr( nrrdCenter, axisA->center ),
*differ < 0 ? "<" : ">",
airEnumStr( nrrdCenter, axisB->center ) );
}
return 0;
}
if ( axisA->kind != axisB->kind ) {
*differ = axisA->kind < axisB->kind ? -1 : 1;
if ( explain ) {
sprintf( explain, "axisA->kind %s %s axisB->kind %s",
airEnumStr( nrrdKind, axisA->kind ),
*differ < 0 ? "<" : ">",
airEnumStr( nrrdKind, axisB->kind ) );
}
return 0;
}
*differ = airStrcmp( axisA->label, axisB->label );
if ( *differ ) {
if ( explain ) {
/* can't safely print whole labels because of fixed-size of explain */
sprintf( explain, "axisA->label %s axisB->label",
*differ < 0 ? "<" : ">" );
if ( strlen( explain ) + airStrlen( axisA->label )
+ airStrlen( axisB->label )
+ 2*strlen( " \"\" " ) + 1 < AIR_STRLEN_LARGE ) {
/* ok, we can print them */
sprintf( explain, "axisA->label \"%s\" %s axisB->label \"%s\"",
axisA->label ? axisA->label : "",
*differ < 0 ? "<" : ">",
axisB->label ? axisB->label : "" );
}
}
return 0;
}
*differ = airStrcmp( axisA->units, axisB->units );
if ( *differ ) {
if ( explain ) {
/* can't print whole string because of fixed-size of explain */
sprintf( explain, "axisA->units %s axisB->units",
*differ < 0 ? "<" : ">" );
}
return 0;
}
return 0;
}
/* ---- END non-NrrdIO */
/*
******** nrrdDomainAxesGet
**
** Based on the per-axis "kind" field, learns which are the domain
1053 ** ( resample-able ) axes of an image, in other words, the axes which
** correspond to independent variables. The return value is the
** number of domain axes, and that many values are set in the given
** axisIdx[] array
**
** NOTE: this takes a wild guess that an unset ( nrrdKindUnknown ) kind
** is a domain axis.
*/
unsigned int
nrrdDomainAxesGet( const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX] ) {
unsigned int domAxi, axi;
if ( !( nrrd && axisIdx ) ) {
return 0;
}
domAxi = 0;
for ( axi=0; axi<nrrd->dim; axi++ ) {
1070 if ( nrrdKindUnknown == nrrd->axis[axi].kind
|| nrrdKindIsDomain( nrrd->axis[axi].kind ) ) {
axisIdx[domAxi++] = axi;
}
}
return domAxi;
}
int
_nrrdSpaceVecExists( const Nrrd *nrrd, unsigned int axi ) {
unsigned int sai;
int ret;
if ( !( nrrd && axi < nrrd->dim && nrrd->spaceDim ) ) {
ret = AIR_FALSE;
} else {
1086 ret = AIR_TRUE;
for ( sai=0; sai<nrrd->spaceDim; sai++ ) {
ret &= AIR_EXISTS( nrrd->axis[axi].spaceDirection[sai] );
}
}
return ret;
}
unsigned int
nrrdSpatialAxesGet( const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX] ) {
unsigned int spcAxi, axi;
if ( !( nrrd && axisIdx && nrrd->spaceDim ) ) {
return 0;
}
spcAxi = 0;
for ( axi=0; axi<nrrd->dim; axi++ ) {
if ( _nrrdSpaceVecExists( nrrd, axi ) ) {
axisIdx[spcAxi++] = axi;
}
}
return spcAxi;
}
/*
******** nrrdRangeAxesGet
**
** Based on the per-axis "kind" field, learns which are the range
1114 ** ( non-resample-able ) axes of an image, in other words, the axes
** which correspond to dependent variables. The return value is the
** number of range axes; that number of values are set in the given
** axisIdx[] array
**
** Note: this really is as simple as returning the complement of the
** axis selected by nrrdDomainAxesGet( )
*/
unsigned int
nrrdRangeAxesGet( const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX] ) {
unsigned int domNum, domIdx[NRRD_DIM_MAX], rngAxi, axi, ii, isDom;
if ( !( nrrd && axisIdx ) ) {
return 0;
}
domNum = nrrdDomainAxesGet( nrrd, domIdx );
rngAxi = 0;
for ( axi=0; axi<nrrd->dim; axi++ ) {
isDom = AIR_FALSE;
for ( ii=0; ii<domNum; ii++ ) { /* yes, inefficient */
isDom |= axi == domIdx[ii];
1135 }
if ( !isDom ) {
axisIdx[rngAxi++] = axi;
}
}
return rngAxi;
}
unsigned int
nrrdNonSpatialAxesGet( const Nrrd *nrrd, unsigned int axisIdx[NRRD_DIM_MAX] ) {
unsigned int spcNum, spcIdx[NRRD_DIM_MAX], nspAxi, axi, ii, isSpc;
if ( !( nrrd && axisIdx ) ) {
return 0;
}
/* HEY: copy and paste, should refactor with above */
spcNum = nrrdSpatialAxesGet( nrrd, spcIdx );
nspAxi = 0;
for ( axi=0; axi<nrrd->dim; axi++ ) {
isSpc = AIR_FALSE;
for ( ii=0; ii<spcNum; ii++ ) { /* yes, inefficient */
isSpc |= axi == spcIdx[ii];
}
if ( !isSpc ) {
axisIdx[nspAxi++] = axi;
}
}
return nspAxi;
}
/*
******** nrrdSpacingCalculate
**
** Determine nrrdSpacingStatus, and whatever can be calculated about
** spacing for a given axis. Takes a nrrd, an axis, a double pointer
** ( for returning a scalar ), a space vector, and an int pointer for
** returning the known length of the space vector.
**
** The behavior of what has been set by the function is determined by
** the return value, which takes values from the nrrdSpacingStatus*
** enum, as follows:
**
** returned status value: what it means, and what it set
** ---------------------------------------------------------------------------
** nrrdSpacingStatusUnknown Something about the given arguments is
** invalid.
** *spacing = NaN,
** vector = all NaNs
**
** nrrdSpacingStatusNone There is no spacing info at all:
** *spacing = NaN,
** vector = all NaNs
**
** nrrdSpacingStatusScalarNoSpace There is no surrounding space, but the
** axis's spacing was known.
** *spacing = axis->spacing,
** vector = all NaNs
**
** nrrdSpacingStatusScalarWithSpace There *is* a surrounding space, but the
** given axis does not live in that space,
** because it has no space direction. Caller
** may want to think about what's going on.
** *spacing = axis->spacing,
** vector = all NaNs
1200 **
** nrrdSpacingStatusDirection There is a surrounding space, in which
** this axis has a direction V:
** *spacing = |V| ( length of direction ),
** vector = V/|V| ( normalized direction )
** NOTE: it is still possible for both
** *spacing and vector to be all NaNs!!
*/
int
nrrdSpacingCalculate( const Nrrd *nrrd, unsigned int ax,
double *spacing, double vector[NRRD_SPACE_DIM_MAX] ) {
int ret;
if ( !( nrrd && spacing && vector
&& ax <= nrrd->dim-1
&& !_nrrdCheck( nrrd, AIR_FALSE, AIR_FALSE ) ) ) {
/* there's a problem with the arguments. Note: the _nrrdCheck( )
call does not check on non-NULL-ity of nrrd->data */
ret = nrrdSpacingStatusUnknown;
if ( spacing ) {
*spacing = AIR_NAN;
}
if ( vector ) {
nrrdSpaceVecSetNaN( vector );
}
} else {
if ( AIR_EXISTS( nrrd->axis[ax].spacing ) ) {
if ( nrrd->spaceDim > 0 ) {
ret = nrrdSpacingStatusScalarWithSpace;
} else {
ret = nrrdSpacingStatusScalarNoSpace;
}
*spacing = nrrd->axis[ax].spacing;
nrrdSpaceVecSetNaN( vector );
} else {
if ( nrrd->spaceDim > 0 && _nrrdSpaceVecExists( nrrd, ax ) ) {
ret = nrrdSpacingStatusDirection;
*spacing = nrrdSpaceVecNorm( nrrd->spaceDim,
nrrd->axis[ax].spaceDirection );
nrrdSpaceVecScale( vector, 1.0/( *spacing ),
nrrd->axis[ax].spaceDirection );
} else {
ret = nrrdSpacingStatusNone;
1243 *spacing = AIR_NAN;
nrrdSpaceVecSetNaN( vector );
}
}
}
return ret;
}
int
nrrdOrientationReduce( Nrrd *nout, const Nrrd *nin,
int setMinsFromOrigin ) {
static const char me[]="nrrdOrientationReduce";
unsigned int spatialAxisNum, spatialAxisIdx[NRRD_DIM_MAX], saxii;
NrrdAxisInfo *axis;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: trouble doing initial copying", me );
return 1;
}
}
if ( !nout->spaceDim ) {
/* we're done! */
return 0;
}
spatialAxisNum = nrrdSpatialAxesGet( nout, spatialAxisIdx );
for ( saxii=0; saxii<spatialAxisNum; saxii++ ) {
axis = nout->axis + spatialAxisIdx[saxii];
axis->spacing = nrrdSpaceVecNorm( nout->spaceDim,
axis->spaceDirection );
if ( setMinsFromOrigin ) {
axis->min = ( saxii < nout->spaceDim
? nout->spaceOrigin[saxii]
: AIR_NAN );
}
}
nrrdSpaceSet( nout, nrrdSpaceUnknown );
return 0;
}
/*
1290 ******** nrrdMetaData
**
** The brains of "unu dnorm" ( for Diderot normalization ): put all meta-data
** of a nrrd into some simpler canonical form.
**
** This function probably doesn't belong in this file, but it is kind
** the opposite of nrrdOrientationReduce ( above ), so here it is
*/
int
nrrdMetaDataNormalize( Nrrd *nout, const Nrrd *nin,
int version,
int trivialOrient,
int permuteComponentAxisFastest,
int recenterGrid,
double sampleSpacing,
int *lostMeasurementFrame ) {
static const char me[]="nrrdMetaDataNormalize";
size_t size[NRRD_DIM_MAX];
int kindIn, kindOut, haveMM, gotmf;
unsigned int kindAxis, axi, si, sj;
Nrrd *ntmp;
airArray *mop;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdMetaDataCanonicalVersion, version ) ) {
biffAddf( NRRD, "%s: version %d not valid %s", me,
version, nrrdMetaDataCanonicalVersion->name );
return 1;
}
if ( nrrdMetaDataCanonicalVersionAlpha != version ) {
biffAddf( NRRD, "%s: sorry, %s %s not implemented ( only %s )", me,
nrrdMetaDataCanonicalVersion->name,
airEnumStr( nrrdMetaDataCanonicalVersion, version ),
airEnumStr( nrrdMetaDataCanonicalVersion,
nrrdMetaDataCanonicalVersionAlpha ) );
return 1;
}
if ( _nrrdCheck( nin, AIR_FALSE /* checkData */, AIR_TRUE /* useBiff */ ) ) {
biffAddf( NRRD, "%s: basic check failed", me );
return 1;
}
/* but can't deal with block type */
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can only have scalar types ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
/* look at all per-axis kinds */
/* see if there's a range kind, verify that there's only one */
/* set haveMM */
haveMM = AIR_TRUE;
kindIn = nrrdKindUnknown;
kindAxis = 0; /* only means something if kindIn != nrrdKindUnknown */
for ( axi=0; axi<nin->dim; axi++ ) {
if ( nrrdKindUnknown == nin->axis[axi].kind
|| nrrdKindIsDomain( nin->axis[axi].kind ) ) {
haveMM &= AIR_EXISTS( nin->axis[axi].min );
haveMM &= AIR_EXISTS( nin->axis[axi].max );
} else {
if ( nrrdKindUnknown != kindIn ) {
biffAddf( NRRD, "%s: got non-domain kind %s on axis %u, but already "
"have kind %s on previous axis %u", me,
airEnumStr( nrrdKind, nin->axis[axi].kind ), axi,
airEnumStr( nrrdKind, kindIn ), kindAxis );
return 1;
}
kindIn = nin->axis[axi].kind;
kindAxis = axi;
}
}
if ( nrrdKindUnknown != kindIn && kindAxis ) {
/* have a non-domain axis, and it isn't the fastest */
if ( permuteComponentAxisFastest ) {
if ( nout == nin ) {
biffAddf( NRRD, "%s: can't permute non-domain axis %u ( kind %s ) "
"to axis 0 with nout == nin", me,
kindAxis, airEnumStr( nrrdKind, kindIn ) );
return 1;
}
biffAddf( NRRD, "%s: sorry, permuting non-domain axis %u ( kind %s ) "
"to axis 0 not yet implemented", me,
kindAxis, airEnumStr( nrrdKind, kindIn ) );
return 1;
} else {
/* caller thinks its okay for non-domain axis to be on
something other than fastest axis */
if ( nrrdMetaDataCanonicalVersionAlpha == version ) {
biffAddf( NRRD, "%s: ( %s ) non-domain axis %u ( kind %s ) "
"must be fastest axis", me,
airEnumStr( nrrdMetaDataCanonicalVersion, version ),
kindAxis, airEnumStr( nrrdKind, kindIn ) );
return 1;
}
/* maybe with nrrdMetaDataCanonicalVersionAlpha != version
it is okay to have non-domain axis on non-fastest axis? */
}
}
/* HEY: would be nice to handle a stub "scalar" axis by deleting it */
/* see if the non-domain kind is something we can interpret as a tensor */
if ( nrrdKindUnknown != kindIn ) {
switch ( kindIn ) {
/* ======= THESE are the kinds that we can possibly output ======= */
case nrrdKind2Vector:
case nrrdKind3Vector:
case nrrdKind4Vector:
case nrrdKind2DSymMatrix:
case nrrdKind2DMatrix:
case nrrdKind3DSymMatrix:
case nrrdKind3DMatrix:
/* =============================================================== */
kindOut = kindIn;
break;
/* Some other kinds are mapped to those above */
case nrrdKind3Color:
case nrrdKindRGBColor:
kindOut = nrrdKind3Vector;
break;
case nrrdKind4Color:
case nrrdKindRGBAColor:
kindOut = nrrdKind4Vector;
break;
default:
biffAddf( NRRD, "%s: got non-conforming kind %s on axis %u", me,
airEnumStr( nrrdKind, kindIn ), kindAxis );
return 1;
}
} else {
/* kindIn is nrrdKindUnknown, so its a simple scalar image,
and that's what the output will be too; kindOut == nrrdKindUnknown
is used in the code below to say "its a scalar image" */
kindOut = nrrdKindUnknown;
}
/* initialize output by copying meta-data from nin to ntmp */
mop = airMopNew( );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNix, airMopAlways );
/* HEY this is doing the work of a shallow copy, which isn't
available in the API. You can pass nrrdCopy( ) a nin with NULL
nin->data, which implements a shallow copy, but we can't set
nin->data=NULL here because of const correctness */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( nrrdWrap_nva( ntmp, NULL, nin->type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: couldn't wrap buffer nrrd around NULL", me );
airMopError( mop ); return 1;
}
/* so ntmp->data == NULL */
nrrdAxisInfoCopy( ntmp, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT );
if ( nrrdBasicInfoCopy( ntmp, nin, NRRD_BASIC_INFO_DATA_BIT ) ) {
biffAddf( NRRD, "%s: trouble copying basic info", me );
airMopError( mop ); return 1;
}
/* no comments */
nrrdCommentClear( ntmp );
/* no measurement frame */
gotmf = AIR_FALSE;
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
for ( sj=0; sj<NRRD_SPACE_DIM_MAX; sj++ ) {
gotmf |= AIR_EXISTS( ntmp->measurementFrame[si][sj] );
}
}
if ( lostMeasurementFrame ) {
*lostMeasurementFrame = gotmf;
}
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
for ( sj=0; sj<NRRD_SPACE_DIM_MAX; sj++ ) {
ntmp->measurementFrame[si][sj] = AIR_NAN;
}
}
/* no key/value pairs */
nrrdKeyValueClear( ntmp );
/* no content field */
ntmp->content = airFree( ntmp->content );
/* normalize domain kinds to "space" */
/* HEY: if Diderot supports time-varying fields, this will have to change */
/* turn off centers ( current Diderot semantics don't expose centering ) */
/* turn off thickness */
/* turn off labels and units */
for ( axi=0; axi<ntmp->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut ) {
ntmp->axis[axi].kind = nrrdKindSpace;
} else {
ntmp->axis[axi].kind = ( kindAxis == axi
? kindOut
: nrrdKindSpace );
}
ntmp->axis[axi].center = nrrdCenterUnknown;
ntmp->axis[axi].thickness = AIR_NAN;
ntmp->axis[axi].label = airFree( ntmp->axis[axi].label );
ntmp->axis[axi].units = airFree( ntmp->axis[axi].units );
ntmp->axis[axi].min = AIR_NAN;
ntmp->axis[axi].max = AIR_NAN;
ntmp->axis[axi].spacing = AIR_NAN;
}
/* logic of orientation definition:
If space dimension is known:
set origin to zero if not already set
set space direction to unit vector if not already set
Else if have per-axis min and max:
set spae origin and directions to communicate same intent
as original per-axis min and max and original centering
Else
set origin to zero and all space directions to units.
( It might be nice to use gage's logic for mapping from world to index,
but we have to accept a greater variety of kinds and dimensions
than gage ever has to process. )
The result is that space origin and space directions are set.
the "space" field is not used, only "spaceDim"
*/
/* no named space */
ntmp->space = nrrdSpaceUnknown;
if ( ntmp->spaceDim && !trivialOrient ) {
int saxi = 0;
if ( !nrrdSpaceVecExists( ntmp->spaceDim, ntmp->spaceOrigin ) ) {
nrrdSpaceVecSetZero( ntmp->spaceOrigin );
}
for ( axi=0; axi<ntmp->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
/* its a domain axis of output */
if ( !nrrdSpaceVecExists( ntmp->spaceDim,
ntmp->axis[axi].spaceDirection ) ) {
nrrdSpaceVecSetZero( ntmp->axis[axi].spaceDirection );
ntmp->axis[axi].spaceDirection[saxi] = sampleSpacing;
}
/* else we leave existing space vector as is */
saxi++;
} else {
/* else its a range ( non-domain, component ) axis */
nrrdSpaceVecSetNaN( ntmp->axis[axi].spaceDirection );
}
}
} else if ( haveMM && !trivialOrient ) {
int saxi = 0;
size_t N;
double rng;
for ( axi=0; axi<ntmp->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
/* its a domain axis of output */
nrrdSpaceVecSetZero( ntmp->axis[axi].spaceDirection );
rng = nin->axis[axi].max - nin->axis[axi].min;
if ( nrrdCenterNode == nin->axis[axi].center ) {
ntmp->spaceOrigin[saxi] = nin->axis[axi].min;
N = nin->axis[axi].size;
ntmp->axis[axi].spaceDirection[saxi] = rng/( N-1 );
} else {
/* unknown centering treated as cell */
N = nin->axis[axi].size;
ntmp->spaceOrigin[saxi] = nin->axis[axi].min + ( rng/N )/2;
ntmp->axis[axi].spaceDirection[saxi] = rng/N;
}
saxi++;
} else {
/* else its a range axis */
nrrdSpaceVecSetNaN( ntmp->axis[axi].spaceDirection );
}
}
ntmp->spaceDim = saxi;
} else {
/* either trivialOrient, or, not spaceDim and not haveMM */
int saxi = 0;
nrrdSpaceVecSetZero( ntmp->spaceOrigin );
for ( axi=0; axi<ntmp->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
/* its a domain axis of output */
nrrdSpaceVecSetZero( ntmp->axis[axi].spaceDirection );
ntmp->axis[axi].spaceDirection[saxi]
= ( AIR_EXISTS( nin->axis[axi].spacing )
? nin->axis[axi].spacing
: sampleSpacing );
saxi++;
} else {
/* else its a range axis */
nrrdSpaceVecSetNaN( ntmp->axis[axi].spaceDirection );
}
}
ntmp->spaceDim = saxi;
}
/* space dimension has to match the number of domain axes */
if ( ntmp->dim != ntmp->spaceDim + !!kindOut ) {
biffAddf( NRRD, "%s: output dim %d != spaceDim %d + %d %s%s%s%s",
me, ntmp->dim, ntmp->spaceDim, !!kindOut,
kindOut ? "for non-scalar ( " : "( scalar data )",
kindOut ? airEnumStr( nrrdKind, kindOut ) : "",
kindOut ? " ) data" : "",
kindOut ? "" : "; a non-domain axis in the input "
"may be missing an informative \"kind\", leading to the "
"false assumption of a scalar array" );
airMopError( mop ); return 1;
}
if ( recenterGrid ) {
/* sets field's origin so field is centered on the origin. capiche? */
/* this code was tacked on later than the stuff above, so its
logic could probably be moved up there, but it seems cleaner to
have it as a separate post-process */
double mean[NRRD_SPACE_DIM_MAX];
nrrdSpaceVecSetZero( mean );
for ( axi=0; axi<ntmp->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
nrrdSpaceVecScaleAdd2( mean, 1.0, mean,
0.5*( ntmp->axis[axi].size - 1 ),
ntmp->axis[axi].spaceDirection );
}
}
nrrdSpaceVecScaleAdd2( mean, 1.0, mean,
1.0, ntmp->spaceOrigin );
/* now mean is the center of the field */
nrrdSpaceVecScaleAdd2( ntmp->spaceOrigin,
1.0, ntmp->spaceOrigin,
-1.0, mean );
}
/* with that all done, now copy from ntmp to nout */
if ( nout != nin ) {
/* have to copy data */
ntmp->data = nin->data;
if ( nrrdCopy( nout, ntmp ) ) {
biffAddf( NRRD, "%s: problem copying ( with data ) to output", me );
airMopError( mop ); return 1;
}
} else {
/* nout == nin; have to copy only meta-data, leave data as is */
void *data = nin->data;
/* ntmp->data == NULL, so this is a shallow copy */
if ( nrrdCopy( nout, ntmp ) ) {
biffAddf( NRRD, "%s: problem copying meta-data to output", me );
airMopError( mop ); return 1;
}
nout->data = data;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
static double
27 returnZero( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double
33 returnOne( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
/*
** These kernels are the cardinal B-splines of different orders
** Using them with convolution assumes that the data has been pre-filtered
** so that the spline interpolates the original values.
*/
/* helper macros for doing abs( ) and remembering sign */
#define ABS_SGN( ax, sgn, x ) \
if ( x < 0 ) { \
sgn = -1; \
ax = -x; \
} else { \
sgn = 1; \
ax = x; \
}
/* helper macro for listing the various members of the kernel */
#define BSPL_DECL( ord, deriv ) \
0, \
_bspl##ord##_sup, \
( 0 == deriv ? returnOne : returnZero ), \
_bspl##ord##d##deriv##_1f, \
_bspl##ord##d##deriv##_Nf, \
_bspl##ord##d##deriv##_1d, \
_bspl##ord##d##deriv##_Nd
/* ( the following preceded some of the function definitions before they
were all packed up in the _METHODS macros; but the question about the
type of the locals is still relevant )
HEY: there is a possibly interesting question to be answered here
about whether, to distinguish the float-specific and
double-specific versions of the kernel eval functions, if the
float-specific versions should actually only use floats for locals,
so that no casting to float is needed at return, or, if its too
72 much of a precision loss to do so, at no real economy of speed, so
doubles should be used for all intermediate calculations, prior to
the final cast to float. The macros below do the casting,
whether or not is as actually needed, so this can be experimented
with by just changing the type of the locals ( without changing the
macro definitions ) */
#define BSPL_EVEN_METHODS( basename, macro ) \
static double \
81 basename##_1d( double x, const double *parm ) { \
double ax, tmp, r; \
AIR_UNUSED( parm ); \
\
ax = AIR_ABS( x ); \
86 macro( r, double, tmp, ax ); \
return r; \
88 } \
\
static float \
basename##_1f( float x, const double *parm ) { \
float ax, tmp, r; \
AIR_UNUSED( parm ); \
\
95 ax = AIR_ABS( x ); \
macro( r, float, tmp, ax ); \
return r; \
} \
\
static void \
basename##_Nd( double *f, const double *x, size_t len, \
102 const double *parm ) { \
double ax, tmp, r; \
size_t i; \
AIR_UNUSED( parm ); \
\
for ( i=0; i<len; i++ ) { \
108 ax = x[i]; ax = AIR_ABS( ax ); \
macro( r, double, tmp, ax ); \
f[i] = r; \
} \
} \
\
static void \
basename##_Nf( float *f, const float *x, size_t len, \
const double *parm ) { \
117 float ax, tmp, r; \
size_t i; \
AIR_UNUSED( parm ); \
\
for ( i=0; i<len; i++ ) { \
122 ax = x[i]; ax = AIR_ABS( ax ); \
macro( r, float, tmp, ax ); \
124 f[i] = r; \
} \
}
#define BSPL_ODD_METHODS( basename, macro ) \
static double \
basename##_1d( double x, const double *parm ) { \
131 double ax, tmp, r; \
int sgn; \
AIR_UNUSED( parm ); \
\
ABS_SGN( ax, sgn, x ); \
macro( r, double, tmp, ax ); \
return sgn*r; \
138 } \
\
static float \
basename##_1f( float x, const double *parm ) { \
float sgn, ax, tmp, r; \
AIR_UNUSED( parm ); \
\
ABS_SGN( ax, sgn, x ); \
macro( r, float, tmp, ax ); \
return sgn*r; \
} \
\
static void \
basename##_Nd( double *f, const double *x, size_t len, \
const double *parm ) { \
double sgn, ax, tmp, r; \
size_t i; \
AIR_UNUSED( parm ); \
\
for ( i=0; i<len; i++ ) { \
ABS_SGN( ax, sgn, x[i] ); \
macro( r, double, tmp, ax ); \
f[i] = sgn*r; \
} \
} \
\
static void \
basename##_Nf( float *f, const float *x, size_t len, \
const double *parm ) { \
float sgn, ax, tmp, r; \
size_t i; \
AIR_UNUSED( parm ); \
\
for ( i=0; i<len; i++ ) { \
ABS_SGN( ax, sgn, x[i] ); \
macro( r, float, tmp, ax ); \
f[i] = sgn*r; \
} \
}
/* ============================= order *1* ============================= */
static double
_bspl1_sup( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
/* ---------------------- order *1* deriv *0* -------------------------- */
#define BSPL1D0( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 1 ) { \
ret = AIR_CAST( TT, 1.0 - x ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl1d0, BSPL1D0 )
static NrrdKernel
_nrrdKernelBSpline1 = {
"bspl1",
BSPL_DECL( 1, 0 )
};
NrrdKernel *const
nrrdKernelBSpline1 = &_nrrdKernelBSpline1;
/* ---------------------- order *1* deriv *1* -------------------------- */
#define BSPL1D1( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 1 ) { \
ret = AIR_CAST( TT, -1.0 ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl1d1, BSPL1D1 )
static NrrdKernel
_nrrdKernelBSpline1D = {
"bspl1d",
BSPL_DECL( 1, 1 )
};
NrrdKernel *const
nrrdKernelBSpline1D = &_nrrdKernelBSpline1D;
/* ============================= order *2* ============================= */
static double
_bspl2_sup( const double *parm ) {
AIR_UNUSED( parm );
return 1.5;
}
/* ---------------------- order *2* deriv *0* -------------------------- */
#define BSPL2D0( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, 3.0/4.0 - x*x ); \
} else if ( x < 1.5 ) { \
t = ( 3 - 2*x ); \
ret = AIR_CAST( TT, t*t/8 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl2d0, BSPL2D0 )
static NrrdKernel
_nrrdKernelBSpline2 = {
"bspl2",
BSPL_DECL( 2, 0 )
};
NrrdKernel *const
nrrdKernelBSpline2 = &_nrrdKernelBSpline2;
/* ---------------------- order *2* deriv *1* -------------------------- */
#define BSPL2D1( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, -2*x ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, -3.0/2.0 + x ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl2d1, BSPL2D1 )
static NrrdKernel
_nrrdKernelBSpline2D = {
"bspl2d",
BSPL_DECL( 2, 1 )
};
NrrdKernel *const
nrrdKernelBSpline2D = &_nrrdKernelBSpline2D;
/* ---------------------- order *2* deriv *2* -------------------------- */
#define BSPL2D2( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, -2.0 ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, 1.0 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl2d2, BSPL2D2 )
static NrrdKernel
_nrrdKernelBSpline2DD = {
"bspl2dd",
BSPL_DECL( 2, 2 )
};
NrrdKernel *const
nrrdKernelBSpline2DD = &_nrrdKernelBSpline2DD;
/* ============================= order *3* ============================= */
static double
_bspl3_sup( const double *parm ) {
AIR_UNUSED( parm );
return 2.0;
}
/* ---------------------- order *3* deriv *0* -------------------------- */
#define BSPL3D0( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, ( 4 + 3*( -2 + x )*x*x )/6 ); \
} else if ( x < 2 ) { \
t = ( -2 + x ); \
ret = AIR_CAST( TT, -t*t*t/6 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl3d0, BSPL3D0 )
static NrrdKernel
_nrrdKernelBSpline3 = {
"bspl3",
BSPL_DECL( 3, 0 )
};
NrrdKernel *const
nrrdKernelBSpline3 = &_nrrdKernelBSpline3;
/* ---------------------- order *3* deriv *1* -------------------------- */
#define BSPL3D1( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, ( -4 + 3*x )*x/2 ); \
} else if ( x < 2 ) { \
t = ( -2 + x ); \
ret = AIR_CAST( TT, -t*t/2 ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl3d1, BSPL3D1 )
static NrrdKernel
_nrrdKernelBSpline3D = {
"bspl3d",
BSPL_DECL( 3, 1 )
};
NrrdKernel *const
nrrdKernelBSpline3D = &_nrrdKernelBSpline3D;
/* ---------------------- order *3* deriv *2* -------------------------- */
/* NOTE: the tmp variable wasn't actually needed here, and this will
** likely be optimized out. But the tmp argument to the macro is kept
** here ( and the macro uses it to avoid a unused variable warning ) to
** facilitate copy-and-paste for higher-order splines
*/
#define BSPL3D2( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 1 ) { \
ret = AIR_CAST( TT, -2 + 3*x ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, 2 - x ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl3d2, BSPL3D2 )
static NrrdKernel
_nrrdKernelBSpline3DD = {
"bspl3dd",
BSPL_DECL( 3, 2 )
};
NrrdKernel *const
nrrdKernelBSpline3DD = &_nrrdKernelBSpline3DD;
/* ---------------------- order *3* deriv *3* -------------------------- */
#define BSPL3D3( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 1 ) { \
ret = 3; \
} else if ( x < 2 ) { \
ret = -1; \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl3d3, BSPL3D3 )
static NrrdKernel
_nrrdKernelBSpline3DDD = {
"bspl3ddd",
BSPL_DECL( 3, 3 )
};
NrrdKernel *const
nrrdKernelBSpline3DDD = &_nrrdKernelBSpline3DDD;
/* ------------- order *3* approximate numerical inverse -------------- */
/* still need to implement:
** Unser et al B-Spline Signal Processing: Part I & II, IEEE
** Transactions on Signal Processing, 1993, 41( 2 ):821-833, 834--848
** but until then here's a slower way of approximating the prefiltering,
** which is still faster than doing iterative deconvolution. These
** weights were determined by GLK with Mathematica, by inverting the
** matrix representing discrete convolution with the spline
**
** Note that with all the approx inverse kernels, the support really
** does end at a half-integer ( they are piece-wise constant on unit
** intervals centered at integers )
*/
#define BSPL3_AI_LEN 12
static double
_bspl3_ANI_kvals[BSPL3_AI_LEN] = {
2672279.0/1542841.0,
-( 716035.0/1542841.0 ),
191861.0/1542841.0,
-( 51409.0/1542841.0 ),
13775.0/1542841.0,
-( 3691.0/1542841.0 ),
989.0/1542841.0,
-( 265.0/1542841.0 ),
71.0/1542841.0,
-( 19.0/1542841.0 ),
5.0/1542841.0,
-( 1.0/1542841.0 )};
static double
_bspl3_ANI_sup( const double *parm ) {
AIR_UNUSED( parm );
return BSPL3_AI_LEN + 0.5;
}
static double
_bspl3_ANI_int( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
#define BSPL3_ANI( ret, tmp, x ) \
tmp = AIR_CAST( unsigned int, x+0.5 ); \
if ( tmp < BSPL3_AI_LEN ) { \
ret = _bspl3_ANI_kvals[tmp]; \
} else { \
ret = 0.0; \
}
static double
_bspl3_ANI_1d( double x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL3_ANI( r, tmp, ax );
return r;
}
static float
_bspl3_ANI_1f( float x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL3_ANI( r, tmp, ax );
return AIR_CAST( float, r );
}
static void
_bspl3_ANI_Nd( double *f, const double *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL3_ANI( r, tmp, ax );
f[i] = r;
}
}
static void
_bspl3_ANI_Nf( float *f, const float *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL3_ANI( r, tmp, ax );
f[i] = AIR_CAST( float, r );
}
}
static NrrdKernel
_nrrdKernelBSpline3ApproxInverse = {
"bspl3ai", 0,
_bspl3_ANI_sup, _bspl3_ANI_int,
_bspl3_ANI_1f, _bspl3_ANI_Nf,
_bspl3_ANI_1d, _bspl3_ANI_Nd
};
NrrdKernel *const
nrrdKernelBSpline3ApproxInverse = &_nrrdKernelBSpline3ApproxInverse;
/* ============================= order *4* ============================= */
static double
_bspl4_sup( const double *parm ) {
AIR_UNUSED( parm );
return 2.5;
}
/* ---------------------- order *4* deriv *0* -------------------------- */
#define BSPL4D0( ret, TT, t, x ) \
if ( x < 0.5 ) { \
t = x*x; \
ret = AIR_CAST( TT, 115.0/192.0 - 5*t/8 + t*t/4 ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, ( 55.0 + 4*x*( 5.0 - 2*x*( 15.0 + 2*( -5 + x )*x ) ) )/96 ); \
} else if ( x < 2.5 ) { \
t = 5 - 2*x; \
ret = AIR_CAST( TT, t*t*t*t/384.0 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl4d0, BSPL4D0 )
static NrrdKernel
_nrrdKernelBSpline4 = {
"bspl4",
BSPL_DECL( 4, 0 )
};
NrrdKernel *const
nrrdKernelBSpline4 = &_nrrdKernelBSpline4;
/* ---------------------- order *4* deriv *1* -------------------------- */
#define BSPL4D1( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, x*( -5.0/4.0 + x*x ) ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, ( 5.0 - 4*x*( 15.0 + x*( -15.0 + 4*x ) ) )/24.0 ); \
} else if ( x < 2.5 ) { \
t = -5 + 2*x; \
ret = AIR_CAST( TT, t*t*t/48.0 ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl4d1, BSPL4D1 )
static NrrdKernel
_nrrdKernelBSpline4D = {
"bspl4d",
BSPL_DECL( 4, 1 )
};
NrrdKernel *const
nrrdKernelBSpline4D = &_nrrdKernelBSpline4D;
/* ---------------------- order *4* deriv *2* -------------------------- */
#define BSPL4D2( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, -5.0/4.0 + 3*x*x ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, -5.0/2.0 + ( 5.0 - 2*x )*x ); \
} else if ( x < 2.5 ) { \
t = 5 - 2*x; \
ret = AIR_CAST( TT, t*t/8.0 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl4d2, BSPL4D2 )
static NrrdKernel
_nrrdKernelBSpline4DD = {
"bspl4dd",
BSPL_DECL( 4, 2 )
};
NrrdKernel *const
nrrdKernelBSpline4DD = &_nrrdKernelBSpline4DD;
/* ---------------------- order *4* deriv *3* -------------------------- */
#define BSPL4D3( ret, TT, t, x ) \
AIR_UNUSED( t ); \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, 6*x ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, 5 - 4*x ); \
} else if ( x < 2.5 ) { \
ret = AIR_CAST( TT, -5.0/2.0 + x ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl4d3, BSPL4D3 )
static NrrdKernel
_nrrdKernelBSpline4DDD = {
"bspl4ddd",
BSPL_DECL( 4, 3 )
};
NrrdKernel *const
nrrdKernelBSpline4DDD = &_nrrdKernelBSpline4DDD;
/* ============================= order *5* ============================= */
static double
_bspl5_sup( const double *parm ) {
AIR_UNUSED( parm );
return 3.0;
}
/* ---------------------- order *5* deriv *0* -------------------------- */
#define BSPL5D0( ret, TT, t, x ) \
if ( x < 1 ) { \
t = x*x; \
ret = AIR_CAST( TT, ( 33 - 5*t*( 6 + ( x-3 )*t ) )/60 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, ( 51 + 5*x*( 15 + x*( -42 + x*( 30 + ( -9 + x )*x ) ) ) )/120 ); \
} else if ( x < 3 ) { \
t = x - 3; \
ret = AIR_CAST( TT, -t*t*t*t*t/120 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl5d0, BSPL5D0 )
static NrrdKernel
_nrrdKernelBSpline5 = {
"bspl5",
BSPL_DECL( 5, 0 )
};
NrrdKernel *const
nrrdKernelBSpline5 = &_nrrdKernelBSpline5;
/* ---------------------- order *5* deriv *1* -------------------------- */
#define BSPL5D1( ret, TT, t, x ) \
if ( x < 1 ) { \
t = x*x*x; \
ret = AIR_CAST( TT, -x + t - ( 5*t*x )/12 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, ( 15 + x*( -84 + x*( 90 + x*( -36 + 5*x ) ) ) )/24 ); \
} else if ( x < 3 ) { \
t = -3 + x; \
ret = AIR_CAST( TT, -t*t*t*t/24 ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl5d1, BSPL5D1 )
static NrrdKernel
_nrrdKernelBSpline5D = {
"bspl5d",
BSPL_DECL( 5, 1 )
};
NrrdKernel *const
nrrdKernelBSpline5D = &_nrrdKernelBSpline5D;
/* ---------------------- order *5* deriv *2* -------------------------- */
#define BSPL5D2( ret, TT, t, x ) \
if ( x < 1 ) { \
t = x*x; \
ret = AIR_CAST( TT, -1 + 3*t - ( 5*t*x )/3 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, ( -21 + x*( 45 + x*( -27 + 5*x ) ) )/6 ); \
} else if ( x < 3 ) { \
t = -3 + x; \
ret = AIR_CAST( TT, -t*t*t/6 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl5d2, BSPL5D2 )
static NrrdKernel
_nrrdKernelBSpline5DD = {
"bspl5dd",
BSPL_DECL( 5, 2 )
};
NrrdKernel *const
nrrdKernelBSpline5DD = &_nrrdKernelBSpline5DD;
/* ---------------------- order *5* deriv *3* -------------------------- */
#define BSPL5D3( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, ( 6 - 5*x )*x ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, 15.0/2.0 - 9*x + 5*x*x/2 ); \
} else if ( x < 3 ) { \
t = -3 + x; \
ret = AIR_CAST( TT, -t*t/2 ); \
} else { \
ret = 0; \
}
BSPL_ODD_METHODS( _bspl5d3, BSPL5D3 )
static NrrdKernel
_nrrdKernelBSpline5DDD = {
"bspl5ddd",
BSPL_DECL( 5, 3 )
};
NrrdKernel *const
nrrdKernelBSpline5DDD = &_nrrdKernelBSpline5DDD;
/* ------------- order *5* approximate numerical inverse -------------- */
#define BSPL5_AI_LEN 19
static double
_bspl5_ANI_kvals[BSPL5_AI_LEN] = {
2.842170922021427870236333,
-1.321729472987239796417307,
0.5733258709611149890510146,
-0.2470419274010479815114381,
0.1063780046404650785440854,
-0.04580408418467518130037713,
0.01972212399699206014654736,
-0.008491860984275658620122180,
0.003656385950780789716770681,
-0.001574349495225446217828165,
0.0006778757185045443332966769,
-0.0002918757322635763049702028,
0.0001256725426338698784062181,
-0.00005410696497728715841372199,
0.00002328659592249373987497103,
-0.00001000218170092531503506361,
4.249940115067599514119408e-6,
-1.698979738236873388431330e-6,
4.475539012615912040164139e-7};
static double
_bspl5_ANI_sup( const double *parm ) {
AIR_UNUSED( parm );
return BSPL5_AI_LEN + 0.5;
}
static double
_bspl5_ANI_int( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
#define BSPL5_ANI_T( ret, TT, tmp, x ) \
tmp = AIR_CAST( unsigned int, x+0.5 ); \
if ( tmp < BSPL5_AI_LEN ) { \
ret = AIR_CAST( TT, _bspl5_ANI_kvals[tmp] ); \
} else { \
ret = 0.0; \
}
static double
_bspl5_ANI_1d( double x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL5_ANI_T( r, double, tmp, ax );
return r;
}
static float
_bspl5_ANI_1f( float x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL5_ANI_T( r, float, tmp, ax );
return AIR_CAST( float, r );
}
static void
_bspl5_ANI_Nd( double *f, const double *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL5_ANI_T( r, double, tmp, ax );
f[i] = r;
}
}
static void
_bspl5_ANI_Nf( float *f, const float *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL5_ANI_T( r, float, tmp, ax );
f[i] = AIR_CAST( float, r );
}
}
static NrrdKernel
_nrrdKernelBSpline5ApproxInverse = {
"bspl5ai", 0,
_bspl5_ANI_sup, _bspl5_ANI_int,
_bspl5_ANI_1f, _bspl5_ANI_Nf,
_bspl5_ANI_1d, _bspl5_ANI_Nd
};
NrrdKernel *const
nrrdKernelBSpline5ApproxInverse = &_nrrdKernelBSpline5ApproxInverse;
/* ============================= order *6* ============================= */
static double
_bspl6_sup( const double *parm ) {
AIR_UNUSED( parm );
return 3.5;
}
/* ---------------------- order *6* deriv *0* -------------------------- */
#define BSPL6D0_SEG0( x ) 0.5110243055555555556 + x*x*( -0.40104166666666666667 + x*x*( 0.14583333333333333333 - 0.027777777777777777778*x*x ) )
#define BSPL6D0_SEG1( x ) 0.02083333333333333*( 5.05890179802561 + ( -4.47046426301056 + x )*x )*( 5.07700929828288 + ( -4.13708416717549 + x )*x )*( 0.956452947962608 + x*( 1.607548430186042 + x ) )
#define BSPL6D0_SEG2( x ) -0.008333333333333*( -2.919623692889 + x )*( 0.11036932382080 + x )*( 8.451507829592 + ( -5.787493668289 + x )*x )*( 7.911791484411 + ( -5.403251962643 + x )*x )
#define BSPL6D0( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, BSPL6D0_SEG0( x ) ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, BSPL6D0_SEG1( x ) ); \
} else if ( x < 2.5 ) { \
ret = AIR_CAST( TT, BSPL6D0_SEG2( x ) ); \
} else if ( x < 3.5 ) { \
t = AIR_CAST( TT, x - 3.5 ); \
ret = AIR_CAST( TT, 0.00139*t*t*t*t*t*t ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl6d0, BSPL6D0 )
static NrrdKernel
_nrrdKernelBSpline6 = {
"bspl6",
BSPL_DECL( 6, 0 )
};
NrrdKernel *const
nrrdKernelBSpline6 = &_nrrdKernelBSpline6;
/* ---------------------- order *6* deriv *1* -------------------------- */
#define BSPL6D1_SEG0( x ) x*( -0.8020833333333333333 + x*x*( 0.5833333333333333333 - x*x*0.16666666666666666667 ) )
#define BSPL6D1_SEG1( x ) 0.1250000000000000*( -2.204221529535419 + x )*( 0.01290998431413690 + x )*( 0.5355244627388528 + x )*( 4.784830284687429 + ( -4.177546250850904 + x )*x )
#define BSPL6D1_SEG2( x ) -0.050000000000000*( -0.39815802840054 + x )*( 8.4005837632394 + ( -5.7883654809137 + x )*x )*( 7.8916975718499 + ( -5.4801431573524 + x )*x )
#define BSPL6D1( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, BSPL6D1_SEG0( x ) ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, BSPL6D1_SEG1( x ) ); \
} else if ( x < 2.5 ) { \
ret = AIR_CAST( TT, BSPL6D1_SEG2( x ) ); \
} else if ( x < 3.5 ) { \
t = AIR_CAST( TT, -3.5 + x ); \
ret = AIR_CAST( TT, 0.00833*t*t*t*t*t ); \
} else { \
ret = 0.0; \
}
BSPL_ODD_METHODS( _bspl6d1, BSPL6D1 )
static NrrdKernel
_nrrdKernelBSpline6D = {
"bspl6d",
BSPL_DECL( 6, 1 )
};
NrrdKernel *const
nrrdKernelBSpline6D = &_nrrdKernelBSpline6D;
/* ---------------------- order *6* deriv *2* -------------------------- */
#define BSPL6D2_SEG0( x ) -0.80208333333333333333 + x*x*( 1.75 - x*x*0.833333333333333333 )
#define BSPL6D2_SEG1( x ) 0.625*( -0.8093237825464294 + x )*( 0.3133677888004832 + x )*( 4.485127047744998 + ( -4.170710672920720 + x )*x )
#define BSPL6D2_SEG2( x ) -0.25*( -2.88072372021534 + x )*( -0.904025842763129 + x )*( 7.89575131106459 + ( -5.54858377035486 + x )*x )
#define BSPL6D2( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, BSPL6D2_SEG0( x ) ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, BSPL6D2_SEG1( x ) ); \
} else if ( x < 2.5 ) { \
ret = AIR_CAST( TT, BSPL6D2_SEG2( x ) ); \
} else if ( x < 3.5 ) { \
t = AIR_CAST( TT, 7.0 - 2.0*x ); \
ret = AIR_CAST( TT, 0.0026041666666666666667*t*t*t*t ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl6d2, BSPL6D2 )
static NrrdKernel
_nrrdKernelBSpline6DD = {
"bspl6dd",
BSPL_DECL( 6, 2 )
};
NrrdKernel *const
nrrdKernelBSpline6DD = &_nrrdKernelBSpline6DD;
/* ---------------------- order *6* deriv *3* -------------------------- */
#define BSPL6D3( ret, TT, t, x ) \
if ( x < 0.5 ) { \
ret = AIR_CAST( TT, x*( 3.5 - 3.3333333333333333333*x*x ) ); \
} else if ( x < 1.5 ) { \
ret = AIR_CAST( TT, 2.5*( -1.99263608511781188 + x )*( -1.40303873392913616 + x )*( -0.104325180953051958 + x ) ); \
} else if ( x < 2.5 ) { \
ret = AIR_CAST( TT, -1*( -1.404627184534107 + x )*( 7.890587235793465 + ( -5.595372815465893 + x )*x ) ); \
} else if ( x < 3.5 ) { \
t = AIR_CAST( TT, -7 + 2*x ); \
ret = AIR_CAST( TT, 0.020833333333333333333*t*t*t ); \
} else { \
ret = 0.0; \
}
BSPL_ODD_METHODS( _bspl6d3, BSPL6D3 )
static NrrdKernel
_nrrdKernelBSpline6DDD = {
"bspl6ddd",
BSPL_DECL( 6, 3 )
};
NrrdKernel *const
nrrdKernelBSpline6DDD = &_nrrdKernelBSpline6DDD;
/* ============================= order *7* ============================= */
static double
_bspl7_sup( const double *parm ) {
AIR_UNUSED( parm );
return 4.0;
}
/* ---------------------- order *7* deriv *0* -------------------------- */
#define BSPL7D0( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, 151.0/315.0 + x*x*( -48.0 + x*x*( 16.0 + x*x*( -4 + x ) ) )/144.0 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, ( 2472 - 7*x*( 56 + x*( 72 + x*( 280 + 3*( -6 + x )*x*( 20 + ( -6 + x )*x ) ) ) ) )/5040.0 ); \
} else if ( x < 3 ) { \
ret = AIR_CAST( TT, ( -1112 + 7*x*( 1736 + x*( -2760 + x*( 1960 + x*( -760 + x*( 168 + ( -20 + x )*x ) ) ) ) ) )/5040.0 ); \
} else if ( x < 4 ) { \
t = x - 4; \
ret = AIR_CAST( TT, -t*t*t*t*t*t*t/5040 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl7d0, BSPL7D0 )
static NrrdKernel
_nrrdKernelBSpline7 = {
"bspl7",
BSPL_DECL( 7, 0 )
};
NrrdKernel *const
nrrdKernelBSpline7 = &_nrrdKernelBSpline7;
/* ---------------------- order *7* deriv *1* -------------------------- */
#define BSPL7D1( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, x*( -96.0 + x*x*( 64.0 + x*x*( -24.0 + 7.0*x ) ) )/144.0 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, -7.0/90.0 - ( -2 + x )*x*( -24 + ( -2 + x )*x*( 76 + x*( -44 + 7*x ) ) )/240.0 ); \
} else if ( x < 3 ) { \
ret = AIR_CAST( TT, ( 2 + ( -4 + x )*x )*( 868 + x*( -1024 + x*( 458 + x*( -92 + 7*x ) ) ) )/720.0 ); \
} else if ( x < 4 ) { \
t = -4 + x; \
ret = AIR_CAST( TT, -t*t*t*t*t*t/720 ); \
} else { \
ret = 0.0; \
}
BSPL_ODD_METHODS( _bspl7d1, BSPL7D1 )
static NrrdKernel
_nrrdKernelBSpline7D = {
"bspl7d",
BSPL_DECL( 7, 1 )
};
NrrdKernel *const
nrrdKernelBSpline7D = &_nrrdKernelBSpline7D;
/* ---------------------- order *7* deriv *2* -------------------------- */
#define BSPL7D2( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, ( -16.0 + x*x*( 32 + x*x*( -20 + 7*x ) ) )/24.0 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, -1.0/5.0 - 7*x/3 + 6*x*x - 14*x*x*x/3 + 3*x*x*x*x/2 - 7*x*x*x*x*x/40 ); \
} else if ( x < 3 ) { \
ret = AIR_CAST( TT, ( -920 + x*( 1960 + x*( -1520 + x*( 560 + x*( -100 + 7*x ) ) ) ) )/120.0 ); \
} else if ( x < 4 ) { \
t = -4 + x; \
ret = AIR_CAST( TT, -t*t*t*t*t/120 ); \
} else { \
ret = 0; \
}
BSPL_EVEN_METHODS( _bspl7d2, BSPL7D2 )
static NrrdKernel
_nrrdKernelBSpline7DD = {
"bspl7dd",
BSPL_DECL( 7, 2 )
};
NrrdKernel *const
nrrdKernelBSpline7DD = &_nrrdKernelBSpline7DD;
/* ---------------------- order *7* deriv *3* -------------------------- */
#define BSPL7D3( ret, TT, t, x ) \
if ( x < 1 ) { \
ret = AIR_CAST( TT, x*( 64 + 5*x*x*( -16 + 7*x ) )/24 ); \
} else if ( x < 2 ) { \
ret = AIR_CAST( TT, -7.0/3.0 + x*( 12 + x*( -14 + x*( 6 - 7*x/8 ) ) ) ); \
} else if ( x < 3 ) { \
ret = AIR_CAST( TT, ( 392 + x*( -608 + x*( 336 + x*( -80 + 7*x ) ) ) )/24 ); \
} else if ( x < 4 ) { \
t = -4 + x; \
ret = AIR_CAST( TT, -t*t*t*t/24 ); \
} else { \
ret = 0.0; \
}
BSPL_ODD_METHODS( _bspl7d3, BSPL7D3 )
static NrrdKernel
_nrrdKernelBSpline7DDD = {
"bspl7ddd",
BSPL_DECL( 7, 3 )
};
NrrdKernel *const
nrrdKernelBSpline7DDD = &_nrrdKernelBSpline7DDD;
/* ------------- order *7* approximate numerical inverse -------------- */
#define BSPL7_AI_LEN 26
static double
_bspl7_ANI_kvals[BSPL7_AI_LEN] = {
4.964732886301469059137801,
-3.091042499769118182213297,
1.707958936669135515487259,
-0.9207818274511302808978934,
0.4936786139601599067344824,
-0.2643548049418435742509870,
0.1415160014538524997926456,
-0.07575222270391683956827192,
0.04054886334181815702759984,
-0.02170503519322401084648773,
0.01161828326728242899507066,
-0.006219039932262414977444894,
0.003328930278070297807163008,
-0.001781910982713036390230280,
0.0009538216015244754251250379,
-0.0005105611456814427816916412,
0.0002732917233015012426069489,
-0.0001462845976614043380333786,
0.00007829746549013888268504229,
-0.00004190023413676309286922788,
0.00002240807576972098806040711,
-0.00001195669542335526044896263,
6.329480796176889498331054e-6,
-3.256910241436675950084186e-6,
1.506132735770447868981087e-6,
-4.260433183779953604188120e-7};
static double
_bspl7_ANI_sup( const double *parm ) {
AIR_UNUSED( parm );
return BSPL7_AI_LEN + 0.5;
}
static double
_bspl7_ANI_int( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
#define BSPL7_ANI( ret, tmp, x ) \
tmp = AIR_CAST( unsigned int, x+0.5 ); \
if ( tmp < BSPL7_AI_LEN ) { \
ret = _bspl7_ANI_kvals[tmp]; \
} else { \
ret = 0.0; \
}
static double
_bspl7_ANI_1d( double x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL7_ANI( r, tmp, ax );
return r;
}
static float
_bspl7_ANI_1f( float x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
BSPL7_ANI( r, tmp, ax );
return AIR_CAST( float, r );
}
static void
_bspl7_ANI_Nd( double *f, const double *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL7_ANI( r, tmp, ax );
f[i] = r;
}
}
static void
_bspl7_ANI_Nf( float *f, const float *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
BSPL7_ANI( r, tmp, ax );
f[i] = AIR_CAST( float, r );
}
}
static NrrdKernel
_nrrdKernelBSpline7ApproxInverse = {
"bspl7ai", 0,
_bspl7_ANI_sup, _bspl7_ANI_int,
_bspl7_ANI_1f, _bspl7_ANI_Nf,
_bspl7_ANI_1d, _bspl7_ANI_Nd
};
NrrdKernel *const
nrrdKernelBSpline7ApproxInverse = &_nrrdKernelBSpline7ApproxInverse;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** learned: if you have globals, such as _nrrdCC_verb, which are
** defined and declared here, but which are NOT initialized, then
** C++ apps which are linking against Teem will have problems!!!
** This was first seen on the mac.
*/
int _nrrdCC_verb = 0;
int _nrrdCC_EqvIncr = 10000; /* HEY: this has to be big so that ccfind is not
stuck constantly re-allocating the eqv array.
This is will be one of the best places to
test the new multiplicative reallocation
strategy, planned for Teem 2.0 */
int
41 _nrrdCCFind_1( Nrrd *nout, unsigned int *numid, const Nrrd *nin ) {
/* static const char me[]="_nrrdCCFind_1"; */
unsigned int sx, I, id, lval, val, *out, ( *lup )( const void *, size_t );
lup = nrrdUILookup[nin->type];
out = AIR_CAST( unsigned int*, nout->data );
out[0] = id = 0;
*numid = 1;
sx = AIR_CAST( unsigned int, nin->axis[0].size );
lval = lup( nin->data, 0 );
for ( I=1; I<sx; I++ ) {
val = lup( nin->data, I );
if ( lval != val ) {
id++;
( *numid )++;
}
out[I] = id;
lval = val;
}
return 0;
}
/*
** layout of value ( pvl ) and index ( pid ) cache:
**
** 2 3 4 --> X
** 1 . . oddly, index 0 is never used
** . . .
** |
** v Y
*/
int
75 _nrrdCCFind_2( Nrrd *nout, unsigned int *numid, airArray *eqvArr,
const Nrrd *nin, unsigned int conny ) {
static const char me[]="_nrrdCCFind_2";
double vl=0, pvl[5]={0, 0, 0, 0, 0};
unsigned int id, pid[5]={0, 0, 0, 0, 0}, ( *lup )( const void *, size_t ), *out;
unsigned int p, x, y, sx, sy;
id = 0; /* sssh! compiler warnings */
lup = nrrdUILookup[nin->type];
out = AIR_CAST( unsigned int*, nout->data );
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
#define GETV_2( x, y ) ( ( AIR_IN_CL( 0, AIR_CAST( int, x ), AIR_CAST( int, sx-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, y ), AIR_CAST( int, sy-1 ) ) ) \
? lup( nin->data, ( x ) + sx*( y ) ) \
: 0.5 ) /* value that can't come from an array of uints */
#define GETI_2( x, y ) ( ( AIR_IN_CL( 0, AIR_CAST( int, x ), AIR_CAST( int, sx-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, y ), AIR_CAST( int, sy-1 ) ) ) \
? out[( x ) + sx*( y )] \
: AIR_CAST( unsigned int, -1 ) ) /* CC index ( probably! )
never assigned */
*numid = 0;
for ( y=0; y<sy; y++ ) {
for ( x=0; x<sx; x++ ) {
if ( _nrrdCC_verb ) {
fprintf( stderr, "%s( %d, %d ) -----------\n", me, x, y );
}
if ( !x ) {
pvl[1] = GETV_2( -1, y ); pid[1] = GETI_2( -1, y );
pvl[2] = GETV_2( -1, y-1 ); pid[2] = GETI_2( -1, y-1 );
pvl[3] = GETV_2( 0, y-1 ); pid[3] = GETI_2( 0, y-1 );
} else {
pvl[1] = vl; pid[1] = id;
pvl[2] = pvl[3]; pid[2] = pid[3];
pvl[3] = pvl[4]; pid[3] = pid[4];
}
pvl[4] = GETV_2( x+1, y-1 ); pid[4] = GETI_2( x+1, y-1 );
vl = GETV_2( x, y );
p = 0;
if ( vl == pvl[1] ) {
id = pid[p=1];
}
#define TEST( P ) \
if ( vl == pvl[( P )] ) { \
if ( p ) { /* we already had a value match */ \
if ( id != pid[( P )] ) { \
airEqvAdd( eqvArr, pid[( P )], id ); \
} \
} else { \
id = pid[p=( P )]; \
} \
}
TEST( 3 );
if ( 2 == conny ) {
TEST( 2 );
TEST( 4 );
}
if ( !p ) {
/* didn't match anything previous */
id = *numid;
( *numid )++;
}
if ( _nrrdCC_verb ) {
fprintf( stderr, "%s: pvl: %g %g %g %g ( vl = %g )\n", me,
pvl[1], pvl[2], pvl[3], pvl[4], vl );
fprintf( stderr, " pid: %d %d %d %d\n",
pid[1], pid[2], pid[3], pid[4] );
fprintf( stderr, " --> p = %d, id = %d, *numid = %d\n",
p, id, *numid );
}
out[x + sx*y] = id;
}
}
return 0;
}
/*
**
** 5 6 7 --> X
** 8 9 10
** 11 12 13
** |
** v Y
** 2 3 4
** / 1 . . again, 0 index never used, for reasons forgotten
** Z . . .
*/
int
_nrrdCCFind_3( Nrrd *nout, unsigned int *numid, airArray *eqvArr,
const Nrrd *nin, unsigned int conny ) {
/* static const char me[]="_nrrdCCFind_3" ; */
double pvl[14]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, vl=0;
unsigned int id, *out, ( *lup )( const void *, size_t ),
pid[14]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned int p, x, y, z, sx, sy, sz;
id = 0; /* sssh! compiler warnings */
lup = nrrdUILookup[nin->type];
out = AIR_CAST( unsigned int*, nout->data );
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
sz = AIR_CAST( unsigned int, nin->axis[2].size );
#define GETV_3( x, y, z ) ( ( AIR_IN_CL( 0, AIR_CAST( int, x ), AIR_CAST( int, sx-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, y ), AIR_CAST( int, sy-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, z ), AIR_CAST( int, sz-1 ) ) )\
? lup( nin->data, ( x ) + sx*( ( y ) + sy*( z ) ) ) \
: 0.5 )
#define GETI_3( x, y, z ) ( ( AIR_IN_CL( 0, AIR_CAST( int, x ), AIR_CAST( int, sx-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, y ), AIR_CAST( int, sy-1 ) ) \
&& AIR_IN_CL( 0, AIR_CAST( int, z ), AIR_CAST( int, sz-1 ) ) )\
? out[( x ) + sx*( ( y ) + sy*( z ) )] \
: AIR_CAST( unsigned int, -1 ) )
*numid = 0;
for ( z=0; z<sz; z++ ) {
for ( y=0; y<sy; y++ ) {
for ( x=0; x<sx; x++ ) {
if ( !x ) {
pvl[ 1] = GETV_3( -1, y, z ); pid[ 1] = GETI_3( -1, y, z );
pvl[ 2] = GETV_3( -1, y-1, z ); pid[ 2] = GETI_3( -1, y-1, z );
pvl[ 3] = GETV_3( 0, y-1, z ); pid[ 3] = GETI_3( 0, y-1, z );
pvl[ 5] = GETV_3( -1, y-1, z-1 ); pid[ 5] = GETI_3( -1, y-1, z-1 );
pvl[ 8] = GETV_3( -1, y, z-1 ); pid[ 8] = GETI_3( -1, y, z-1 );
pvl[11] = GETV_3( -1, y+1, z-1 ); pid[11] = GETI_3( -1, y+1, z-1 );
pvl[ 6] = GETV_3( 0, y-1, z-1 ); pid[ 6] = GETI_3( 0, y-1, z-1 );
pvl[ 9] = GETV_3( 0, y, z-1 ); pid[ 9] = GETI_3( 0, y, z-1 );
pvl[12] = GETV_3( 0, y+1, z-1 ); pid[12] = GETI_3( 0, y+1, z-1 );
} else {
pvl[ 1] = vl; pid[ 1] = id;
pvl[ 2] = pvl[ 3]; pid[ 2] = pid[ 3];
pvl[ 3] = pvl[ 4]; pid[ 3] = pid[ 4];
pvl[ 5] = pvl[ 6]; pid[ 5] = pid[ 6];
pvl[ 8] = pvl[ 9]; pid[ 8] = pid[ 9];
pvl[11] = pvl[12]; pid[11] = pid[12];
pvl[ 6] = pvl[ 7]; pid[ 6] = pid[ 7];
pvl[ 9] = pvl[10]; pid[ 9] = pid[10];
pvl[12] = pvl[13]; pid[12] = pid[13];
}
pvl[ 4] = GETV_3( x+1, y-1, z ); pid[ 4] = GETI_3( x+1, y-1, z );
pvl[ 7] = GETV_3( x+1, y-1, z-1 ); pid[ 7] = GETI_3( x+1, y-1, z-1 );
pvl[10] = GETV_3( x+1, y, z-1 ); pid[10] = GETI_3( x+1, y, z-1 );
pvl[13] = GETV_3( x+1, y+1, z-1 ); pid[13] = GETI_3( x+1, y+1, z-1 );
vl = GETV_3( x, y, z );
p = 0;
if ( vl == pvl[1] ) {
id = pid[p=1];
}
TEST( 3 );
TEST( 9 );
if ( 2 <= conny ) {
TEST( 2 ); TEST( 4 );
TEST( 6 ); TEST( 8 ); TEST( 10 ); TEST( 12 );
if ( 3 == conny ) {
TEST( 5 ); TEST( 7 ); TEST( 11 ); TEST( 13 );
}
}
if ( !p ) {
/* didn't match anything previous */
id = *numid;
( *numid )++;
}
out[x + sx*( y + sy*z )] = id;
}
}
}
return 0;
}
int
_nrrdCCFind_N( Nrrd *nout, unsigned int *numid, airArray *eqvArr,
const Nrrd *nin, unsigned int conny ) {
static const char me[]="_nrrdCCFind_N";
AIR_UNUSED( nout );
AIR_UNUSED( numid );
AIR_UNUSED( eqvArr );
AIR_UNUSED( nin );
AIR_UNUSED( conny );
biffAddf( NRRD, "%s: sorry, not implemented yet", me );
return 1;
}
/*
******** nrrdCCFind
**
** finds connected components ( CCs ) in given integral type nrrd "nin",
** according to connectivity "conny", putting the results in "nout".
** The "type" argument controls what type the output will be. If
** type == nrrdTypeDefault, the type used will be the smallest that
** can contain the CC id values. Otherwise, the specified type "type"
** will be used, assuming that it is large enough to hold the CC ids.
**
** "conny": the number of coordinates that need to varied together in
** order to reach all the samples that are to consitute the neighborhood
** around a sample. For 2-D, conny==1 specifies the 4 edge-connected
** pixels, and 2 specifies the 8 edge- and corner-connected.
**
** The caller can get a record of the values in each CC by passing a
** non-NULL nval, which will be allocated to an array of the same type
** as nin, so that nval->data[I] is the value in nin inside CC #I.
*/
int
nrrdCCFind( Nrrd *nout, Nrrd **nvalP, const Nrrd *nin, int type,
unsigned int conny ) {
static const char me[]="nrrdCCFind", func[]="ccfind";
Nrrd *nfpid; /* first-pass IDs */
airArray *mop, *eqvArr;
unsigned int *fpid, numid, numsettleid, *map,
( *lup )( const void *, size_t ), ( *ins )( void *, size_t, unsigned int );
int ret;
size_t I, NN;
void *val;
if ( !( nout && nin ) ) {
/* NULL nvalP okay */
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout == nin disallowed", me );
return 1;
}
if ( !( nrrdTypeIsIntegral[nin->type]
&& nrrdTypeIsUnsigned[nin->type]
&& nrrdTypeSize[nin->type] <= 4 ) ) {
biffAddf( NRRD, "%s: can only find connected components in "
"1, 2, or 4 byte unsigned integral values ( not %s )",
me, airEnumStr( nrrdType, nin->type ) );
return 1;
}
if ( nrrdTypeDefault != type ) {
if ( !( AIR_IN_OP( nrrdTypeUnknown, type, nrrdTypeLast ) ) ) {
biffAddf( NRRD, "%s: got invalid target type %d", me, type );
return 1;
}
if ( !( nrrdTypeIsIntegral[type]
&& nrrdTypeIsUnsigned[nin->type]
&& nrrdTypeSize[type] <= 4 ) ) {
biffAddf( NRRD,
"%s: can only save connected components to 1, 2, or 4 byte "
"unsigned integral values ( not %s )",
me, airEnumStr( nrrdType, type ) );
return 1;
}
}
if ( !( conny <= nin->dim ) ) {
biffAddf( NRRD, "%s: connectivity value must be in [1..%d] for %d-D "
"data ( not %d )", me, nin->dim, nin->dim, conny );
return 1;
}
if ( nrrdConvert( nfpid=nrrdNew( ), nin, nrrdTypeUInt ) ) {
biffAddf( NRRD, "%s: couldn't allocate fpid %s array to match input size",
me, airEnumStr( nrrdType, nrrdTypeUInt ) );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nfpid, ( airMopper )nrrdNuke, airMopAlways );
eqvArr = airArrayNew( NULL, NULL, 2*sizeof( unsigned int ), _nrrdCC_EqvIncr );
airMopAdd( mop, eqvArr, ( airMopper )airArrayNuke, airMopAlways );
ret = 0;
switch( nin->dim ) {
case 1:
ret = _nrrdCCFind_1( nfpid, &numid, nin );
break;
case 2:
ret = _nrrdCCFind_2( nfpid, &numid, eqvArr, nin, conny );
break;
case 3:
ret = _nrrdCCFind_3( nfpid, &numid, eqvArr, nin, conny );
break;
default:
ret = _nrrdCCFind_N( nfpid, &numid, eqvArr, nin, conny );
break;
}
if ( ret ) {
biffAddf( NRRD, "%s: initial pass failed", me );
airMopError( mop ); return 1;
}
map = AIR_MALLOC( numid, unsigned int );
airMopAdd( mop, map, airFree, airMopAlways );
numsettleid = airEqvMap( eqvArr, map, numid );
/* convert fpid values to final id values */
fpid = ( unsigned int* )( nfpid->data );
NN = nrrdElementNumber( nfpid );
for ( I=0; I<NN; I++ ) {
fpid[I] = map[fpid[I]];
}
if ( nvalP ) {
if ( !( *nvalP ) ) {
*nvalP = nrrdNew( );
}
if ( nrrdMaybeAlloc_va( *nvalP, nin->type, 1,
AIR_CAST( size_t, numsettleid ) ) ) {
biffAddf( NRRD, "%s: couldn't allocate output value list", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nvalP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, *nvalP, ( airMopper )nrrdNuke, airMopOnError );
val = ( *nvalP )->data;
lup = nrrdUILookup[nin->type];
ins = nrrdUIInsert[nin->type];
/* I'm not sure if its more work to do all the redundant assignments
or to check whether or not to do them */
for ( I=0; I<NN; I++ ) {
ins( val, fpid[I], lup( nin->data, I ) );
}
}
if ( nrrdTypeDefault != type ) {
if ( numsettleid-1 > nrrdTypeMax[type] ) {
biffAddf( NRRD,
"%s: max cc id %u is too large to fit in output type %s",
me, numsettleid-1, airEnumStr( nrrdType, type ) );
airMopError( mop ); return 1;
}
} else {
type = ( numsettleid-1 <= nrrdTypeMax[nrrdTypeUChar]
? nrrdTypeUChar
: ( numsettleid-1 <= nrrdTypeMax[nrrdTypeUShort]
? nrrdTypeUShort
: nrrdTypeUInt ) );
}
if ( nrrdConvert( nout, nfpid, type ) ) {
biffAddf( NRRD, "%s: trouble converting to final output", me );
airMopError( mop ); return 1;
}
if ( nrrdContentSet_va( nout, func, nin, "%s, %d",
airEnumStr( nrrdType, type ), conny ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
/* basic info handled by nrrdConvert */
airMopOkay( mop );
return 0;
}
int
_nrrdCCAdj_1( unsigned char *out, int numid, const Nrrd *nin ) {
AIR_UNUSED( out );
AIR_UNUSED( numid );
AIR_UNUSED( nin );
return 0;
}
int
_nrrdCCAdj_2( unsigned char *out, unsigned int numid, const Nrrd *nin,
unsigned int conny ) {
unsigned int ( *lup )( const void *, size_t ), x, y, sx, sy, id=0;
double pid[5]={0, 0, 0, 0, 0};
lup = nrrdUILookup[nin->type];
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
for ( y=0; y<sy; y++ ) {
for ( x=0; x<sx; x++ ) {
if ( !x ) {
pid[1] = GETV_2( -1, y );
pid[2] = GETV_2( -1, y-1 );
pid[3] = GETV_2( 0, y-1 );
} else {
pid[1] = id;
pid[2] = pid[3];
pid[3] = pid[4];
}
pid[4] = GETV_2( x+1, y-1 );
id = AIR_CAST( unsigned int, GETV_2( x, y ) );
#define TADJ( P ) \
if ( pid[( P )] != 0.5 && id != pid[( P )] ) { \
out[id + numid*AIR_CAST( unsigned int, pid[( P )] )] = \
out[AIR_CAST( unsigned int, pid[( P )] ) + numid*id] = 1; \
}
TADJ( 1 );
TADJ( 3 );
if ( 2 == conny ) {
TADJ( 2 );
TADJ( 4 );
}
}
}
return 0;
}
int
_nrrdCCAdj_3( unsigned char *out, int numid, const Nrrd *nin,
unsigned int conny ) {
unsigned int ( *lup )( const void *, size_t ), x, y, z, sx, sy, sz, id=0;
double pid[14]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
lup = nrrdUILookup[nin->type];
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
sz = AIR_CAST( unsigned int, nin->axis[2].size );
for ( z=0; z<sz; z++ ) {
for ( y=0; y<sy; y++ ) {
for ( x=0; x<sx; x++ ) {
if ( !x ) {
pid[ 1] = GETV_3( -1, y, z );
pid[ 2] = GETV_3( -1, y-1, z );
pid[ 3] = GETV_3( 0, y-1, z );
pid[ 5] = GETV_3( -1, y-1, z-1 );
pid[ 8] = GETV_3( -1, y, z-1 );
pid[11] = GETV_3( -1, y+1, z-1 );
pid[ 6] = GETV_3( 0, y-1, z-1 );
pid[ 9] = GETV_3( 0, y, z-1 );
pid[12] = GETV_3( 0, y+1, z-1 );
} else {
pid[ 1] = id;
pid[ 2] = pid[ 3];
pid[ 3] = pid[ 4];
pid[ 5] = pid[ 6];
pid[ 8] = pid[ 9];
pid[11] = pid[12];
pid[ 6] = pid[ 7];
pid[ 9] = pid[10];
pid[12] = pid[13];
}
pid[ 4] = GETV_3( x+1, y-1, z );
pid[ 7] = GETV_3( x+1, y-1, z-1 );
pid[10] = GETV_3( x+1, y, z-1 );
pid[13] = GETV_3( x+1, y+1, z-1 );
id = AIR_CAST( unsigned int, GETV_3( x, y, z ) );
TADJ( 1 );
TADJ( 3 );
TADJ( 9 );
if ( 2 <= conny ) {
TADJ( 2 ); TADJ( 4 );
TADJ( 6 ); TADJ( 8 ); TADJ( 10 ); TADJ( 12 );
if ( 3 == conny ) {
TADJ( 5 ); TADJ( 7 ); TADJ( 11 ); TADJ( 13 );
}
}
}
}
}
return 0;
}
int
_nrrdCCAdj_N( unsigned char *out, int numid, const Nrrd *nin,
unsigned int conny ) {
static const char me[]="_nrrdCCAdj_N";
AIR_UNUSED( out );
AIR_UNUSED( numid );
AIR_UNUSED( nin );
AIR_UNUSED( conny );
biffAddf( NRRD, "%s: sorry, not implemented", me );
return 1;
}
int
nrrdCCAdjacency( Nrrd *nout, const Nrrd *nin, unsigned int conny ) {
static const char me[]="nrrdCCAdjacency", func[]="ccadj";
int ret;
unsigned int maxid;
unsigned char *out;
if ( !( nout && nrrdCCValid( nin ) ) ) {
biffAddf( NRRD, "%s: invalid args", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout == nin disallowed", me );
return 1;
}
if ( !( AIR_IN_CL( 1, conny, nin->dim ) ) ) {
biffAddf( NRRD, "%s: connectivity value must be in [1..%d] for %d-D "
"data ( not %d )", me, nin->dim, nin->dim, conny );
return 1;
}
maxid = nrrdCCMax( nin );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeUChar, 2,
AIR_CAST( size_t, maxid+1 ),
AIR_CAST( size_t, maxid+1 ) ) ) {
biffAddf( NRRD, "%s: trouble allocating output", me );
return 1;
}
out = ( unsigned char * )( nout->data );
switch( nin->dim ) {
case 1:
ret = _nrrdCCAdj_1( out, maxid+1, nin );
break;
case 2:
ret = _nrrdCCAdj_2( out, maxid+1, nin, conny );
break;
case 3:
ret = _nrrdCCAdj_3( out, maxid+1, nin, conny );
break;
default:
ret = _nrrdCCAdj_N( out, maxid+1, nin, conny );
break;
}
if ( ret ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
/* this goofiness is just so that histo-based projections
return the sorts of values that we expect */
nout->axis[0].center = nout->axis[1].center = nrrdCenterCell;
nout->axis[0].min = nout->axis[1].min = -0.5;
nout->axis[0].max = nout->axis[1].max = maxid + 0.5;
if ( nrrdContentSet_va( nout, func, nin, "%d", conny ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdCCMerge
**
** Slightly-too-multi-purpose tool for merging small connected components
** ( CCs ) into larger ones, according to a number of possible different
** constraints, as explained below.
**
** valDir: ( value direction ) uses information about the original values
** in the CC to constrain whether darker gets merged into brighter, or vice
** versa, or neither. For non-zero valDir values, a non-NULL _nval ( from
** nrrdCCFind ) must be passed.
** valDir > 0 : merge dark CCs into bright, but not vice versa
** valDir = 0 : merge either way, values are irrelevant
** valDir < 0 : merge bright CCs into dark, but not vice versa
** When merging with multiple neighbors ( maxNeighbor > 1 ), the value
** of the largest neighbor is considered.
**
** maxSize: a cap on how large "small" is- CCs any larger than maxSize are
** not merged, as they are deemed too significant. Or, a maxSize of 0 says
** size is no object for merging CCs.
**
** maxNeighbor: a maximum number of neighbors that a CC can have ( either
** bigger than the CC or not ) if it is to be merged. Use 1 to merge
** isolated islands into their surrounds, 2 to merge CC with the larger
** of their two neighbors, etc., or 0 to allow any number of neighbors.
**
** conny: passed to nrrdCCAdjacency( ) when determining neighbors
**
** In order to prevent weirdness, the merging done in one call to this
** function is not transitive: if A is merged to B, then B will not be
** merged to anything else, even if meets all the requirements defined
** by the given parameters. This is accomplished by working from the
** smallest CCs to the largest. Iterated calls may be needed to acheive
** the desired effect.
**
** Note: the output of this is not "settled"- the CC id values are not
** shiftward downwards to their lowest possible values, since this would
** needlessly invalidate the nval value store.
*/
int
nrrdCCMerge( Nrrd *nout, const Nrrd *nin, Nrrd *_nval,
int valDir, unsigned int maxSize, unsigned int maxNeighbor,
unsigned int conny ) {
static const char me[]="nrrdCCMerge", func[]="ccmerge";
const char *valcnt;
unsigned int _i, i, j, bigi=0, numid, *size, *sizeId,
*nn, /* number of neighbors */
*val=NULL, *hit,
( *lup )( const void *, size_t ), ( *ins )( void *, size_t, unsigned int );
Nrrd *nadj, *nsize, *nval=NULL, *nnn;
unsigned char *adj;
unsigned int *map, *id;
airArray *mop;
size_t I, NN;
mop = airMopNew( );
if ( !( nout && nrrdCCValid( nin ) ) ) {
/* _nval can be NULL */
biffAddf( NRRD, "%s: invalid args", me );
airMopError( mop ); return 1;
}
if ( valDir ) {
airMopAdd( mop, nval = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nval, _nval, nrrdTypeUInt ) ) {
biffAddf( NRRD, "%s: value-directed merging needs usable nval", me );
airMopError( mop ); return 1;
}
val = ( unsigned int* )( nval->data );
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
airMopAdd( mop, nadj = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nsize = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nnn = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCCSize( nsize, nin )
|| nrrdCopy( nnn, nsize ) /* just to allocate to right size and type */
|| nrrdCCAdjacency( nadj, nin, conny ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
size = ( unsigned int* )( nsize->data );
adj = ( unsigned char* )( nadj->data );
nn = ( unsigned int* )( nnn->data );
numid = AIR_CAST( unsigned int, nsize->axis[0].size );
for ( i=0; i<numid; i++ ) {
nn[i] = 0;
for ( j=0; j<numid; j++ ) {
nn[i] += adj[j + numid*i];
}
}
map = AIR_MALLOC( numid, unsigned int );
id = AIR_MALLOC( numid, unsigned int );
hit = AIR_MALLOC( numid, unsigned int );
sizeId = AIR_MALLOC( 2*numid, unsigned int );
/* we add to the mops BEFORE error checking so that anything non-NULL
will get airFree'd, and happily airFree is a no-op on NULL */
airMopAdd( mop, map, airFree, airMopAlways );
airMopAdd( mop, id, airFree, airMopAlways );
airMopAdd( mop, hit, airFree, airMopAlways );
airMopAdd( mop, sizeId, airFree, airMopAlways );
if ( !( map && id && hit && sizeId ) ) {
biffAddf( NRRD, "%s: couldn't allocate buffers", me );
airMopError( mop ); return 1;
}
/* store and sort size/id pairs */
for ( i=0; i<numid; i++ ) {
sizeId[0 + 2*i] = size[i];
sizeId[1 + 2*i] = i;
}
qsort( sizeId, numid, 2*sizeof( unsigned int ), nrrdValCompare[nrrdTypeUInt] );
for ( i=0; i<numid; i++ ) {
id[i] = sizeId[1 + 2*i];
}
/* initialize arrays */
for ( i=0; i<numid; i++ ) {
map[i] = i;
hit[i] = AIR_FALSE;
}
/* _i goes through 0 to numid-1,
i goes through the CC ids in ascending order of size */
for ( _i=0; _i<numid; _i++ ) {
i = id[_i];
if ( hit[i] ) {
continue;
}
if ( maxSize && ( size[i] > maxSize ) ) {
continue;
}
if ( maxNeighbor && ( nn[i] > maxNeighbor ) ) {
continue;
}
/* find biggest neighbor, exploiting the fact that we already
sorted CC ids on size. j descends through indices of id[],
bigi goes through CC ids which are larger than CC i */
for ( j=numid-1; j>_i; j-- ) {
bigi = id[j];
if ( adj[bigi + numid*i] )
break;
}
if ( j == _i ) {
continue; /* we had no neighbors ?!?! */
}
if ( valDir && ( AIR_CAST( int, val[bigi] )
- AIR_CAST( int, val[i] ) )*valDir < 0 ) {
continue;
}
/* else all criteria for merging have been met */
map[i] = bigi;
hit[bigi] = AIR_TRUE;
}
lup = nrrdUILookup[nin->type];
ins = nrrdUIInsert[nout->type];
NN = nrrdElementNumber( nin );
for ( I=0; I<NN; I++ ) {
ins( nout->data, I, map[lup( nin->data, I )] );
}
valcnt = ( ( _nval && _nval->content )
? _nval->content
: nrrdStateUnknownContent );
if ( ( valDir && nrrdContentSet_va( nout, func, nin, "%c( %s ), %d, %d, %d",
( valDir > 0 ? '+' : '-' ), valcnt,
maxSize, maxNeighbor, conny ) )
||
( !valDir && nrrdContentSet_va( nout, func, nin, "., %d, %d, %d",
maxSize, maxNeighbor, conny ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* basic info handled by nrrdCopy */
airMopOkay( mop );
return 0;
}
/*
******** nrrdCCRevalue( )
**
** assigns the original values back to the connected components
** obviously, this could be subsumed by nrrdApply1DLut( ), but this
** is so special purpose that it seemed simpler to code from scratch
*/
int
nrrdCCRevalue ( Nrrd *nout, const Nrrd *nin, const Nrrd *nval ) {
static const char me[]="nrrdCCRevalue";
size_t I, NN;
unsigned int ( *vlup )( const void *, size_t ), ( *ilup )( const void *, size_t ),
( *ins )( void *, size_t, unsigned int );
if ( !( nout && nrrdCCValid( nin ) && nval ) ) {
biffAddf( NRRD, "%s: invalid args", me );
return 1;
}
if ( nrrdConvert( nout, nin, nval->type ) ) {
biffAddf( NRRD, "%s: couldn't initialize output", me );
return 1;
}
NN = nrrdElementNumber( nin );
vlup = nrrdUILookup[nval->type];
ilup = nrrdUILookup[nin->type];
ins = nrrdUIInsert[nout->type];
for ( I=0; I<NN; I++ ) {
ins( nout->data, I, vlup( nval->data, ilup( nin->data, I ) ) );
}
/* basic info handled by nrrdConvert */
return 0;
}
int
nrrdCCSettle( Nrrd *nout, Nrrd **nvalP, const Nrrd *nin ) {
static const char me[]="nrrdCCSettle", func[]="ccsettle";
unsigned int numid, maxid, jd, id, *map,
( *lup )( const void *, size_t ), ( *ins )( void *, size_t, unsigned int );
size_t I, NN;
airArray *mop;
mop = airMopNew( );
if ( !( nout && nrrdCCValid( nin ) ) ) {
/* nvalP can be NULL */
biffAddf( NRRD, "%s: invalid args", me );
airMopError( mop ); return 1;
}
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: initial copy failed", me );
airMopError( mop ); return 1;
}
maxid = nrrdCCMax( nin );
lup = nrrdUILookup[nin->type];
ins = nrrdUIInsert[nin->type];
NN = nrrdElementNumber( nin );
map = AIR_CALLOC( maxid+1, unsigned int ); /* we do need it zeroed out */
if ( !map ) {
biffAddf( NRRD, "%s: couldn't allocate internal LUT", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, map, airFree, airMopAlways );
for ( I=0; I<NN; I++ ) {
map[lup( nin->data, I )] = 1;
}
numid = 0;
for ( jd=0; jd<=maxid; jd++ ) {
numid += map[jd];
}
if ( nvalP ) {
if ( !( *nvalP ) ) {
*nvalP = nrrdNew( );
}
if ( nrrdMaybeAlloc_va( *nvalP, nin->type, 1,
AIR_CAST( size_t, numid ) ) ) {
biffAddf( NRRD, "%s: couldn't allocate output value list", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nvalP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, *nvalP, ( airMopper )nrrdNuke, airMopOnError );
}
id = 0;
for ( jd=0; jd<=maxid; jd++ ) {
if ( map[jd] ) {
map[jd] = id;
if ( nvalP ) {
ins( ( *nvalP )->data, id, jd );
}
id++;
}
}
for ( I=0; I<NN; I++ ) {
ins( nout->data, I, map[lup( nin->data, I )] );
}
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* basic info handled by nrrdCopy */
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
int
28 nrrdCCValid( const Nrrd *nin ) {
static const char me[]="nrrdCCValid";
if ( nrrdCheck( nin ) ) {
biffAddf( NRRD, "%s: basic validity check failed", me );
return 0;
}
if ( !( nrrdTypeIsIntegral[nin->type] ) ) {
biffAddf( NRRD, "%s: need an integral type ( not %s )", me,
airEnumStr( nrrdType, nin->type ) );
return 0;
}
if ( !( nrrdTypeSize[nin->type] <= 2 ||
nrrdTypeInt == nin->type ||
nrrdTypeUInt == nin->type ) ) {
biffAddf( NRRD, "%s: valid connected component types are 1- and 2-byte "
"integers, and %s and %s", me,
airEnumStr( nrrdType, nrrdTypeInt ),
airEnumStr( nrrdType, nrrdTypeUInt ) );
return 0;
}
return 1;
}
/*
** things we could sensibly measure on CCs:
** - size
** - # neighbors ( needs conny argument )
** - what else?
*/
unsigned int
60 nrrdCCSize( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="nrrdCCSize", func[]="ccsize";
unsigned int *out, maxid, ( *lup )( const void *, size_t );
size_t I, NN;
if ( !( nout && nrrdCCValid( nin ) ) ) {
biffAddf( NRRD, "%s: invalid args", me );
return 1;
}
maxid = nrrdCCMax( nin );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeUInt, 1,
AIR_CAST( size_t, maxid+1 ) ) ) {
biffAddf( NRRD, "%s: can't allocate output", me );
return 1;
}
out = ( unsigned int * )( nout->data );
lup = nrrdUILookup[nin->type];
NN = nrrdElementNumber( nin );
for ( I=0; I<NN; I++ ) {
out[lup( nin->data, I )] += 1;
}
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdCCMax
**
** returns the highest CC ID, or 0 if there were problems
**
** does NOT use biff
*/
unsigned int
97 nrrdCCMax( const Nrrd *nin ) {
unsigned int ( *lup )( const void *, size_t ), id, max;
size_t I, NN;
if ( !nrrdCCValid( nin ) ) {
return 0;
}
lup = nrrdUILookup[nin->type];
NN = nrrdElementNumber( nin );
max = 0;
for ( I=0; I<NN; I++ ) {
id = lup( nin->data, I );
max = AIR_MAX( max, id );
}
return max;
}
/*
******** nrrdCCNum
**
** returns the number of connected components ( the # of CC IDs assigned )
** a return of 0 indicates an error
*/
unsigned int
121 nrrdCCNum( const Nrrd *nin ) {
unsigned int ( *lup )( const void *, size_t ), num;
size_t I, max, NN;
unsigned char *hist;
if ( !nrrdCCValid( nin ) ) {
return 0;
}
lup = nrrdUILookup[nin->type];
NN = nrrdElementNumber( nin );
max = nrrdCCMax( nin );
hist = ( unsigned char * )calloc( max+1, sizeof( unsigned char ) );
if ( !hist ) {
return 0;
}
for ( I=0; I<NN; I++ ) {
hist[lup( nin->data, I )] = 1;
}
num = 0;
for ( I=0; I<=max; I++ ) {
num += hist[I];
}
free( hist );
return num;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdCommentAdd( )
**
** Adds a given string to the list of comments
** Leading spaces ( ' ' ) and comment chars ( '#' ) are not included.
**
** This function does NOT use biff.
*/
int
36 nrrdCommentAdd( Nrrd *nrrd, const char *_str ) {
/* static const char me[]="nrrdCommentAdd";*/
char *str;
unsigned int ii;
if ( !( nrrd && _str ) ) {
/*
sprintf( err, "%s: got NULL pointer", me );
biffMaybeAdd( NRRD, err, useBiff );
*/
return 1;
}
_str += strspn( _str, " #" );
if ( !strlen( _str ) ) {
/* we don't bother adding comments with no length */
return 0;
}
if ( !strcmp( _str, _nrrdFormatURLLine0 )
|| !strcmp( _str, _nrrdFormatURLLine1 ) ) {
/* sneaky hack: don't store the format URL comment lines */
return 0;
}
str = airStrdup( _str );
if ( !str ) {
/*
sprintf( err, "%s: couldn't strdup given string", me );
biffMaybeAdd( NRRD, err, useBiff );
*/
return 1;
}
/* clean out carraige returns that would screw up reader */
airOneLinify( str );
ii = airArrayLenIncr( nrrd->cmtArr, 1 );
if ( !nrrd->cmtArr->data ) {
/*
sprintf( err, "%s: couldn't lengthen comment array", me );
biffMaybeAdd( NRRD, err, useBiff );
*/
return 1;
}
nrrd->cmt[ii] = str;
return 0;
}
/*
******** nrrdCommentClear( )
**
** blows away comments, but does not blow away the comment airArray
*/
void
86 nrrdCommentClear( Nrrd *nrrd ) {
if ( nrrd ) {
airArrayLenSet( nrrd->cmtArr, 0 );
}
}
/*
******** nrrdCommentCopy( )
**
** copies comments from one nrrd to another
** Existing comments in nout are blown away
**
** This does NOT use biff.
*/
int
102 nrrdCommentCopy( Nrrd *nout, const Nrrd *nin ) {
/* static const char me[]="nrrdCommentCopy"; */
int E;
unsigned int numc, ii;
if ( !( nout && nin ) ) {
/*
sprintf( err, "%s: got NULL pointer", me );
biffMaybeAdd( NRRD, err, useBiff );
*/
return 1;
}
if ( nout == nin ) {
/* can't satisfy semantics of copying with nout==nin */
return 2;
}
nrrdCommentClear( nout );
numc = nin->cmtArr->len;
E = 0;
for ( ii=0; ii<numc; ii++ ) {
if ( !E ) E |= nrrdCommentAdd( nout, nin->cmt[ii] );
}
if ( E ) {
/*
sprintf( err, "%s: couldn't add all comments", me );
biffMaybeAdd( NRRD, err, useBiff );
*/
return 3;
}
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** making these typedefs here allows us to use one token for both
** constructing function names, and for specifying argument types
*/
typedef signed char CH;
typedef unsigned char UC;
typedef signed short SH;
typedef unsigned short US;
/* Microsoft apparently uses 'IN' as a keyword, so we changed 'IN' to 'JN'. */
typedef signed int JN;
typedef unsigned int UI;
typedef airLLong LL;
/* ui64 to double conversion is not implemented, sorry */
#if _MSC_VER < 1300
typedef airLLong UL;
#else
typedef airULLong UL;
#endif
typedef float FL;
typedef double DB;
typedef size_t IT;
/* typedef long double LD; */
/*
** I don't think that I can get out of defining this macro twice,
** because of the rules of C preprocessor macro expansion. If
** you can figure out a way to not use two identical macros, then
** email me ( glk@uchicago.edu ) and I'll send you money for dinner.
**
** >>> MAP1 and MAP2 need to be identical <<<
*/
#define MAP1( F, A ) \
F( A, CH ) \
F( A, UC ) \
F( A, SH ) \
F( A, US ) \
F( A, JN ) \
F( A, UI ) \
F( A, LL ) \
F( A, UL ) \
F( A, FL ) \
F( A, DB )
/* F( A, LD ) */
#define MAP2( F, A ) \
F( A, CH ) \
F( A, UC ) \
F( A, SH ) \
F( A, US ) \
F( A, JN ) \
F( A, UI ) \
F( A, LL ) \
F( A, UL ) \
F( A, FL ) \
F( A, DB )
/* F( A, LD ) */
/*
** _nrrdConv<Ta><Tb>( )
**
** given two arrays, a and b, of different types ( Ta and Tb ) but equal
** size N, _nrrdConvTaTb( a, b, N ) will copy all the values from b into
** a, thereby effecting the same type-conversion as one gets with a
** cast. See K+R Appendix A6 ( pg. 197 ) for the details of what that
** entails. There are plenty of situations where the results are
** "undefined" ( assigning -23 to an unsigned char ); the point here is
** simply to make available on arrays all the same behavior you can
** get from scalars.
*/
#define CONV_DEF( TA, TB ) \
static void \
_nrrdConv##TA##TB( TA *a, const TB *b, IT N ) { \
size_t ii; \
for ( ii=0; ii<N; ii++ ) { \
a[ii] = AIR_CAST( TA, b[ii] ); \
} \
}
/*
** _nrrdClCv<Ta><Tb>( )
**
** same as _nrrdConv<Ta><Tb>( ), but with clamping to the representable
** range of values of the output type, using a double as intermediate
** storage type HEY WHICH MEANS THAT LONG LONG ( BOTH SIGNED AND UNSIGNED )
** may suffer loss of data!!!
*/
#define CLCV_DEF( TA, TB ) \
static void \
_nrrdClCv##TA##TB( TA *a, const TB *b, IT N ) { \
size_t ii; \
for ( ii=0; ii<N; ii++ ) { \
a[ii] = AIR_CAST( TA, _nrrdDClamp##TA( AIR_CAST( double, b[ii] ) ) ); \
} \
}
/*
** _nrrdCcrd<Ta><Tb>( )
**
** like _nrrdClCv<Ta><Tb>( ) and _nrrdConv<Ta><Tb>( ), but with the
** ability to control if there is rounding and/or clamping. As above,
** there may be loss of precision with long long input.
*/
#define CCRD_DEF( TA, TB ) \
static void \
_nrrdCcrd##TA##TB( TA *a, const TB *b, IT N, \
int doClamp, int roundd ) { \
size_t ii; \
for ( ii=0; ii<N; ii++ ) { \
double ccrdTmp = AIR_CAST( double, b[ii] ); \
ccrdTmp = ( roundd > 0 \
138 ? floor( ccrdTmp + 0.5 ) \
139 : ( roundd < 0 \
140 ? ceil( ccrdTmp - 0.5 ) \
141 : ccrdTmp ) ); \
142 ccrdTmp = ( doClamp ? _nrrdDClamp##TA( ccrdTmp ) : ccrdTmp ); \
143 a[ii] = AIR_CAST( TA, ccrdTmp ); \
144 } \
}
146
147 /*
148 ** These makes the definition of later arrays shorter
149 */
typedef void ( *CF )( void *, const void *, IT );
typedef void ( *CN )( void *, const void *, IT, int, int );
/*
** the individual converter's appearance in the array initialization,
** using the cast to the typedefs above
*/
#define CONV_LIST( TA, TB ) ( CF )_nrrdConv##TA##TB,
#define CLCV_LIST( TA, TB ) ( CF )_nrrdClCv##TA##TB,
#define CCRD_LIST( TA, TB ) ( CN )_nrrdCcrd##TA##TB,
/*
** the brace-delimited list of all converters _to_ type TA
*/
#define CONVTO_LIST( _dummy_, TA ) {NULL, MAP2( CONV_LIST, TA ) NULL},
#define CLCVTO_LIST( _dummy_, TA ) {NULL, MAP2( CLCV_LIST, TA ) NULL},
#define CCRDTO_LIST( _dummy_, TA ) {NULL, MAP2( CCRD_LIST, TA ) NULL},
170 /*
171 ** This is where the actual emitted code starts ...
172 */
173
174
175 /*
176 ** the clamping functions were moved here from accessors.c in order
** to create the combined clamp-and-convert functions
178 */
179
180 /*
181 ******** nrrdFClamp
**
** for integral types, clamps a given float to the range representable
** by that type; for floating point types we just return
** the given number, since every float must fit in a double.
*/
static float _nrrdFClampCH( FL v ) { return AIR_CLAMP( SCHAR_MIN, v, SCHAR_MAX );}
static float _nrrdFClampUC( FL v ) { return AIR_CLAMP( 0, v, UCHAR_MAX );}
static float _nrrdFClampSH( FL v ) { return AIR_CLAMP( SHRT_MIN, v, SHRT_MAX );}
static float _nrrdFClampUS( FL v ) { return AIR_CLAMP( 0, v, USHRT_MAX );}
static float _nrrdFClampJN( FL v ) { return AIR_CLAMP( INT_MIN, v, INT_MAX );}
static float _nrrdFClampUI( FL v ) { return AIR_CLAMP( 0, v, UINT_MAX );}
static float _nrrdFClampLL( FL v ) { return AIR_CLAMP( NRRD_LLONG_MIN, v,
NRRD_LLONG_MAX );}
static float _nrrdFClampUL( FL v ) { return AIR_CLAMP( 0, v, NRRD_ULLONG_MAX );}
static float _nrrdFClampFL( FL v ) { return v; }
static float _nrrdFClampDB( FL v ) { return v; }
float ( *
nrrdFClamp[NRRD_TYPE_MAX+1] )( FL ) = {
NULL,
_nrrdFClampCH,
_nrrdFClampUC,
_nrrdFClampSH,
_nrrdFClampUS,
_nrrdFClampJN,
_nrrdFClampUI,
_nrrdFClampLL,
_nrrdFClampUL,
_nrrdFClampFL,
_nrrdFClampDB,
NULL};
/*
******** nrrdDClamp
**
** same as nrrdDClamp, but for doubles. One change: in the case of
** floats, doubles are clamped to the range -FLT_MAX to FLT_MAX.
*/
static double _nrrdDClampCH( DB v ) { return AIR_CLAMP( SCHAR_MIN, v, SCHAR_MAX );}
static double _nrrdDClampUC( DB v ) { return AIR_CLAMP( 0, v, UCHAR_MAX );}
static double _nrrdDClampSH( DB v ) { return AIR_CLAMP( SHRT_MIN, v, SHRT_MAX );}
static double _nrrdDClampUS( DB v ) { return AIR_CLAMP( 0, v, USHRT_MAX );}
static double _nrrdDClampJN( DB v ) { return AIR_CLAMP( INT_MIN, v, INT_MAX );}
static double _nrrdDClampUI( DB v ) { return AIR_CLAMP( 0, v, UINT_MAX );}
static double _nrrdDClampLL( DB v ) { return AIR_CLAMP( NRRD_LLONG_MIN, v,
NRRD_LLONG_MAX );}
static double _nrrdDClampUL( DB v ) { return AIR_CLAMP( 0, v, NRRD_ULLONG_MAX );}
static double _nrrdDClampFL( DB v ) { return AIR_CLAMP( -FLT_MAX, v, FLT_MAX ); }
static double _nrrdDClampDB( DB v ) { return v; }
double ( *
nrrdDClamp[NRRD_TYPE_MAX+1] )( DB ) = {
NULL,
_nrrdDClampCH,
_nrrdDClampUC,
_nrrdDClampSH,
_nrrdDClampUS,
_nrrdDClampJN,
_nrrdDClampUI,
_nrrdDClampLL,
_nrrdDClampUL,
_nrrdDClampFL,
_nrrdDClampDB,
NULL};
/*
** Define all the converters.
*/
MAP1( MAP2, CONV_DEF )
MAP1( MAP2, CLCV_DEF )
MAP1( MAP2, CCRD_DEF )
/*
** Initialize the converter arrays.
**
** Each definition generates one incredibly long line of text, which
** hopefully will not break a poor compiler with limitations on
** line-length...
*/
CF
_nrrdConv[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = {
{NULL},
MAP1( CONVTO_LIST, _dummy_ )
{NULL}
};
CF
_nrrdClampConv[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = {
{NULL},
MAP1( CLCVTO_LIST, _dummy_ )
{NULL}
};
CN
_nrrdCastClampRound[NRRD_TYPE_MAX+1][NRRD_TYPE_MAX+1] = {
{NULL},
MAP1( CCRDTO_LIST, _dummy_ )
{NULL}
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** these aren't "const"s because the user should be able to change
** default behavior- until a more sophisticated mechanism for this
** kind of control is developed, it seems simple and usable enough to
** have this be global state which we agree to treat nicely, as in,
** threads shouldn't be changing these willy-nilly.
**
** What IS a "default"? A default is the assertion of a certain
** choice in situations where the user hasn't set it explicitly, but
** COULD. The pad value in resampling is a good example: it is set by
** a constructor to nrrdDefaultResamplePadValue, but the user can also set it
** explicitly.
*/
int nrrdDefaultWriteEncodingType = nrrdEncodingTypeRaw;
int nrrdDefaultWriteBareText = AIR_TRUE;
unsigned int nrrdDefaultWriteCharsPerLine = 75;
unsigned int nrrdDefaultWriteValsPerLine = 8;
/* ---- BEGIN non-NrrdIO */
int nrrdDefaultResampleBoundary = nrrdBoundaryBleed;
int nrrdDefaultResampleType = nrrdTypeDefault;
int nrrdDefaultResampleRenormalize = AIR_TRUE;
int nrrdDefaultResampleRound = AIR_TRUE;
int nrrdDefaultResampleClamp = AIR_TRUE;
int nrrdDefaultResampleCheap = AIR_FALSE;
double nrrdDefaultResamplePadValue = 0.0;
int nrrdDefaultResampleNonExistent = nrrdResampleNonExistentNoop;
double nrrdDefaultKernelParm0 = 1.0;
/* ---- END non-NrrdIO */
int nrrdDefaultCenter = nrrdCenterCell;
double nrrdDefaultSpacing = 1.0;
/* these aren't really "defaults" because there's no other channel for
specifying this information. It is just global state. Obviously,
like defaults, they are not thread-safe if different threads ever
set them differently. */
int nrrdStateVerboseIO = 1; /* NrrdIO-hack-003 */
int nrrdStateKeyValuePairsPropagate = AIR_FALSE;
/* ---- BEGIN non-NrrdIO */
int nrrdStateBlind8BitRange = AIR_TRUE;
int nrrdStateMeasureType = nrrdTypeFloat;
int nrrdStateMeasureModeBins = 1024;
int nrrdStateMeasureHistoType = nrrdTypeFloat;
int nrrdStateDisallowIntegerNonExist = AIR_TRUE;
/* ---- END non-NrrdIO */
int nrrdStateAlwaysSetContent = AIR_TRUE;
int nrrdStateDisableContent = AIR_FALSE;
const char *nrrdStateUnknownContent = NRRD_UNKNOWN;
int nrrdStateGrayscaleImage3D = AIR_FALSE;
/* there is no sane reason to change this initialization */
int nrrdStateKeyValueReturnInternalPointers = AIR_FALSE;
/* Making the default for this be AIR_TRUE means that nrrd is not only
completely conservative about updating kind, but purposely stupid.
Nrrd is only going to implement the most converative kind of logic
anyway, based on existing sementics nailed down by the format spec. */
int nrrdStateKindNoop = AIR_FALSE;
/* these are helper functions for min/max testing */
airLLong
86 _nrrdLLongMaxHelp( airLLong val ) {
return val*2 + 1;
}
airLLong
90 _nrrdLLongMinHelp( airLLong val ) {
return val*2;
}
airULLong
94 _nrrdULLongMaxHelp( airULLong val ) {
return val + 1;
}
/* should the acceptance ( or not ) of malformed NRRD header fields
embedded in PNM or text comments be controlled here? */
/* Are there other assumptions currently built into nrrd which could
stand to be user-controllable? */
/* ---- BEGIN non-NrrdIO */
106 const char *const nrrdEnvVarDefaultWriteEncodingType
= "NRRD_DEFAULT_WRITE_ENCODING_TYPE";
108 const char *const nrrdEnvVarDefaultWriteBareText
= "NRRD_DEFAULT_WRITE_BARE_TEXT";
110 const char *const nrrdEnvVarDefaultWriteBareTextOld
= "NRRD_DEF_WRITE_BARE_TEXT";
112 const char *const nrrdEnvVarDefaultCenter
= "NRRD_DEFAULT_CENTER";
114 const char *const nrrdEnvVarDefaultCenterOld
= "NRRD_DEF_CENTER";
116 const char *const nrrdEnvVarDefaultWriteCharsPerLine
= "NRRD_DEFAULT_WRITE_CHARS_PER_LINE";
118 const char *const nrrdEnvVarDefaultWriteValsPerLine
= "NRRD_DEFAULT_WRITE_VALS_PER_LINE";
120 const char *const nrrdEnvVarDefaultKernelParm0
= "NRRD_DEFAULT_KERNEL_PARM0";
122 const char *const nrrdEnvVarDefaultSpacing
= "NRRD_DEFAULT_SPACING";
125 const char *const nrrdEnvVarStateKindNoop
= "NRRD_STATE_KIND_NOOP";
127 const char *const nrrdEnvVarStateVerboseIO
= "NRRD_STATE_VERBOSE_IO";
129 const char *const nrrdEnvVarStateKeyValuePairsPropagate
= "NRRD_STATE_KEYVALUEPAIRS_PROPAGATE";
131 const char *const nrrdEnvVarStateBlind8BitRange
= "NRRD_STATE_BLIND_8_BIT_RANGE";
133 const char *const nrrdEnvVarStateAlwaysSetContent
= "NRRD_STATE_ALWAYS_SET_CONTENT";
135 const char *const nrrdEnvVarStateDisableContent
= "NRRD_STATE_DISABLE_CONTENT";
137 const char *const nrrdEnvVarStateMeasureType
= "NRRD_STATE_MEASURE_TYPE";
139 const char *const nrrdEnvVarStateMeasureModeBins
= "NRRD_STATE_MEASURE_MODE_BINS";
141 const char *const nrrdEnvVarStateMeasureHistoType
= "NRRD_STATE_MEASURE_HISTO_TYPE";
143 const char *const nrrdEnvVarStateGrayscaleImage3D
= "NRRD_STATE_GRAYSCALE_IMAGE_3D";
/*
** return
** value:
** -1: unset, or bad args ==> *val NOT set
** AIR_TRUE: set in a valid way ==> *val set ( to something )
** AIR_FALSE: set in an invalid way ==> *val NOT set
*/
int
155 nrrdGetenvBool( int *val, char **envStr, const char *envVar ) {
char *env;
int tmp;
if ( !( val && envVar ) ) {
return -1;
}
env = getenv( envVar );
if ( envStr ) {
*envStr = env;
}
if ( !env ) {
return -1;
}
if ( !strlen( env ) ) {
/* for bools, being merely set ( but not to any string ) means "true" */
*val = AIR_TRUE;
return AIR_TRUE;
}
tmp = airEnumVal( airBool, env );
if ( airEnumUnknown( airBool ) == tmp ) {
return AIR_FALSE;
} else {
*val = tmp;
return AIR_TRUE;
}
}
int
184 nrrdGetenvEnum( int *val, char **envStr, const airEnum *enm,
const char *envVar ) {
char *env;
int tmp;
if ( !( val && envVar ) ) {
return -1;
}
env = getenv( envVar );
if ( envStr ) {
*envStr = env;
}
if ( !env ) {
return -1;
}
tmp = airEnumVal( enm, env );
if ( airEnumUnknown( enm ) == tmp ) {
return AIR_FALSE;
} else {
*val = tmp;
return AIR_TRUE;
}
}
int
209 nrrdGetenvUInt( unsigned int *val, char **envStr, const char *envVar ) {
char *env;
unsigned int tmp;
if ( !( val && envVar ) ) {
return -1;
}
env = getenv( envVar );
if ( envStr ) {
*envStr = env;
}
if ( !env ) {
return -1;
}
if ( 1 != sscanf( env, "%u", &tmp ) ) {
return AIR_FALSE;
} else {
*val = tmp;
return AIR_TRUE;
}
}
int
232 nrrdGetenvInt( int *val, char **envStr, const char *envVar ) {
char *env;
int tmp;
if ( !( val && envVar ) ) {
return -1;
}
env = getenv( envVar );
if ( envStr ) {
*envStr = env;
}
if ( !env ) {
return -1;
}
if ( 1 != sscanf( env, "%d", &tmp ) ) {
return AIR_FALSE;
} else {
*val = tmp;
return AIR_TRUE;
}
}
int
255 nrrdGetenvDouble( double *val, char **envStr, const char *envVar ) {
char *env;
double tmp;
if ( !( val && envVar ) ) {
return -1;
}
env = getenv( envVar );
if ( envStr ) {
*envStr = env;
}
if ( !env ) {
return -1;
}
if ( 1 != airSingleSscanf( env, "%lf", &tmp ) ) {
return AIR_FALSE;
} else {
*val = tmp;
return AIR_TRUE;
}
}
/*
** This function is not used in the same way within nrrd the same way
** as the other nrrdGetenv functions; it was added just to have a more
** convenient wrapper around getenv for strings.
*/
int
283 nrrdGetenvString( char **envStr, const char *envVar ) {
if ( !( envStr && envVar ) ) {
return -1;
}
*envStr = getenv( envVar );
if ( !( *envStr ) ) {
return AIR_FALSE;
} else {
return AIR_TRUE;
}
}
void
297 nrrdDefaultGetenv( void ) {
/* these two pre-date Def --> Default rename */
if ( -1 == nrrdGetenvBool( /**/ &nrrdDefaultWriteBareText, NULL,
nrrdEnvVarDefaultWriteBareTextOld ) ) {
nrrdGetenvBool( /**/ &nrrdDefaultWriteBareText, NULL,
nrrdEnvVarDefaultWriteBareText );
}
if ( -1 == nrrdGetenvEnum( /**/ &nrrdDefaultCenter, NULL, nrrdCenter,
nrrdEnvVarDefaultCenterOld ) ) {
nrrdGetenvEnum( /**/ &nrrdDefaultCenter, NULL, nrrdCenter,
nrrdEnvVarDefaultCenter );
}
/* these post-date the Def --> Default rename */
nrrdGetenvEnum( /**/ &nrrdDefaultWriteEncodingType, NULL, nrrdEncodingType,
nrrdEnvVarDefaultWriteEncodingType );
nrrdGetenvUInt( /**/ &nrrdDefaultWriteCharsPerLine, NULL,
nrrdEnvVarDefaultWriteCharsPerLine );
nrrdGetenvUInt( /**/ &nrrdDefaultWriteValsPerLine, NULL,
nrrdEnvVarDefaultWriteValsPerLine );
nrrdGetenvDouble( /**/ &nrrdDefaultKernelParm0, NULL,
nrrdEnvVarDefaultKernelParm0 );
nrrdGetenvDouble( /**/ &nrrdDefaultSpacing, NULL,
nrrdEnvVarDefaultSpacing );
return;
}
void
327 nrrdStateGetenv( void ) {
nrrdGetenvBool( /**/ &nrrdStateKindNoop, NULL,
nrrdEnvVarStateKindNoop );
nrrdGetenvInt( /**/ &nrrdStateVerboseIO, NULL,
nrrdEnvVarStateVerboseIO );
nrrdGetenvBool( /**/ &nrrdStateKeyValuePairsPropagate, NULL,
nrrdEnvVarStateKeyValuePairsPropagate );
nrrdGetenvBool( /**/ &nrrdStateBlind8BitRange, NULL,
nrrdEnvVarStateBlind8BitRange );
nrrdGetenvBool( /**/ &nrrdStateAlwaysSetContent, NULL,
nrrdEnvVarStateAlwaysSetContent );
nrrdGetenvBool( /**/ &nrrdStateDisableContent, NULL,
nrrdEnvVarStateDisableContent );
nrrdGetenvEnum( /**/ &nrrdStateMeasureType, NULL, nrrdType,
nrrdEnvVarStateMeasureType );
nrrdGetenvInt( /**/ &nrrdStateMeasureModeBins, NULL,
nrrdEnvVarStateMeasureModeBins );
nrrdGetenvEnum( /**/ &nrrdStateMeasureHistoType, NULL, nrrdType,
nrrdEnvVarStateMeasureHistoType );
nrrdGetenvBool( /**/ &nrrdStateGrayscaleImage3D, NULL,
nrrdEnvVarStateGrayscaleImage3D );
return;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#define CLAMP_HIST_BINS_MIN 512
#define CLAMP_PERC_MAX 30.0
#define DEBUG 0
/* TODO:
*
* make sure all outout values exist ( test with d/xiao/todering/xing )
*
* permit controlling the extent of correction ( don't subtract out
* all the estimated ring signal )
*
* valgrind
*
* make it multi-threaded
*
* try fix for round object boundaries being confused for rings
- ( relies on properties of discrete gauss for radial blurring )
- after initial ptxf
- make a few radial blurs ( up to scale of radial high-pass )
- measuring scale-normalized |df/dr| on each one
- do non-maximal suppression along radius, zeroing out edges below some
percentile of gradient strength
- where the gradients are high on the most-blurring, and had similar
grad mag at smaller scales, that's a real edge: make a mask for this
- blur this along theta just like the ring map, then multiply w/ ring map
*
* try fix for low-theta-frequency rings
* with high thetaNum, find mode along theta
*/
NrrdDeringContext *
59 nrrdDeringContextNew( void ) {
NrrdDeringContext *drc;
unsigned int pi;
drc = AIR_CALLOC( 1, NrrdDeringContext );
if ( !drc ) {
return NULL;
}
drc->verbose = 0;
drc->linearInterp = AIR_FALSE;
drc->verticalSeam = AIR_FALSE;
drc->nin = NULL;
drc->center[0] = AIR_NAN;
drc->center[1] = AIR_NAN;
drc->clampPerc[0] = 0.0;
drc->clampPerc[1] = 0.0;
drc->radiusScale = 1.0;
drc->thetaNum = 0;
drc->clampHistoBins = CLAMP_HIST_BINS_MIN*4;
drc->rkernel = NULL;
drc->tkernel = NULL;
for ( pi=0; pi<NRRD_KERNEL_PARMS_NUM; pi++ ) {
drc->rkparm[pi] = drc->tkparm[pi] = AIR_NAN;
}
drc->cdataIn = NULL;
drc->cdataOut = NULL;
drc->sliceSize = 0;
drc->clampDo = AIR_FALSE;
drc->clamp[0] = AIR_NAN;
drc->clamp[1] = AIR_NAN;
drc->ringMagnitude = AIR_NAN;
return drc;
}
NrrdDeringContext *
94 nrrdDeringContextNix( NrrdDeringContext *drc ) {
if ( drc ) {
free( drc );
}
return NULL;
}
int
103 nrrdDeringVerboseSet( NrrdDeringContext *drc, int verbose ) {
static const char me[]="nrrdDeringVerboseSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
drc->verbose = verbose;
return 0;
}
int
116 nrrdDeringLinearInterpSet( NrrdDeringContext *drc, int linterp ) {
static const char me[]="nrrdDeringLinearInterpSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
drc->linearInterp = linterp;
return 0;
}
int
129 nrrdDeringVerticalSeamSet( NrrdDeringContext *drc, int vertSeam ) {
static const char me[]="nrrdDeringVerticalSeamSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
drc->verticalSeam = vertSeam;
return 0;
}
int
142 nrrdDeringInputSet( NrrdDeringContext *drc, const Nrrd *nin ) {
static const char me[]="nrrdDeringInputSet";
if ( !( drc && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nin ) ) {
biffAddf( NRRD, "%s: problems with given nrrd", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can't resample from type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( 2 == nin->dim || 3 == nin->dim ) ) {
biffAddf( NRRD, "%s: need 2 or 3 dim nrrd ( not %u )", me, nin->dim );
return 1;
}
if ( drc->verbose > 2 ) {
fprintf( stderr, "%s: hi\n", me );
}
drc->nin = nin;
drc->cdataIn = AIR_CAST( const char *, nin->data );
drc->sliceSize = ( nin->axis[0].size
* nin->axis[1].size
* nrrdElementSize( nin ) );
if ( drc->verbose > 2 ) {
fprintf( stderr, "%s: sliceSize = %u\n", me,
AIR_CAST( unsigned int, drc->sliceSize ) );
}
return 0;
}
int
180 nrrdDeringCenterSet( NrrdDeringContext *drc, double cx, double cy ) {
static const char me[]="nrrdDeringCenterSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( cx ) && AIR_EXISTS( cy ) ) ) {
biffAddf( NRRD, "%s: center ( %g, %g ) doesn't exist", me, cx, cy );
return 1;
}
drc->center[0] = cx;
drc->center[1] = cy;
return 0;
}
int
199 nrrdDeringClampPercSet( NrrdDeringContext *drc,
double lo, double hi ) {
static const char me[]="nrrdDeringClampPercSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( lo ) && AIR_EXISTS( hi )
&& lo >= 0 && lo < CLAMP_PERC_MAX
&& hi >= 0 && hi < CLAMP_PERC_MAX ) ) {
biffAddf( NRRD, "%s: need finite lo and hi both in [0.0, %g ), not %g, %g",
me, CLAMP_PERC_MAX, lo, hi );
return 1;
}
drc->clampPerc[0] = lo;
drc->clampPerc[1] = hi;
return 0;
}
int
222 nrrdDeringClampHistoBinsSet( NrrdDeringContext *drc,
unsigned int bins ) {
static const char me[]="nrrdDeringClampHistoBinsSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( bins >= CLAMP_HIST_BINS_MIN ) ) {
biffAddf( NRRD, "%s: given bins %u not >= reasonable min %u",
me, bins, CLAMP_HIST_BINS_MIN );
return 1;
}
drc->clampHistoBins = bins;
return 0;
}
int
242 nrrdDeringRadiusScaleSet( NrrdDeringContext *drc, double rsc ) {
static const char me[]="nrrdDeringRadiusScaleSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( rsc ) && rsc > 0.0 ) ) {
biffAddf( NRRD, "%s: need finite positive radius scale, not %g", me, rsc );
return 1;
}
drc->radiusScale = rsc;
return 0;
}
int
260 nrrdDeringThetaNumSet( NrrdDeringContext *drc, unsigned int thetaNum ) {
static const char me[]="nrrdDeringThetaNumSet";
if ( !drc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !thetaNum ) {
biffAddf( NRRD, "%s: need non-zero thetaNum", me );
return 1;
}
drc->thetaNum = thetaNum;
return 0;
}
int
278 nrrdDeringRadialKernelSet( NrrdDeringContext *drc,
const NrrdKernel *rkernel,
const double rkparm[NRRD_KERNEL_PARMS_NUM] ) {
static const char me[]="nrrdDeringRadialKernelSet";
unsigned int pi;
if ( !( drc && rkernel && rkparm ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
drc->rkernel = rkernel;
for ( pi=0; pi<NRRD_KERNEL_PARMS_NUM; pi++ ) {
drc->rkparm[pi] = rkparm[pi];
}
return 0;
}
int
298 nrrdDeringThetaKernelSet( NrrdDeringContext *drc,
const NrrdKernel *tkernel,
const double tkparm[NRRD_KERNEL_PARMS_NUM] ) {
static const char me[]="nrrdDeringThetaKernelSet";
unsigned int pi;
if ( !( drc && tkernel && tkparm ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
drc->tkernel = tkernel;
for ( pi=0; pi<NRRD_KERNEL_PARMS_NUM; pi++ ) {
drc->tkparm[pi] = tkparm[pi];
}
return 0;
}
/*
** per-thread state for deringing
*/
#define ORIG 0
#define WGHT 1
#define BLRR 2
#define DIFF 3
#define RSHP 4
#define CROP 5
#define CBLR 6
#define RING 7
#define PTXF_NUM 8
typedef struct {
unsigned int zi;
double radMax;
size_t radNum;
airArray *mop;
Nrrd *nsliceOrig, /* wrapped slice of nin, sneakily non-const */
*nslice, /* slice of nin, converted to double */
*nsliceOut, /* ( may be different type than nslice ) */
*nptxf[PTXF_NUM];
double *slice, *ptxf, *wght, *ring;
NrrdResampleContext *rsmc[2]; /* rsmc[0]: radial blurring ORIG -> BLRR
rsmc[1]: theta blurring DIFF -> RING */
double ringMag;
} deringBag;
static deringBag *
345 deringBagNew( NrrdDeringContext *drc, double radMax ) {
deringBag *dbg;
unsigned int pi;
dbg = AIR_CALLOC( 1, deringBag );
dbg->radMax = radMax;
dbg->radNum = AIR_ROUNDUP( drc->radiusScale*radMax );
dbg->mop = airMopNew( );
dbg->nsliceOrig = nrrdNew( );
airMopAdd( dbg->mop, dbg->nsliceOrig, ( airMopper )nrrdNix /* not Nuke! */,
airMopAlways );
dbg->nslice = nrrdNew( );
airMopAdd( dbg->mop, dbg->nslice, ( airMopper )nrrdNuke, airMopAlways );
dbg->nsliceOut = nrrdNew( );
airMopAdd( dbg->mop, dbg->nsliceOut, ( airMopper )nrrdNix /* not Nuke! */,
airMopAlways );
for ( pi=0; pi<PTXF_NUM; pi++ ) {
dbg->nptxf[pi] = nrrdNew( );
airMopAdd( dbg->mop, dbg->nptxf[pi], ( airMopper )nrrdNuke, airMopAlways );
}
dbg->rsmc[0] = nrrdResampleContextNew( );
airMopAdd( dbg->mop, dbg->rsmc[0], ( airMopper )nrrdResampleContextNix,
airMopAlways );
dbg->rsmc[1] = nrrdResampleContextNew( );
airMopAdd( dbg->mop, dbg->rsmc[1], ( airMopper )nrrdResampleContextNix,
airMopAlways );
dbg->ringMag = 0.0;
return dbg;
}
static deringBag *
379 deringBagNix( deringBag *dbg ) {
airMopOkay( dbg->mop );
airFree( dbg );
return NULL;
}
static int
387 deringPtxfAlloc( NrrdDeringContext *drc, deringBag *dbg ) {
static const char me[]="deringPtxfAlloc";
unsigned int pi, ri;
int E;
/* polar transform setup */
for ( pi=0; pi<PTXF_NUM; pi++ ) {
if ( drc->verticalSeam && ( CROP == pi || CBLR == pi ) ) {
E = nrrdMaybeAlloc_va( dbg->nptxf[pi], nrrdTypeDouble, 3,
dbg->radNum,
AIR_CAST( size_t, drc->thetaNum/2 - 1 ),
AIR_CAST( size_t, 2 ) );
} else {
E = nrrdMaybeAlloc_va( dbg->nptxf[pi], nrrdTypeDouble, 2,
dbg->radNum,
AIR_CAST( size_t, drc->thetaNum ) );
}
if ( E ) {
biffAddf( NRRD, "%s: polar transform allocation problem", me );
return 1;
}
}
dbg->ptxf = AIR_CAST( double *, dbg->nptxf[ORIG]->data );
dbg->wght = AIR_CAST( double *, dbg->nptxf[WGHT]->data );
dbg->ring = AIR_CAST( double *, dbg->nptxf[RING]->data );
E = AIR_FALSE;
for ( ri=0; ri<2; ri++ ) {
char kstr[AIR_STRLEN_LARGE];
if ( 0 == ri ) {
if ( !E ) E |= nrrdResampleInputSet( dbg->rsmc[0], dbg->nptxf[ORIG] );
nrrdKernelSprint( kstr, drc->rkernel, drc->rkparm );
if ( drc->verbose > 1 ) {
fprintf( stderr, "%s: rsmc[0] axis 0 kernel: %s\n", me, kstr );
}
if ( !E ) E |= nrrdResampleKernelSet( dbg->rsmc[0], 0,
drc->rkernel, drc->rkparm );
if ( !E ) E |= nrrdResampleKernelSet( dbg->rsmc[0], 1, NULL, NULL );
if ( !E ) E |= nrrdResampleSamplesSet( dbg->rsmc[0], 1, drc->thetaNum );
if ( !E ) E |= nrrdResampleBoundarySet( dbg->rsmc[0], nrrdBoundaryWrap );
} else {
if ( drc->verticalSeam ) {
if ( !E ) E |= nrrdResampleInputSet( dbg->rsmc[1], dbg->nptxf[CROP] );
} else {
if ( !E ) E |= nrrdResampleInputSet( dbg->rsmc[1], dbg->nptxf[DIFF] );
}
nrrdKernelSprint( kstr, drc->tkernel, drc->tkparm );
if ( drc->verbose > 1 ) {
fprintf( stderr, "%s: rsmc[1] axis 1 kernel: %s\n", me, kstr );
}
if ( !E ) E |= nrrdResampleKernelSet( dbg->rsmc[1], 0, NULL, NULL );
if ( !E ) E |= nrrdResampleKernelSet( dbg->rsmc[1], 1,
drc->tkernel, drc->tkparm );
if ( drc->verticalSeam ) {
if ( !E ) E |= nrrdResampleSamplesSet( dbg->rsmc[1], 1, drc->thetaNum/2 - 1 );
if ( !E ) E |= nrrdResampleKernelSet( dbg->rsmc[1], 2, NULL, NULL );
if ( !E ) E |= nrrdResampleSamplesSet( dbg->rsmc[1], 2, 2 );
if ( !E ) E |= nrrdResampleBoundarySet( dbg->rsmc[1], nrrdBoundaryPad );
if ( !E ) E |= nrrdResamplePadValueSet( dbg->rsmc[1], 0.0 );
} else {
if ( !E ) E |= nrrdResampleSamplesSet( dbg->rsmc[1], 1, drc->thetaNum );
if ( !E ) E |= nrrdResampleBoundarySet( dbg->rsmc[1], nrrdBoundaryWrap );
}
}
if ( !E ) E |= nrrdResampleDefaultCenterSet( dbg->rsmc[ri], nrrdCenterCell );
if ( !E ) E |= nrrdResampleSamplesSet( dbg->rsmc[ri], 0, dbg->radNum );
if ( !E ) E |= nrrdResampleTypeOutSet( dbg->rsmc[ri], nrrdTypeDefault );
if ( !E ) E |= nrrdResampleRenormalizeSet( dbg->rsmc[ri], AIR_TRUE );
if ( !E ) E |= nrrdResampleNonExistentSet( dbg->rsmc[ri],
nrrdResampleNonExistentRenormalize );
if ( !E ) E |= nrrdResampleRangeFullSet( dbg->rsmc[ri], 0 );
if ( !E ) E |= nrrdResampleRangeFullSet( dbg->rsmc[ri], 1 );
}
if ( E ) {
biffAddf( NRRD, "%s: couldn't set up resampler", me );
return 1;
}
return 0;
}
static int
470 deringSliceGet( NrrdDeringContext *drc, deringBag *dbg, unsigned int zi ) {
static const char me[]="deringSliceGet";
void *nonconstdata;
const char *cdata;
/* HEY: bypass of const-ness of drc->cdataIn; should think about how
to do this without breaking const-correctness... */
cdata = drc->cdataIn + zi*( drc->sliceSize );
memcpy( &nonconstdata, &cdata, sizeof( void* ) );
/* slice setup */
if ( nrrdWrap_va( dbg->nsliceOrig,
nonconstdata,
drc->nin->type, 2,
drc->nin->axis[0].size,
drc->nin->axis[1].size )
|| ( nrrdTypeDouble == drc->nin->type
? nrrdCopy( dbg->nslice, dbg->nsliceOrig )
: nrrdConvert( dbg->nslice, dbg->nsliceOrig, nrrdTypeDouble ) ) ) {
biffAddf( NRRD, "%s: slice setup trouble", me );
return 1;
}
dbg->slice = AIR_CAST( double *, dbg->nslice->data );
dbg->zi = zi;
return 0;
}
static int
498 deringSliceSet( NrrdDeringContext *drc, deringBag *dbg,
Nrrd *nout, unsigned int zi ) {
static const char me[]="deringSliceSet";
if ( nrrdWrap_va( dbg->nsliceOut,
AIR_CAST( void *, drc->cdataOut + zi*( drc->sliceSize ) ),
nout->type, 2,
nout->axis[0].size,
nout->axis[1].size )
|| ( nrrdTypeDouble == nout->type
? nrrdCopy( dbg->nsliceOut, dbg->nslice )
: nrrdConvert( dbg->nsliceOut, dbg->nslice, nout->type ) ) ) {
biffAddf( NRRD, "%s: slice output trouble", me );
return 1;
}
return 0;
}
#define EPS 0.000001
static void
520 deringXYtoRT( NrrdDeringContext *drc, deringBag *dbg,
unsigned int xi, unsigned int yi,
unsigned int *rrIdx, unsigned int *thIdx,
double *rrFrc, double *thFrc ) {
double dx, dy, rr, th, rrScl, thScl;
dx = xi - drc->center[0];
dy = yi - drc->center[1];
rr = sqrt( dx*dx + dy*dy );
th = atan2( -dx, dy );
rrScl = AIR_AFFINE( -EPS, rr, dbg->radMax+EPS, 0.0, dbg->radNum-1 );
*rrIdx = AIR_CAST( unsigned int, 0.5 + rrScl );
thScl = AIR_AFFINE( -AIR_PI-EPS, th, AIR_PI+EPS, 0.0, drc->thetaNum );
*thIdx = AIR_CAST( unsigned int, 0.5 + thScl );
if ( rrFrc && thFrc ) {
*rrFrc = rrScl - *rrIdx;
*thFrc = thScl - *thIdx;
if ( *rrFrc < 0 ) {
*rrIdx -= 1;
*rrFrc += 1;
}
if ( *thFrc < 0 ) {
*thIdx -= 1;
*thFrc += 1;
}
}
return;
}
static int
549 deringPtxfDo( NrrdDeringContext *drc, deringBag *dbg ) {
/* static const char me[]="deringPtxfDo"; */
unsigned int sx, sy, xi, yi, rrIdx, thIdx;
nrrdSetZero( dbg->nptxf[ORIG] );
nrrdSetZero( dbg->nptxf[WGHT] );
sx = AIR_CAST( unsigned int, drc->nin->axis[0].size );
sy = AIR_CAST( unsigned int, drc->nin->axis[1].size );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
double rrFrc, thFrc, val;
if ( drc->linearInterp ) {
unsigned int bidx, bidxPlus;
deringXYtoRT( drc, dbg, xi, yi, &rrIdx, &thIdx, &rrFrc, &thFrc );
bidx = AIR_UINT( rrIdx + dbg->radNum*thIdx );
bidxPlus = AIR_UINT( thIdx < drc->thetaNum-1
? bidx + dbg->radNum
: rrIdx );
val = dbg->slice[xi + sx*yi];
if ( drc->clampDo ) {
val = AIR_CLAMP( drc->clamp[0], val, drc->clamp[1] );
}
dbg->ptxf[bidx ] += ( 1-rrFrc )*( 1-thFrc )*val;
dbg->ptxf[bidx + 1] += rrFrc*( 1-thFrc )*val;
dbg->ptxf[bidxPlus ] += ( 1-rrFrc )*thFrc*val;
dbg->ptxf[bidxPlus + 1] += rrFrc*thFrc*val;
dbg->wght[bidx ] += ( 1-rrFrc )*( 1-thFrc );
dbg->wght[bidx + 1] += rrFrc*( 1-thFrc );
dbg->wght[bidxPlus ] += ( 1-rrFrc )*thFrc;
dbg->wght[bidxPlus + 1] += rrFrc*thFrc;
} else {
deringXYtoRT( drc, dbg, xi, yi, &rrIdx, &thIdx, NULL, NULL );
thIdx = thIdx % drc->thetaNum;
dbg->ptxf[rrIdx + dbg->radNum*thIdx] += dbg->slice[xi + sx*yi];
dbg->wght[rrIdx + dbg->radNum*thIdx] += 1;
}
}
}
for ( thIdx=0; thIdx<drc->thetaNum; thIdx++ ) {
for ( rrIdx=0; rrIdx<dbg->radNum; rrIdx++ ) {
double tmpW;
tmpW = dbg->wght[rrIdx + dbg->radNum*thIdx];
if ( tmpW ) {
dbg->ptxf[rrIdx + dbg->radNum*thIdx] /= tmpW;
} else {
dbg->ptxf[rrIdx + dbg->radNum*thIdx] = AIR_NAN;
}
}
}
if ( DEBUG ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "wght-%02u.nrrd", dbg->zi );
nrrdSave( fname, dbg->nptxf[WGHT], NULL );
}
return 0;
}
static int
608 deringPtxfFilter( NrrdDeringContext *drc, deringBag *dbg, unsigned int zi ) {
static const char me[]="deringPtxfFilter";
if ( ( !zi ? 0 : nrrdResampleInputSet( dbg->rsmc[0], dbg->nptxf[ORIG] ) )
|| nrrdResampleExecute( dbg->rsmc[0], dbg->nptxf[BLRR] )
|| nrrdArithBinaryOp( dbg->nptxf[DIFF], nrrdBinaryOpSubtract,
dbg->nptxf[ORIG], dbg->nptxf[BLRR] ) ) {
biffAddf( NRRD, "%s: trouble with radial blur", me );
return 1;
}
if ( !drc->verticalSeam ) {
if ( ( !zi ? 0 : nrrdResampleInputSet( dbg->rsmc[1], dbg->nptxf[DIFF] ) )
|| nrrdResampleExecute( dbg->rsmc[1], dbg->nptxf[RING] ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
} else {
size_t cmin[3], cmax[3];
ptrdiff_t pmin[3], pmax[3];
cmin[0] = 0; cmin[1] = 1; cmin[2] = 0;
pmin[0] = 0; pmin[1] = -1; pmin[2] = 0;
pmax[0] = cmax[0] = dbg->radNum - 1;
cmax[1] = drc->thetaNum/2 - 1;
pmax[1] = drc->thetaNum/2 - 2; /* max sample # of cropped axis 1 */
pmax[2] = cmax[2] = 1;
if ( nrrdAxesSplit( dbg->nptxf[RSHP], dbg->nptxf[DIFF], 1,
drc->thetaNum/2, 2 )
|| nrrdCrop( dbg->nptxf[CROP], dbg->nptxf[RSHP], cmin, cmax )
|| ( !zi ? 0 : nrrdResampleInputSet( dbg->rsmc[1], dbg->nptxf[CROP] ) )
|| nrrdResampleExecute( dbg->rsmc[1], dbg->nptxf[CBLR] )
|| nrrdPad_nva( dbg->nptxf[RSHP], dbg->nptxf[CBLR], pmin, pmax,
nrrdBoundaryPad, 0 )
|| nrrdAxesMerge( dbg->nptxf[RING], dbg->nptxf[RSHP], 1 ) ) {
biffAddf( NRRD, "%s: trouble with vertical seam", me );
return 1;
}
if ( DEBUG ) {
char fn[AIR_STRLEN_SMALL];
sprintf( fn, "rshp-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[RSHP], NULL );
sprintf( fn, "crop-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[CROP], NULL );
}
}
if ( DEBUG ) {
char fn[AIR_STRLEN_SMALL];
sprintf( fn, "orig-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[ORIG], NULL );
sprintf( fn, "blrr-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[BLRR], NULL );
sprintf( fn, "diff-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[DIFF], NULL );
sprintf( fn, "ring-%02u.nrrd", dbg->zi ); nrrdSave( fn, dbg->nptxf[RING], NULL );
}
return 0;
}
static int
662 deringRingMagMeasure( NrrdDeringContext *drc, deringBag *dbg ) {
static const char me[]="deringRingMagMeasure";
airArray *mop;
Nrrd *ntmp[2];
AIR_UNUSED( drc );
mop = airMopNew( );
ntmp[0] = nrrdNew( );
airMopAdd( mop, ntmp[0], ( airMopper )nrrdNuke, airMopAlways );
ntmp[1] = nrrdNew( );
airMopAdd( mop, ntmp[1], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdReshape_va( ntmp[0], dbg->nptxf[RING], 2,
( dbg->nptxf[RING]->axis[0].size
* dbg->nptxf[RING]->axis[1].size ),
AIR_CAST( size_t, 1 ) )
|| nrrdProject( ntmp[1], ntmp[0], 0, nrrdMeasureL2, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: trouble", me );
airMopError( mop ); return 1;
}
dbg->ringMag = *( AIR_CAST( double *, ntmp[1]->data ) );
airMopOkay( mop );
return 0;
}
static int
688 deringSubtract( NrrdDeringContext *drc, deringBag *dbg ) {
/* static const char me[]="deringSubtract"; */
unsigned int sx, sy, xi, yi, rrIdx, thIdx;
sx = AIR_CAST( unsigned int, drc->nin->axis[0].size );
sy = AIR_CAST( unsigned int, drc->nin->axis[1].size );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
double rrFrc, thFrc, val;
if ( drc->linearInterp ) {
unsigned int bidx, bidxPlus;
deringXYtoRT( drc, dbg, xi, yi, &rrIdx, &thIdx, &rrFrc, &thFrc );
bidx = AIR_UINT( rrIdx + dbg->radNum*thIdx );
bidxPlus = AIR_UINT( thIdx < drc->thetaNum-1
? bidx + dbg->radNum
: rrIdx );
val = ( dbg->ring[bidx ]*( 1-rrFrc )*( 1-thFrc ) +
dbg->ring[bidx + 1]*rrFrc*( 1-thFrc ) +
dbg->ring[bidxPlus ]*( 1-rrFrc )*thFrc +
dbg->ring[bidxPlus + 1]*rrFrc*thFrc );
dbg->slice[xi + sx*yi] -= val;
} else {
deringXYtoRT( drc, dbg, xi, yi, &rrIdx, &thIdx, NULL, NULL );
thIdx = thIdx % drc->thetaNum;
dbg->slice[xi + sx*yi] -= dbg->ring[rrIdx + dbg->radNum*thIdx];
}
}
}
if ( DEBUG ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "ring2-%02u.nrrd", dbg->zi ); nrrdSave( fname, dbg->nptxf[RING], NULL );
sprintf( fname, "drng-%02u.nrrd", dbg->zi ); nrrdSave( fname, dbg->nslice, NULL );
}
return 0;
}
static int
725 deringDo( NrrdDeringContext *drc, deringBag *dbg,
Nrrd *nout, unsigned int zi ) {
static const char me[]="deringDo";
if ( deringSliceGet( drc, dbg, zi )
|| deringPtxfDo( drc, dbg )
|| deringPtxfFilter( drc, dbg, zi )
|| deringRingMagMeasure( drc, dbg )
|| deringSubtract( drc, dbg )
|| deringSliceSet( drc, dbg, nout, zi ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
743 deringCheck( NrrdDeringContext *drc ) {
static const char me[]="deringCheck";
if ( !( drc->nin ) ) {
biffAddf( NRRD, "%s: no input set", me );
return 1;
}
if ( !( AIR_EXISTS( drc->center[0] ) && AIR_EXISTS( drc->center[1] ) ) ) {
biffAddf( NRRD, "%s: no center set", me );
return 1;
}
if ( !( drc->thetaNum ) ) {
biffAddf( NRRD, "%s: no thetaNum set", me );
return 1;
}
if ( !( drc->rkernel && drc->tkernel ) ) {
biffAddf( NRRD, "%s: R and T kernels not both set", me );
return 1;
}
if ( drc->verticalSeam && !( 0 == ( drc->thetaNum % 2 ) ) ) {
biffAddf( NRRD, "%s: need even thetaNum ( not %u ) if wanting verticalSeam",
me, drc->thetaNum );
return 1;
}
return 0;
}
int
771 nrrdDeringExecute( NrrdDeringContext *drc, Nrrd *nout ) {
static const char me[]="nrrdDeringExecute";
unsigned int sx, sy, sz, zi;
double dx, dy, radLen, len;
deringBag *dbg;
airArray *mop;
if ( !( drc && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( deringCheck( drc ) ) {
biffAddf( NRRD, "%s: trouble with setup", me );
return 1;
}
/* initialize output */
if ( nrrdCopy( nout, drc->nin ) ) {
biffAddf( NRRD, "%s: trouble initializing output with input", me );
return 1;
}
drc->cdataOut = AIR_CAST( char *, nout->data );
mop = airMopNew( );
/* set radLen: radial length of polar transform of data */
radLen = 0;
sx = AIR_CAST( unsigned int, drc->nin->axis[0].size );
sy = AIR_CAST( unsigned int, drc->nin->axis[1].size );
dx = 0 - drc->center[0];
dy = 0 - drc->center[1];
len = sqrt( dx*dx + dy*dy );
radLen = AIR_MAX( radLen, len );
dx = sx-1 - drc->center[0];
dy = 0 - drc->center[1];
len = sqrt( dx*dx + dy*dy );
radLen = AIR_MAX( radLen, len );
dx = sx-1 - drc->center[0];
dy = sy-1 - drc->center[1];
len = sqrt( dx*dx + dy*dy );
radLen = AIR_MAX( radLen, len );
dx = 0 - drc->center[0];
dy = sy-1 - drc->center[1];
len = sqrt( dx*dx + dy*dy );
radLen = AIR_MAX( radLen, len );
if ( drc->verbose ) {
fprintf( stderr, "%s: radLen = %g\n", me, radLen );
}
/* determine clamping, if any */
if ( drc->clampPerc[0] > 0.0 || drc->clampPerc[1] > 0.0 ) {
Nrrd *nhist;
double *hist, total, sum;
unsigned int hi;
nhist = nrrdNew( );
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdHisto( nhist, drc->nin, NULL, NULL, drc->clampHistoBins,
nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: trouble making histogram", me );
return 1;
}
hist = AIR_CAST( double *, nhist->data );
total = AIR_CAST( double, nrrdElementNumber( drc->nin ) );
sum = 0;
for ( hi=0; hi<drc->clampHistoBins; hi++ ) {
sum += hist[hi];
if ( sum >= drc->clampPerc[0]*total/100.0 ) {
drc->clamp[0] = AIR_AFFINE( 0, hi, drc->clampHistoBins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( hi == drc->clampHistoBins ) {
biffAddf( NRRD, "%s: failed to find lower %g-percentile value", me,
drc->clampPerc[0] );
return 1;
}
sum = 0;
for ( hi=drc->clampHistoBins; hi; hi-- ) {
sum += hist[hi-1];
if ( sum >= drc->clampPerc[1]*total/100.0 ) {
drc->clamp[1] = AIR_AFFINE( 0, hi-1, drc->clampHistoBins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( !hi ) {
biffAddf( NRRD, "%s: failed to find upper %g-percentile value", me,
drc->clampPerc[1] );
return 1;
}
if ( drc->verbose ) {
fprintf( stderr, "%s: [%g, %g]-%%ile clamping of [%g, %g] --> [%g, %g]\n",
me, drc->clampPerc[0], drc->clampPerc[1],
nhist->axis[0].min, nhist->axis[0].max,
drc->clamp[0], drc->clamp[1] );
}
drc->clampDo = AIR_TRUE;
} else {
drc->clamp[0] = drc->clamp[1] = AIR_NAN;
drc->clampDo = AIR_FALSE;
}
/* create deringBag( s ) */
dbg = deringBagNew( drc, radLen );
airMopAdd( mop, dbg, ( airMopper )deringBagNix, airMopAlways );
if ( deringPtxfAlloc( drc, dbg ) ) {
biffAddf( NRRD, "%s: trouble on setup", me );
return 1;
}
sz = ( 2 == drc->nin->dim
? 1
: AIR_CAST( unsigned int, drc->nin->axis[2].size ) );
drc->ringMagnitude = 0.0;
for ( zi=0; zi<sz; zi++ ) {
if ( drc->verbose ) {
fprintf( stderr, "%s: slice %u of %u ...\n", me, zi, sz );
}
if ( deringDo( drc, dbg, nout, zi ) ) {
biffAddf( NRRD, "%s: trouble on slice %u", me, zi );
return 1;
}
drc->ringMagnitude += dbg->ringMag;
if ( drc->verbose ) {
fprintf( stderr, "%s: ... %u done\n", me, zi );
}
}
if ( drc->verbose ) {
fprintf( stderr, "%s: ring magnitude = %g\n", me, drc->ringMagnitude );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** what a NrrdEncoding can assume:
** -- the given nrrd struct has been filled out for the sake of knowing
** nrrd->dim, nrrd->axis[0].size, nrrd->type, and nrrd->blockSize
** AND NOTHING ELSE. See nrrd.h for why those fields, of all things
** are needed for {en/de}coding
**
** what a NrrdEncoding has to do:
** -- read data from file into the "data" argument ( BUT NOT nrrd->data!! ),
** or vice versa.
** -- respect nrrdStateVerboseIO with messages to stderr, if possible
** -- in case of error, put text error messages into biff via
** biffAddf( NRRD, <error char*> ... )
**
** The "unknown" encoding below is intended to serve as a template for
** any new encodings being developed.
*/
static int
46 _nrrdEncodingUnknown_available( void ) {
/* insert code here */
return AIR_FALSE;
}
static int
54 _nrrdEncodingUnknown_read( FILE *file, void *data,
size_t elementNum, Nrrd *nrrd,
struct NrrdIoState_t *nio ) {
static const char me[]="_nrrdEncodingUnknown_read";
/* insert code here, and remove error handling below */
AIR_UNUSED( file );
AIR_UNUSED( data );
AIR_UNUSED( elementNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: ERROR!!! trying to read unknown encoding", me );
return 1;
}
static int
71 _nrrdEncodingUnknown_write( FILE *file, const void *data,
size_t elementNum, const Nrrd *nrrd,
struct NrrdIoState_t *nio ) {
static const char me[]="_nrrdEncodingUnknown_write";
/* insert code here, and remove error handling below */
AIR_UNUSED( file );
AIR_UNUSED( data );
AIR_UNUSED( elementNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: ERROR!!! trying to write unknown encoding", me );
return 1;
}
const NrrdEncoding
_nrrdEncodingUnknown = {
"unknown", /* name */
"unknown", /* suffix */
AIR_FALSE, /* endianMatters */
AIR_FALSE, /* isCompression */
_nrrdEncodingUnknown_available,
_nrrdEncodingUnknown_read,
_nrrdEncodingUnknown_write
};
98 const NrrdEncoding *const
nrrdEncodingUnknown = &_nrrdEncodingUnknown;
101 const NrrdEncoding *const
nrrdEncodingArray[NRRD_ENCODING_TYPE_MAX+1] = {
&_nrrdEncodingUnknown,
&_nrrdEncodingRaw,
&_nrrdEncodingAscii,
&_nrrdEncodingHex,
&_nrrdEncodingGzip,
&_nrrdEncodingBzip2,
&_nrrdEncodingZRL,
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static FILE *_fileSave = NULL;
static int
30 _nrrdEncodingAscii_available( void ) {
return AIR_TRUE;
}
static int
36 _nrrdEncodingAscii_read( FILE *file, void *_data, size_t elNum,
Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingAscii_read";
char numbStr[AIR_STRLEN_HUGE]; /* HEY: fix this */
char *nstr;
size_t I;
char *data;
int tmp;
AIR_UNUSED( nio );
_fileSave = file;
if ( nrrdTypeBlock == nrrd->type ) {
biffAddf( NRRD, "%s: can't read nrrd type %s from %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
nrrdEncodingAscii->name );
return 1;
}
data = ( char* )_data;
I = 0;
while ( I < elNum ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
/* HEY: we can easily suffer here from a standard buffer overflow problem;
this was a source of a mysterious unu crash:
echo "0 0 0 0 1 0 0 0 0" \
| unu reshape -s 9 1 1 \
| unu pad -min 0 0 0 -max 8 8 8 \
| unu make -s 9 9 9 -t float -e ascii -ls 9 \
-spc LPS -orig "( 0, 0, 0 )" -dirs "( 1, 0, 0 ) ( 0, 1, 0 ) ( 0, 0, 1 )"
This particular case is resolved by changing AIR_STRLEN_HUGE
to AIR_STRLEN_HUGE*100, but the general problem remains. This
motivated adding the memory corruption test */
if ( 1 != fscanf( file, "%s", numbStr ) ) {
biffAddf( NRRD, "%s: couldn't parse element %s of %s", me,
airSprintSize_t( stmp1, I+1 ),
airSprintSize_t( stmp2, elNum ) );
return 1;
}
if ( file != _fileSave ) {
fprintf( stderr, "%s: PANIC memory corruption detected\n", me );
/* this may crash, hence the fprintf above to help debug */
biffAddf( NRRD, "%s: PANIC memory corruption detected", me );
return 1;
}
if ( !strcmp( ", ", numbStr ) ) {
/* its an isolated comma, not a value, pass over this */
continue;
}
/* get past any commas prefixing a number ( without space ) */
nstr = numbStr + strspn( numbStr, ", " );
if ( nrrd->type >= nrrdTypeInt ) {
/* sscanf supports putting value directly into this type */
if ( 1 != airSingleSscanf( nstr, nrrdTypePrintfStr[nrrd->type],
( void* )( data + I*nrrdElementSize( nrrd ) ) ) ) {
biffAddf( NRRD, "%s: couldn't parse %s %s of %s ( \"%s\" )", me,
airEnumStr( nrrdType, nrrd->type ),
airSprintSize_t( stmp1, I+1 ),
airSprintSize_t( stmp2, elNum ), nstr );
return 1;
}
} else {
/* sscanf value into an int first */
if ( 1 != airSingleSscanf( nstr, "%d", &tmp ) ) {
biffAddf( NRRD, "%s: couldn't parse element %s of %s ( \"%s\" )", me,
airSprintSize_t( stmp1, I+1 ),
airSprintSize_t( stmp2, elNum ), nstr );
return 1;
}
nrrdIInsert[nrrd->type]( data, I, tmp );
}
I++;
}
return 0;
}
static int
112 _nrrdEncodingAscii_write( FILE *file, const void *_data, size_t elNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingAscii_write";
char buff[AIR_STRLEN_MED];
size_t bufflen, linelen;
const char *data;
size_t I;
int newlined;
if ( nrrdTypeBlock == nrrd->type ) {
biffAddf( NRRD, "%s: can't write nrrd type %s to %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
nrrdEncodingAscii->name );
return 1;
}
data = AIR_CAST( const char*, _data );
linelen = 0;
for ( I=0; I<elNum; I++ ) {
nrrdSprint[nrrd->type]( buff, data );
if ( 1 == nrrd->dim ) {
fprintf( file, "%s\n", buff );
newlined = AIR_TRUE;
} else if ( nrrd->dim == 2
&& nrrd->axis[0].size <= nio->valsPerLine ) {
int nonewline = ( I+1 )%( nrrd->axis[0].size );
fprintf( file, "%s%c", buff, nonewline ? ' ' : '\n' );
newlined = !nonewline;
} else {
bufflen = strlen( buff );
if ( linelen+bufflen+1 <= nio->charsPerLine ) {
fprintf( file, "%s%s", I ? " " : "", buff );
linelen += ( I ? 1 : 0 ) + bufflen;
} else {
fprintf( file, "\n%s", buff );
linelen = bufflen;
}
newlined = AIR_FALSE;
}
data += nrrdElementSize( nrrd );
}
if ( !newlined ) {
/* always end file with a carraige return; but guard with this
conditional so we don't create a final blank line */
fprintf( file, "\n" );
}
fflush( file );
return 0;
}
const NrrdEncoding
_nrrdEncodingAscii = {
"ASCII", /* name */
"ascii", /* suffix */
AIR_FALSE, /* endianMatters */
AIR_FALSE, /* isCompression */
_nrrdEncodingAscii_available,
_nrrdEncodingAscii_read,
_nrrdEncodingAscii_write
};
173 const NrrdEncoding *const
nrrdEncodingAscii = &_nrrdEncodingAscii;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#if TEEM_BZIP2
#include <bzlib.h>
#endif
static int
32 _nrrdEncodingBzip2_available( void ) {
#if TEEM_BZIP2
return AIR_TRUE;
#else
return AIR_FALSE;
#endif
}
static int
42 _nrrdEncodingBzip2_read( FILE *file, void *_data, size_t elNum,
Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingBzip2_read";
#if TEEM_BZIP2
size_t bsize, total_read, block_size;
int read, bzerror=BZ_OK;
long int bi;
char *data;
BZFILE* bzfin;
bsize = nrrdElementSize( nrrd )*elNum;
/* Create the BZFILE* for reading in the gzipped data. */
bzfin = BZ2_bzReadOpen( &bzerror, file, 0, 0, NULL, 0 );
if ( bzerror != BZ_OK ) {
/* there was a problem */
biffAddf( NRRD, "%s: error opening BZFILE: %s", me,
BZ2_bzerror( bzfin, &bzerror ) );
BZ2_bzReadClose( &bzerror, bzfin );
return 1;
}
/* Here is where we do the byte skipping. */
for( bi=0; bi<nio->byteSkip; bi++ ) {
unsigned char b;
/* Check to see if a single byte was able to be read. */
read = BZ2_bzRead( &bzerror, bzfin, &b, 1 );
if ( read != 1 || bzerror != BZ_OK ) {
biffAddf( NRRD, "%s: hit an error skipping byte %ld of %ld: %s",
me, bi, nio->byteSkip, BZ2_bzerror( bzfin, &bzerror ) );
return 1;
}
}
/* bzip2 can handle data sizes up to INT_MAX, so we can't just
pass in the bsize, because it might be too large for an int.
Therefore it must be read in chunks if the size is larger
than INT_MAX. */
if ( bsize <= INT_MAX ) {
block_size = bsize;
} else {
block_size = INT_MAX;
}
/* This counter will help us to make sure that we read as much data
as we think we should. */
total_read = 0;
/* Pointer to the blocks as we read them. */
data = ( char * )_data;
/* Ok, now we can begin reading. */
bzerror = BZ_OK;
while ( ( read = BZ2_bzRead( &bzerror, bzfin, data, block_size ) )
&& ( BZ_OK == bzerror || BZ_STREAM_END == bzerror ) ) {
/* Increment the data pointer to the next available spot. */
data += read;
total_read += read;
/* We only want to read as much data as we need, so we need to check
to make sure that we don't request data that might be there but that
we don't want. This will reduce block_size when we get to the last
block ( which may be smaller than block_size ).
*/
if ( bsize >= total_read
&& bsize - total_read < block_size )
block_size = bsize - total_read;
}
if ( !( BZ_OK == bzerror || BZ_STREAM_END == bzerror ) ) {
biffAddf( NRRD, "%s: error reading from BZFILE: %s",
me, BZ2_bzerror( bzfin, &bzerror ) );
return 1;
}
/* Close the BZFILE. */
BZ2_bzReadClose( &bzerror, bzfin );
if ( BZ_OK != bzerror ) {
biffAddf( NRRD, "%s: error closing BZFILE: %s", me,
BZ2_bzerror( bzfin, &bzerror ) );
return 1;
}
/* Check to see if we got out as much as we thought we should. */
if ( total_read != bsize ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: expected %s bytes but received %s", me,
airSprintSize_t( stmp1, bsize ),
airSprintSize_t( stmp2, total_read ) );
return 1;
}
return 0;
#else
AIR_UNUSED( file );
AIR_UNUSED( _data );
AIR_UNUSED( elNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, this nrrd not compiled with bzip2 enabled", me );
return 1;
#endif
}
static int
145 _nrrdEncodingBzip2_write( FILE *file, const void *_data, size_t elNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingBzip2_write";
#if TEEM_BZIP2
size_t bsize, total_written, block_size;
int bs, bzerror=BZ_OK;
char *data;
BZFILE* bzfout;
bsize = nrrdElementSize( nrrd )*elNum;
/* Set compression block size. */
if ( 1 <= nio->bzip2BlockSize && nio->bzip2BlockSize <= 9 ) {
bs = nio->bzip2BlockSize;
} else {
bs = 9;
}
/* Open bzfile for writing. Verbosity and work factor are set
to default values. */
bzfout = BZ2_bzWriteOpen( &bzerror, file, bs, 0, 0 );
if ( BZ_OK != bzerror ) {
biffAddf( NRRD, "%s: error opening BZFILE: %s", me,
BZ2_bzerror( bzfout, &bzerror ) );
BZ2_bzWriteClose( &bzerror, bzfout, 0, NULL, NULL );
return 1;
}
/* bzip2 can handle data sizes up to INT_MAX, so we can't just
pass in the bsize, because it might be too large for an int.
Therefore it must be read in chunks if the bsize is larger
than INT_MAX. */
if ( bsize <= INT_MAX ) {
block_size = bsize;
} else {
block_size = INT_MAX;
}
/* This counter will help us to make sure that we write as much data
as we think we should. */
total_written = 0;
/* Pointer to the blocks as we write them. */
data = ( char * )_data;
/* Ok, now we can begin writing. */
bzerror = BZ_OK;
while ( bsize - total_written > block_size ) {
BZ2_bzWrite( &bzerror, bzfout, data, block_size );
if ( BZ_OK != bzerror ) break;
/* Increment the data pointer to the next available spot. */
data += block_size;
total_written += block_size;
}
/* write the last ( possibly smaller ) block when its humungous data;
write the whole data when its small */
if ( BZ_OK == bzerror ) {
block_size = bsize >= total_written ? bsize - total_written : 0;
BZ2_bzWrite( &bzerror, bzfout, data, block_size );
total_written += block_size;
}
if ( BZ_OK != bzerror ) {
biffAddf( NRRD, "%s: error writing to BZFILE: %s",
me, BZ2_bzerror( bzfout, &bzerror ) );
return 1;
}
/* Close the BZFILE. */
BZ2_bzWriteClose( &bzerror, bzfout, 0, NULL, NULL );
if ( BZ_OK != bzerror ) {
biffAddf( NRRD, "%s: error closing BZFILE: %s", me,
BZ2_bzerror( bzfout, &bzerror ) );
return 1;
}
/* Check to see if we got out as much as we thought we should. */
if ( total_written != bsize ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: expected to write %s bytes, but only wrote %s", me,
airSprintSize_t( stmp1, bsize ),
airSprintSize_t( stmp2, total_written ) );
return 1;
}
return 0;
#else
AIR_UNUSED( file );
AIR_UNUSED( _data );
AIR_UNUSED( elNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, this nrrd not compiled with bzip2 enabled", me );
return 1;
#endif
}
const NrrdEncoding
_nrrdEncodingBzip2 = {
"bzip2", /* name */
"raw.bz2", /* suffix */
AIR_TRUE, /* endianMatters */
AIR_TRUE, /* isCompression */
_nrrdEncodingBzip2_available,
_nrrdEncodingBzip2_read,
_nrrdEncodingBzip2_write
};
251 const NrrdEncoding *const
nrrdEncodingBzip2 = &_nrrdEncodingBzip2;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static int
28 _nrrdEncodingGzip_available( void ) {
#if TEEM_ZLIB
return AIR_TRUE;
#else
return AIR_FALSE;
#endif
}
#if TEEM_ZLIB
/*
** Maximum size that allow zlib to try to read or write at once.
** The real limit is UINT_MAX, but a smaller value here permits
** exercising the multi-chunk capability of the code below.
*/
static unsigned int
_nrrdZlibMaxChunk = UINT_MAX;
#endif
/*
** nio->byteSkip < 0 functionality contributed by Katharina Quintus
*/
static int
51 _nrrdEncodingGzip_read( FILE *file, void *_data, size_t elNum,
Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingGzip_read";
#if TEEM_ZLIB
size_t sizeData, sizeRed;
int error;
long int bi;
unsigned int didread, sizeChunk, maxChunk;
char *data;
gzFile gzfin;
airPtrPtrUnion appu;
sizeData = nrrdElementSize( nrrd )*elNum;
/* Create the gzFile for reading in the gzipped data. */
if ( ( gzfin = _nrrdGzOpen( file, "rb" ) ) == Z_NULL ) {
/* there was a problem */
biffAddf( NRRD, "%s: error opening gzFile", me );
return 1;
}
/* keeps track of how many bytes have been successfully read in */
sizeRed = 0;
/* zlib can only handle data sizes up to UINT_MAX ==> if there's more than
UINT_MAX bytes to read in, we read in in chunks. However, we wrap a value
_nrrdZlibMaxChunk around UINT_MAX for testing purposes. Given how
sizeChunk is used below, we also cap chunk size at _nrrdZlibMaxChunk/2 to
prevent overflow. */
maxChunk = _nrrdZlibMaxChunk/2;
sizeChunk = AIR_CAST( unsigned int, AIR_MIN( sizeData, maxChunk ) );
if ( nio->byteSkip < 0 ) {
/* We don't know the size of the size to skip before the data, so
decompress the data first into a temporary memory buffer. Then
the byteskipping is then just memcpy-ing the appropriate region
of memory from "buff" into the given "_data" pointer */
char *buff;
airArray *buffArr;
long backwards;
/* setting the airArray increment to twice the chunk size means that for
headers that are small compared to the data, the airArray never
actually has to reallocate. The unit is 1 because we are managing
the reading in terms of bytes ( sizeof( char )==1 by definition ) */
buff = NULL;
appu.c = &buff;
buffArr = airArrayNew( appu.v, NULL, 1, 2*sizeChunk );
airArrayLenSet( buffArr, sizeChunk );
if ( !( buffArr && buffArr->data ) ) {
biffAddf( NRRD, "%s: couldn't initialize airArray\n", me );
return 1;
}
/* we keep reading in chunks as long as there hasn't been an error,
and we haven't hit EOF ( EOF signified by read == 0 ). Unlike the
code below ( for positive byteskip ), we are obligated to read until
the bitter end, and can't update sizeChunk to encompass only the
required data. */
while ( !( error = _nrrdGzRead( gzfin, buff + sizeRed,
sizeChunk, &didread ) )
&& didread > 0 ) {
sizeRed += didread;
if ( didread >= sizeChunk ) {
/* we were able to read as much data as we requested, maybe there is
more, so we need to make our temp buffer bigger */
unsigned int newlen = buffArr->len + sizeChunk;
if ( newlen < buffArr->len ) {
biffAddf( NRRD, "%s: array size will exceed uint capacity", me );
return 1;
}
airArrayLenSet( buffArr, newlen );
if ( !buffArr->data ) {
biffAddf( NRRD, "%s: couldn't re-allocate data buffer", me );
return 1;
}
}
}
if ( error ) {
biffAddf( NRRD, "%s: error reading from gzFile", me );
return 1;
}
/* backwards is ( positive ) number of bytes AFTER data that we ignore */
backwards = -nio->byteSkip - 1;
if ( sizeRed < sizeData + AIR_CAST( size_t, backwards ) ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: expected %s bytes but received only %s", me,
airSprintSize_t( stmp1, sizeData + AIR_CAST( size_t, backwards ) ),
airSprintSize_t( stmp2, sizeRed ) );
return 1;
}
/* also handles nio->byteSkip == -N-1 signifying extra N bytes at end */
memcpy( _data, buff + sizeRed - sizeData - backwards, sizeData );
airArrayNuke( buffArr );
} else {
/* no negative byteskip: after byteskipping, we can read directly
into given data buffer */
if ( nio->byteSkip > 0 ) {
for ( bi=0; bi<nio->byteSkip; bi++ ) {
unsigned char b;
/* Check to see if a single byte was able to be read. */
if ( _nrrdGzRead( gzfin, &b, 1, &didread ) != 0 || didread != 1 ) {
biffAddf( NRRD, "%s: hit an error skipping byte %ld of %ld",
me, bi, nio->byteSkip );
return 1;
}
}
}
/* Pointer to chunks as we read them. */
data = AIR_CAST( char *, _data );
while ( !( error = _nrrdGzRead( gzfin, data, sizeChunk, &didread ) )
&& didread > 0 ) {
/* Increment the data pointer to the next available chunk. */
data += didread;
sizeRed += didread;
/* We only want to read as much data as we need, so we need to check
to make sure that we don't request data that might be there but that
we don't want. This will reduce sizeChunk when we get to the last
block ( which may be smaller than the original sizeChunk ). */
if ( sizeData >= sizeRed
&& sizeData - sizeRed < sizeChunk ) {
sizeChunk = AIR_CAST( unsigned int, sizeData - sizeRed );
}
}
if ( error ) {
biffAddf( NRRD, "%s: error reading from gzFile", me );
return 1;
}
/* Check to see if we got out as much as we thought we should. */
if ( sizeRed != sizeData ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: expected %s bytes but received %s", me,
airSprintSize_t( stmp1, sizeData ),
airSprintSize_t( stmp2, sizeRed ) );
return 1;
}
}
/* Close the gzFile. Since _nrrdGzClose does not close the FILE* we
will not encounter problems when dataFile is closed later. */
if ( _nrrdGzClose( gzfin ) ) {
biffAddf( NRRD, "%s: error closing gzFile", me );
return 1;
}
return 0;
#else
AIR_UNUSED( file );
AIR_UNUSED( _data );
AIR_UNUSED( elNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, this nrrd not compiled with gzip enabled", me );
return 1;
#endif
}
static int
208 _nrrdEncodingGzip_write( FILE *file, const void *_data, size_t elNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingGzip_write";
#if TEEM_ZLIB
size_t sizeData, sizeWrit;
int fmt_i=0, error;
const char *data;
char fmt[4];
gzFile gzfout;
unsigned int wrote, sizeChunk;
sizeData = nrrdElementSize( nrrd )*elNum;
/* Set format string based on the NrrdIoState parameters. */
fmt[fmt_i++] = 'w';
if ( 0 <= nio->zlibLevel && nio->zlibLevel <= 9 )
fmt[fmt_i++] = AIR_CAST( char, '0' + nio->zlibLevel );
switch ( nio->zlibStrategy ) {
case nrrdZlibStrategyHuffman:
fmt[fmt_i++] = 'h';
break;
case nrrdZlibStrategyFiltered:
fmt[fmt_i++] = 'f';
break;
case nrrdZlibStrategyDefault:
default:
break;
}
fmt[fmt_i] = 0;
/* Create the gzFile for writing in the gzipped data. */
if ( ( gzfout = _nrrdGzOpen( file, fmt ) ) == Z_NULL ) {
/* there was a problem */
biffAddf( NRRD, "%s: error opening gzFile", me );
return 1;
}
/* zlib can only handle data sizes up to UINT_MAX ==> if there's more than
UINT_MAX bytes to write out, we write out in chunks. As above, we wrap
_nrrdZlibMaxChunk around UINT_MAX for testing purposes. */
sizeChunk = AIR_CAST( unsigned int, AIR_MIN( sizeData, _nrrdZlibMaxChunk ) );
/* keeps track of what how much has been successfully written */
sizeWrit = 0;
/* Pointer to the chunks as we write them. */
data = AIR_CAST( const char *, _data );
/* Ok, now we can begin writing. */
while ( ( error = _nrrdGzWrite( gzfout, AIR_CVOIDP( data ),
sizeChunk, &wrote ) ) == 0
&& wrote > 0 ) {
/* Increment the data pointer to the next available spot. */
data += wrote;
sizeWrit += wrote;
/* We only want to write as much data as we need, so we need to check
to make sure that we don't write more data than is there. This
will reduce sizeChunk when we get to the last block ( which may
be smaller than the original sizeChunk ).
*/
if ( sizeData >= sizeWrit
&& sizeData - sizeWrit < sizeChunk )
sizeChunk = AIR_CAST( unsigned int, sizeData - sizeWrit );
}
if ( error ) {
biffAddf( NRRD, "%s: error writing to gzFile", me );
return 1;
}
/* Check to see if we wrote out as much as we thought we should. */
if ( sizeWrit != sizeData ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: expected to write %s bytes, but only wrote %s", me,
airSprintSize_t( stmp1, sizeData ),
airSprintSize_t( stmp2, sizeWrit ) );
return 1;
}
/* Close the gzFile. Since _nrrdGzClose does not close the FILE* we
will not encounter problems when dataFile is closed later. */
if ( _nrrdGzClose( gzfout ) ) {
biffAddf( NRRD, "%s: error closing gzFile", me );
return 1;
}
return 0;
#else
AIR_UNUSED( file );
AIR_UNUSED( _data );
AIR_UNUSED( elNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, this nrrd not compiled with zlib "
"( needed for gzip ) enabled", me );
return 1;
#endif
}
const NrrdEncoding
_nrrdEncodingGzip = {
"gzip", /* name */
"raw.gz", /* suffix */
AIR_TRUE, /* endianMatters */
AIR_TRUE, /* isCompression */
_nrrdEncodingGzip_available,
_nrrdEncodingGzip_read,
_nrrdEncodingGzip_write
};
317 const NrrdEncoding *const
nrrdEncodingGzip = &_nrrdEncodingGzip;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static const int
_nrrdWriteHexTable[16] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
/*
** -2: not allowed, error
** -1: whitespace
** [0, 15]: values
*/
static const int
_nrrdReadHexTable[128] = {
/* 0 1 2 3 4 5 6 7 8 9 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, /* 0 */
-1, -1, -1, -1, -2, -2, -2, -2, -2, -2, /* 10 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 20 */
-2, -2, -1, -2, -2, -2, -2, -2, -2, -2, /* 30 */
-2, -2, -2, -2, -2, -2, -2, -2, 0, 1, /* 40 */
2, 3, 4, 5, 6, 7, 8, 9, -2, -2, /* 50 */
-2, -2, -2, -2, -2, 10, 11, 12, 13, 14, /* 60 */
15, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 70 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 80 */
-2, -2, -2, -2, -2, -2, -2, 10, 11, 12, /* 90 */
13, 14, 15, -2, -2, -2, -2, -2, -2, -2, /* 100 */
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, /* 110 */
-2, -2, -2, -2, -2, -2, -2, -2 /* 120 */
};
static int
58 _nrrdEncodingHex_available( void ) {
return AIR_TRUE;
}
static int
64 _nrrdEncodingHex_read( FILE *file, void *_data, size_t elNum,
Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingHex_read";
size_t nibIdx, nibNum;
unsigned char *data;
int car=0, nib;
AIR_UNUSED( nio );
data = AIR_CAST( unsigned char *, _data );
nibIdx = 0;
nibNum = 2*elNum*nrrdElementSize( nrrd );
if ( nibNum/elNum != 2*nrrdElementSize( nrrd ) ) {
biffAddf( NRRD, "%s: size_t can't hold 2*( #bytes in array )\n", me );
return 1;
}
while ( nibIdx < nibNum ) {
unsigned char nibshift;
car = fgetc( file );
if ( EOF == car ) break;
nib = _nrrdReadHexTable[car & 127];
if ( -2 == nib ) {
/* not a valid hex character */
break;
}
if ( -1 == nib ) {
/* its white space */
continue;
}
/* else it is a valid character, representing a value from 0 to 15 */
nibshift = AIR_CAST( unsigned char, nib << ( 4*( 1-( nibIdx & 1 ) ) ) );
/* HEY not sure why the cast is needed with gcc v4.8 -Wconversion */
*data = AIR_CAST( unsigned char, *data + nibshift );
data += nibIdx & 1;
nibIdx++;
}
if ( nibIdx != nibNum ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
if ( EOF == car ) {
biffAddf( NRRD, "%s: hit EOF getting byte %s of %s", me,
airSprintSize_t( stmp1, nibIdx/2 ),
airSprintSize_t( stmp2, nibNum/2 ) );
} else {
biffAddf( NRRD, "%s: hit invalid character ( '%c' ) getting "
"byte %s of %s", me, car,
airSprintSize_t( stmp1, nibIdx/2 ),
airSprintSize_t( stmp2, nibNum/2 ) );
}
return 1;
}
return 0;
}
static int
117 _nrrdEncodingHex_write( FILE *file, const void *_data, size_t elNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
/* static const char me[]="_nrrdEncodingHex_write"; */
const unsigned char *data;
size_t byteIdx, byteNum;
unsigned int bytesPerLine;
bytesPerLine = AIR_MAX( 1, nio->charsPerLine/2 );
data = AIR_CAST( const unsigned char*, _data );
byteNum = elNum*nrrdElementSize( nrrd );
for ( byteIdx=0; byteIdx<byteNum; byteIdx++ ) {
fprintf( file, "%c%c",
_nrrdWriteHexTable[( *data )>>4],
_nrrdWriteHexTable[( *data )&15] );
if ( bytesPerLine-1 == byteIdx % bytesPerLine ) {
fprintf( file, "\n" );
}
data++;
}
/* just to be sure, we always end with a carraige return */
fprintf( file, "\n" );
return 0;
}
const NrrdEncoding
_nrrdEncodingHex = {
"hex", /* name */
"hex", /* suffix */
AIR_TRUE, /* endianMatters */
AIR_FALSE, /* isCompression */
_nrrdEncodingHex_available,
_nrrdEncodingHex_read,
_nrrdEncodingHex_write
};
152 const NrrdEncoding *const
nrrdEncodingHex = &_nrrdEncodingHex;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static int
28 _nrrdEncodingRaw_available( void ) {
return AIR_TRUE;
}
static int
34 _nrrdEncodingRaw_read( FILE *file, void *data, size_t elementNum,
Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingRaw_read";
size_t ret, bsize;
int fd, dio, car;
long savePos;
char *data_c;
size_t elementSize, maxChunkSize, remainderValue, chunkSize;
size_t retTmp;
char stmp[3][AIR_STRLEN_SMALL];
bsize = nrrdElementSize( nrrd )*elementNum;
if ( nio->format->usesDIO ) {
fd = fileno( file );
dio = airDioTest( fd, data, bsize );
} else {
fd = -1;
dio = airNoDio_format;
}
if ( airNoDio_okay == dio ) {
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "with direct I/O ... " );
}
ret = airDioRead( fd, data, bsize );
if ( ret != bsize ) {
biffAddf( NRRD, "%s: airDioRead got read only %s of %sbytes "
"( %g%% of expected )", me,
airSprintSize_t( stmp[0], ret ),
airSprintSize_t( stmp[1], bsize ),
100.0*AIR_CAST( double, ret )/AIR_CAST( double, bsize ) );
return 1;
}
} else {
if ( 2 <= nrrdStateVerboseIO ) {
if ( AIR_DIO && nio->format->usesDIO ) {
fprintf( stderr, "with fread( ), not DIO: %s ...", airNoDioErr( dio ) );
}
}
/* HEY: There's a bug in fread/fwrite in gcc 4.2.1 ( with SnowLeopard ).
When it reads/writes a >=2GB data array, it pretends to succeed
( i.e. the return value is the right number ) but it hasn't
actually read/written the data. The work-around is to loop
over the data, reading/writing 1GB ( or smaller ) chunks. */
ret = 0;
data_c = ( char * )data;
elementSize = nrrdElementSize( nrrd );
maxChunkSize = 1024 * 1024 * 1024 / elementSize;
while( ret < elementNum ) {
remainderValue = elementNum-ret;
if ( remainderValue < maxChunkSize ) {
chunkSize = remainderValue;
} else {
chunkSize = maxChunkSize;
}
retTmp =
fread( &( data_c[ret*elementSize] ), elementSize, chunkSize, file );
ret += retTmp;
if ( retTmp != chunkSize ) {
biffAddf( NRRD, "%s: fread got only %s %s-sized things, not %s "
"( %g%% of expected )", me,
airSprintSize_t( stmp[0], ret ),
airSprintSize_t( stmp[1], nrrdElementSize( nrrd ) ),
airSprintSize_t( stmp[2], elementNum ),
100.0*AIR_CAST( double, ret )/AIR_CAST( double, elementNum ) );
return 1;
}
}
car = fgetc( file );
if ( EOF != car ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "%s: WARNING: finished reading raw data, "
"but file not at EOF\n", me );
}
ungetc( car, file );
}
if ( 2 <= nrrdStateVerboseIO && nio->byteSkip && stdin != file ) {
savePos = ftell( file );
if ( !fseek( file, 0, SEEK_END ) ) {
double frac = ( AIR_CAST( double, bsize )
/AIR_CAST( double, ftell( file ) + 1 ) );
fprintf( stderr, "( %s: used %g%% of file for nrrd data )\n", me,
100.0*frac );
fseek( file, savePos, SEEK_SET );
}
}
}
return 0;
}
static int
127 _nrrdEncodingRaw_write( FILE *file, const void *data, size_t elementNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingRaw_write";
int fd, dio;
size_t ret, bsize;
const char *data_c;
size_t elementSize, maxChunkSize, remainderValue, chunkSize;
size_t retTmp;
char stmp[3][AIR_STRLEN_SMALL];
bsize = nrrdElementSize( nrrd )*elementNum;
if ( nio->format->usesDIO ) {
fd = fileno( file );
dio = airDioTest( fd, data, bsize );
} else {
fd = -1;
dio = airNoDio_format;
}
if ( airNoDio_okay == dio ) {
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "with direct I/O ... " );
}
ret = airDioWrite( fd, data, bsize );
if ( ret != bsize ) {
biffAddf( NRRD, "%s: airDioWrite wrote only %s of %s bytes "
"( %g%% of expected )", me,
airSprintSize_t( stmp[0], ret ),
airSprintSize_t( stmp[1], bsize ),
100.0*AIR_CAST( double, ret )/AIR_CAST( double, bsize ) );
return 1;
}
} else {
if ( 2 <= nrrdStateVerboseIO ) {
if ( AIR_DIO && nio->format->usesDIO ) {
fprintf( stderr, "with fread( ), not DIO: %s ...", airNoDioErr( dio ) );
}
}
/* HEY: There's a bug in fread/fwrite in gcc 4.2.1 ( with SnowLeopard ).
When it reads/writes a >=2GB data array, it pretends to succeed
( i.e. the return value is the right number ) but it hasn't
actually read/written the data. The work-around is to loop
over the data, reading/writing 1GB ( or smaller ) chunks. */
ret = 0;
data_c = AIR_CAST( const char *, data );
elementSize = nrrdElementSize( nrrd );
maxChunkSize = 1024 * 1024 * 1024 / elementSize;
while( ret < elementNum ) {
remainderValue = elementNum-ret;
if ( remainderValue < maxChunkSize ) {
chunkSize = remainderValue;
} else {
chunkSize = maxChunkSize;
}
retTmp =
fwrite( &( data_c[ret*elementSize] ), elementSize, chunkSize, file );
ret += retTmp;
if ( retTmp != chunkSize ) {
biffAddf( NRRD, "%s: fwrite wrote only %s %s-sized things, not %s "
"( %g%% of expected )", me,
airSprintSize_t( stmp[0], ret ),
airSprintSize_t( stmp[1], nrrdElementSize( nrrd ) ),
airSprintSize_t( stmp[2], elementNum ),
100.0*AIR_CAST( double, ret )/AIR_CAST( double, elementNum ) );
return 1;
}
}
fflush( file );
/*
if ( ferror( file ) ) {
biffAddf( NRRD, "%s: ferror returned non-zero", me );
return 1;
}
*/
}
return 0;
}
const NrrdEncoding
_nrrdEncodingRaw = {
"raw", /* name */
"raw", /* suffix */
AIR_TRUE, /* endianMatters */
AIR_FALSE, /* isCompression */
_nrrdEncodingRaw_available,
_nrrdEncodingRaw_read,
_nrrdEncodingRaw_write
};
217 const NrrdEncoding *const
nrrdEncodingRaw = &_nrrdEncodingRaw;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static int
28 _nrrdEncodingZRL_available( void ) {
return AIR_TRUE;
}
static int
34 _nrrdEncodingZRL_read( FILE *file, void *data, size_t elementNum,
Nrrd *nrrd, NrrdIoState *nio ) {
AIR_UNUSED( nio );
unsigned char *output_buffer = ( unsigned char * ) data;
size_t toread = elementNum*nrrdElementSize( nrrd );
/*
static const char me[]="_nrrdEncodingZRL_read";
printf( "!%s: looking for %u values ( %u bytes ) of type %s\n", me,
( unsigned int )elementNum, ( unsigned int )toread,
airEnumStr( nrrdType, nrrd->type ) ); */
int cc, dd;
unsigned int j = 0;
while ( j < toread ) {
cc = fgetc( file );
if ( cc == 0 ) {
dd = fgetc( file );
if ( dd == 0 ) {
dd = fgetc( file );
j += dd + fgetc( file )*256;
} else {
j += ( unsigned char )dd;
}
} else {
output_buffer[j] = ( unsigned char )cc;
j++;
}
}
return 0;
}
static int
67 _nrrdEncodingZRL_write( FILE *file, const void *data, size_t elementNum,
const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingZRL_write";
AIR_UNUSED( file );
AIR_UNUSED( data );
AIR_UNUSED( elementNum );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, currently a read-only encoding", me );
return 0;
}
const NrrdEncoding
_nrrdEncodingZRL = {
"zrl", /* name */
"zrl", /* suffix */
AIR_TRUE, /* endianMatters */
AIR_FALSE, /* isCompression: HEY this is a hack: this IS certainly a
compression. However, with compressed encodings the nrrd
format has no way of specifying whether a byteskip
between be outside the encoding ( in the uncompressed
data ) vs inside the encoding ( within the compuressed
data ). To date the convention has been that byte skip is
done *inside* compressions, but for the ZRL-encoded data
as currently generated, the relevant byte skipping is
certainly *outside* the compression. Thus we claim
ignorance about how ZRL is a compression, so that byte
skipping can be used. */
_nrrdEncodingZRL_available,
_nrrdEncodingZRL_read,
_nrrdEncodingZRL_write
};
102 const NrrdEncoding *const
nrrdEncodingZRL = &_nrrdEncodingZRL;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
static void
27 _nrrdSwap16Endian( void *_data, size_t N ) {
unsigned short *data, dd, fix, mask;
size_t I;
if ( !_data ) {
return;
}
data = AIR_CAST( unsigned short *, _data );
mask = AIR_CAST( unsigned short, 0x00FFu );
for ( I=0; I<N; I++ ) {
dd = data[I];
fix = ( dd & mask ); dd >>= 0x08;
fix = ( dd & mask ) | AIR_CAST( unsigned short, fix << 0x08 );
data[I] = fix;
}
}
static void
45 _nrrdSwap32Endian( void *_data, size_t N ) {
unsigned int *data, dd, fix, mask;
size_t I;
if ( !_data ) {
return;
}
data = AIR_CAST( unsigned int *, _data );
mask = 0x000000FFu;
for ( I=0; I<N; I++ ) {
dd = data[I];
fix = ( dd & mask ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 );
data[I] = fix;
}
}
static void
65 _nrrdSwap64Endian( void *_data, size_t N ) {
airULLong *data, dd, fix, mask;
size_t I;
if ( !_data ) {
return;
}
data = AIR_CAST( airULLong *, _data );
mask = AIR_ULLONG( 0x00000000000000FF );
for ( I=0; I<N; I++ ) {
dd = data[I];
fix = ( dd & mask ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 ); dd >>= 0x08;
fix = ( dd & mask ) | ( fix << 0x08 );
data[I] = fix;
}
}
static void
89 _nrrdNoopEndian( void *data, size_t N ) {
AIR_UNUSED( data );
AIR_UNUSED( N );
return;
}
static void
96 _nrrdBlockEndian( void *data, size_t N ) {
char me[]="_nrrdBlockEndian";
AIR_UNUSED( data );
AIR_UNUSED( N );
fprintf( stderr, "%s: WARNING: can't fix endiannes of nrrd type %s\n", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
}
105 static void
( *_nrrdSwapEndian[] )( void *, size_t ) = {
_nrrdNoopEndian, /* 0: nobody knows! */
_nrrdNoopEndian, /* 1: signed 1-byte integer */
_nrrdNoopEndian, /* 2: unsigned 1-byte integer */
_nrrdSwap16Endian, /* 3: signed 2-byte integer */
_nrrdSwap16Endian, /* 4: unsigned 2-byte integer */
_nrrdSwap32Endian, /* 5: signed 4-byte integer */
_nrrdSwap32Endian, /* 6: unsigned 4-byte integer */
_nrrdSwap64Endian, /* 7: signed 8-byte integer */
_nrrdSwap64Endian, /* 8: unsigned 8-byte integer */
_nrrdSwap32Endian, /* 9: 4-byte floating point */
_nrrdSwap64Endian, /* 10: 8-byte floating point */
_nrrdBlockEndian /* 11: size user defined at run time */
};
void
122 nrrdSwapEndian( Nrrd *nrrd ) {
if ( nrrd
&& nrrd->data
&& !airEnumValCheck( nrrdType, nrrd->type ) ) {
_nrrdSwapEndian[nrrd->type]( nrrd->data, nrrdElementNumber( nrrd ) );
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
/*
** Rules of thumb for editing these things. The airEnum definitions are
** unfortunately EXTREMELY sensitive to small typo errors, and there is
** no good way to detect the errors. So:
**
** 1 ) Be awake and undistracted. Turn down the music.
** 2 ) When editing the char arrays, make sure that you put commas
** where you mean them to be. C's automatic string concatenation
** is not your friend here. In fact, EXPLOIT the fact that you can have
** a comma after the last element of a list ( of strings )- it decreases
** the chances that adding a new element at the end will be thwarted by
** the lack of a comma at the end of the previous ( and previously last )
** string.
** 3 ) When editing the *StrEqv and *ValEqv arrays, make absolutely
** sure that both are changed in parallel. Use only one enum value
** per line; putting all equivalents on that line, and make sure that
** there is one line in both *StrEqv and *ValEqv for all the possible
** enum values, and that there are as many elements in each line.
** 4 ) Make sure that additions here are reflected in nrrdEnums.h and
** vice versa.
*/
/* ------------------------ nrrdFormat ------------------------- */
static const char *
_nrrdFormatTypeStr[NRRD_FORMAT_TYPE_MAX+1] = {
"( unknown_format )",
"nrrd",
"pnm",
"png",
"vtk",
"text",
"eps",
};
static const char *
_nrrdFormatTypeDesc[NRRD_FORMAT_TYPE_MAX+1] = {
"unknown_format",
"native format for nearly raw raster data",
"Portable aNy Map: includes PGM for grayscale and PPM for color",
"Portable Network Graphics: lossless compression of 8- and 16-bit data",
"Visualization ToolKit STRUCTURED_POINTS data",
"white-space-delimited plain text encoding of 2-D float array",
"Encapsulated PostScript images",
};
static const char *
_nrrdFormatTypeStrEqv[] = {
"nrrd",
"pnm",
"png",
"vtk",
"table", "text", "txt",
"eps",
""
};
static const int
_nrrdFormatTypeValEqv[] = {
nrrdFormatTypeNRRD,
nrrdFormatTypePNM,
nrrdFormatTypePNG,
nrrdFormatTypeVTK,
nrrdFormatTypeText, nrrdFormatTypeText, nrrdFormatTypeText,
nrrdFormatTypeEPS,
};
airEnum
_nrrdFormatType = {
"format",
NRRD_FORMAT_TYPE_MAX,
_nrrdFormatTypeStr, NULL,
_nrrdFormatTypeDesc,
_nrrdFormatTypeStrEqv, _nrrdFormatTypeValEqv,
AIR_FALSE
};
102 const airEnum *const
nrrdFormatType = &_nrrdFormatType;
/* ------------------------ nrrdType ------------------------- */
static const char *
_nrrdTypeStr[NRRD_TYPE_MAX+1] = {
"( unknown_type )",
"signed char",
"unsigned char",
"short",
"unsigned short",
"int",
"unsigned int",
"long long int",
"unsigned long long int",
"float",
"double",
"block",
};
static const char *
_nrrdTypeDesc[NRRD_TYPE_MAX+1] = {
"unknown type",
"signed 1-byte integer",
"unsigned 1-byte integer",
"signed 2-byte integer",
"unsigned 2-byte integer",
"signed 4-byte integer",
"unsigned 4-byte integer",
"signed 8-byte integer",
"unsigned 8-byte integer",
"4-byte floating point",
"8-byte floating point",
"size user-defined at run-time",
};
#define ntCH nrrdTypeChar
#define ntUC nrrdTypeUChar
#define ntSH nrrdTypeShort
#define ntUS nrrdTypeUShort
#define ntIN nrrdTypeInt
#define ntUI nrrdTypeUInt
#define ntLL nrrdTypeLLong
#define ntUL nrrdTypeULLong
#define ntFL nrrdTypeFloat
#define ntDB nrrdTypeDouble
#define ntBL nrrdTypeBlock
static const char *
_nrrdTypeStrEqv[] = {
"signed char", /* but NOT just "char" */ "int8", "int8_t",
"uchar", "unsigned char", "uint8", "uint8_t",
"short", "short int", "signed short", "signed short int", "int16", "int16_t",
"ushort", "unsigned short", "unsigned short int", "uint16", "uint16_t",
"int", "signed int", "int32", "int32_t",
"uint", "unsigned int", "uint32", "uint32_t",
"longlong", "long long", "long long int", "signed long long",
"signed long long int", "int64", "int64_t",
"ulonglong", "unsigned long long", "unsigned long long int",
"uint64", "uint64_t",
"float",
"double",
"block",
""
};
static const int
_nrrdTypeValEqv[] = {
ntCH, ntCH, ntCH,
ntUC, ntUC, ntUC, ntUC,
ntSH, ntSH, ntSH, ntSH, ntSH, ntSH,
ntUS, ntUS, ntUS, ntUS, ntUS,
ntIN, ntIN, ntIN, ntIN,
ntUI, ntUI, ntUI, ntUI,
ntLL, ntLL, ntLL, ntLL, ntLL, ntLL, ntLL,
ntUL, ntUL, ntUL, ntUL, ntUL,
ntFL,
ntDB,
ntBL,
};
airEnum
_nrrdType = {
"type",
NRRD_TYPE_MAX,
_nrrdTypeStr, NULL,
_nrrdTypeDesc,
_nrrdTypeStrEqv, _nrrdTypeValEqv,
AIR_FALSE
};
193 const airEnum *const
nrrdType = &_nrrdType;
/* ------------------------ nrrdEncodingType ------------------------- */
static const char *
_nrrdEncodingTypeStr[NRRD_ENCODING_TYPE_MAX+1] = {
"( unknown_encoding )",
"raw",
"ascii",
"hex",
"gz",
"bz2",
"zrl"
};
static const char *
_nrrdEncodingTypeDesc[NRRD_ENCODING_TYPE_MAX+1] = {
"unknown encoding",
"file is byte-for-byte same as memory representation",
"values written out in ASCII",
"case-insenstive hexadecimal encoding ( 2 chars / byte )",
"gzip compression of binary encoding",
"bzip2 compression of binary encoding",
"simple compression by encoding run-length of zeros",
};
static const char *
_nrrdEncodingTypeStrEqv[] = {
"raw",
"txt", "text", "ascii",
"hex",
"gz", "gzip",
"bz2", "bzip2",
"zrl",
""
};
static const int
_nrrdEncodingTypeValEqv[] = {
nrrdEncodingTypeRaw,
nrrdEncodingTypeAscii, nrrdEncodingTypeAscii, nrrdEncodingTypeAscii,
nrrdEncodingTypeHex,
nrrdEncodingTypeGzip, nrrdEncodingTypeGzip,
nrrdEncodingTypeBzip2, nrrdEncodingTypeBzip2,
nrrdEncodingTypeZRL
};
airEnum
_nrrdEncodingType = {
"encoding",
NRRD_ENCODING_TYPE_MAX,
_nrrdEncodingTypeStr, NULL,
_nrrdEncodingTypeDesc,
_nrrdEncodingTypeStrEqv, _nrrdEncodingTypeValEqv,
AIR_FALSE
};
250 const airEnum *const
nrrdEncodingType = &_nrrdEncodingType;
/* ------------------------ nrrdCenter ------------------------- */
static const char *
_nrrdCenterStr[NRRD_CENTER_MAX+1] = {
"( unknown_center )",
"node",
"cell",
};
static const char *
_nrrdCenterDesc[NRRD_CENTER_MAX+1] = {
"unknown centering",
"samples are at boundaries between elements along axis",
"samples are at centers of elements along axis",
};
static const airEnum
_nrrdCenter_enum = {
"centering",
NRRD_CENTER_MAX,
_nrrdCenterStr, NULL,
_nrrdCenterDesc,
NULL, NULL,
AIR_FALSE
};
278 const airEnum *const
nrrdCenter = &_nrrdCenter_enum;
/* ------------------------ nrrdKind ------------------------- */
/*
nrrdKindUnknown,
nrrdKindDomain, * 1: any image domain *
nrrdKindSpace, * 2: a spatial domain *
nrrdKindTime, * 3: a temporal domain *
* -------------------------- end domain kinds *
* -------------------------- begin range kinds *
nrrdKindList, * 4: any list of values, non-resample-able *
nrrdKindPoint, * 5: coords of a point *
nrrdKindVector, * 6: coeffs of ( contravariant ) vector *
nrrdKindCovariantVector, * 7: coeffs of covariant vector ( eg gradient ) *
nrrdKindNormal, * 8: coeffs of unit-length covariant vector *
* -------------------------- end arbitrary size kinds *
* -------------------------- begin size-specific kinds *
nrrdKindStub, * 9: axis with one sample ( a placeholder ) *
nrrdKindScalar, * 10: effectively, same as a stub *
nrrdKindComplex, * 11: real and imaginary components *
nrrdKind2Vector, * 12: 2 component vector *
nrrdKind3Color, * 13: ANY 3-component color value *
nrrdKindRGBColor, * 14: RGB, no colorimetry *
nrrdKindHSVColor, * 15: HSV, no colorimetry *
nrrdKindXYZColor, * 16: perceptual primary colors *
nrrdKind4Color, * 17: ANY 4-component color value *
nrrdKindRGBAColor, * 18: RGBA, no colorimetry *
nrrdKind3Vector, * 19: 3-component vector *
nrrdKind3Gradient, * 20: 3-component covariant vector *
nrrdKind3Normal, * 21: 3-component covector, assumed normalized *
nrrdKind4Vector, * 22: 4-component vector *
nrrdKindQuaternion, * 23: ( w, x, y, z ), not necessarily normalized *
nrrdKind2DSymMatrix, * 24: Mxx Mxy Myy *
nrrdKind2DMaskedSymMatrix, * 25: mask Mxx Mxy Myy *
nrrdKind2DMatrix, * 26: Mxx Mxy Myx Myy *
nrrdKind2DMaskedMatrix, * 27: mask Mxx Mxy Myx Myy *
nrrdKind3DSymMatrix, * 28: Mxx Mxy Mxz Myy Myz Mzz *
nrrdKind3DMaskedSymMatrix, * 29: mask Mxx Mxy Mxz Myy Myz Mzz *
nrrdKind3DMatrix, * 30: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz *
nrrdKind3DMaskedMatrix, * 31: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz *
*/
static const char *
_nrrdKindStr[NRRD_KIND_MAX+1] = {
"( unknown_kind )",
"domain",
"space",
"time",
"list",
"point",
"vector",
"covariant-vector",
"normal",
"stub",
"scalar",
"complex",
"2-vector",
"3-color",
"RGB-color",
"HSV-color",
"XYZ-color",
"4-color",
"RGBA-color",
"3-vector",
"3-gradient",
"3-normal",
"4-vector",
"quaternion",
"2D-symmetric-matrix",
"2D-masked-symmetric-matrix",
"2D-matrix",
"2D-masked-matrix",
"3D-symmetric-matrix",
"3D-masked-symmetric-matrix",
"3D-matrix",
"3D-masked-matrix",
};
static const char *
_nrrdKindDesc[NRRD_KIND_MAX+1] = {
"unknown kind",
"a domain variable of the function which the nrrd samples",
"a spatial domain, like the axes of a measured volume image",
"a temporal domain, as from time-varying measurements",
"some list of attributes; it makes no sense to resample along these",
"coordinates of a point",
"coefficients of a ( contravariant ) vector",
"coefficients of a covariant vector, such as a gradient",
"coefficients of a normalized covariant vector",
"a place-holder axis with a single sample",
"axis used to indicate that the nrrd contains a scalar value",
"real and imaginary parts of a value",
"a 2-component vector",
"any 3-component color value",
"red-green-blue color",
"hue-saturation-value single hexcone color",
"perceptual primaries color",
"any 4-component color value",
"red-green-blue-alpha color",
"a 3-element ( contravariant ) vector",
"a 3-element gradient ( covariant ) vector",
"a 3-element ( covariant ) vector which is assumed normalized",
"a 4-element ( contravariant ) vector",
"quaternion: x y z w",
"3 elements of 2D symmetric matrix: Mxx Mxy Myy",
"mask plus 3 elements of 2D symmetric matrix: mask Mxx Mxy Myy",
"4 elements of general 2D matrix: Mxx Mxy Myx Myy",
"mask plus 4 elements of general 2D matrix: mask Mxx Mxy Myx Myy",
"6 elements of 3D symmetric matrix: Mxx Mxy Mxz Myy Myz Mzz",
"mask plus 6 elements of 3D symmetric matrix: mask Mxx Mxy Mxz Myy Myz Mzz",
"9 elements of general 3D matrix: Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz",
"mask plus 9 elements of general 3D matrix: mask Mxx Mxy Mxz Myx Myy Myz Mzx Mzy Mzz",
};
static const char *
_nrrdKindStr_Eqv[] = {
"domain",
"space",
"time",
"list",
"point",
"vector", "contravariant-vector",
"covariant-vector",
"normal",
"stub",
"scalar",
"complex",
"2-vector",
"3-color",
"RGB-color", "RGBcolor", "RGB",
"HSV-color", "HSVcolor", "HSV",
"XYZ-color",
"4-color",
"RGBA-color", "RGBAcolor", "RGBA",
"3-vector",
"3-gradient",
"3-normal",
"4-vector",
"quaternion",
"2D-symmetric-matrix", "2D-sym-matrix",
"2D-symmetric-tensor", "2D-sym-tensor",
"2D-masked-symmetric-matrix", "2D-masked-sym-matrix",
"2D-masked-symmetric-tensor", "2D-masked-sym-tensor",
"2D-matrix",
"2D-tensor",
"2D-masked-matrix",
"2D-masked-tensor",
"3D-symmetric-matrix", "3D-sym-matrix",
"3D-symmetric-tensor", "3D-sym-tensor",
"3D-masked-symmetric-matrix", "3D-masked-sym-matrix",
"3D-masked-symmetric-tensor", "3D-masked-sym-tensor",
"3D-matrix",
"3D-tensor",
"3D-masked-matrix",
"3D-masked-tensor",
""
};
static int
_nrrdKindVal_Eqv[] = {
nrrdKindDomain,
nrrdKindSpace,
nrrdKindTime,
nrrdKindList,
nrrdKindPoint,
nrrdKindVector, nrrdKindVector,
nrrdKindCovariantVector,
nrrdKindNormal,
nrrdKindStub,
nrrdKindScalar,
nrrdKindComplex,
nrrdKind2Vector,
nrrdKind3Color,
nrrdKindRGBColor, nrrdKindRGBColor, nrrdKindRGBColor,
nrrdKindHSVColor, nrrdKindHSVColor, nrrdKindHSVColor,
nrrdKindXYZColor,
nrrdKind4Color,
nrrdKindRGBAColor, nrrdKindRGBAColor, nrrdKindRGBAColor,
nrrdKind3Vector,
nrrdKind3Gradient,
nrrdKind3Normal,
nrrdKind4Vector,
nrrdKindQuaternion,
nrrdKind2DSymMatrix, nrrdKind2DSymMatrix,
nrrdKind2DSymMatrix, nrrdKind2DSymMatrix,
nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix,
nrrdKind2DMaskedSymMatrix, nrrdKind2DMaskedSymMatrix,
nrrdKind2DMatrix,
nrrdKind2DMatrix,
nrrdKind2DMaskedMatrix,
nrrdKind2DMaskedMatrix,
nrrdKind3DSymMatrix, nrrdKind3DSymMatrix,
nrrdKind3DSymMatrix, nrrdKind3DSymMatrix,
nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix,
nrrdKind3DMaskedSymMatrix, nrrdKind3DMaskedSymMatrix,
nrrdKind3DMatrix,
nrrdKind3DMatrix,
nrrdKind3DMaskedMatrix,
nrrdKind3DMaskedMatrix,
};
static const airEnum
_nrrdKind_enum = {
"kind",
NRRD_KIND_MAX,
_nrrdKindStr, NULL,
_nrrdKindDesc,
_nrrdKindStr_Eqv, _nrrdKindVal_Eqv,
AIR_FALSE
};
490 const airEnum *const
nrrdKind = &_nrrdKind_enum;
/* ------------------------ nrrdField ------------------------- */
static const char *
_nrrdFieldStr[NRRD_FIELD_MAX+1] = {
"Ernesto \"Che\" Guevara",
"#",
"content",
"number",
"type",
"block size",
"dimension",
"space",
"space dimension",
"sizes",
"spacings",
"thicknesses",
"axis mins",
"axis maxs",
"space directions",
"centerings",
"kinds",
"labels",
"units",
"min",
"max",
"old min",
"old max",
"endian",
"encoding",
"line skip",
"byte skip",
"key/value", /* this is the one field for which the canonical string
here is totally different from the field identifier in the
NRRD file format ( ":=" ). We include nrrdField_keyvalue
in the enum because it is very useful to have a consistent
way of identifying lines in the format */
"sample units",
"space units",
"space origin",
"measurement frame",
"data file",
};
static const char *
_nrrdFieldDesc[NRRD_FIELD_MAX+1] = {
"unknown field identifier",
"comment",
"short description of whole array and/or its provenance",
"total number of samples in array",
"type of sample value",
"number of bytes in one block ( for block-type )",
"number of axes in array",
"identifier for space in which array grid lies",
"dimension of space in which array grid lies",
"list of number of samples along each axis, aka \"dimensions\" of the array",
"list of sample spacings along each axis",
"list of sample thicknesses along each axis",
"list of minimum positions associated with each axis",
"list of maximum positions associated with each axis",
"list of direction inter-sample vectors for each axis",
"list of sample centerings for each axis",
"list of kinds for each axis",
"list of short descriptions for each axis",
"list of units in which each axes' spacing and thickness is measured",
"supposed minimum array value",
"supposed maximum array value",
"minimum array value prior to quantization",
"maximum array value prior to quantization",
"endiannes of data as written in file",
"encoding of data written in file",
"number of lines to skip prior to byte skip and reading data",
"number of bytes to skip after line skip and prior to reading data",
"string-based key/value pairs",
"units of measurement of ( scalar ) values inside array itself",
"list of units for measuring origin and direct vectors' coefficients",
"location in space of center of first ( lowest memory address ) sample",
"maps coords of ( non-scalar ) values to coords of surrounding space",
"with detached headers, where is data to be found",
};
static const char *
_nrrdFieldStrEqv[] = {
"#",
"content",
"number",
"type",
"block size", "blocksize",
"dimension",
"space",
"space dimension", "spacedimension",
"sizes",
"spacings",
"thicknesses",
"axis mins", "axismins",
"axis maxs", "axismaxs",
"space directions", "spacedirections",
"centers", "centerings",
"kinds",
"labels",
"units",
"min",
"max",
"old min", "oldmin",
"old max", "oldmax",
"endian",
"encoding",
"line skip", "lineskip",
"byte skip", "byteskip",
"key/value", /* bogus, here to keep the airEnum complete */
"sample units", "sampleunits",
"space units", "spaceunits",
"space origin", "spaceorigin",
"measurement frame", "measurementframe",
"data file", "datafile",
""
};
static const int
_nrrdFieldValEqv[] = {
nrrdField_comment,
nrrdField_content,
nrrdField_number,
nrrdField_type,
nrrdField_block_size, nrrdField_block_size,
nrrdField_dimension,
nrrdField_space,
nrrdField_space_dimension, nrrdField_space_dimension,
nrrdField_sizes,
nrrdField_spacings,
nrrdField_thicknesses,
nrrdField_axis_mins, nrrdField_axis_mins,
nrrdField_axis_maxs, nrrdField_axis_maxs,
nrrdField_space_directions, nrrdField_space_directions,
nrrdField_centers, nrrdField_centers,
nrrdField_kinds,
nrrdField_labels,
nrrdField_units,
nrrdField_min,
nrrdField_max,
nrrdField_old_min, nrrdField_old_min,
nrrdField_old_max, nrrdField_old_max,
nrrdField_endian,
nrrdField_encoding,
nrrdField_line_skip, nrrdField_line_skip,
nrrdField_byte_skip, nrrdField_byte_skip,
nrrdField_keyvalue,
nrrdField_sample_units, nrrdField_sample_units,
nrrdField_space_units, nrrdField_space_units,
nrrdField_space_origin, nrrdField_space_origin,
nrrdField_measurement_frame, nrrdField_measurement_frame,
nrrdField_data_file, nrrdField_data_file,
};
static const airEnum
_nrrdField = {
"nrrd_field",
NRRD_FIELD_MAX,
_nrrdFieldStr, NULL,
_nrrdFieldDesc,
_nrrdFieldStrEqv, _nrrdFieldValEqv,
AIR_FALSE /* field identifiers not case sensitive */
};
655 const airEnum *const
nrrdField = &_nrrdField;
/* ------------------------ nrrdSpace ------------------------- */
/*
nrrdSpaceUnknown,
nrrdSpaceRightUp, * 1: 2-D, oriented like upper right
Cartesian quadrant, number I *
nrrdSpaceRightDown, * 2: 2-D, oriented like raster
coordinates *
nrrdSpaceRightAnteriorSuperior, * 3: NIFTI-1 ( right-handed ) *
nrrdSpaceLeftAnteriorSuperior, * 4: standard Analyze ( left-handed ) *
nrrdSpaceLeftPosteriorSuperior, * 5: DICOM 3.0 ( right-handed ) *
nrrdSpaceRightAnteriorSuperiorTime, * 6: *
nrrdSpaceLeftAnteriorSuperiorTime, * 7: *
nrrdSpaceLeftPosteriorSuperiorTime, * 8: *
nrrdSpaceScannerXYZ, * 9: ACR/NEMA 2.0 ( pre-DICOM 3.0 ) *
nrrdSpaceScannerXYZTime, * 10: *
nrrdSpace3DRightHanded, * 11: *
nrrdSpace3DLeftHanded, * 12: *
nrrdSpace3DRightHandedTime, * 13: *
nrrdSpace3DLeftHandedTime, * 14: *
nrrdSpaceLast
*/
static const char *
_nrrdSpaceStr[NRRD_SPACE_MAX+1] = {
"( unknown_space )",
"right-up",
"right-down",
"right-anterior-superior",
"left-anterior-superior",
"left-posterior-superior",
"right-anterior-superior-time",
"left-anterior-superior-time",
"left-posterior-superior-time",
"scanner-xyz",
"scanner-xyz-time",
"3D-right-handed",
"3D-left-handed",
"3D-right-handed-time",
"3D-left-handed-time",
};
static const char *
_nrrdSpaceDesc[NRRD_SPACE_MAX+1] = {
"unknown space",
"right-up ( like Cartesian quadrant I )",
"right-down ( like raster coordinates )",
"right-anterior-superior ( used in NIFTI-1 and SPL's 3D Slicer )",
"left-anterior-superior ( used in Analyze 7.5 )",
"left-posterior-superior ( used in DICOM 3 )",
"right-anterior-superior-time",
"left-anterior-superior-time",
"left-posterior-superior-time",
"scanner-xyz ( used in ACR/NEMA 2.0 )",
"scanner-xyz-time",
"3D-right-handed",
"3D-left-handed",
"3D-right-handed-time",
"3D-left-handed-time",
};
static const char *
_nrrdSpaceStrEqv[] = {
"right-up", "right up",
"right-down", "right down",
"right-anterior-superior", "right anterior superior",
"rightanteriorsuperior", "RAS",
"left-anterior-superior", "left anterior superior",
"leftanteriorsuperior", "LAS",
"left-posterior-superior", "left posterior superior",
"leftposteriorsuperior", "LPS",
"right-anterior-superior-time", "right anterior superior time",
"rightanteriorsuperiortime", "RAST",
"left-anterior-superior-time", "left anterior superior time",
"leftanteriorsuperiortime", "LAST",
"left-posterior-superior-time", "left posterior superior time",
"leftposteriorsuperiortime", "LPST",
"scanner-xyz",
"scanner-xyz-time", "scanner-xyzt",
"3D-right-handed", "3D right handed", "3Drighthanded",
"3D-left-handed", "3D left handed", "3Dlefthanded",
"3D-right-handed-time", "3D right handed time",
"3Drighthandedtime",
"3D-left-handed-time", "3D left handed time",
"3Dlefthandedtime",
""
};
static const int
_nrrdSpaceValEqv[] = {
nrrdSpaceRightUp, nrrdSpaceRightUp,
nrrdSpaceRightDown, nrrdSpaceRightDown,
nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior,
nrrdSpaceRightAnteriorSuperior, nrrdSpaceRightAnteriorSuperior,
nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior,
nrrdSpaceLeftAnteriorSuperior, nrrdSpaceLeftAnteriorSuperior,
nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior,
nrrdSpaceLeftPosteriorSuperior, nrrdSpaceLeftPosteriorSuperior,
nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime,
nrrdSpaceRightAnteriorSuperiorTime, nrrdSpaceRightAnteriorSuperiorTime,
nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime,
nrrdSpaceLeftAnteriorSuperiorTime, nrrdSpaceLeftAnteriorSuperiorTime,
nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime,
nrrdSpaceLeftPosteriorSuperiorTime, nrrdSpaceLeftPosteriorSuperiorTime,
nrrdSpaceScannerXYZ,
nrrdSpaceScannerXYZTime, nrrdSpaceScannerXYZTime,
nrrdSpace3DRightHanded, nrrdSpace3DRightHanded, nrrdSpace3DRightHanded,
nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded, nrrdSpace3DLeftHanded,
nrrdSpace3DRightHandedTime, nrrdSpace3DRightHandedTime,
nrrdSpace3DRightHandedTime,
nrrdSpace3DLeftHandedTime, nrrdSpace3DLeftHandedTime,
nrrdSpace3DLeftHandedTime
};
static const airEnum
_nrrdSpace = {
"space",
NRRD_SPACE_MAX,
_nrrdSpaceStr, NULL,
_nrrdSpaceDesc,
_nrrdSpaceStrEqv, _nrrdSpaceValEqv,
AIR_FALSE
};
781 const airEnum *const
nrrdSpace = &_nrrdSpace;
/* ------------------------ nrrdSpacingStatus ------------------------- */
static const char *
_nrrdSpacingStatusStr[NRRD_SPACING_STATUS_MAX+1] = {
"( unknown_status )",
"none",
"scalarNoSpace",
"scalarWithSpace",
"direction",
};
static const char *
_nrrdSpacingStatusDesc[NRRD_BOUNDARY_MAX+1] = {
"unknown spacing status behavior",
"neither axis->spacing nor axis->spaceDirection set",
"axis->spacing set normally",
"axis->spacing set, with surround space ( ? )",
"axis->spaceDirection set normally",
};
static const airEnum
_nrrdSpacingStatus = {
"spacing status",
NRRD_SPACING_STATUS_MAX,
_nrrdSpacingStatusStr, NULL,
_nrrdSpacingStatusDesc,
NULL, NULL,
AIR_FALSE
};
813 const airEnum *const
nrrdSpacingStatus = &_nrrdSpacingStatus;
/* ---- BEGIN non-NrrdIO */
/* -------------------- nrrdOrientationHave --------------------- */
static const char *
_nrrdOrientationHaveStr[NRRD_ORIENTATION_HAVE_MAX+1] = {
"( unknown_orientation_have )",
"nothing",
"spacing",
"min+spacing",
"min+max",
"directions",
"origin+directions"
};
static const char *
_nrrdOrientationHaveDesc[NRRD_ORIENTATION_HAVE_MAX+1] = {
"unknown orientation have",
"know nothing",
"know per-axis spacing",
"know per-axis min and spacing",
"know per-axis min and max",
"know space directions",
"know space origin and directions"
};
static const char *
_nrrdOrientationHaveStrEqv[] = {
"nothing",
"spacing", "spc",
"min+spacing", "min+spc", "minspacing", "minspc",
"min+max", "minmax",
"directions", "dirs",
"origin+directions", "origindirections", "oridirs", "full",
""
};
static const int
_nrrdOrientationHaveValEqv[] = {
nrrdOrientationHaveNothing,
nrrdOrientationHaveSpacing,
nrrdOrientationHaveSpacing,
nrrdOrientationHaveMinSpacing,
nrrdOrientationHaveMinSpacing,
nrrdOrientationHaveMinSpacing,
nrrdOrientationHaveMinSpacing,
nrrdOrientationHaveMinMax,
nrrdOrientationHaveMinMax,
nrrdOrientationHaveDirections,
nrrdOrientationHaveDirections,
nrrdOrientationHaveOriginDirections,
nrrdOrientationHaveOriginDirections,
nrrdOrientationHaveOriginDirections,
nrrdOrientationHaveOriginDirections
};
static const airEnum
_nrrdOrientationHave = {
"orientation have",
NRRD_ORIENTATION_HAVE_MAX,
_nrrdOrientationHaveStr, NULL,
_nrrdOrientationHaveDesc,
_nrrdOrientationHaveStrEqv, _nrrdOrientationHaveValEqv,
AIR_FALSE
};
881 const airEnum *const
nrrdOrientationHave = &_nrrdOrientationHave;
/* ------------------------ nrrdBoundary ------------------------- */
static const char *
_nrrdBoundaryStr[NRRD_BOUNDARY_MAX+1] = {
"( unknown_boundary )",
"pad",
"bleed",
"wrap",
"weight",
"mirror"
};
static const char *
_nrrdBoundaryDesc[NRRD_BOUNDARY_MAX+1] = {
"unknown boundary behavior",
"pad with some specified value",
"copy values from edge outward as needed",
"wrap around to other end of axis",
"re-weight ( by normalization ) samples within axis range",
"mirror folding"
};
static const airEnum
_nrrdBoundary = {
"boundary behavior",
NRRD_BOUNDARY_MAX,
_nrrdBoundaryStr, NULL,
_nrrdBoundaryDesc,
NULL, NULL,
AIR_FALSE
};
915 const airEnum *const
nrrdBoundary = &_nrrdBoundary;
/* ------------------------ nrrdMeasure ------------------------- */
static const char *
_nrrdMeasureStr[NRRD_MEASURE_MAX+1] = {
"( unknown_measure )",
"min",
"max",
"mean",
"median",
"mode",
"product",
"sum",
"L1",
"L2",
"L4",
"normalizedL2",
"RMS",
"Linf",
"variance",
"stdv",
"CoV",
"skew",
"line-slope",
"line-intercept",
"line-error",
"histo-min",
"histo-max",
"histo-mean",
"histo-median",
"histo-mode",
"histo-product",
"histo-sum",
"histo-L2",
"histo-variance",
"histo-SD",
};
static const char *
_nrrdMeasureDesc[NRRD_MEASURE_MAX+1] = {
"unknown measure",
"minimum of values",
"maximum of values",
"mean of values",
"median of values",
"mode of values",
"product of values",
"sum of values",
"L1 norm of values",
"L2 norm of values",
"L4 norm of values",
"L2 norm of values divided by # of values",
"Root of Mean of Squares",
"Linf norm of values",
"variance of values",
"standard deviation of values",
"coefficient of variation of values",
"skew of values",
"slope of line of best fit",
"y-intercept of line of best fit",
"error of line fitting",
"minimum of histogrammed values",
"maximum of histogrammed values",
"mean of histogrammed values",
"median of histogrammed values",
"mode of histogrammed values",
"product of histogrammed values",
"sum of histogrammed values",
"L2 norm of histogrammed values",
"variance of histogrammed values",
"standard deviation of histogrammed values",
};
static const char *
_nrrdMeasureStrEqv[] = {
"min",
"max",
"mean",
"median",
"mode",
"product", "prod",
"sum",
"L1",
"L2",
"L4",
"normalizedL2", "normL2", "nL2",
"rootmeansquare", "rms",
"Linf",
"variance", "var",
"SD", "stdv",
"cov",
"skew", "skewness",
"slope", "line-slope",
"intc", "intercept", "line-intc", "line-intercept",
"error", "line-error",
"histo-min",
"histo-max",
"histo-mean",
"histo-median",
"histo-mode",
"histo-product",
"histo-sum",
"histo-l2",
"histo-variance", "histo-var",
"histo-sd",
""
};
static const int
_nrrdMeasureValEqv[] = {
nrrdMeasureMin,
nrrdMeasureMax,
nrrdMeasureMean,
nrrdMeasureMedian,
nrrdMeasureMode,
nrrdMeasureProduct, nrrdMeasureProduct,
nrrdMeasureSum,
nrrdMeasureL1,
nrrdMeasureL2,
nrrdMeasureL4,
nrrdMeasureNormalizedL2, nrrdMeasureNormalizedL2, nrrdMeasureNormalizedL2,
nrrdMeasureRootMeanSquare, nrrdMeasureRootMeanSquare,
nrrdMeasureLinf,
nrrdMeasureVariance, nrrdMeasureVariance,
nrrdMeasureSD, nrrdMeasureSD,
nrrdMeasureCoV,
nrrdMeasureSkew, nrrdMeasureSkew,
nrrdMeasureLineSlope, nrrdMeasureLineSlope,
nrrdMeasureLineIntercept, nrrdMeasureLineIntercept,
nrrdMeasureLineIntercept, nrrdMeasureLineIntercept,
nrrdMeasureLineError, nrrdMeasureLineError,
nrrdMeasureHistoMin,
nrrdMeasureHistoMax,
nrrdMeasureHistoMean,
nrrdMeasureHistoMedian,
nrrdMeasureHistoMode,
nrrdMeasureHistoProduct,
nrrdMeasureHistoSum,
nrrdMeasureHistoL2,
nrrdMeasureHistoVariance, nrrdMeasureHistoVariance,
nrrdMeasureHistoSD,
};
static const airEnum
_nrrdMeasure = {
"measure",
NRRD_MEASURE_MAX,
_nrrdMeasureStr, NULL,
_nrrdMeasureDesc,
_nrrdMeasureStrEqv, _nrrdMeasureValEqv,
AIR_FALSE
};
1069 const airEnum *const
nrrdMeasure = &_nrrdMeasure;
/* ------------------------ nrrdUnaryOp ---------------------- */
#define nuNeg nrrdUnaryOpNegative
#define nuRcp nrrdUnaryOpReciprocal
#define nuSin nrrdUnaryOpSin
#define nuCos nrrdUnaryOpCos
#define nuTan nrrdUnaryOpTan
#define nuAsn nrrdUnaryOpAsin
#define nuAcs nrrdUnaryOpAcos
#define nuAtn nrrdUnaryOpAtan
#define nuExp nrrdUnaryOpExp
#define nuLge nrrdUnaryOpLog
#define nuLg2 nrrdUnaryOpLog2
#define nuLgt nrrdUnaryOpLog10
#define nuL1p nrrdUnaryOpLog1p
#define nuEm1 nrrdUnaryOpExpm1
#define nuSqt nrrdUnaryOpSqrt
#define nuCbt nrrdUnaryOpCbrt
#define nuErf nrrdUnaryOpErf
#define nuNerf nrrdUnaryOpNerf
#define nuCil nrrdUnaryOpCeil
#define nuFlr nrrdUnaryOpFloor
#define nuRup nrrdUnaryOpRoundUp
#define nuRdn nrrdUnaryOpRoundDown
#define nuAbs nrrdUnaryOpAbs
#define nuSgn nrrdUnaryOpSgn
#define nuExs nrrdUnaryOpExists
#define nuRnd nrrdUnaryOpRand
#define nuNrn nrrdUnaryOpNormalRand
#define nuoIf nrrdUnaryOpIf
#define nuZer nrrdUnaryOpZero
#define nuOne nrrdUnaryOpOne
static const char *
_nrrdUnaryOpStr[NRRD_UNARY_OP_MAX+1] = {
"( unknown_unary_op )",
"-",
"r",
"sin",
"cos",
"tan",
"asin",
"acos",
"atan",
"exp",
"log",
"log2",
"log10",
"log1p",
"expm1",
"sqrt",
"cbrt",
"erf",
"nerf",
"ceil",
"floor",
"roundup",
"rounddown",
"abs",
"sgn",
"exists",
"rand",
"normrand",
"if",
"zero",
"one",
"tauofsig",
"sigoftau"
};
static const char *
_nrrdUnaryOpDesc[NRRD_UNARY_OP_MAX+1] = {
"unknown unary op",
"negative; additive inverse",
"reciprocal; multiplicative inverse",
"sin",
"cos",
"tan",
"arcsin",
"arccos",
"arctan",
"e raised to something",
"natural ( base e ) logarithm",
"base 2 logarithm",
"base 10 logarithm",
"accurate ln( 1+x )",
"accurate exp( x )-1",
"square root",
"cube root",
"error function ( integral of gaussian )",
"erf, mapped to range ( 0, 1 )",
"smallest integer greater than or equal",
"largest integer less than or equal",
"round to closest integer ( 0.5 rounded to 1 )",
"round to closest integer ( 0.5 rounded to 0 )",
"absolute value",
"sign of value ( -1, 0, or 1 )",
"value is not infinity or NaN",
"uniformly distributed random value between 0 and 1",
"normally distributed random value, mean 0, stdv 1",
"if nonzero, 1, else 0",
"always zero",
"always one",
"Lindeberg effective scale Tau as function of Sigma",
"Sigma as function of Lindeberg effective scale Tau"
};
static const char *
_nrrdUnaryOpStrEqv[] = {
"-", "neg", "negative", "minus",
"r", "recip",
"sin",
"cos",
"tan",
"asin", "arcsin",
"acos", "arccos",
"atan", "arctan",
"exp",
"ln", "log",
"log2",
"log10",
"ln1p", "log1p",
"expm1",
"sqrt",
"cbrt",
"erf",
"nerf",
"ceil",
"floor",
"roundup", "rup",
"rounddown", "rdown", "rdn",
"abs", "fabs",
"sgn", "sign",
"exists",
"rand",
"normalrand", "normrand", "nrand",
"if",
"zero", "0",
"one", "1",
"tauofsig", "tos",
"sigoftau", "sot",
""
};
static const int
_nrrdUnaryOpValEqv[] = {
nuNeg, nuNeg, nuNeg, nuNeg,
nuRcp, nuRcp,
nuSin,
nuCos,
nuTan,
nuAsn, nuAsn,
nuAcs, nuAcs,
nuAtn, nuAtn,
nuExp,
nuLge, nuLge,
nuLg2,
nuLgt,
nuL1p, nuL1p,
nuEm1,
nuSqt,
nuCbt,
nuErf,
nuNerf,
nuCil,
nuFlr,
nuRup, nuRup,
nuRdn, nuRdn, nuRdn,
nuAbs, nuAbs,
nuSgn, nuSgn,
nuExs,
nuRnd,
nuNrn, nuNrn, nuNrn,
nuoIf,
nuZer, nuZer,
nuOne, nuOne,
nrrdUnaryOpTauOfSigma, nrrdUnaryOpTauOfSigma,
nrrdUnaryOpSigmaOfTau, nrrdUnaryOpSigmaOfTau
};
static const airEnum
_nrrdUnaryOp_enum = {
"unary op",
NRRD_UNARY_OP_MAX,
_nrrdUnaryOpStr, NULL,
_nrrdUnaryOpDesc,
_nrrdUnaryOpStrEqv, _nrrdUnaryOpValEqv,
AIR_FALSE
};
1261 const airEnum *const
nrrdUnaryOp = &_nrrdUnaryOp_enum;
/* ------------------------ nrrdBinaryOp ---------------------- */
static const char *
_nrrdBinaryOpStr[NRRD_BINARY_OP_MAX+1] = {
"( unknown_binary_op )",
"+",
"-",
"x",
"/",
"^",
"spow",
"fpow",
"%",
"fmod",
"atan2",
"min",
"max",
"lt",
"lte",
"gt",
"gte",
"comp",
"eq",
"neq",
"exists",
"if",
"nrand",
"rrand",
"+c",
"-c",
"xc",
};
static const char *
_nrrdBinaryOpDesc[NRRD_BINARY_OP_MAX+1] = {
"unknown binary op",
"add",
"subtract",
"multiply",
"divide",
"power",
"signed power",
"one minus power of one minus",
"integer modulo",
"fractional modulo",
"two-argment arctangent based on atan2( )",
"miniumum",
"maximum",
"less then",
"less then or equal",
"greater than",
"greater than or equal",
"compare ( resulting in -1, 0, or 1 )",
"equal",
"not equal",
"if exists( a ), then a, else b",
"if a, then a, else b",
"a + b*gaussianNoise",
"sample of Rician with mu a and sigma b",
"add, but clamp to integer representation range",
"subtract, but clamp to integer representation range",
"multiply, but clamp to integer representation range",
};
#define nbAdd nrrdBinaryOpAdd
#define nbSub nrrdBinaryOpSubtract
#define nbMul nrrdBinaryOpMultiply
#define nbDiv nrrdBinaryOpDivide
#define nbPow nrrdBinaryOpPow
#define nbSpw nrrdBinaryOpSgnPow
#define nbFpw nrrdBinaryOpFlippedSgnPow
#define nbMod nrrdBinaryOpMod
#define nbFmd nrrdBinaryOpFmod
#define nbAtn nrrdBinaryOpAtan2
#define nbMin nrrdBinaryOpMin
#define nbMax nrrdBinaryOpMax
#define nbLt nrrdBinaryOpLT
#define nbLte nrrdBinaryOpLTE
#define nbGt nrrdBinaryOpGT
#define nbGte nrrdBinaryOpGTE
#define nbCmp nrrdBinaryOpCompare
#define nbEq nrrdBinaryOpEqual
#define nbNeq nrrdBinaryOpNotEqual
#define nbExt nrrdBinaryOpExists
#define nbIf nrrdBinaryOpIf
static const char *
_nrrdBinaryOpStrEqv[] = {
"+", "plus", "add",
"-", "minus", "subtract", "sub",
"x", "*", "times", "multiply", "product",
"/", "divide", "quotient",
"^", "pow", "power",
"spow", "sgnpow", "sgnpower",
"fpow",
"%", "mod", "modulo",
"fmod",
"atan2",
"min", "minimum",
"max", "maximum",
"lt", "<", "less", "lessthan",
"lte", "<=", "lessthanorequal",
"gt", ">", "greater", "greaterthan",
"gte", ">=", "greaterthanorequal",
"comp", "compare",
"eq", "=", "==", "equal",
"neq", "ne", "!=", "notequal",
"exists",
"if",
"nrand",
"rrand",
"+c", "addclamp",
"-c", "subtractclamp",
"xc", "multiplyclamp",
""
};
static const int
_nrrdBinaryOpValEqv[] = {
nbAdd, nbAdd, nbAdd,
nbSub, nbSub, nbSub, nbSub,
nbMul, nbMul, nbMul, nbMul, nbMul,
nbDiv, nbDiv, nbDiv,
nbPow, nbPow, nbPow,
nbSpw, nbSpw, nbSpw,
nbFpw,
nbMod, nbMod, nbMod,
nbFmd,
nbAtn,
nbMin, nbMin,
nbMax, nbMax,
nbLt, nbLt, nbLt, nbLt,
nbLte, nbLte, nbLte,
nbGt, nbGt, nbGt, nbGt,
nbGte, nbGte, nbGte,
nbCmp, nbCmp,
nbEq, nbEq, nbEq, nbEq,
nbNeq, nbNeq, nbNeq, nbNeq,
nbExt,
nbIf,
nrrdBinaryOpNormalRandScaleAdd,
nrrdBinaryOpRicianRand,
nrrdBinaryOpAddClamp, nrrdBinaryOpAddClamp,
nrrdBinaryOpSubtractClamp, nrrdBinaryOpSubtractClamp,
nrrdBinaryOpMultiplyClamp, nrrdBinaryOpMultiplyClamp,
};
static const airEnum
_nrrdBinaryOp_enum = {
"binary op",
NRRD_BINARY_OP_MAX,
_nrrdBinaryOpStr, NULL,
_nrrdBinaryOpDesc,
_nrrdBinaryOpStrEqv, _nrrdBinaryOpValEqv,
AIR_FALSE
};
1420 const airEnum *const
nrrdBinaryOp = &_nrrdBinaryOp_enum;
/* ------------------------ nrrdTernaryOp ---------------------- */
static const char *
_nrrdTernaryOpStr[NRRD_TERNARY_OP_MAX+1] = {
"( unknown_ternary_op )",
"add",
"multiply",
"min",
"min_sm",
"max",
"max_sm",
"lt_sm",
"gt_sm",
"clamp",
"ifelse",
"lerp",
"exists",
"in_op",
"in_cl",
"gauss",
"rician"
};
static const char *
_nrrdTernaryOpDesc[NRRD_TERNARY_OP_MAX+1] = {
"unknown ternary op",
"add three values",
"multiply three values",
"minimum of three values",
"smooth minimum of 1st and 3rd value, starting at 2nd",
"maximum of three values",
"smooth maximum of 1st and 3rd value, starting at 2nd",
"1st less than 3rd, smoothed by 2nd",
"1st greater than 3rd, smoothed by 2nd",
"clamp 2nd value to closed interval between 1st and 3rd",
"if 1st value is non-zero, then 2nd value, else 3rd value",
"linearly interpolate between 2nd value ( 1st = 0.0 ) and 3rd ( 1st = 1.0 )",
"if 1st value exists, the 2nd value, else the 3rd",
"2nd value is inside OPEN interval range between 1st and 3rd",
"2nd value is inside CLOSED interval range between 1st and 3rd",
"evaluate ( at 1st value ) Gaussian with mean=2nd and stdv=3rd value",
"evaluate ( at 1st value ) Rician with mean=2nd and stdv=3rd value"
};
#define ntAdd nrrdTernaryOpAdd
#define ntMul nrrdTernaryOpMultiply
static const char *
_nrrdTernaryOpStrEqv[] = {
"+", "plus", "add",
"x", "*", "times", "multiply", "product",
"min",
"min_sm", "minsm", /* HEY want to deprecate minsm */
"max",
"max_sm",
"lt_sm",
"gt_sm",
"clamp",
"ifelse", "if",
"lerp",
"exists",
"in_op",
"in_cl",
"gauss",
"rician",
""
};
static const int
_nrrdTernaryOpValEqv[] = {
ntAdd, ntAdd, ntAdd,
ntMul, ntMul, ntMul, ntMul, ntMul,
nrrdTernaryOpMin,
nrrdTernaryOpMinSmooth, nrrdTernaryOpMinSmooth,
nrrdTernaryOpMax,
nrrdTernaryOpMaxSmooth,
nrrdTernaryOpLTSmooth,
nrrdTernaryOpGTSmooth,
nrrdTernaryOpClamp,
nrrdTernaryOpIfElse, nrrdTernaryOpIfElse,
nrrdTernaryOpLerp,
nrrdTernaryOpExists,
nrrdTernaryOpInOpen,
nrrdTernaryOpInClosed,
nrrdTernaryOpGaussian,
nrrdTernaryOpRician,
};
static const airEnum
_nrrdTernaryOp_enum = {
"ternary op",
NRRD_TERNARY_OP_MAX,
_nrrdTernaryOpStr, NULL,
_nrrdTernaryOpDesc,
_nrrdTernaryOpStrEqv, _nrrdTernaryOpValEqv,
AIR_FALSE
};
1520 const airEnum *const
nrrdTernaryOp = &_nrrdTernaryOp_enum;
/* ------------------------ nrrdFFTWPlanRigor ---------------------- */
static const char *
_nrrdFFTWPlanRigorStr[NRRD_FFTW_PLAN_RIGOR_MAX+1] = {
"( unknown_rigor )",
"estimate",
"measure",
"patient",
"exhaustive"
};
static const char *
_nrrdFFTWPlanRigorDesc[NRRD_FFTW_PLAN_RIGOR_MAX+1] = {
"unknown rigor",
"no machine-specific measurements, just estimate a plan",
"do several machine-specific measurements to determine plan",
"performs more measurements to find \"more optimal\" plan",
"considers even wider range of algorithms to find most optimal plan"
};
static const char *
_nrrdFFTWPlanRigorStrEqv[] = {
"e", "est", "estimate",
"m", "meas", "measure",
"p", "pat", "patient",
"x", "ex", "exhaustive",
""
};
static const int
_nrrdFFTWPlanRigorValEqv[] = {
nrrdFFTWPlanRigorEstimate, nrrdFFTWPlanRigorEstimate, nrrdFFTWPlanRigorEstimate,
nrrdFFTWPlanRigorMeasure, nrrdFFTWPlanRigorMeasure, nrrdFFTWPlanRigorMeasure,
nrrdFFTWPlanRigorPatient, nrrdFFTWPlanRigorPatient, nrrdFFTWPlanRigorPatient,
nrrdFFTWPlanRigorExhaustive, nrrdFFTWPlanRigorExhaustive, nrrdFFTWPlanRigorExhaustive
};
static const airEnum
_nrrdFFTWPlanRigor_enum = {
"fftw plan rigor",
NRRD_FFTW_PLAN_RIGOR_MAX,
_nrrdFFTWPlanRigorStr, NULL,
_nrrdFFTWPlanRigorDesc,
_nrrdFFTWPlanRigorStrEqv, _nrrdFFTWPlanRigorValEqv,
AIR_FALSE
};
1569 const airEnum *const
nrrdFFTWPlanRigor = &_nrrdFFTWPlanRigor_enum;
/* ---------------------- nrrdResampleNonExistent -------------------- */
static const char *
_nrrdResampleNonExistentStr[NRRD_RESAMPLE_NON_EXISTENT_MAX+1] = {
"( unknown_resample_non_existent )",
"noop",
"renormalize",
"weight"
};
static const char *
_nrrdResampleNonExistentDesc[NRRD_RESAMPLE_NON_EXISTENT_MAX+1] = {
"unknown resample non-existent",
"no-op; include non-existent values in convolution sum",
"use only existent values in kernel support and renormalize weights",
"use only existent values in kernel support and use weights as is"
};
static const char *
_nrrdResampleNonExistentStrEqv[] = {
"noop",
"renorm", "renormalize",
"wght", "weight",
""
};
static const int
_nrrdResampleNonExistentValEqv[] = {
nrrdResampleNonExistentNoop,
nrrdResampleNonExistentRenormalize, nrrdResampleNonExistentRenormalize,
nrrdResampleNonExistentWeight, nrrdResampleNonExistentWeight
};
static const airEnum
_nrrdResampleNonExistent_enum = {
"resample non-existent",
NRRD_RESAMPLE_NON_EXISTENT_MAX,
_nrrdResampleNonExistentStr, NULL,
_nrrdResampleNonExistentDesc,
_nrrdResampleNonExistentStrEqv, _nrrdResampleNonExistentValEqv,
AIR_FALSE
};
1614 const airEnum *const
nrrdResampleNonExistent = &_nrrdResampleNonExistent_enum;
/* ---------------------- nrrdMetaDataCanonicalVersion -------------------- */
static const char *
_nrrdMetaDataCanonicalVersionStr[NRRD_META_DATA_CANONICAL_VERSION_MAX+1] = {
"( unknown_meta_data_canonical_version )",
"alpha",
};
static const char *
_nrrdMetaDataCanonicalVersionDesc[NRRD_META_DATA_CANONICAL_VERSION_MAX+1] = {
"unknown meta data canonical version",
"initial version, used for Diderot until at least 2016",
};
static const airEnum
_nrrdMetaDataCanonicalVersion_enum = {
"canonical meta-data version",
NRRD_META_DATA_CANONICAL_VERSION_MAX,
_nrrdMetaDataCanonicalVersionStr, NULL,
_nrrdMetaDataCanonicalVersionDesc,
NULL, NULL,
AIR_FALSE
};
1640 const airEnum *const
nrrdMetaDataCanonicalVersion = &_nrrdMetaDataCanonicalVersion_enum;
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#if TEEM_FFTW3 /* =========================================================== */
#include <fftw3.h>
const int nrrdFFTWEnabled = AIR_TRUE;
int
34 nrrdFFTWWisdomRead( FILE *file ) {
static const char me[]="nrrdFFTWWisdomRead";
if ( !( file ) ) {
biffAddf( NRRD, "%s: given file NULL", me );
return 1;
}
if ( !fftw_import_wisdom_from_file( file ) ) {
biffAddf( NRRD, "%s: trouble importing wisdom", me );
return 1;
}
return 0;
}
static void *
49 _nrrdFftwFreeWrapper( void *ptr ) {
fftw_free( ptr );
return NULL;
}
static void *
55 _nrrdFftwDestroyPlanWrapper( void *ptr ) {
fftw_plan plan;
plan = AIR_CAST( fftw_plan, ptr );
fftw_destroy_plan( plan );
return NULL;
}
static void
63 _nrrdDimsReverse( fftw_iodim *dims, unsigned int len ) {
fftw_iodim buff[NRRD_DIM_MAX];
unsigned int ii;
for ( ii=0; ii<len; ii++ ) {
buff[len-1-ii].n = dims[ii].n;
buff[len-1-ii].is = dims[ii].is;
buff[len-1-ii].os = dims[ii].os;
}
for ( ii=0; ii<len; ii++ ) {
dims[ii].n = buff[ii].n;
dims[ii].is = buff[ii].is;
dims[ii].os = buff[ii].os;
}
}
/*
******** nrrdFFT
**
** First pass at a wrapper around FFTW. This was implemented out of need for a
** specific project; and better decisions and different interfaces will become
** apparent with time and experience; these can be in Teem 2.0.
**
** currently *requires* that input be complex-valued, in that axis 0 has to
** have size 2. nrrdKindComplex would be sensible for input axis 0 but we don't
** require it, though it is set on the output.
*/
int
90 nrrdFFT( Nrrd *nout, const Nrrd *_nin,
unsigned int *axes, unsigned int axesNum,
int sign, int rescale, int rigor ) {
static const char me[]="nrrdFFT";
size_t inSize[NRRD_DIM_MAX], II, NN, nprod;
double *inData, *outData;
airArray *mop;
Nrrd *nin;
unsigned int axi, axisDo[NRRD_DIM_MAX];
fftw_plan plan;
void *dataBef;
unsigned int txfRank, howRank, flags;
size_t stride;
fftw_iodim txfDims[NRRD_DIM_MAX], howDims[NRRD_DIM_MAX];
if ( !( nout && _nin && axes ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( _nin->dim > 1 && 2 == _nin->axis[0].size ) ) {
biffAddf( NRRD, "%s: nin doesn't look like a complex-valued array", me );
return 1;
}
if ( !( axesNum >= 1 ) ) {
biffAddf( NRRD, "%s: axesNum 0, no axes to transform?", me );
return 1;
}
for ( axi=0; axi<_nin->dim; axi++ ) {
axisDo[axi] = 0;
}
for ( axi=0; axi<axesNum; axi++ ) {
if ( 0 == axes[axi] ) {
biffAddf( NRRD, "%s: can't transform axis 0 ( axes[%u] ) for "
"real/complex values", me, axi );
return 1;
}
if ( !( axes[axi] < _nin->dim ) ) {
biffAddf( NRRD, "%s: axis %u ( axes[%u] ) out of range [1, %u]", me,
axes[axi], axi, _nin->dim-1 );
return 1;
}
axisDo[axes[axi]]++;
if ( 2 == axisDo[axes[axi]] ) {
biffAddf( NRRD, "%s: axis %u ( axes[%u] ) already transformed",
me, axes[axi], axi );
return 1;
}
}
NN = nrrdElementNumber( _nin );
/* We always make a new buffer to hold the double-type copy of input for two
reasons: if input is not double we have to convert it, and we want input
to be const, and we can't have const with the plan creation over-writing
the input ( except with FFTW_ESTIMATE ). Given that, we might as well use
the memory-alignment-savvy fftw_malloc; the freeing is handled by both
_nrrdFftwFreeWrapper and nrrdNix. */
/* ( NN = 2 * number of complex values ) */
inData = AIR_CAST( double *, fftw_malloc( NN*sizeof( double ) ) );
if ( !inData ) {
biffAddf( NRRD, "%s: couldn't allocate input data copy", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, inData, _nrrdFftwFreeWrapper, airMopAlways );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNix /* NOT Nuke */, airMopAlways );
nrrdAxisInfoGet_nva( _nin, nrrdAxisInfoSize, inSize );
/* we don't copy data yet; it may be over-written during plan creation */
if ( nrrdWrap_nva( nin, inData, nrrdTypeDouble, _nin->dim, inSize ) ) {
biffAddf( NRRD, "%s: couldn't wrap or copy input", me );
airMopError( mop );
return 1;
}
/* But on the output, we just use regular malloc, because we don't ( yet ) have
a way of telling nrrd to use fftw_malloc/fftw_free instead of the generic
malloc/free, and we don't want two whole copies of the output ( one that is
memory-aligned, internal to this function, and one that isn't, in nout ) */
if ( nrrdMaybeAlloc_nva( nout, nrrdTypeDouble, _nin->dim, inSize ) ) {
biffAddf( NRRD, "%s: couldn't allocate output", me );
airMopError( mop );
return 1;
}
outData = AIR_CAST( double *, nout->data );
/* As far as GLK can tell, the guru interface is needed, and the "advanced"
fftw_plan_many_dft won't work, because its simplistic accounting of stride
can't handle having non-contiguous non-transformed axes ( e.g. transforming
only axes 2 and not 1, 3 in a 3-D complex-valued array ) */
txfRank = howRank = 0;
stride = 1;
nprod = 1;
for ( axi=1; axi<nin->dim; axi++ ) {
if ( axisDo[axi] ) {
txfDims[txfRank].n = AIR_CAST( int, inSize[axi] );
txfDims[txfRank].is = txfDims[txfRank].os = AIR_CAST( int, stride );
nprod *= inSize[axi];
txfRank++;
} else {
howDims[howRank].n = AIR_CAST( int, inSize[axi] );
howDims[howRank].is = howDims[howRank].os = AIR_CAST( int, stride );
howRank++;
}
stride *= inSize[axi];
}
_nrrdDimsReverse( txfDims, txfRank );
_nrrdDimsReverse( howDims, howRank );
/*
fprintf( stderr, "!%s: ------------- txfRank %u, howRank %u\n",
me, txfRank, howRank );
for ( axi=0; axi<txfRank; axi++ ) {
fprintf( stderr, "!%s: txfDims[%u]: n %d; s %d\n", me, axi,
txfDims[axi].n, txfDims[axi].is );
}
for ( axi=0; axi<howRank; axi++ ) {
fprintf( stderr, "!%s: howDims[%u]: n %d; s %d\n", me, axi,
howDims[axi].n, howDims[axi].is );
}
*/
switch ( rigor ) {
case nrrdFFTWPlanRigorEstimate:
flags = FFTW_ESTIMATE;
break;
case nrrdFFTWPlanRigorMeasure:
flags = FFTW_MEASURE;
break;
case nrrdFFTWPlanRigorPatient:
flags = FFTW_PATIENT;
break;
case nrrdFFTWPlanRigorExhaustive:
flags = FFTW_EXHAUSTIVE;
break;
default:
biffAddf( NRRD, "%s: unsupported rigor %d", me, rigor );
airMopError( mop ); return 1;
}
/* HEY: figure out why fftw expects txfRank and howRank to be
signed and not unsigned */
plan = fftw_plan_guru_dft( AIR_CAST( int, txfRank ), txfDims,
AIR_CAST( int, howRank ), howDims,
AIR_CAST( fftw_complex *, inData ),
AIR_CAST( fftw_complex *, outData ),
sign, flags );
if ( !plan ) {
biffAddf( NRRD, "%s: unable to create plan", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, plan, _nrrdFftwDestroyPlanWrapper, airMopAlways );
/* only after planning is done ( which can over-write contents of inData )
do we copy the real input values over */
dataBef = nin->data;
if ( nrrdConvert( nin, _nin, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: couldn't initialize input", me );
airMopError( mop ); return 1;
}
/* a reasonable assert: data buffer was allocated correctly */
if ( dataBef != nin->data ) {
biffAddf( NRRD, "%s: pre-allocation error; nrrdConvert reallocated data",
me );
airMopError( mop ); return 1;
}
/* run the transform; HEY, no indication of success? */
fftw_execute( plan );
/* if wanted, remove the sqrt( nprod ) scaling that fftw adds at each pass */
if ( rescale ) {
double scale;
scale = sqrt( 1.0/AIR_CAST( double, nprod ) );
for ( II=0; II<NN; II++ ) {
outData[II] *= scale;
}
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE )
|| nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
nout->axis[0].kind = nrrdKindComplex;
airMopOkay( mop );
return 0;
}
int
287 nrrdFFTWWisdomWrite( FILE *file ) {
static const char me[]="nrrdFFTWWisdomWrite";
if ( !( file ) ) {
biffAddf( NRRD, "%s: given file NULL", me );
return 1;
}
/* HEY: weird that there's no return value on this function? */
fftw_export_wisdom_to_file( file );
return 0;
}
#else /* TEEM_FFTW3 ========================================================= */
/* we do NOT have the FFTW library to link against; have to
supply the same symbols anyway */
const int nrrdFFTWEnabled = AIR_FALSE;
int
308 nrrdFFTWWisdomRead( FILE *file ) {
AIR_UNUSED( file );
return 0;
}
int
314 nrrdFFT( Nrrd *nout, const Nrrd *nin,
unsigned int *axes, unsigned int axesNum,
int sign, int rescale, int rigor ) {
static const char me[]="nrrdFFT";
AIR_UNUSED( nout );
AIR_UNUSED( nin );
AIR_UNUSED( axes );
AIR_UNUSED( axesNum );
AIR_UNUSED( sign );
AIR_UNUSED( rescale );
AIR_UNUSED( rigor );
biffAddf( NRRD, "%s: sorry, non-fftw3 version not yet implemented\n", me );
return 1;
}
int
331 nrrdFFTWWisdomWrite( FILE *file ) {
AIR_UNUSED( file );
return 0;
}
#endif /* TEEM_FFTW3 ======================================================== */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
int
28 _nrrdCM_median( const float *hist, float half ) {
float sum = 0;
const float *hpt;
hpt = hist;
while( sum < half )
sum += *hpt++;
return AIR_CAST( int, hpt - 1 - hist );
}
int
41 _nrrdCM_mode( const float *hist, int bins ) {
float max;
int i, mi;
mi = -1;
max = 0;
for ( i=0; i<bins; i++ ) {
if ( hist[i] && ( !max || hist[i] > max ) ) {
max = hist[i];
mi = i;
}
}
return mi;
}
#define INDEX( nin, range, lup, idxIn, bins, val ) ( \
val = ( lup )( ( nin )->data, ( idxIn ) ), \
airIndex( ( range )->min, ( val ), ( range )->max, bins ) \
60 )
void
_nrrdCM_printhist( const float *hist, int bins, const char *desc ) {
int i;
printf( "%s:\n", desc );
for ( i=0; i<bins; i++ ) {
if ( hist[i] ) {
printf( " %d: %g\n", i, hist[i] );
}
}
72 }
float *
_nrrdCM_wtAlloc( int radius, float wght ) {
float *wt, sum;
int diam, r;
diam = 2*radius + 1;
wt = ( float * )calloc( diam, sizeof( float ) );
wt[radius] = 1.0;
for ( r=1; r<=radius; r++ ) {
wt[radius+r] = AIR_CAST( float, pow( 1.0/wght, r ) );
wt[radius-r] = AIR_CAST( float, pow( 1.0/wght, r ) );
}
sum = 0.0;
for ( r=0; r<diam; r++ ) {
sum += wt[r];
}
for ( r=0; r<diam; r++ ) {
wt[r] /= sum;
}
/*
for ( r=0; r<diam; r++ ) {
fprintf( stderr, "!%s: wt[%d] = %g\n", "_nrrdCM_wtAlloc", r, wt[r] );
}
*/
return wt;
99 }
void
_nrrdCheapMedian1D( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
int radius, float wght,
int bins, int mode, float *hist ) {
/* static const char me[]="_nrrdCheapMedian1D"; */
size_t num;
int X, I, idx, diam;
float half, *wt;
double val, ( *lup )( const void *, size_t );
diam = 2*radius + 1;
lup = nrrdDLookup[nin->type];
num = nrrdElementNumber( nin );
if ( 1 == wght ) {
/* uniform weighting-> can do sliding histogram optimization */
/* initialize histogram */
half = AIR_CAST( float, diam/2 + 1 );
memset( hist, 0, bins*sizeof( float ) );
for ( X=0; X<diam; X++ ) {
hist[INDEX( nin, range, lup, X, bins, val )]++;
}
/* _nrrdCM_printhist( hist, bins, "after init" ); */
/* find median at each point using existing histogram */
for ( X=radius; X<( int )num-radius; X++ ) {
/* _nrrdCM_printhist( hist, bins, "----------" ); */
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
/* printf( " median idx = %d -> val = %g\n", idx, val ); */
nrrdDInsert[nout->type]( nout->data, X, val );
/* probably update histogram for next iteration */
if ( X < ( int )num-radius-1 ) {
hist[INDEX( nin, range, lup, X+radius+1, bins, val )]++;
hist[INDEX( nin, range, lup, X-radius, bins, val )]--;
}
}
} else {
/* non-uniform weighting --> slow and stupid */
wt = _nrrdCM_wtAlloc( radius, wght );
half = 0.5;
for ( X=radius; X<( int )num-radius; X++ ) {
memset( hist, 0, bins*sizeof( float ) );
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, I+X, bins, val )] += wt[I+radius];
}
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, X, val );
}
free( wt );
}
151 }
void
_nrrdCheapMedian2D( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
int radius, float wght,
int bins, int mode, float *hist ) {
/* static const char me[]="_nrrdCheapMedian2D"; */
int X, Y, I, J;
int sx, sy, idx, diam;
float half, *wt;
double val, ( *lup )( const void *, size_t );
diam = 2*radius + 1;
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
lup = nrrdDLookup[nin->type];
if ( 1 == wght ) {
/* uniform weighting-> can do sliding histogram optimization */
half = AIR_CAST( float, diam*diam/2 + 1 );
for ( Y=radius; Y<sy-radius; Y++ ) {
/* initialize histogram */
memset( hist, 0, bins*sizeof( float ) );
X = radius;
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, X+I + sx*( J+Y ), bins, val )]++;
}
}
/* _nrrdCM_printhist( hist, bins, "after init" ); */
/* find median at each point using existing histogram */
for ( X=radius; X<sx-radius; X++ ) {
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, X + sx*Y, val );
/* probably update histogram for next iteration */
if ( X < sx-radius-1 ) {
for ( J=-radius; J<=radius; J++ ) {
hist[INDEX( nin, range, lup, X+radius+1 + sx*( J+Y ), bins, val )]++;
hist[INDEX( nin, range, lup, X-radius + sx*( J+Y ), bins, val )]--;
}
}
}
}
} else {
/* non-uniform weighting --> slow and stupid */
wt = _nrrdCM_wtAlloc( radius, wght );
half = 0.5;
for ( Y=radius; Y<sy-radius; Y++ ) {
for ( X=radius; X<sx-radius; X++ ) {
memset( hist, 0, bins*sizeof( float ) );
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, I+X + sx*( J+Y ),
bins, val )] += wt[I+radius]*wt[J+radius];
}
}
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, X + sx*Y, val );
}
}
free( wt );
}
214 }
void
_nrrdCheapMedian3D( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
int radius, float wght,
int bins, int mode, float *hist ) {
static const char me[]="_nrrdCheapMedian3D";
char done[13];
int X, Y, Z, I, J, K;
int sx, sy, sz, idx, diam;
float half, *wt;
double val, ( *lup )( const void *, size_t );
diam = 2*radius + 1;
sx = AIR_CAST( int, nin->axis[0].size );
sy = AIR_CAST( int, nin->axis[1].size );
sz = AIR_CAST( int, nin->axis[2].size );
lup = nrrdDLookup[nin->type];
fprintf( stderr, "%s: ... ", me );
if ( 1 == wght ) {
/* uniform weighting-> can do sliding histogram optimization */
half = AIR_CAST( float, diam*diam*diam/2 + 1 );
fflush( stderr );
for ( Z=radius; Z<sz-radius; Z++ ) {
fprintf( stderr, "%s", airDoneStr( radius, Z, sz-radius-1, done ) );
fflush( stderr );
for ( Y=radius; Y<sy-radius; Y++ ) {
/* initialize histogram */
memset( hist, 0, bins*sizeof( float ) );
X = radius;
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, I+X + sx*( J+Y + sy*( K+Z ) ),
bins, val )]++;
}
}
}
/* find median at each point using existing histogram */
for ( X=radius; X<sx-radius; X++ ) {
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, X + sx*( Y + sy*Z ), val );
/* probably update histogram for next iteration */
if ( X < sx-radius-1 ) {
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
hist[INDEX( nin, range, lup, X+radius+1 + sx*( J+Y + sy*( K+Z ) ),
bins, val )]++;
hist[INDEX( nin, range, lup, X-radius + sx*( J+Y + sy*( K+Z ) ),
bins, val )]--;
}
}
}
}
}
}
} else {
/* non-uniform weighting --> slow and stupid */
wt = _nrrdCM_wtAlloc( radius, wght );
half = 0.5;
for ( Z=radius; Z<sz-radius; Z++ ) {
fprintf( stderr, "%s", airDoneStr( radius, Z, sz-radius-1, done ) );
fflush( stderr );
for ( Y=radius; Y<sy-radius; Y++ ) {
for ( X=radius; X<sx-radius; X++ ) {
memset( hist, 0, bins*sizeof( float ) );
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, I+X + sx*( J+Y + sy*( K+Z ) ),
bins, val )]
+= wt[I+radius]*wt[J+radius]*wt[K+radius];
}
}
}
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, X + sx*( Y + sy*Z ), val );
}
}
}
free( wt );
}
fprintf( stderr, "\b\b\b\b\b\b done\n" );
}
300
/* HEY: total copy-and-paste from _nrrdCheapMedian3D */
void
_nrrdCheapMedian4D( Nrrd *nout, const Nrrd *nin, const NrrdRange *range,
int radius, float wght,
int bins, int mode, float *hist ) {
static const char me[]="_nrrdCheapMedian4D";
char done[13];
int W, X, Y, Z, H, I, J, K;
int sw, sx, sy, sz, idx, diam;
float half, *wt;
double val, ( *lup )( const void *, size_t );
diam = 2*radius + 1;
sw = AIR_CAST( int, nin->axis[0].size );
sx = AIR_CAST( int, nin->axis[1].size );
sy = AIR_CAST( int, nin->axis[2].size );
sz = AIR_CAST( int, nin->axis[3].size );
lup = nrrdDLookup[nin->type];
fprintf( stderr, "%s: ... ", me );
if ( 1 == wght ) {
/* uniform weighting-> can do sliding histogram optimization */
half = AIR_CAST( float, diam*diam*diam*diam/2 + 1 );
fflush( stderr );
for ( Z=radius; Z<sz-radius; Z++ ) {
fprintf( stderr, "%s", airDoneStr( radius, Z, sz-radius-1, done ) );
fflush( stderr );
for ( Y=radius; Y<sy-radius; Y++ ) {
for ( X=radius; X<sx-radius; X++ ) {
/* initialize histogram */
memset( hist, 0, bins*sizeof( float ) );
W = radius;
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
for ( H=-radius; H<=radius; H++ ) {
hist[INDEX( nin, range, lup,
H+W + sw*( I+X + sx*( J+Y + sy*( K+Z ) ) ),
bins, val )]++;
}
}
}
}
/* find median at each point using existing histogram */
for ( W=radius; W<sw-radius; W++ ) {
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, W + sw*( X + sx*( Y + sy*Z ) ), val );
/* probably update histogram for next iteration */
if ( W < sw-radius-1 ) {
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
hist[INDEX( nin, range, lup, W+radius+1 + sw*( I+X + sx*( J+Y + sy*( K+Z ) ) ),
bins, val )]++;
hist[INDEX( nin, range, lup, W-radius + sw*( I+X + sx*( J+Y + sy*( K+Z ) ) ),
bins, val )]--;
}
}
}
}
}
}
}
}
} else {
/* non-uniform weighting --> slow and stupid */
wt = _nrrdCM_wtAlloc( radius, wght );
half = 0.5;
for ( Z=radius; Z<sz-radius; Z++ ) {
fprintf( stderr, "%s", airDoneStr( radius, Z, sz-radius-1, done ) );
fflush( stderr );
for ( Y=radius; Y<sy-radius; Y++ ) {
for ( X=radius; X<sx-radius; X++ ) {
for ( W=radius; W<sw-radius; W++ ) {
memset( hist, 0, bins*sizeof( float ) );
for ( K=-radius; K<=radius; K++ ) {
for ( J=-radius; J<=radius; J++ ) {
for ( I=-radius; I<=radius; I++ ) {
for ( H=-radius; H<=radius; H++ ) {
hist[INDEX( nin, range, lup,
H+W + sw*( I+X + sx*( J+Y + sy*( K+Z ) ) ),
bins, val )]
+= wt[H+radius]*wt[I+radius]*wt[J+radius]*wt[K+radius];
}
}
}
}
idx = mode ? _nrrdCM_mode( hist, bins ) : _nrrdCM_median( hist, half );
val = NRRD_NODE_POS( range->min, range->max, bins, idx );
nrrdDInsert[nout->type]( nout->data, W + sw*( X + sx*( Y + sy*Z ) ), val );
}
}
}
}
free( wt );
}
fprintf( stderr, "\b\b\b\b\b\b done\n" );
}
/*
******** nrrdCheapMedian
**
** histogram-based median or mode filtering
** !mode: median filtering
405 ** mode: mode filtering
*/
int
nrrdCheapMedian( Nrrd *_nout, const Nrrd *_nin,
int pad, int mode,
unsigned int radius, float wght, unsigned int bins ) {
static const char me[]="nrrdCheapMedian", func[]="cmedian";
NrrdRange *range;
float *hist;
Nrrd *nout, *nin;
airArray *mop;
unsigned int minsize;
if ( !( _nin && _nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( radius >= 1 ) ) {
biffAddf( NRRD, "%s: need radius >= 1 ( got %d )", me, radius );
return 1;
}
if ( !( bins >= 1 ) ) {
biffAddf( NRRD, "%s: need bins >= 1 ( got %d )", me, bins );
return 1;
}
if ( !( AIR_IN_CL( 1, _nin->dim, 4 ) ) ) {
biffAddf( NRRD, "%s: sorry, can only handle dim 1, 2, 3 ( not %d )",
me, _nin->dim );
return 1;
}
minsize = AIR_CAST( unsigned int, _nin->axis[0].size );
if ( _nin->dim > 1 ) {
minsize = AIR_MIN( minsize, AIR_CAST( unsigned int, _nin->axis[1].size ) );
}
if ( _nin->dim > 2 ) {
minsize = AIR_MIN( minsize, AIR_CAST( unsigned int, _nin->axis[2].size ) );
}
if ( !pad && minsize < 2*radius+1 ) {
biffAddf( NRRD, "%s: minimum nrrd size ( %d ) smaller than filtering "
"window size ( %d ) with radius %d; must enable padding", me,
minsize, 2*radius+1, radius );
return 1;
}
if ( _nout == _nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( nrrdTypeBlock == _nin->type ) {
biffAddf( NRRD, "%s: can't filter nrrd type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
mop = airMopNew( );
/* set nin based on _nin */
airMopAdd( mop, nin=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( pad ) {
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSimplePad_va( nin, _nin, radius, nrrdBoundaryBleed ) ) {
biffAddf( NRRD, "%s: trouble padding input", me );
airMopError( mop ); return 1;
}
} else {
if ( nrrdCopy( nin, _nin ) ) {
biffAddf( NRRD, "%s: trouble copying input", me );
airMopError( mop ); return 1;
}
nout = _nout;
}
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: failed to create initial copy of input", me );
airMopError( mop ); return 1;
}
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeFalse );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( !( hist = ( float* )calloc( bins, sizeof( float ) ) ) ) {
biffAddf( NRRD, "%s: couldn't allocate histogram ( %d bins )", me, bins );
airMopError( mop ); return 1;
}
airMopAdd( mop, hist, airFree, airMopAlways );
if ( !AIR_EXISTS( wght ) ) {
wght = 1.0;
}
switch ( nin->dim ) {
case 1:
_nrrdCheapMedian1D( nout, nin, range, radius, wght, bins, mode, hist );
break;
case 2:
_nrrdCheapMedian2D( nout, nin, range, radius, wght, bins, mode, hist );
break;
case 3:
_nrrdCheapMedian3D( nout, nin, range, radius, wght, bins, mode, hist );
break;
case 4:
_nrrdCheapMedian4D( nout, nin, range, radius, wght, bins, mode, hist );
break;
default:
biffAddf( NRRD, "%s: sorry, %d-dimensional median unimplemented",
me, nin->dim );
airMopError( mop ); return 1;
}
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
if ( nrrdContentSet_va( nout, func, nin, "%d, %d, %g, %d",
mode, radius, wght, bins ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* basic info handled by nrrdCopy above */
/* set _nout based on nout */
if ( pad ) {
if ( nrrdSimpleCrop( _nout, nout, radius ) ) {
biffAddf( NRRD, "%s: trouble cropping output", me );
airMopError( mop ); return 1;
}
} else {
/* we've already set output in _nout == nout */
}
airMopOkay( mop );
return 0;
}
/*
530 ** returns intersection of parabolas c( x ) = spc^2 ( x - xi )^2 + yi
*/
static double
intx( double xx0, double yy0, double xx1, double yy1, double spc ) {
double ss;
ss = spc*spc;
return ( yy1/ss + xx1*xx1 - ( yy0/ss + xx0*xx0 ) )/( 2*( xx1 - xx0 ) );
}
/*
** squared-distance transform of single scanline
**
** based on code published with:
** Distance Transforms of Sampled Functions
** Pedro F. Felzenszwalb and Daniel P. Huttenlocher
** Cornell Computing and Information Science TR2004-1963
**
** dd: output ( pre-allocated for "len" )
** ff: input function ( pre-allocated for "len" )
** zz: buffer ( pre-allocated for "len"+1 ) for locations of
** boundaries between parabolas
** vv: buffer ( pre-allocated for "len" ) for locations of
** parabolas in lower envelope
**
** The major modification from the published method is the inclusion
** of the "spc" parameter that gives the inter-sample spacing, so that
** the multi-dimensional version can work on non-isotropic samples.
**
** FLT_MIN and FLT_MAX are used to be consistent with the
** initialization in nrrdDistanceL2( ), which uses FLT_MAX
561 ** to be compatible with the case of using floats
*/
static void
distanceL2Sqrd1D( double *dd, const double *ff,
double *zz, unsigned int *vv,
size_t len, double spc ) {
size_t kk, qq;
if ( !( dd && ff && zz && vv && len > 0 ) ) {
/* error */
return;
}
kk = 0;
vv[0] = 0;
zz[0] = -FLT_MAX;
zz[1] = +FLT_MAX;
for ( qq=1; qq<len; qq++ ) {
double ss;
ss = intx( AIR_CAST( double, qq ), ff[qq], vv[kk], ff[vv[kk]], spc );
/* while ( ss <= zz[kk] ) {
** HEY this can have kk going to -1 and into memory errors!
*/
while ( ss <= zz[kk] && kk ) {
kk--;
ss = intx( AIR_CAST( double, qq ), ff[qq], vv[kk], ff[vv[kk]], spc );
}
kk++;
vv[kk] = AIR_CAST( unsigned int, qq );
zz[kk] = ss;
zz[kk+1] = +FLT_MAX;
}
kk = 0;
for ( qq=00; qq<len; qq++ ) {
double dx;
while ( zz[kk+1] < qq ) {
kk++;
}
/* cast to avoid overflow weirdness on the unsigned ints */
dx = AIR_CAST( double, qq ) - vv[kk];
dd[qq] = spc*spc*dx*dx + ff[vv[kk]];
}
return;
606 }
static int
distanceL2Sqrd( Nrrd *ndist, double *spcMean ) {
static const char me[]="distanceL2Sqrd";
size_t sizeMax; /* max size of all axes */
Nrrd *ntmpA, *ntmpB, *npass[NRRD_DIM_MAX+1];
int spcSomeExist, spcSomeNonExist;
unsigned int di, *vv;
double *dd, *ff, *zz;
double spc[NRRD_DIM_MAX], vector[NRRD_SPACE_DIM_MAX];
double ( *lup )( const void *, size_t ), ( *ins )( void *, size_t, double );
airArray *mop;
if ( !( nrrdTypeFloat == ndist->type || nrrdTypeDouble == ndist->type ) ) {
/* MWC: This error condition was/is being ignored. */
/* biffAddf( NRRD, "%s: sorry, can only process type %s or %s ( not %s )",
me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, ndist->type ) );
*/
}
spcSomeExist = AIR_FALSE;
spcSomeNonExist = AIR_FALSE;
for ( di=0; di<ndist->dim; di++ ) {
nrrdSpacingCalculate( ndist, di, spc + di, vector );
spcSomeExist |= AIR_EXISTS( spc[di] );
spcSomeNonExist |= !AIR_EXISTS( spc[di] );
}
if ( spcSomeExist && spcSomeNonExist ) {
biffAddf( NRRD, "%s: axis spacings must all exist or all non-exist", me );
return 1;
}
if ( !spcSomeExist ) {
for ( di=0; di<ndist->dim; di++ ) {
spc[di] = 1.0;
}
}
*spcMean = 0;
for ( di=0; di<ndist->dim; di++ ) {
*spcMean += spc[di];
}
*spcMean /= ndist->dim;
sizeMax = 0;
for ( di=0; di<ndist->dim; di++ ) {
sizeMax = AIR_MAX( sizeMax, ndist->axis[di].size );
}
/* create mop and allocate tmp buffers */
mop = airMopNew( );
ntmpA = nrrdNew( );
airMopAdd( mop, ntmpA, ( airMopper )nrrdNuke, airMopAlways );
if ( ndist->dim > 2 ) {
ntmpB = nrrdNew( );
airMopAdd( mop, ntmpB, ( airMopper )nrrdNuke, airMopAlways );
} else {
ntmpB = NULL;
}
if ( nrrdCopy( ntmpA, ndist )
|| ( ndist->dim > 2 && nrrdCopy( ntmpB, ndist ) ) ) {
biffAddf( NRRD, "%s: couldn't allocate image buffers", me );
}
dd = AIR_CAST( double *, calloc( sizeMax, sizeof( double ) ) );
ff = AIR_CAST( double *, calloc( sizeMax, sizeof( double ) ) );
zz = AIR_CAST( double *, calloc( sizeMax+1, sizeof( double ) ) );
vv = AIR_CAST( unsigned int *, calloc( sizeMax, sizeof( unsigned int ) ) );
airMopAdd( mop, dd, airFree, airMopAlways );
airMopAdd( mop, ff, airFree, airMopAlways );
airMopAdd( mop, zz, airFree, airMopAlways );
airMopAdd( mop, vv, airFree, airMopAlways );
if ( !( dd && ff && zz && vv ) ) {
/* MWC: This error condition was/is being ignored. */
/* biffAddf( NRRD, "%s: couldn't allocate scanline buffers", me ); */
}
/* set up array of buffers */
npass[0] = ndist;
for ( di=1; di<ndist->dim; di++ ) {
npass[di] = ( di % 2 ) ? ntmpA : ntmpB;
}
npass[ndist->dim] = ndist;
/* run the multiple passes */
/* what makes the indexing here so simple is that by assuming that
we're processing every axis, the input to any given pass can be
logically considered a 2-D image ( a list of scanlines ), where the
second axis is the merge of all input axes but the first. With
the rotational shuffle of axes through passes, the initial axis
and the set of other axes swap places, so its like the 2-D image
is being transposed. NOTE: the Nrrds that were allocated as
buffers are really being mis-used, in that the axis sizes and
raster ordering of what we're storing there is *not* the same as
told by axis[].size */
lup = nrrdDLookup[ndist->type];
ins = nrrdDInsert[ndist->type];
for ( di=0; di<ndist->dim; di++ ) {
size_t lineIdx, lineNum, valIdx, valNum;
valNum = ndist->axis[di].size;
lineNum = nrrdElementNumber( ndist )/valNum;
for ( lineIdx=0; lineIdx<lineNum; lineIdx++ ) {
/* read input scanline into ff */
for ( valIdx=0; valIdx<valNum; valIdx++ ) {
ff[valIdx] = lup( npass[di]->data, valIdx + valNum*lineIdx );
}
/* do the transform */
distanceL2Sqrd1D( dd, ff, zz, vv, valNum, spc[di] );
/* write dd to output scanline */
for ( valIdx=0; valIdx<valNum; valIdx++ ) {
ins( npass[di+1]->data, lineIdx + lineNum*valIdx, dd[valIdx] );
}
}
}
airMopOkay( mop );
return 0;
}
/*
** helper function for distance transforms, is called by things that want to do
729 ** specific kinds of transforms.
*/
static int
_distanceBase( Nrrd *nout, const Nrrd *nin,
int typeOut, const int *axisDo,
double thresh, double bias, int insideHigher ) {
static const char me[]="_distanceBase";
size_t ii, nn;
double ( *lup )( const void *, size_t ), ( *ins )( void *, size_t, double );
double spcMean;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: need scalar type for distance transform ( not %s )",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( nrrdTypeDouble == typeOut || nrrdTypeFloat == typeOut ) ) {
biffAddf( NRRD, "%s: sorry, can only transform to type %s or %s "
"( not %s )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, typeOut ) );
return 1;
}
if ( axisDo ) {
biffAddf( NRRD, "%s: sorry, selective axis transform not implemented",
me );
return 1;
}
if ( !AIR_EXISTS( thresh ) ) {
biffAddf( NRRD, "%s: threshold ( %g ) doesn't exist", me, thresh );
return 1;
}
if ( nrrdConvert( nout, nin, typeOut ) ) {
biffAddf( NRRD, "%s: couldn't allocate output", me );
return 1;
}
lup = nrrdDLookup[nout->type];
ins = nrrdDInsert[nout->type];
nn = nrrdElementNumber( nout );
for ( ii=0; ii<nn; ii++ ) {
double val, bb;
val = lup( nout->data, ii );
if ( insideHigher ) {
bb = bias*( val - thresh );
ins( nout->data, ii, val > thresh ? bb*bb : FLT_MAX );
} else {
bb = bias*( thresh - val );
ins( nout->data, ii, val <= thresh ? bb*bb : FLT_MAX );
}
}
if ( distanceL2Sqrd( nout, &spcMean ) ) {
biffAddf( NRRD, "%s: trouble doing transform", me );
return 1;
}
for ( ii=0; ii<nn; ii++ ) {
double val;
val = sqrt( lup( nout->data, ii ) );
/* here's where the distance is tweaked downwards by half a sample width */
ins( nout->data, ii, AIR_MAX( 0, val - spcMean/2 ) );
}
return 0;
}
/*
******** nrrdDistanceL2
**
** computes euclidean ( L2 ) distance transform of input image, after
** thresholding at "thresh".
**
** NOTE: the output of this is slightly offset from what one might
** expect; decreased by half of the average ( over all axes ) sample
** spacing. The reason for this is so that when the transform is
** applied to the inverted image and negated, to create a full
** signed distance map, the transition from interior to exterior
** distance values is smooth. Without this trick, there is a
814 ** small little plateau at the transition.
*/
int
nrrdDistanceL2( Nrrd *nout, const Nrrd *nin,
int typeOut, const int *axisDo,
double thresh, int insideHigher ) {
static const char me[]="nrrdDistanceL2";
if ( _distanceBase( nout, nin, typeOut, axisDo, thresh, 0, insideHigher ) ) {
biffAddf( NRRD, "%s: trouble doing distance transform", me );
return 1;
}
return 0;
827 }
int
nrrdDistanceL2Biased( Nrrd *nout, const Nrrd *nin,
int typeOut, const int *axisDo,
double thresh, double bias, int insideHigher ) {
static const char me[]="nrrdDistanceL2Biased";
if ( _distanceBase( nout, nin, typeOut, axisDo, thresh, bias, insideHigher ) ) {
biffAddf( NRRD, "%s: trouble doing distance transform", me );
return 1;
}
return 0;
840 }
int
nrrdDistanceL2Signed( Nrrd *nout, const Nrrd *nin,
int typeOut, const int *axisDo,
double thresh, int insideHigher ) {
static const char me[]="nrrdDistanceL2Signed";
airArray *mop;
Nrrd *ninv;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
ninv = nrrdNew( );
airMopAdd( mop, ninv, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdDistanceL2( nout, nin, typeOut, axisDo, thresh, insideHigher )
|| nrrdDistanceL2( ninv, nin, typeOut, axisDo, thresh, !insideHigher )
|| nrrdArithUnaryOp( ninv, nrrdUnaryOpNegative, ninv )
|| nrrdArithBinaryOp( nout, nrrdBinaryOpAdd, nout, ninv ) ) {
biffAddf( NRRD, "%s: trouble doing or combining transforms", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** what a NrrdFormat can assume:
** -- that nio->format has been set to you already
** -- for read( ): that nio->path has been set to the path of the file being
** read in, if the information was ever available
** -- for contentStartsLike( ) and read( ): that nio->line contains the
** first line of of the file, in order to determine the file type
**
** what a NrrdFormat has to do:
** -- respect nio->skipData to whatever extent makes sense on top of how the
** NrrdEncoding respects it ( by making read and write no-ops ).
** nrrdFormatNRRD, for instance, won't create empty detached data files
** if nio->skipData.
** -- determine what NrrdEncoding to use, if there's a choice
** -- respect nrrdStateVerboseIO with messages to stderr, if possible
**
** The "unknown" format is intended as a template for writing new formats.
*/
static int
47 _nrrdFormatUnknown_available( void ) {
/* insert code here */
return AIR_FALSE;
}
static int
55 _nrrdFormatUnknown_nameLooksLike( const char *filename ) {
/* insert code here */
AIR_UNUSED( filename );
return AIR_FALSE;
}
static int
64 _nrrdFormatUnknown_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatUnknown_fitsInto";
if ( !( nrrd && encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p ) or encoding ( %p )",
me, AIR_CVOIDP( nrrd ), AIR_CVOIDP( encoding ) );
return AIR_FALSE;
}
/* insert code here */
return AIR_FALSE;
}
static int
80 _nrrdFormatUnknown_contentStartsLike( NrrdIoState *nio ) {
/* insert code here */
AIR_UNUSED( nio );
return AIR_FALSE;
}
static int
89 _nrrdFormatUnknown_read( FILE *file, Nrrd *nrrd,
NrrdIoState *nio ) {
static const char me[]="_nrrdFormatUnknown_read";
/* insert code here, and remove error handling below */
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: ERROR!!! trying to read unknown format", me );
return 1;
}
static int
103 _nrrdFormatUnknown_write( FILE *file, const Nrrd *nrrd,
NrrdIoState *nio ) {
static const char me[]="_nrrdFormatUnknown_write";
/* insert code here, and remove error handling below */
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: ERROR!!! trying to write unknown format", me );
return 1;
}
static const NrrdFormat
_nrrdFormatUnknown = {
"unknown",
AIR_FALSE, /* isImage */
AIR_TRUE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatUnknown_available,
_nrrdFormatUnknown_nameLooksLike,
_nrrdFormatUnknown_fitsInto,
_nrrdFormatUnknown_contentStartsLike,
_nrrdFormatUnknown_read,
_nrrdFormatUnknown_write
};
130 const NrrdFormat *const
nrrdFormatUnknown = &_nrrdFormatUnknown;
133 const NrrdFormat *const
nrrdFormatArray[NRRD_FORMAT_TYPE_MAX+1] = {
&_nrrdFormatUnknown,
&_nrrdFormatNRRD,
&_nrrdFormatPNM,
&_nrrdFormatPNG,
&_nrrdFormatVTK,
&_nrrdFormatText,
&_nrrdFormatEPS
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static int
28 _nrrdFormatEPS_available( void ) {
/* but only for writing ... */
return AIR_TRUE;
}
static int
35 _nrrdFormatEPS_nameLooksLike( const char *filename ) {
return airEndsWith( filename, NRRD_EXT_EPS );
}
static int
41 _nrrdFormatEPS_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatEPS_fitsInto";
int ret;
AIR_UNUSED( encoding );
/* encoding information is ignored- its always going to be hex */
if ( !nrrd ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p )",
me, AIR_CVOIDP( nrrd ) );
return AIR_FALSE;
}
if ( nrrdTypeUChar != nrrd->type ) {
biffMaybeAddf( useBiff, NRRD, "%s: type must be %s ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, nrrd->type ) );
return AIR_FALSE;
}
if ( 2 == nrrd->dim ) {
/* its a gray-scale image */
ret = 2;
} else if ( 3 == nrrd->dim ) {
if ( 1 == nrrd->axis[0].size ) {
/* its a faux-3D image, really grayscale */
ret = 2;
} else if ( 3 == nrrd->axis[0].size ) {
/* its a real RGB color image */
ret = 3;
} else if ( 4 == nrrd->axis[0].size ) {
/* its a real CMYK ( our best guess ) color image */
ret = 3;
} else {
/* else its no good */
char stmp[AIR_STRLEN_SMALL];
biffMaybeAddf( useBiff, NRRD,
"%s: dim is 3, but 1st axis size is %s, not 1, 3, or 4",
me, airSprintSize_t( stmp, nrrd->axis[0].size ) );
return AIR_FALSE;
}
} else {
biffMaybeAddf( useBiff, NRRD, "%s: dimension is %d, not 2 or 3",
me, nrrd->dim );
return AIR_FALSE;
}
return ret;
}
static int
89 _nrrdFormatEPS_contentStartsLike( NrrdIoState *nio ) {
AIR_UNUSED( nio );
/* this is a write-only format */
return AIR_FALSE;
}
static int
97 _nrrdFormatEPS_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatEPS_read";
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: sorry, this is a write-only format", me );
return 1;
}
static int
108 _nrrdFormatEPS_write( FILE *file, const Nrrd *_nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatEPS_write";
int color, cmyk, sx, sy;
Nrrd *nrrd;
double aspect, minX, minY, maxX, maxY, scale;
airArray *mop;
mop = airMopNew( );
airMopAdd( mop, nrrd = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nrrd, _nrrd ) ) {
biffAddf( NRRD, "%s: couldn't make private copy", me );
airMopError( mop ); return 1;
}
if ( 3 == nrrd->dim && 1 == nrrd->axis[0].size ) {
if ( nrrdAxesDelete( nrrd, nrrd, 0 ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
color = ( 3 == nrrd->dim ) && ( 3 == nrrd->axis[0].size
|| 4 == nrrd->axis[0].size );
cmyk = color && 4 == nrrd->axis[0].size;
if ( color ) {
sx = AIR_CAST( int, nrrd->axis[1].size );
sy = AIR_CAST( int, nrrd->axis[2].size );
} else {
sx = AIR_CAST( int, nrrd->axis[0].size );
sy = AIR_CAST( int, nrrd->axis[1].size );
}
aspect = AIR_CAST( double, sx )/sy;
if ( aspect > 7.5/10 ) {
/* image has a wider aspect ratio than safely printable page area */
minX = 0.5;
maxX = 8.0;
minY = 5.50 - 7.5*sy/sx/2;
maxY = 5.50 + 7.5*sy/sx/2;
scale = 7.5/sx;
} else {
/* image is taller ... */
minX = 4.25 - 10.0*sx/sy/2;
maxX = 4.25 + 10.0*sx/sy/2;
minY = 0.5;
maxY = 10.5;
scale = 10.0/sy;
}
minX *= 72; minY *= 72;
maxX *= 72; maxY *= 72;
scale *= 72;
fprintf( file, "%%!PS-Adobe-3.0 EPSF-3.0\n" );
fprintf( file, "%%%%Creator: Nrrd Utilities From the "
"Great Nation of Deseret\n" );
fprintf( file, "%%%%Title: %s\n",
nrrd->content ? nrrd->content : "A lovely little image" );
fprintf( file, "%%%%Pages: 1\n" );
fprintf( file, "%%%%BoundingBox: %d %d %d %d\n",
( int )floor( minX ), ( int )floor( minY ),
( int )ceil( maxX ), ( int )ceil( maxY ) );
fprintf( file, "%%%%HiResBoundingBox: %g %g %g %g\n",
minX, minY, maxX, maxY );
fprintf( file, "%%%%EndComments\n" );
fprintf( file, "%%%%BeginProlog\n" );
fprintf( file, "%% linestr creates an empty string to hold "
"one scanline\n" );
fprintf( file, "/linestr %d string def\n", sx*( color
? ( cmyk
? 4
: 3 )
: 1 ) );
fprintf( file, "%%%%EndProlog\n" );
fprintf( file, "%%%%Page: 1 1\n" );
fprintf( file, "gsave\n" );
fprintf( file, "%g %g moveto\n", minX, minY );
fprintf( file, "%g %g lineto\n", maxX, minY );
fprintf( file, "%g %g lineto\n", maxX, maxY );
fprintf( file, "%g %g lineto\n", minX, maxY );
fprintf( file, "closepath\n" );
fprintf( file, "clip\n" );
fprintf( file, "gsave newpath\n" );
fprintf( file, "%g %g translate\n", minX, minY );
fprintf( file, "%g %g scale\n", sx*scale, sy*scale );
fprintf( file, "%d %d 8\n", sx, sy );
fprintf( file, "[%d 0 0 -%d 0 %d]\n", sx, sy, sy );
if ( color ) {
fprintf( file, "{currentfile linestr readhexstring pop} "
"false %d colorimage\n", cmyk ? 4 : 3 );
} else {
fprintf( file, "{currentfile linestr readhexstring pop} image\n" );
}
nrrdEncodingHex->write( file, nrrd->data, nrrdElementNumber( nrrd ),
nrrd, nio );
fprintf( file, "\n" );
fprintf( file, "grestore\n" );
fprintf( file, "grestore\n" );
airMopError( mop );
return 0;
}
const NrrdFormat
_nrrdFormatEPS = {
"EPS",
AIR_FALSE, /* isImage */
AIR_FALSE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatEPS_available,
_nrrdFormatEPS_nameLooksLike,
_nrrdFormatEPS_fitsInto,
_nrrdFormatEPS_contentStartsLike,
_nrrdFormatEPS_read,
_nrrdFormatEPS_write
};
221 const NrrdFormat *const
nrrdFormatEPS = &_nrrdFormatEPS;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2015, 2014, 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/***
**** Info about string handling for fields
The Nrrd format can include strings in different fields, with different
rules for each one; see http://teem.sourceforge.net/nrrd/format.html Below
is some documentation of how the strings are handled, which is mostly a
documentation of old ( long-established ) code, the gathering of which
uncovered some problems and led to some new code ( as of Thu Aug 23 09:32:11
CDT 2012 ). Conceptual inconsistencies in the different handlings of strings
merit further review, including updating the file format spec ( e.g. what
non-ASCII characters can be allowed where? Unicode? what encoding? etc ).
This all also highlights the need for having a completely uniform way of
setting these fields at one-time via the nrrd library ( e.g. can use
nrrdAxisInfoSet for "units" but there is no API for setting "space units" ).
Should that API flag as error if you try to include characters that can't
be losslessly saved, or should it silently transform things?
** Comments:
On disk, delimited by the NRRD_COMMENT_CHAR ( '#' ) and the
end of the line, but the format spec doesn't address any escaping.
Input comments processed via:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_comment( )
--> nrrd/comment.c/nrrdCommentAdd( )
--> air/string.c/airOneLinify( )
On write, output comments processed via:
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> air/string.c/airOneLinify( )
==> There is no escaping of anything: white-space is compressed into
a single ' '. Probably justified justified given format spec.
** Content: On disk, finished with the end of line. No mention
of escaping in the format spec. Input content processed via:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_content( )
which does NO processing, just airStrdup
On write, output content processed via:
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> nrrd/write.c/_nrrdSprintFieldInfo( ) ( maybe via _nrrdFprintFieldInfo( ) )
--> air/string.c/airOneLinify( )
==> not only is there no escaping, but there's some assymmetry in the
use of airOneLinify. If information is being encoded in the number of
contiguous spaces in the content, its preserved on input but not on
output. Still, there's no chance of writing a broken file.
** key/value pairs: The keys and values are separated by ":=", and the
format spec says ( string ) "\n" means ( character ) '\n' and "\\" means '\\'.
On input, both keys and values processed via:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_keyvalue( )
--> air/string.c/airUnescape( ), which deals with "\n" and "\\" ONLY
( not quotes, not other whitespace ), and then
--> nrrd/keyvalue.c/nrrdKeyValueAdd( ),
which only does an airStrdup
On output, keys and values processed via
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> nrrd/keyvalue.c/_nrrdKeyValueWrite( )
--> nrrd/keyvalue.c/_nrrdWriteEscaped( )
which is invoked to escape \n and \,
and ( NOTE! ) to convert all other whitespace to ' '
Aside from the file format spec, the nrrd *library* does not really have
any strictures about the characters that are allowed at run-time in key/values
( and indeed nrrdKeyValueAdd just does an airStrdup ). But without
converting or escaping, say, '\r', you'll generate a broken NRRD file,
hence the new handling of converting other whitespace to ' '.
** labels and units: A "-delimited string per axis.
Format spec is very specific for labels, and implies units are the same:
"Within each label, double quotes may be included by escaping them
( \" ), but no other form of escaping is supported". On input:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_labels( )
or _nrrdReadNrrdParse_units( )
--> nrrd/parseNrrd.c/_nrrdGetQuotedString( )
which does the work of unescaping \"
On output:
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> nrrd/write.c/_nrrdSprintFieldInfo( ) ( maybe via _nrrdFprintFieldInfo( ) )
--> nrrd/keyvalue.c/_nrrdWriteEscaped( )
which is invoked to escape ",
and ( NOTE! ) to convert all other whitespace to ' '
Same concern above about characters that when written would generate a
bad NRRD file, but which are not documented as escape-able in label or unit
** space units: A "-delimited string per axis of *world-space* ( NOT
the same a per-axis field, like units ). Format is sadly silent on issue of
escaping for these; so we might as well treat them like labels & units
units. On input:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_space_units
--> nrrd/parseNrrd.c/_nrrdGetQuotedString( )
which does the work of unescaping \"
On output:
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> nrrd/write.c/_nrrdSprintFieldInfo( ) ( maybe via _nrrdFprintFieldInfo( ) )
--> nrrd/keyvalue.c/_nrrdWriteEscaped( )
which is invoked to escape ",
and ( NOTE! ) to convert all other whitespace to ' '
** sample units: like content and comments, not a quoted string. On input:
nrrd/formatNrrd.c/_nrrdFormatNRRD_read( )
--> nrrd/parseNrrd.c/_nrrdReadNrrdParse_sample_units( )
which does nothing except a strdup
On output:
nrrd/formatNrrd.c/_nrrdFormatNRRD_write( )
--> nrrd/write.c/_nrrdSprintFieldInfo( ) ( maybe via _nrrdFprintFieldInfo( ) )
--> air/string.c/airOneLinify( )
****
***/
#define MAGIC "NRRD"
#define MAGIC0 "NRRD00.01"
#define MAGIC1 "NRRD0001"
#define MAGIC2 "NRRD0002"
#define MAGIC3 "NRRD0003"
#define MAGIC4 "NRRD0004"
#define MAGIC5 "NRRD0005"
#define MAGIC6 "NRRD0006"
const char *
_nrrdFormatURLLine0 = "Complete NRRD file format specification at:";
const char *
_nrrdFormatURLLine1 = "http://teem.sourceforge.net/nrrd/format.html";
void
153 nrrdIoStateDataFileIterBegin( NrrdIoState *nio ) {
nio->dataFNIndex = 0;
return;
}
/* this macro suggested by Bryan Worthen */
/* if str = '-', strcmp( ) is 0, && short circuits, return false
** else str != '-'
** if str[1] = ':', its probably a windows full path, != is 0, return false
** else str[1] != ':'
** if str[0] = '/', its a normal full path, return false
*/
#define _NEED_PATH( str ) ( strcmp( "-", ( str ) ) \
&& ':' != ( str )[1] \
&& '/' != ( str )[0] )
/*
** this is responsible for the header-relative path processing
**
** NOTE: if the filename is "-", then because it does not start with '/',
** it would normally be prefixed by nio->path, so it needs special handling
**
** NOTE: this should work okay with nio->headerStringRead, I think ...
177 */
int
nrrdIoStateDataFileIterNext( FILE **fileP, NrrdIoState *nio, int reading ) {
static const char me[]="nrrdIoStateDataFileIterNext";
char *fname=NULL;
int ii, needPath;
unsigned int num, fi;
size_t maxl;
airArray *mop;
mop = airMopNew( );
airMopAdd( mop, ( void* )fileP, ( airMopper )airSetNull, airMopOnError );
if ( !fileP ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
if ( !_nrrdDataFNNumber( nio ) ) {
biffAddf( NRRD, "%s: there appear to be zero datafiles!", me );
airMopError( mop ); return 1;
}
if ( nio->dataFNIndex >= _nrrdDataFNNumber( nio ) ) {
/* there is no next data file, but we don't make that an error
( though as of Tue Oct 2 22:53:14 CDT 2012, GLK can't remember
why this condition would ever occur ) */
nio->dataFNIndex = _nrrdDataFNNumber( nio );
airMopOkay( mop );
*fileP = NULL;
return 0;
}
/* HEY: some of this error checking is done far more often than needed */
if ( nio->dataFNFormat || nio->dataFNArr->len ) {
needPath = AIR_FALSE;
maxl = 0;
if ( nio->dataFNFormat ) {
needPath = _NEED_PATH( nio->dataFNFormat );
/* assuming 10-digit integers is plenty big */
maxl = 10 + strlen( nio->dataFNFormat );
} else {
for ( fi=0; fi<nio->dataFNArr->len; fi++ ) {
needPath |= _NEED_PATH( nio->dataFN[fi] );
maxl = AIR_MAX( maxl, strlen( nio->dataFN[fi] ) );
}
}
if ( needPath && !airStrlen( nio->path ) ) {
biffAddf( NRRD, "%s: need nio->path for header-relative datafiles", me );
airMopError( mop ); return 1;
}
fname = ( char* )malloc( airStrlen( nio->path ) + strlen( "/" ) + maxl + 1 );
if ( !fname ) {
biffAddf( NRRD, "%s: couldn't allocate filename buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, fname, airFree, airMopAlways );
}
if ( nio->dataFNFormat ) {
/* ---------------------------------------------------------- */
/* --------- base.%d <min> <max> <step> [<dim>] ------------- */
/* ---------------------------------------------------------- */
num = 0;
for ( ii = nio->dataFNMin;
( ( nio->dataFNStep > 0 && ii <= nio->dataFNMax )
|| ( nio->dataFNStep < 0 && ii >= nio->dataFNMax ) );
ii += nio->dataFNStep ) {
if ( num == nio->dataFNIndex ) {
break;
}
num += 1;
}
if ( _NEED_PATH( nio->dataFNFormat ) ) {
strcpy( fname, nio->path );
strcat( fname, "/" );
sprintf( fname + strlen( nio->path ) + strlen( "/" ), nio->dataFNFormat, ii );
} else {
sprintf( fname, nio->dataFNFormat, ii );
}
} else if ( nio->dataFNArr->len ) {
/* ---------------------------------------------------------- */
/* ------------------- LIST or single ----------------------- */
/* ---------------------------------------------------------- */
if ( _NEED_PATH( nio->dataFN[nio->dataFNIndex] ) ) {
sprintf( fname, "%s/%s", nio->path, nio->dataFN[nio->dataFNIndex] );
} else {
strcpy( fname, nio->dataFN[nio->dataFNIndex] );
}
}
/* else data file is attached */
if ( nio->dataFNFormat || nio->dataFNArr->len ) {
*fileP = airFopen( fname, reading ? stdin : stdout, reading ? "rb" : "wb" );
if ( !( *fileP ) ) {
biffAddf( NRRD, "%s: couldn't open \"%s\" ( data file %u of %u ) for %s",
me, fname, nio->dataFNIndex+1, _nrrdDataFNNumber( nio ),
reading ? "reading" : "writing" );
airMopError( mop ); return 1;
}
} else {
/* data file is attached */
if ( nio->headerStringRead ) {
/* except we were never reading from a file to begin with, but this
isn't an error */
*fileP = NULL;
} else {
*fileP = nio->headerFile;
}
}
nio->dataFNIndex++;
airMopOkay( mop );
return 0;
}
/*
** we try to use the oldest format that will hold the nrrd
294 */
int
_nrrdFormatNRRD_whichVersion( const Nrrd *nrrd, NrrdIoState *nio ) {
int ret;
if ( nrrdEncodingZRL == nio->encoding
|| nrrdSpaceRightUp == nrrd->space
|| nrrdSpaceRightDown == nrrd->space ) {
ret = 6;
} else if ( _nrrdFieldInteresting( nrrd, nio, nrrdField_measurement_frame ) ) {
ret = 5;
} else if ( _nrrdFieldInteresting( nrrd, nio, nrrdField_thicknesses )
|| _nrrdFieldInteresting( nrrd, nio, nrrdField_space )
|| _nrrdFieldInteresting( nrrd, nio, nrrdField_space_dimension )
|| _nrrdFieldInteresting( nrrd, nio, nrrdField_sample_units )
|| airStrlen( nio->dataFNFormat ) || nio->dataFNArr->len > 1 ) {
ret = 4;
} else if ( _nrrdFieldInteresting( nrrd, nio, nrrdField_kinds ) ) {
ret = 3;
} else if ( nrrdKeyValueSize( nrrd ) ) {
ret = 2;
} else {
ret = 1;
}
return ret;
}
320
static int
_nrrdFormatNRRD_available( void ) {
return AIR_TRUE;
}
326
static int
_nrrdFormatNRRD_nameLooksLike( const char *filename ) {
return ( airEndsWith( filename, NRRD_EXT_NRRD )
|| airEndsWith( filename, NRRD_EXT_NHDR ) );
}
333
static int
_nrrdFormatNRRD_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatNRRD_fitsInto";
if ( !( nrrd && encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p ) or encoding ( %p )",
me, AIR_CVOIDP( nrrd ), AIR_CVOIDP( encoding ) );
return AIR_FALSE;
}
/* everything fits in a nrrd */
return AIR_TRUE;
}
348
static int
_nrrdFormatNRRD_contentStartsLike( NrrdIoState *nio ) {
return ( !strcmp( MAGIC0, nio->line )
|| !strcmp( MAGIC1, nio->line )
|| !strcmp( MAGIC2, nio->line )
|| !strcmp( MAGIC3, nio->line )
|| !strcmp( MAGIC4, nio->line )
|| !strcmp( MAGIC5, nio->line )
|| !strcmp( MAGIC6, nio->line )
);
}
/*
** _nrrdHeaderCheck( )
**
** minimal consistency checks on relationship between fields of nrrd,
** only to be used after the headers is parsed, and before the data is
** read, to make sure that information required for reading data is in
** fact known.
**
** NOTE: this is not the place to do the sort of checking done by
** nrrdCheck( ), because it includes I/O-specific stuff
**
373 */
int
_nrrdHeaderCheck( Nrrd *nrrd, NrrdIoState *nio, int checkSeen ) {
static const char me[]="_nrrdHeaderCheck";
int i;
if ( checkSeen ) {
for ( i=1; i<=NRRD_FIELD_MAX; i++ ) {
if ( _nrrdFieldRequired[i] && !nio->seen[i] ) {
biffAddf( NRRD, "%s: didn't see required field: %s",
me, airEnumStr( nrrdField, i ) );
return 1;
}
}
}
if ( nrrdTypeBlock == nrrd->type && !nrrd->blockSize ) {
biffAddf( NRRD, "%s: type is %s, but missing field: %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
airEnumStr( nrrdField, nrrdField_block_size ) );
return 1;
}
if ( !nrrdElementSize( nrrd ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
/* _nrrdReadNrrdParse_sizes( ) checks axis[i].size, which completely
determines the return of nrrdElementNumber( ) */
if ( airEndianUnknown == nio->endian
&& nio->encoding->endianMatters
&& 1 != nrrdElementSize( nrrd ) ) {
biffAddf( NRRD, "%s: type ( %s ) and encoding ( %s ) require %s info", me,
airEnumStr( nrrdType, nrrd->type ),
nio->encoding->name,
airEnumStr( nrrdField, nrrdField_endian ) );
return 1;
}
/* we don't really try to enforce consistency with the
min/max/center/size information on each axis, other than the
value checking done by the _nrrdReadNrrdParse_* functions,
because we only really care that we know each axis size. Past
that, if the user messes it up, its not really our problem ... */
return 0;
}
/*
** NOTE: currently, this will read, without complaints or errors,
** newer NRRD format features from older NRRD files ( as indicated by
** magic ), such as key/value pairs from a NRRD0001 file, even though
** strictly speaking these are violations of the format.
**
** NOTE: by giving a NULL "file", you can make this function basically
** do the work of reading in datafiles, without any header parsing
427 */
static int
_nrrdFormatNRRD_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatNRRD_read";
/* Dynamically allocated for space reasons. */
/* MWC: These strlen usages look really unsafe. */
int ret;
unsigned int llen;
size_t valsPerPiece;
char *data;
FILE *dataFile=NULL;
/* record where the header is being read from for the sake of
nrrdIoStateDataFileIterNext( ) */
nio->headerFile = file;
/* GLK forgets the context in which file might be reasonably NULL
but on Fri Sep 23 09:48:41 EDT 2005 this was "if ( file ) { ..." */
/* nio->headerStringRead is NULL whenever IO from string is not being done */
if ( file || nio->headerStringRead ) {
if ( !_nrrdFormatNRRD_contentStartsLike( nio ) ) {
biffAddf( NRRD, "%s: this doesn't look like a %s file", me,
nrrdFormatNRRD->name );
return 1;
}
/* parse all the header lines */
do {
nio->pos = 0;
if ( _nrrdOneLine( &llen, nio, file ) ) {
biffAddf( NRRD, "%s: trouble getting line of header", me );
return 1;
}
if ( llen > 1 ) {
ret = _nrrdReadNrrdParseField( nio, AIR_TRUE );
if ( !ret ) {
biffAddf( NRRD, "%s: trouble parsing NRRD field identifier from "
"in \"%s\"", me, nio->line );
return 1;
}
/* comments and key/values are allowed multiple times */
if ( nio->seen[ret]
&& !( ret == nrrdField_comment || ret == nrrdField_keyvalue ) ) {
biffAddf( NRRD, "%s: already set field %s", me,
airEnumStr( nrrdField, ret ) );
return 1;
}
if ( nrrdFieldInfoParse[ret]( file, nrrd, nio, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: trouble parsing %s info |%s|", me,
airEnumStr( nrrdField, ret ), nio->line + nio->pos );
return 1;
}
nio->seen[ret] = AIR_TRUE;
}
} while ( llen > 1 );
/* either
0 == llen: we're at EOF ( or end of nio->headerStringRead ), or
1 == llen: we just read the empty line separating header from data */
if ( 0 == llen
&& !nio->headerStringRead
&& !nio->dataFNFormat
&& 0 == nio->dataFNArr->len ) {
/* we're at EOF, we're not reading from a string, but there's
apparently no separate data file */
biffAddf( NRRD, "%s: hit end of header, but no \"%s\" given", me,
airEnumStr( nrrdField, nrrdField_data_file ) );
return 1;
}
}
if ( _nrrdHeaderCheck( nrrd, nio, !!file ) ) {
biffAddf( NRRD, "%s: %s", me,
( llen ? "finished reading header, but there were problems"
: "hit EOF before seeing a complete valid header" ) );
return 1;
}
/* we seemed to have read in a valid header; now allocate the memory.
For directIO-compatible allocation we need to get the first datafile */
nrrdIoStateDataFileIterBegin( nio );
/* NOTE: if nio->headerStringRead, this may set dataFile to NULL */
if ( nrrdIoStateDataFileIterNext( &dataFile, nio, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: couldn't open the first datafile", me );
return 1;
}
if ( nio->skipData ) {
nrrd->data = NULL;
data = NULL;
} else {
if ( _nrrdCalloc( nrrd, nio, dataFile ) ) {
biffAddf( NRRD, "%s: couldn't allocate memory for data", me );
return 1;
}
data = ( char* )nrrd->data;
}
/* iterate through datafiles and read them in */
/* NOTE: you have to open dataFile even in the case of skipData, because
caller might have set keepNrrdDataFileOpen, in which case you need to
do any line or byte skipping if it is specified */
valsPerPiece = nrrdElementNumber( nrrd )/_nrrdDataFNNumber( nio );
while ( dataFile ) {
/* ---------------- skip, if need be */
if ( nrrdLineSkip( dataFile, nio ) ) {
biffAddf( NRRD, "%s: couldn't skip lines", me );
return 1;
}
if ( !nio->encoding->isCompression ) {
/* bytes are skipped here for non-compression encodings, but are
skipped within the decompressed stream for compression encodings */
if ( nio->dataFSkip ) {
/* this error checking is clearly done unnecessarily repeated,
but it was logically the simplest place to add it */
if ( nio->byteSkip ) {
biffAddf( NRRD, "%s: using per-list-line skip, "
"but also set global byte skip %ld", me, nio->byteSkip );
return 1;
}
/* wow, the meaning of nio->dataFNIndex is a little confusing */
if ( _nrrdByteSkipSkip( dataFile, nrrd, nio, nio->dataFSkip[nio->dataFNIndex-1] ) ) {
biffAddf( NRRD, "%s: couldn't skip %ld bytes on for list line %u",
me, nio->dataFSkip[nio->dataFNIndex-1], nio->dataFNIndex-1 );
return 1;
}
} else {
if ( nrrdByteSkip( dataFile, nrrd, nio ) ) {
biffAddf( NRRD, "%s: couldn't skip bytes", me );
return 1;
}
}
}
/* ---------------- read the data itself */
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: reading %s data ... ", me, nio->encoding->name );
fflush( stderr );
}
if ( !nio->skipData ) {
if ( nio->encoding->read( dataFile, data, valsPerPiece, nrrd, nio ) ) {
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "error!\n" );
}
biffAddf( NRRD, "%s:", me );
return 1;
}
}
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "done )\n" );
}
/* ---------------- go to next data file */
if ( nio->keepNrrdDataFileOpen && _nrrdDataFNNumber( nio ) == 1 ) {
nio->dataFile = dataFile;
} else {
if ( dataFile != nio->headerFile ) {
dataFile = airFclose( dataFile );
}
}
data += valsPerPiece*nrrdElementSize( nrrd );
if ( nrrdIoStateDataFileIterNext( &dataFile, nio, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: couldn't get the next datafile", me );
return 1;
}
}
if ( airEndianUnknown != nio->endian && nrrd->data ) {
/* we positively know the endianness of data just read */
if ( 1 < nrrdElementSize( nrrd )
&& nio->encoding->endianMatters
&& nio->endian != airMyEndian( ) ) {
/* endianness exposed in encoding, and its wrong */
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: fixing endianness ... ", me );
fflush( stderr );
}
nrrdSwapEndian( nrrd );
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "done )\n" );
fflush( stderr );
}
}
}
return 0;
}
608
static int
_nrrdFormatNRRD_write( FILE *file, const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatNRRD_write";
char strbuf[AIR_STRLEN_MED], *strptr, *tmp;
int ii;
unsigned int jj;
airArray *mop;
FILE *dataFile=NULL;
size_t valsPerPiece;
char *data;
mop = airMopNew( );
if ( !( file
|| nio->headerStringWrite
|| nio->learningHeaderStrlen ) ) {
biffAddf( NRRD, "%s: have no file or string to write to, nor are "
"learning header string length", me );
airMopError( mop ); return 1;
}
if ( nrrdTypeBlock == nrrd->type && nrrdEncodingAscii == nio->encoding ) {
biffAddf( NRRD, "%s: can't write nrrd type %s with %s encoding", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
nrrdEncodingAscii->name );
airMopError( mop ); return 1;
}
/* record where the header is being written to for the sake of
nrrdIoStateDataFileIterNext( ). This may be NULL if
nio->headerStringWrite is non-NULL */
nio->headerFile = file;
/* we have to make sure that the data filename information is set
( if needed ), so that it can be printed by _nrrdFprintFieldInfo */
if ( nio->detachedHeader
&& !nio->dataFNFormat
&& 0 == nio->dataFNArr->len ) {
/* NOTE: this means someone requested a detached header, but we
don't already have implicit ( via dataFNFormat ) or explicit
( via dataFN[] ) information about the data file */
/* NOTE: whether or not nio->skipData, we have to contrive a filename to
say in the "data file" field, which is stored in nio->dataFN[0],
because the data filename will be "interesting", according to
_nrrdFieldInteresting( ) */
/* NOTE: Fri Feb 4 01:42:20 EST 2005 the way this is now set up, having
a name in dataFN[0] will trump the name implied by nio->{path, base},
which is a useful way for the user to explicitly set the output
data filename ( as with unu make -od ) */
if ( !( !!airStrlen( nio->path ) && !!airStrlen( nio->base ) ) ) {
biffAddf( NRRD, "%s: can't create data file name: nio's "
"path and base empty", me );
airMopError( mop ); return 1;
}
tmp = ( char* )malloc( strlen( nio->base )
+ strlen( "." )
+ strlen( nio->encoding->suffix ) + 1 );
if ( !tmp ) {
biffAddf( NRRD, "%s: couldn't allocate data filename", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, tmp, airFree, airMopOnError );
sprintf( tmp, "%s.%s", nio->base, nio->encoding->suffix );
jj = airArrayLenIncr( nio->dataFNArr, 1 );
if ( !nio->dataFNArr->data ) {
biffAddf( NRRD, "%s: can't increase dataFNArr storage", me );
airMopError( mop ); return 1;
}
nio->dataFN[jj] = tmp;
}
/* the magic is in fact the first thing to be written */
if ( file ) {
fprintf( file, "%s%04d\n", MAGIC, _nrrdFormatNRRD_whichVersion( nrrd, nio ) );
} else if ( nio->headerStringWrite ) {
sprintf( nio->headerStringWrite, "%s%04d\n",
MAGIC, _nrrdFormatNRRD_whichVersion( nrrd, nio ) );
} else {
nio->headerStrlen = AIR_CAST( unsigned int, strlen( MAGIC ) + strlen( "0000" ) ) + 1;
}
/* write the advertisement about where to get the file format */
if ( !nio->skipFormatURL ) {
if ( file ) {
fprintf( file, "# %s\n", _nrrdFormatURLLine0 );
fprintf( file, "# %s\n", _nrrdFormatURLLine1 );
} else if ( nio->headerStringWrite ) {
sprintf( strbuf, "# %s\n", _nrrdFormatURLLine0 );
strcat( nio->headerStringWrite, strbuf );
sprintf( strbuf, "# %s\n", _nrrdFormatURLLine1 );
strcat( nio->headerStringWrite, strbuf );
} else {
nio->headerStrlen += sprintf( strbuf, "# %s\n", _nrrdFormatURLLine0 );
nio->headerStrlen += sprintf( strbuf, "# %s\n", _nrrdFormatURLLine1 );
}
}
/* this is where the majority of the header printing happens */
for ( ii=1; ii<=NRRD_FIELD_MAX; ii++ ) {
if ( _nrrdFieldInteresting( nrrd, nio, ii ) ) {
if ( file ) {
_nrrdFprintFieldInfo( file, "", nrrd, nio, ii, AIR_FALSE );
} else if ( nio->headerStringWrite ) {
_nrrdSprintFieldInfo( &strptr, "", nrrd, nio, ii, AIR_FALSE );
if ( strptr ) {
strcat( nio->headerStringWrite, strptr );
strcat( nio->headerStringWrite, "\n" );
free( strptr );
strptr = NULL;
}
} else {
_nrrdSprintFieldInfo( &strptr, "", nrrd, nio, ii, AIR_FALSE );
if ( strptr ) {
nio->headerStrlen += AIR_CAST( unsigned int, strlen( strptr ) );
nio->headerStrlen += AIR_CAST( unsigned int, strlen( "\n" ) );
free( strptr );
strptr = NULL;
}
}
}
}
/* comments and key/value pairs handled differently */
for ( jj=0; jj<nrrd->cmtArr->len; jj++ ) {
char *strtmp;
strtmp = airOneLinify( airStrdup( nrrd->cmt[jj] ) );
if ( file ) {
fprintf( file, "%c %s\n", NRRD_COMMENT_CHAR, strtmp );
} else if ( nio->headerStringWrite ) {
strptr = ( char* )malloc( 1 + strlen( " " )
+ strlen( strtmp ) + strlen( "\n" ) + 1 );
sprintf( strptr, "%c %s\n", NRRD_COMMENT_CHAR, strtmp );
strcat( nio->headerStringWrite, strptr );
free( strptr );
strptr = NULL;
} else {
nio->headerStrlen += ( 1 + AIR_CAST( unsigned int, strlen( " " )
+ strlen( strtmp )
+ strlen( "\n" ) ) + 1 );
}
airFree( strtmp );
}
for ( jj=0; jj<nrrd->kvpArr->len; jj++ ) {
if ( file ) {
_nrrdKeyValueWrite( file, NULL,
NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj] );
} else if ( nio->headerStringWrite ) {
_nrrdKeyValueWrite( NULL, &strptr,
NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj] );
if ( strptr ) {
strcat( nio->headerStringWrite, strptr );
free( strptr );
strptr = NULL;
}
} else {
_nrrdKeyValueWrite( NULL, &strptr,
NULL, nrrd->kvp[0 + 2*jj], nrrd->kvp[1 + 2*jj] );
if ( strptr ) {
nio->headerStrlen += AIR_CAST( unsigned int, strlen( strptr ) );
free( strptr );
strptr = NULL;
}
}
}
if ( file ) {
if ( !( nio->detachedHeader || _nrrdDataFNNumber( nio ) > 1 ) ) {
fprintf( file, "\n" );
}
}
if ( file && !nio->skipData ) {
nrrdIoStateDataFileIterBegin( nio );
if ( nrrdIoStateDataFileIterNext( &dataFile, nio, AIR_FALSE ) ) {
biffAddf( NRRD, "%s: couldn't write the first datafile", me );
airMopError( mop ); return 1;
}
valsPerPiece = nrrdElementNumber( nrrd )/_nrrdDataFNNumber( nio );
data = ( char* )nrrd->data;
do {
/* ---------------- write data */
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: writing %s data ", me, nio->encoding->name );
fflush( stderr );
}
if ( nio->encoding->write( dataFile, data, valsPerPiece, nrrd, nio ) ) {
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "error!\n" );
}
biffAddf( NRRD, "%s: couldn't write %s data", me, nio->encoding->name );
airMopError( mop ); return 1;
}
if ( 2 <= nrrdStateVerboseIO ) {
fprintf( stderr, "done )\n" );
}
/* ---------------- go to next data file */
if ( dataFile != nio->headerFile ) {
dataFile = airFclose( dataFile );
}
data += valsPerPiece*nrrdElementSize( nrrd );
if ( nrrdIoStateDataFileIterNext( &dataFile, nio, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: couldn't get the next datafile", me );
airMopError( mop ); return 1;
}
} while ( dataFile );
}
airMopOkay( mop );
return 0;
}
const NrrdFormat
_nrrdFormatNRRD = {
"NRRD",
AIR_FALSE, /* isImage */
AIR_TRUE, /* readable */
AIR_TRUE, /* usesDIO */
_nrrdFormatNRRD_available,
_nrrdFormatNRRD_nameLooksLike,
_nrrdFormatNRRD_fitsInto,
_nrrdFormatNRRD_contentStartsLike,
_nrrdFormatNRRD_read,
_nrrdFormatNRRD_write
832 };
const NrrdFormat *const
nrrdFormatNRRD = &_nrrdFormatNRRD;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#include <teemPng.h>
#if TEEM_PNG
#include <png.h>
#endif
#define MAGIC "\211PNG"
static int
35 _nrrdFormatPNG_available( void ) {
#if TEEM_PNG
return AIR_TRUE;
#else
return AIR_FALSE;
#endif
}
static int
45 _nrrdFormatPNG_nameLooksLike( const char *filename ) {
return airEndsWith( filename, NRRD_EXT_PNG );
}
static int
51 _nrrdFormatPNG_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatPNG_fitsInto";
#if !TEEM_PNG /* ------------------------------------------- */
AIR_UNUSED( nrrd );
AIR_UNUSED( encoding );
biffMaybeAddf( useBiff, NRRD,
"%s: %s format not available in this Teem build",
me, nrrdFormatPNG->name );
return AIR_FALSE;
#else /* ------------------------------------------- */
int ret;
if ( !( nrrd && encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p ) or encoding ( %p )",
me, AIR_CVOIDP( nrrd ), AIR_CVOIDP( encoding ) );
return AIR_FALSE;
}
if ( !( nrrdTypeUChar == nrrd->type || nrrdTypeUShort == nrrd->type ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: type must be %s or %s ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, nrrdTypeUShort ),
airEnumStr( nrrdType, nrrd->type ) );
return AIR_FALSE;
}
/* else */
/* encoding ignored- always gzip */
if ( 2 == nrrd->dim ) {
/* its a gray-scale image */
ret = AIR_TRUE;
} else if ( 3 == nrrd->dim ) {
if ( !( 1 == nrrd->axis[0].size
|| 2 == nrrd->axis[0].size
|| 3 == nrrd->axis[0].size
|| 4 == nrrd->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffMaybeAddf( useBiff, NRRD,
"%s: 1st axis size is %s, not 1, 2, 3, or 4", me,
airSprintSize_t( stmp, nrrd->axis[0].size ) );
return AIR_FALSE;
}
/* else */
ret = AIR_TRUE;
} else {
biffMaybeAddf( useBiff, NRRD,
"%s: dimension is %d, not 2 or 3", me, nrrd->dim );
return AIR_FALSE;
}
return ret;
#endif /* ------------------------------------------- */
}
static int
110 _nrrdFormatPNG_contentStartsLike( NrrdIoState *nio ) {
return !strcmp( MAGIC, nio->line );
}
#if TEEM_PNG
static void
117 _nrrdErrorHandlerPNG ( png_structp png, png_const_charp message )
{
static const char me[]="_nrrdErrorHandlerPNG";
/* add PNG error message to biff */
biffAddf( NRRD, "%s: PNG error: %s", me, message );
/* longjmp back to the setjmp, return 1 */
longjmp( png_jmpbuf( png ), 1 );
}
static void
127 _nrrdWarningHandlerPNG ( png_structp png, png_const_charp message )
{
static const char me[]="_nrrdWarningHandlerPNG";
AIR_UNUSED( png );
/* add the png warning message to biff */
biffAddf( NRRD, "%s: PNG warning: %s", me, message );
/* no longjump, execution continues */
}
/* we need to use the file I/O callbacks on windows
to make sure we can mix VC6 libpng with VC7 Teem */
#ifdef _WIN32
static void
140 _nrrdReadDataPNG ( png_structp png, png_bytep data, png_size_t len )
{
png_size_t read;
read = ( png_size_t )fread( data, ( png_size_t )1, len, ( FILE* )png_get_io_ptr( png ) );
if ( read != len ) png_error( png, "file read error" );
}
static void
147 _nrrdWriteDataPNG ( png_structp png, png_bytep data, png_size_t len )
{
png_size_t written;
written = fwrite( data, 1, len, ( FILE* )png_get_io_ptr( png ) );
if ( written != len ) png_error( png, "file write error" );
}
static void
155 _nrrdFlushDataPNG ( png_structp png )
{
FILE *io_ptr = png_get_io_ptr( png );
if ( io_ptr != NULL ) fflush( io_ptr );
}
#endif /* _WIN32 */
#endif /* TEEM_PNG */
static int
164 _nrrdFormatPNG_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatPNG_read";
#if TEEM_PNG
png_structp png;
png_infop info;
png_bytep *row;
png_uint_32 width, height, rowsize, hi;
png_text* txt;
int depth, type, i, channels, numtxt, ret;
int ntype, ndim;
size_t nsize[3];
#endif /* TEEM_PNG */
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
if ( !_nrrdFormatPNG_contentStartsLike( nio ) ) {
biffAddf( NRRD, "%s: this doesn't look like a %s file", me,
nrrdFormatPNG->name );
return 1;
}
#if TEEM_PNG
/* create png struct with the error handlers above */
png = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL,
_nrrdErrorHandlerPNG,
_nrrdWarningHandlerPNG );
if ( png == NULL ) {
biffAddf( NRRD, "%s: failed to create PNG read struct", me );
return 1;
}
/* create image info struct */
info = png_create_info_struct( png );
if ( info == NULL ) {
png_destroy_read_struct( &png, NULL, NULL );
biffAddf( NRRD, "%s: failed to create PNG image info struct", me );
return 1;
}
/* set up png style error handling */
if ( setjmp( png_jmpbuf( png ) ) ) {
/* the error is reported inside the handler,
but we still need to clean up and return */
png_destroy_read_struct( &png, &info, NULL );
return 1;
}
/* initialize png I/O */
#ifdef _WIN32
png_set_read_fn( png, ( png_voidp )file, _nrrdReadDataPNG );
#else
png_init_io( png, file );
#endif
/* if we are here, we have already read 6 bytes from the file */
png_set_sig_bytes( png, 6 );
/* png_read_info( ) returns all information from the file
before the first data chunk */
png_read_info( png, info );
png_get_IHDR( png, info, &width, &height, &depth, &type,
NULL, NULL, NULL );
/* expand paletted colors into rgb triplets */
if ( type == PNG_COLOR_TYPE_PALETTE )
png_set_palette_to_rgb( png );
/* expand grayscale images to 8 bits from 1, 2, or 4 bits */
if ( type == PNG_COLOR_TYPE_GRAY && depth < 8 )
#if PNG_LIBPNG_VER_MINOR > 1
png_set_expand_gray_1_2_4_to_8( png );
#else
png_set_expand( png );
#endif
/* expand paletted or rgb images with transparency to full alpha
channels so the data will be available as rgba quartets */
if ( png_get_valid( png, info, PNG_INFO_tRNS ) )
png_set_tRNS_to_alpha( png );
/* fix endianness for 16 bit formats */
if ( depth > 8 && airMyEndian( ) == airEndianLittle )
png_set_swap( png );
#if 0
/* HEY GLK asks why is this commented out? */
/* set up gamma correction */
/* NOTE: screen_gamma is platform dependent,
it can hardwired or set from a parameter/environment variable */
if ( png_get_sRGB( png_ptr, info_ptr, &intent ) ) {
/* if the image has sRGB info,
pass in standard nrrd file gamma 1.0 */
png_set_gamma( png_ptr, screen_gamma, 1.0 );
} else {
double gamma;
/* set image gamma if present */
if ( png_get_gAMA( png, info, &gamma ) )
png_set_gamma( png, screen_gamma, gamma );
else
png_set_gamma( png, screen_gamma, 1.0 );
}
#endif
/* update reader */
png_read_update_info( png, info );
/* allocate memory for the image data */
ntype = depth > 8 ? nrrdTypeUShort : nrrdTypeUChar;
switch ( type ) {
case PNG_COLOR_TYPE_GRAY:
ndim = 2; nsize[0] = width; nsize[1] = height;
nsize[2] = 1; /* to simplify code below */
break;
case PNG_COLOR_TYPE_GRAY_ALPHA:
ndim = 3; nsize[0] = 2; nsize[1] = width; nsize[2] = height;
break;
case PNG_COLOR_TYPE_RGB:
ndim = 3; nsize[0] = 3; nsize[1] = width; nsize[2] = height;
break;
case PNG_COLOR_TYPE_RGB_ALPHA:
ndim = 3; nsize[0] = 4; nsize[1] = width; nsize[2] = height;
break;
case PNG_COLOR_TYPE_PALETTE:
/* TODO: merge this with the outer switch, needs to be tested */
channels = png_get_channels( png, info );
if ( channels < 2 ) {
ndim = 2; nsize[0] = width; nsize[1] = height;
} else {
ndim = 3; nsize[0] = channels; nsize[1] = width; nsize[2] = height;
}
break;
default:
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: unknown png type: %d", me, type );
return 1;
break;
}
if ( nio->oldData
&& ( nio->oldDataSize
== ( size_t )( nrrdTypeSize[ntype]*nsize[0]*nsize[1]*nsize[2] ) ) ) {
ret = nrrdWrap_nva( nrrd, nio->oldData, ntype, ndim, nsize );
} else {
ret = nrrdMaybeAlloc_nva( nrrd, ntype, ndim, nsize );
}
if ( ret ) {
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: failed to allocate nrrd", me );
return 1;
}
/* query row size */
rowsize = png_get_rowbytes( png, info );
/* check byte size */
if ( nrrdElementNumber( nrrd )*nrrdElementSize( nrrd ) != height*rowsize ) {
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: size mismatch", me );
return 1;
}
/* set up row pointers */
row = ( png_bytep* )malloc( sizeof( png_bytep )*height );
for ( hi=0; hi<height; hi++ ) {
row[hi] = &( ( png_bytep )nrrd->data )[hi*rowsize];
}
/* read the entire image in one pass */
png_read_image( png, row );
/* read all text fields from the text chunk */
numtxt = png_get_text( png, info, &txt, NULL );
for ( i=0; i<numtxt; i++ ) {
if ( !strcmp( txt[i].key, NRRD_PNG_FIELD_KEY ) ) {
nio->pos = 0;
/* Reading PNGs teaches Gordon that his scheme for parsing nrrd header
information is inappropriately specific to reading PNMs and NRRDs,
since in this case the text from which we parse a nrrd field
descriptor did NOT come from a line of text as read by
_nrrdOneLine */
nio->line = ( char * )airFree( nio->line );
nio->line = airStrdup( txt[i].text );
ret = _nrrdReadNrrdParseField( nio, AIR_FALSE );
if ( ret ) {
const char* fs = airEnumStr( nrrdField, ret );
if ( nrrdField_comment == ret ) {
ret = 0;
goto plain;
}
if ( !_nrrdFieldValidInImage[ret] ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: field \"%s\" ( not allowed in PNG ) "
"--> plain comment )\n", me, fs );
}
ret = 0;
goto plain;
}
if ( !nio->seen[ret]
&& nrrdFieldInfoParse[ret]( file, nrrd, nio, AIR_FALSE ) ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: unparsable info \"%s\" for field \"%s\" "
"--> plain comment )\n", me, nio->line + nio->pos, fs );
}
ret = 0;
goto plain;
}
nio->seen[ret] = AIR_TRUE;
plain:
if ( !ret ) {
if ( nrrdCommentAdd( nrrd, nio->line ) ) {
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: couldn't add comment", me );
return 1;
}
}
}
} else if ( !strcmp( txt[i].key, NRRD_PNG_COMMENT_KEY ) ) {
char *p, *c;
c = airStrtok( txt[i].text, "\n", &p );
while ( c ) {
if ( nrrdCommentAdd( nrrd, c ) ) {
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: couldn't add comment", me );
return 1;
}
c = airStrtok( NULL, "\n", &p );
}
} else {
if ( nrrdKeyValueAdd( nrrd, txt[i].key, txt[i].text ) ) {
png_destroy_read_struct( &png, &info, NULL );
biffAddf( NRRD, "%s: couldn't add key/value pair", me );
return 1;
}
}
}
/* finish reading */
png_read_end( png, info );
/* clean up */
row = ( png_byte** )airFree( row );
png_destroy_read_struct( &png, &info, NULL );
return 0;
#else
biffAddf( NRRD, "%s: Sorry, this nrrd not compiled with PNG enabled", me );
return 1;
#endif
}
static int
395 _nrrdFormatPNG_write( FILE *file, const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatPNG_write";
#if TEEM_PNG
int fi, depth, type, csize;
unsigned int jj, numtxt, txtidx;
png_structp png;
png_infop info;
png_bytep *row;
png_uint_32 width, height, rowsize, hi;
png_text *txt;
char *key, *value;
/* no need to check type and format, done in FitsInFormat */
/* create png struct with the error handlers above */
png = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL,
_nrrdErrorHandlerPNG,
_nrrdWarningHandlerPNG );
if ( png == NULL ) {
biffAddf( NRRD, "%s: failed to create PNG write struct ( compiled with "
"PNG_LIBPNG_VER_STRING=" PNG_LIBPNG_VER_STRING " )", me );
return 1;
}
/* create image info struct */
info = png_create_info_struct( png );
if ( info == NULL ) {
png_destroy_write_struct( &png, NULL );
biffAddf( NRRD, "%s: failed to create PNG image info struct", me );
return 1;
}
/* set up error png style error handling */
if ( setjmp( png_jmpbuf( png ) ) )
{
/* the error is reported inside the error handler,
but we still need to clean up an return with an error */
png_destroy_write_struct( &png, &info );
return 1;
}
/* initialize png I/O */
#ifdef _WIN32
png_set_write_fn( png, file, _nrrdWriteDataPNG, _nrrdFlushDataPNG );
#else
png_init_io( png, file );
#endif
/* calculate depth, width, height, and row size */
depth = nrrd->type == nrrdTypeUChar ? 8 : 16;
switch ( nrrd->dim ) {
char stmp[AIR_STRLEN_SMALL];
case 2: /* g only */
width = nrrd->axis[0].size;
height = nrrd->axis[1].size;
type = PNG_COLOR_TYPE_GRAY;
rowsize = width*nrrdElementSize( nrrd );
break;
case 3: /* g, ga, rgb, rgba */
width = nrrd->axis[1].size;
height = nrrd->axis[2].size;
rowsize = width*nrrd->axis[0].size*nrrdElementSize( nrrd );
switch ( nrrd->axis[0].size ) {
case 1:
type = PNG_COLOR_TYPE_GRAY;
break;
case 2:
type = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
case 3:
type = PNG_COLOR_TYPE_RGB;
break;
case 4:
type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
default:
png_destroy_write_struct( &png, &info );
biffAddf( NRRD, "%s: nrrd->axis[0].size ( %s ) not compatible with PNG", me,
airSprintSize_t( stmp, nrrd->axis[0].size ) );
return 1;
break;
}
break;
default:
png_destroy_write_struct( &png, &info );
biffAddf( NRRD, "%s: dimension ( %d ) not compatible with PNG",
me, nrrd->dim );
return 1;
break;
}
/* set image header info */
png_set_IHDR( png, info, width, height, depth, type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE );
/* calculate numtxt and allocate txt[] array */
numtxt = 0;
for ( fi=nrrdField_unknown+1; fi<nrrdField_last; fi++ ) {
if ( _nrrdFieldValidInImage[fi] && _nrrdFieldInteresting( nrrd, nio, fi ) ) {
numtxt++;
}
}
for ( jj=0; jj<nrrdKeyValueSize( nrrd ); jj++ ) {
nrrdKeyValueIndex( nrrd, &key, &value, jj );
/* HEY: why is the NULL check needed?? */
if ( NULL != key && NULL != value ) {
numtxt++;
}
free( key );
free( value );
key = NULL;
value = NULL;
}
if ( nrrd->cmtArr->len > 0 ) {
/* all comments are put into single text field */
numtxt += 1;
}
if ( 0 == numtxt ) {
txt = NULL;
} else {
txt = AIR_CAST( png_text *, calloc( numtxt, sizeof( png_text ) ) );
/* add nrrd fields to the text chunk */
csize = 0;
txtidx = 0;
for ( fi=nrrdField_unknown+1; fi<nrrdField_last; fi++ ) {
if ( _nrrdFieldValidInImage[fi] && _nrrdFieldInteresting( nrrd, nio, fi ) ) {
txt[txtidx].key = airStrdup( NRRD_PNG_FIELD_KEY );
txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
_nrrdSprintFieldInfo( &( txt[txtidx].text ), "", nrrd, nio, fi,
( 3 == nrrd->dim && 1 == nrrd->axis[0].size ) );
txtidx++;
}
}
/* add nrrd key/value pairs to the chunk */
for ( jj=0; jj<nrrdKeyValueSize( nrrd ); jj++ ) {
nrrdKeyValueIndex( nrrd, &key, &value, jj );
if ( NULL != key && NULL != value ) {
txt[txtidx].key = key;
txt[txtidx].text = value;
txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
txtidx++;
}
}
/* add nrrd comments as a single text field */
if ( nrrd->cmtArr->len > 0 ) {
txt[txtidx].key = airStrdup( NRRD_PNG_COMMENT_KEY );
txt[txtidx].compression = PNG_TEXT_COMPRESSION_NONE;
for ( jj=0; jj<nrrd->cmtArr->len; jj++ ) {
csize += airStrlen( nrrd->cmt[jj] ) + 1;
}
txt[txtidx].text = ( png_charp )malloc( csize + 1 );
txt[txtidx].text[0] = 0;
for ( jj=0; jj<nrrd->cmtArr->len; jj++ ) {
strcat( txt[txtidx].text, nrrd->cmt[jj] );
strcat( txt[txtidx].text, "\n" );
}
txtidx++;
}
png_set_text( png, info, txt, numtxt );
}
/* write header */
png_write_info( png, info );
/* fix endianness for 16 bit formats */
if ( depth > 8 && airMyEndian( ) == airEndianLittle ) {
png_set_swap( png );
}
/* set up row pointers */
row = ( png_bytep* )malloc( sizeof( png_bytep )*height );
for ( hi=0; hi<height; hi++ ) {
row[hi] = &( ( png_bytep )nrrd->data )[hi*rowsize];
}
png_set_rows( png, info, row );
/* write the entire image in one pass */
png_write_image( png, row );
/* finish writing */
png_write_end( png, info );
/* clean up */
if ( txt ) {
for ( jj=0; jj<numtxt; jj++ ) {
txt[jj].key = ( char * )airFree( txt[jj].key );
txt[jj].text = ( char * )airFree( txt[jj].text );
}
free( txt );
}
row = ( png_byte** )airFree( row );
png_destroy_write_struct( &png, &info );
return 0;
#else
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
biffAddf( NRRD, "%s: Sorry, this nrrd not compiled with PNG enabled", me );
return 1;
#endif
}
const NrrdFormat
_nrrdFormatPNG = {
"PNG",
AIR_TRUE, /* isImage */
AIR_TRUE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatPNG_available,
_nrrdFormatPNG_nameLooksLike,
_nrrdFormatPNG_fitsInto,
_nrrdFormatPNG_contentStartsLike,
_nrrdFormatPNG_read,
_nrrdFormatPNG_write
};
600 const NrrdFormat *const
nrrdFormatPNG = &_nrrdFormatPNG;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#define MAGIC_P6 "P6"
#define MAGIC_P5 "P5"
#define MAGIC_P3 "P3"
#define MAGIC_P2 "P2"
static int
33 _nrrdFormatPNM_available( void ) {
return AIR_TRUE;
}
static int
39 _nrrdFormatPNM_nameLooksLike( const char *filename ) {
return ( airEndsWith( filename, NRRD_EXT_PGM )
|| airEndsWith( filename, NRRD_EXT_PPM ) );
}
static int
46 _nrrdFormatPNM_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatPNM_fitsInto";
int ret;
if ( !( nrrd && encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p ) or encoding ( %p )",
me, AIR_CVOIDP( nrrd ), AIR_CVOIDP( encoding ) );
return AIR_FALSE;
}
if ( nrrdTypeUChar != nrrd->type ) {
biffMaybeAddf( useBiff, NRRD, "%s: type must be %s ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
airEnumStr( nrrdType, nrrd->type ) );
return AIR_FALSE;
}
if ( !( nrrdEncodingRaw == encoding || nrrdEncodingAscii == encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: encoding can only be %s or %s", me,
nrrdEncodingRaw->name, nrrdEncodingAscii->name );
return AIR_FALSE;
}
if ( 2 == nrrd->dim ) {
/* its a gray-scale image */
ret = 2;
} else if ( 3 == nrrd->dim ) {
if ( 1 == nrrd->axis[0].size ) {
/* its a faux-3D image, really grayscale */
ret = 2;
} else if ( 3 == nrrd->axis[0].size ) {
/* its a real color image */
ret = 3;
} else {
/* else its no good */
char stmp[AIR_STRLEN_SMALL];
biffMaybeAddf( useBiff, NRRD,
"%s: dim is 3, but 1st axis size is %s, not 1 or 3", me,
airSprintSize_t( stmp, nrrd->axis[0].size ) );
return AIR_FALSE;
}
} else {
biffMaybeAddf( useBiff, NRRD,
"%s: dimension is %d, not 2 or 3", me, nrrd->dim );
return AIR_FALSE;
}
return ret;
}
int
94 _nrrdFormatPNM_contentStartsLike( NrrdIoState *nio ) {
return ( !strcmp( MAGIC_P6, nio->line )
|| !strcmp( MAGIC_P5, nio->line )
|| !strcmp( MAGIC_P3, nio->line )
|| !strcmp( MAGIC_P2, nio->line ) );
}
static int
103 _nrrdFormatPNM_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatPNM_read";
const char *fs;
char *perr;
int color, got, want, ret, val[5], sx, sy, max, magic;
unsigned int i, llen;
if ( !_nrrdFormatPNM_contentStartsLike( nio ) ) {
biffAddf( NRRD, "%s: this doesn't look like a %s file", me,
nrrdFormatPNM->name );
return 1;
}
nrrd->type = nrrdTypeUChar;
if ( !strcmp( MAGIC_P6, nio->line ) ) {
magic = 6;
} else if ( !strcmp( MAGIC_P5, nio->line ) ) {
magic = 5;
} else if ( !strcmp( MAGIC_P3, nio->line ) ) {
magic = 3;
} else if ( !strcmp( MAGIC_P2, nio->line ) ) {
magic = 2;
} else {
fprintf( stderr, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line );
biffAddf( NRRD, "%s: PANIC: magic \"%s\" not handled\n", me, nio->line );
return 1;
}
switch( magic ) {
case 2:
color = AIR_FALSE;
nio->encoding = nrrdEncodingAscii;
nrrd->dim = 2;
break;
case 3:
color = AIR_TRUE;
nio->encoding = nrrdEncodingAscii;
nrrd->dim = 3;
break;
case 5:
color = AIR_FALSE;
nio->encoding = nrrdEncodingRaw;
nrrd->dim = 2;
break;
case 6:
color = AIR_TRUE;
nio->encoding = nrrdEncodingRaw;
nrrd->dim = 3;
break;
default:
biffAddf( NRRD, "%s: sorry, PNM magic %d not implemented", me, magic );
return 1;
break;
}
val[0] = val[1] = val[2] = 0;
got = 0;
want = 3;
while ( got < want ) {
nio->pos = 0;
if ( _nrrdOneLine( &llen, nio, file ) ) {
biffAddf( NRRD, "%s: failed to get line from PNM header", me );
return 1;
}
if ( !( 0 < llen ) ) {
biffAddf( NRRD, "%s: hit EOF in header with %d of %d ints parsed",
me, got, want );
return 1;
}
if ( '#' == nio->line[0] ) {
if ( strncmp( nio->line, NRRD_PNM_COMMENT, strlen( NRRD_PNM_COMMENT ) ) ) {
/* this is a generic comment */
ret = 0;
goto plain;
}
/* else this PNM comment is trying to tell us something */
nio->pos = AIR_CAST( int, strlen( NRRD_PNM_COMMENT ) );
nio->pos += AIR_CAST( int, strspn( nio->line + nio->pos, _nrrdFieldSep ) );
ret = _nrrdReadNrrdParseField( nio, AIR_FALSE );
if ( !ret ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: unparsable field \"%s\" --> plain comment )\n",
me, nio->line );
}
goto plain;
}
if ( nrrdField_comment == ret ) {
ret = 0;
goto plain;
}
fs = airEnumStr( nrrdField, ret );
if ( !_nrrdFieldValidInImage[ret] ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: field \"%s\" ( not allowed in PNM ) "
"--> plain comment )\n", me, fs );
}
ret = 0;
goto plain;
}
if ( !nio->seen[ret]
&& nrrdFieldInfoParse[ret]( file, nrrd, nio, AIR_TRUE ) ) {
perr = biffGetDone( NRRD );
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: unparsable info for field \"%s\" "
"--> plain comment:\n%s )\n", me, fs, perr );
}
free( perr );
ret = 0;
goto plain;
}
nio->seen[ret] = AIR_TRUE;
plain:
if ( !ret ) {
if ( nrrdCommentAdd( nrrd, nio->line+1 ) ) {
biffAddf( NRRD, "%s: couldn't add comment", me );
return 1;
}
}
continue;
}
if ( 3 == sscanf( nio->line, "%d%d%d", val+got+0, val+got+1, val+got+2 ) ) {
got += 3;
continue;
}
if ( 2 == sscanf( nio->line, "%d%d", val+got+0, val+got+1 ) ) {
got += 2;
continue;
}
if ( 1 == sscanf( nio->line, "%d", val+got+0 ) ) {
got += 1;
continue;
}
/* else, we couldn't parse ANY numbers on this line, which is okay
as long as the line contains nothing but white space */
for ( i=0; ( i<=strlen( nio->line )-1
&& isspace( AIR_INT( nio->line[i] ) ) ); i++ )
;
if ( i != strlen( nio->line ) ) {
biffAddf( NRRD, "%s: \"%s\" has no integers but isn't just whitespace",
me, nio->line );
return 1;
}
}
/* this assumes 3 == want */
sx = val[0];
sy = val[1];
max = val[2];
if ( !( sx > 0 && sy > 0 && max > 0 ) ) {
biffAddf( NRRD, "%s: sx, sy, max of %d, %d, %d has problem", me, sx, sy, max );
return 1;
}
if ( 255 != max ) {
biffAddf( NRRD, "%s: sorry, can only deal with max value 255 ( not %d )",
me, max );
return 1;
}
/* we know what we need in order to set nrrd fields and read data */
if ( color ) {
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSize,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ) );
} else {
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSize,
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ) );
}
if ( !nio->skipData ) {
if ( _nrrdCalloc( nrrd, nio, file ) ) {
biffAddf( NRRD, "%s: couldn't allocate memory for data", me );
return 1;
}
if ( nio->encoding->read( file, nrrd->data, nrrdElementNumber( nrrd ),
nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
} else {
nrrd->data = NULL;
}
return 0;
}
static int
289 _nrrdFormatPNM_write( FILE *file, const Nrrd *_nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatPNM_write";
int color, sx, sy, magic, fi;
unsigned int ci;
Nrrd *nrrd;
airArray *mop;
mop = airMopNew( );
airMopAdd( mop, nrrd = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nrrd, _nrrd ) ) {
biffAddf( NRRD, "%s: couldn't make private copy", me );
airMopError( mop ); return 1;
}
if ( 3 == nrrd->dim && 1 == nrrd->axis[0].size ) {
if ( nrrdAxesDelete( nrrd, nrrd, 0 ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
color = ( 3 == nrrd->dim );
if ( !color ) {
magic = ( nrrdEncodingAscii == nio->encoding ? 2 : 5 );
sx = AIR_CAST( int, nrrd->axis[0].size );
sy = AIR_CAST( int, nrrd->axis[1].size );
} else {
magic = ( nrrdEncodingAscii == nio->encoding ? 3 : 6 );
sx = AIR_CAST( int, nrrd->axis[1].size );
sy = AIR_CAST( int, nrrd->axis[2].size );
}
fprintf( file, "P%d\n", magic );
fprintf( file, "%d %d\n", sx, sy );
for ( fi=nrrdField_unknown+1; fi<nrrdField_last; fi++ ) {
if ( _nrrdFieldValidInImage[fi]
&& _nrrdFieldInteresting( nrrd, nio, fi ) ) {
/* dropAxis0 is always AIR_FALSE because of code
above to delete a stub axis 0 */
_nrrdFprintFieldInfo( file, NRRD_PNM_COMMENT, nrrd, nio, fi, AIR_FALSE );
}
}
for ( ci=0; ci<nrrd->cmtArr->len; ci++ ) {
fprintf( file, "# %s\n", nrrd->cmt[ci] );
}
fprintf( file, "255\n" );
if ( !nio->skipData ) {
if ( nio->encoding->write( file, nrrd->data, nrrdElementNumber( nrrd ),
nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
airMopError( mop );
return 0;
}
const NrrdFormat
_nrrdFormatPNM = {
"PNM",
AIR_TRUE, /* isImage */
AIR_TRUE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatPNM_available,
_nrrdFormatPNM_nameLooksLike,
_nrrdFormatPNM_fitsInto,
_nrrdFormatPNM_contentStartsLike,
_nrrdFormatPNM_read,
_nrrdFormatPNM_write
};
360 const NrrdFormat *const
nrrdFormatPNM = &_nrrdFormatPNM;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
static int
28 _nrrdFormatText_available( void ) {
return AIR_TRUE;
}
static int
34 _nrrdFormatText_nameLooksLike( const char *fname ) {
return ( airEndsWith( fname, NRRD_EXT_TEXT )
|| airEndsWith( fname, ".text" )
|| airEndsWith( fname, ".ascii" ) );
}
static int
42 _nrrdFormatText_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatText_fitsInto";
AIR_UNUSED( encoding );
/* encoding ignored- always ascii */
if ( !( 1 == nrrd->dim || 2 == nrrd->dim ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: dimension is %d, not 1 or 2",
me, nrrd->dim );
return AIR_FALSE;
}
if ( nrrdTypeBlock == nrrd->type ) {
biffMaybeAddf( useBiff, NRRD, "%s: can't save blocks to plain text", me );
return AIR_FALSE;
}
/* NOTE: type of array not guaranteed to survive */
return AIR_TRUE;
}
static int
62 _nrrdFormatText_contentStartsLike( NrrdIoState *nio ) {
float oneFloat;
return ( NRRD_COMMENT_CHAR == nio->line[0]
|| airParseStrF( &oneFloat, nio->line, _nrrdTextSep, 1 ) );
}
static int
70 _nrrdFormatText_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatText_read";
const char *fs;
char *errS;
unsigned int plen, llen;
size_t line, sx, sy, size[NRRD_DIM_MAX];
int nret, fidx, settwo = 0, gotOnePerAxis = AIR_FALSE;
/* fl: first line, al: all lines */
airArray *flArr, *alArr;
float *fl, *al, oneFloat;
airPtrPtrUnion appu;
if ( !_nrrdFormatText_contentStartsLike( nio ) ) {
biffAddf( NRRD, "%s: this doesn't look like a %s file", me,
nrrdFormatText->name );
return 1;
}
/* this goofiness is just to leave the nrrd as we found it
( specifically, nrrd->dim ) when we hit an error */
#define UNSETTWO if ( settwo ) nrrd->dim = settwo
/* we only get here with the first line already in nio->line */
line = 1;
llen = AIR_CAST( unsigned int, strlen( nio->line ) );
if ( 0 == nrrd->dim ) {
settwo = nrrd->dim;
nrrd->dim = 2;
}
/* first, we get through comments */
while ( NRRD_COMMENT_CHAR == nio->line[0] ) {
nio->pos = 1;
nio->pos += AIR_CAST( int, strspn( nio->line + nio->pos, _nrrdFieldSep ) );
fidx = _nrrdReadNrrdParseField( nio, AIR_FALSE );
/* could we parse anything? */
if ( !fidx ) {
/* being unable to parse a comment as a nrrd field is not
any kind of error */
goto plain;
}
if ( nrrdField_comment == fidx ) {
fidx = 0;
goto plain;
}
fs = airEnumStr( nrrdField, fidx );
if ( !_nrrdFieldValidInText[fidx] ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: field \"%s\" not allowed in plain text "
"--> plain comment )\n", me, fs );
}
fidx = 0;
goto plain;
}
/* when reading plain text, we simply ignore repetitions of a field */
if ( ( nrrdField_keyvalue == fidx || !nio->seen[fidx] )
&& nrrdFieldInfoParse[fidx]( file, nrrd, nio, AIR_TRUE ) ) {
errS = biffGetDone( NRRD );
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "%s: %s", me, errS );
fprintf( stderr, "( %s: malformed field \"%s\" --> plain comment )\n",
me, fs );
}
if ( nrrdField_dimension == fidx ) {
/* "# dimension: 0" lead nrrd->dim being set to 0 */
nrrd->dim = 2;
}
free( errS );
fidx = 0;
goto plain;
}
if ( nrrdField_dimension == fidx ) {
if ( !( 1 == nrrd->dim || 2 == nrrd->dim ) ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: plain text dimension can only be 1 or 2; "
"resetting to 2 )\n", me );
}
nrrd->dim = 2;
}
if ( 1 == nrrd->dim && gotOnePerAxis ) {
fprintf( stderr, "( %s: already parsed per-axis field, can't reset "
"dimension to 1; resetting to 2 )\n", me );
nrrd->dim = 2;
}
}
if ( _nrrdFieldOnePerAxis[fidx] ) {
gotOnePerAxis = AIR_TRUE;
}
nio->seen[fidx] = AIR_TRUE;
plain:
if ( !fidx ) {
if ( nrrdCommentAdd( nrrd, nio->line + 1 ) ) {
biffAddf( NRRD, "%s: couldn't add comment", me );
UNSETTWO; return 1;
}
}
if ( _nrrdOneLine( &llen, nio, file ) ) {
biffAddf( NRRD, "%s: error getting a line", me );
UNSETTWO; return 1;
}
if ( !llen ) {
biffAddf( NRRD, "%s: hit EOF before any numbers parsed", me );
UNSETTWO; return 1;
}
line++;
}
/* we supposedly have a line of numbers, see how many there are */
if ( !airParseStrF( &oneFloat, nio->line, _nrrdTextSep, 1 ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't parse a single number on line %s", me,
airSprintSize_t( stmp, line ) );
UNSETTWO; return 1;
}
appu.f = &fl;
flArr = airArrayNew( appu.v, NULL, sizeof( float ), _NRRD_TEXT_INCR );
if ( !flArr ) {
biffAddf( NRRD, "%s: couldn't create array for first line values", me );
UNSETTWO; return 1;
}
for ( sx=1; 1; sx++ ) {
/* there is obviously a limit to the number of numbers that can
be parsed from a single finite line of input text */
airArrayLenSet( flArr, AIR_CAST( unsigned int, sx ) );
if ( !flArr->data ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't alloc space for %s values", me,
airSprintSize_t( stmp, sx ) );
UNSETTWO; return 1;
}
if ( sx > airParseStrF( fl, nio->line, _nrrdTextSep, AIR_CAST( unsigned int, sx ) ) ) {
/* We asked for sx ints and got less. We know that we successfully
got one value, so we did succeed in parsing sx-1 values */
sx--;
break;
}
}
flArr = airArrayNuke( flArr );
if ( 1 == nrrd->dim && 1 != sx ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: wanted 1-D nrrd, but got %s values on 1st line", me,
airSprintSize_t( stmp, sx ) );
UNSETTWO; return 1;
}
/* else sx == 1 when nrrd->dim == 1 */
/* now see how many more lines there are */
appu.f = &al;
alArr = airArrayNew( appu.v, NULL, sx*sizeof( float ), _NRRD_TEXT_INCR );
if ( !alArr ) {
biffAddf( NRRD, "%s: couldn't create data buffer", me );
UNSETTWO; return 1;
}
sy = 0;
while ( llen ) {
airArrayLenIncr( alArr, 1 );
if ( !alArr->data ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't create scanline of %s values", me,
airSprintSize_t( stmp, sx ) );
UNSETTWO; return 1;
}
plen = airParseStrF( al + sy*sx, nio->line, _nrrdTextSep, AIR_CAST( unsigned int, sx ) );
if ( sx > plen ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: could only parse %d values ( not %s ) on line %s",
me, plen, airSprintSize_t( stmp1, sx ),
airSprintSize_t( stmp2, line ) );
UNSETTWO; return 1;
}
sy++;
line++;
if ( _nrrdOneLine( &llen, nio, file ) ) {
biffAddf( NRRD, "%s: error getting a line", me );
UNSETTWO; return 1;
}
}
/*
fprintf( stderr, "%s: nrrd->dim = %d, sx = %d; sy = %d\n",
me, nrrd->dim, sx, sy );
*/
if ( !( 1 == nrrd->dim || 2 == nrrd->dim ) ) {
fprintf( stderr, "%s: PANIC about to save, but dim = %d\n", me, nrrd->dim );
exit( 1 );
}
if ( 1 == nrrd->dim ) {
size[0] = sy;
} else {
size[0] = sx;
size[1] = sy;
}
if ( nio->oldData
&& nio->oldDataSize == ( size_t )( nrrdTypeSize[nrrdTypeFloat]*sx*sy ) ) {
nret = nrrdWrap_nva( nrrd, nio->oldData, nrrdTypeFloat, nrrd->dim, size );
} else {
nret = nrrdMaybeAlloc_nva( nrrd, nrrdTypeFloat, nrrd->dim, size );
}
if ( nret ) {
biffAddf( NRRD, "%s: couldn't create nrrd for plain text data", me );
UNSETTWO; return 1;
}
memcpy( nrrd->data, al, sx*sy*sizeof( float ) );
alArr = airArrayNuke( alArr );
return 0;
}
static int
280 _nrrdFormatText_write( FILE *file, const Nrrd *nrrd, NrrdIoState *nio ) {
char cmt[AIR_STRLEN_SMALL], buff[AIR_STRLEN_SMALL];
size_t I;
int i, x, y, sx, sy;
void *data;
float val;
sprintf( cmt, "%c ", NRRD_COMMENT_CHAR );
if ( !nio->bareText ) {
if ( 1 == nrrd->dim ) {
_nrrdFprintFieldInfo( file, cmt, nrrd, nio, nrrdField_dimension,
AIR_FALSE );
}
for ( i=1; i<=NRRD_FIELD_MAX; i++ ) {
if ( _nrrdFieldValidInText[i]
&& nrrdField_dimension != i /* dimension is handled above */
&& _nrrdFieldInteresting( nrrd, nio, i ) ) {
_nrrdFprintFieldInfo( file, cmt, nrrd, nio, i, AIR_FALSE );
}
}
if ( nrrdKeyValueSize( nrrd ) ) {
unsigned int kvi;
for ( kvi=0; kvi<nrrd->kvpArr->len; kvi++ ) {
_nrrdKeyValueWrite( file, NULL, "#",
nrrd->kvp[0 + 2*kvi],
nrrd->kvp[1 + 2*kvi] );
}
}
}
if ( 1 == nrrd->dim ) {
sx = 1;
sy = AIR_CAST( int, nrrd->axis[0].size );
}
else {
sx = AIR_CAST( int, nrrd->axis[0].size );
sy = AIR_CAST( int, nrrd->axis[1].size );
}
data = nrrd->data;
I = 0;
for ( y=0; y<sy; y++ ) {
for ( x=0; x<sx; x++ ) {
val = nrrdFLookup[nrrd->type]( data, I );
nrrdSprint[nrrdTypeFloat]( buff, &val );
if ( x ) fprintf( file, " " );
fprintf( file, "%s", buff );
I++;
}
fprintf( file, "\n" );
}
return 0;
}
const NrrdFormat
_nrrdFormatText = {
"text",
AIR_FALSE, /* isImage */
AIR_TRUE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatText_available,
_nrrdFormatText_nameLooksLike,
_nrrdFormatText_fitsInto,
_nrrdFormatText_contentStartsLike,
_nrrdFormatText_read,
_nrrdFormatText_write
};
348 const NrrdFormat *const
nrrdFormatText = &_nrrdFormatText;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#define MAGIC1 "# vtk DataFile Version 1.0"
#define MAGIC2 "# vtk DataFile Version 2.0"
#define MAGIC3 "# vtk DataFile Version 3.0"
int
32 _nrrdFormatVTK_available( void ) {
return AIR_TRUE;
}
int
38 _nrrdFormatVTK_nameLooksLike( const char *fname ) {
return airEndsWith( fname, NRRD_EXT_VTK );
}
int
44 _nrrdFormatVTK_fitsInto( const Nrrd *nrrd, const NrrdEncoding *encoding,
int useBiff ) {
static const char me[]="_nrrdFormatVTK_fitsInto";
if ( !( nrrd && encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL nrrd ( %p ) or encoding ( %p )",
me, AIR_CVOIDP( nrrd ), AIR_CVOIDP( encoding ) );
return AIR_FALSE;
}
if ( !( nrrdEncodingRaw == encoding || nrrdEncodingAscii == encoding ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: encoding can only be %s or %s", me,
nrrdEncodingRaw->name, nrrdEncodingAscii->name );
return AIR_FALSE;
}
if ( !( nrrdTypeUChar == nrrd->type
|| nrrdTypeChar == nrrd->type
|| nrrdTypeUShort == nrrd->type
|| nrrdTypeShort == nrrd->type
|| nrrdTypeUInt == nrrd->type
|| nrrdTypeInt == nrrd->type
|| nrrdTypeFloat == nrrd->type
|| nrrdTypeDouble == nrrd->type ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: type %s doesn't fit in VTK ( as currently implemented )",
me, airEnumStr( nrrdType, nrrd->type ) );
return AIR_FALSE;
}
if ( !( 3 == nrrd->dim
|| ( 4 == nrrd->dim && 3 == nrrd->axis[0].size )
|| ( 4 == nrrd->dim && 9 == nrrd->axis[0].size ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: nrrd didn't look like a volume of "
"scalars, vectors, or matrices", me );
return AIR_FALSE;
}
return AIR_TRUE;
}
int
82 _nrrdFormatVTK_contentStartsLike( NrrdIoState *nio ) {
return ( !strcmp( MAGIC1, nio->line )
|| !strcmp( MAGIC2, nio->line )
|| !strcmp( MAGIC3, nio->line ) );
}
int
90 _nrrdFormatVTK_read( FILE *file, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdReadVTK";
char *three[3];
int sx, sy, sz, ret, N;
double xm=0.0, ym=0.0, zm=0.0, xs=1.0, ys=1.0, zs=1.0;
airArray *mop;
unsigned int llen;
if ( !_nrrdFormatVTK_contentStartsLike( nio ) ) {
biffAddf( NRRD, "%s: this doesn't look like a %s file", me,
nrrdFormatVTK->name );
return 1;
}
#define GETLINE( what ) \
do { \
ret = _nrrdOneLine( &llen, nio, file ); \
} while ( !ret && ( 1 == llen ) ); \
if ( ret || !llen ) { \
biffAddf( NRRD, "%s: couldn't get " #what " line", me ); \
return 1; \
}
/* read in content */
GETLINE( content );
if ( strcmp( NRRD_UNKNOWN, nio->line ) ) {
if ( !( nrrd->content = airStrdup( nio->line ) ) ) {
biffAddf( NRRD, "%s: couldn't read or copy content string", me );
return 1;
}
}
GETLINE( encoding ); airToUpper( nio->line );
if ( !strcmp( "ASCII", nio->line ) ) {
nio->encoding = nrrdEncodingAscii;
} else if ( !strcmp( "BINARY", nio->line ) ) {
nio->encoding = nrrdEncodingRaw;
} else {
biffAddf( NRRD, "%s: encoding \"%s\" wasn't \"ASCII\" or \"BINARY\"",
me, nio->line );
return 1;
}
GETLINE( DATASET ); airToUpper( nio->line );
if ( !strstr( nio->line, "STRUCTURED_POINTS" ) ) {
biffAddf( NRRD,
"%s: sorry, only STRUCTURED_POINTS data is nrrd-ready", me );
return 1;
}
GETLINE( DIMENSIONS ); airToUpper( nio->line );
if ( !strstr( nio->line, "DIMENSIONS" )
|| 3 != sscanf( nio->line, "DIMENSIONS %d %d %d", &sx, &sy, &sz ) ) {
biffAddf( NRRD, "%s: couldn't parse DIMENSIONS line ( \"%s\" )",
me, nio->line );
return 1;
}
GETLINE( next ); airToUpper( nio->line );
while ( !strstr( nio->line, "POINT_DATA" ) ) {
if ( strstr( nio->line, "ORIGIN" ) ) {
if ( 3 != sscanf( nio->line, "ORIGIN %lf %lf %lf", &xm, &ym, &zm ) ) {
biffAddf( NRRD, "%s: couldn't parse ORIGIN line ( \"%s\" )",
me, nio->line );
return 1;
}
} else if ( strstr( nio->line, "SPACING" ) ) {
if ( 3 != sscanf( nio->line, "SPACING %lf %lf %lf",
&xs, &ys, &zs ) ) {
biffAddf( NRRD, "%s: couldn't parse SPACING line ( \"%s\" )",
me, nio->line );
return 1;
}
} else if ( strstr( nio->line, "ASPECT_RATIO" ) ) {
if ( 3 != sscanf( nio->line, "ASPECT_RATIO %lf %lf %lf",
&xs, &ys, &zs ) ) {
biffAddf( NRRD, "%s: couldn't parse ASPECT_RATIO line ( \"%s\" )",
me, nio->line );
return 1;
}
}
GETLINE( next ); airToUpper( nio->line );
}
if ( 1 != sscanf( nio->line, "POINT_DATA %d", &N ) ) {
biffAddf( NRRD, "%s: couldn't parse POINT_DATA line ( \"%s\" )",
me, nio->line );
return 1;
}
if ( N != sx*sy*sz ) {
biffAddf( NRRD,
"%s: product of sizes ( %d*%d*%d == %d ) != # elements ( %d )",
me, sx, sy, sz, sx*sy*sz, N );
return 1;
}
GETLINE( attribute declaration );
mop = airMopNew( );
if ( 3 != airParseStrS( three, nio->line, AIR_WHITESPACE, 3, AIR_FALSE ) ) {
biffAddf( NRRD,
"%s: didn't see three words in attribute declaration \"%s\"",
me, nio->line );
return 1;
}
airMopAdd( mop, three[0], airFree, airMopAlways );
airMopAdd( mop, three[1], airFree, airMopAlways );
airMopAdd( mop, three[2], airFree, airMopAlways );
airToLower( three[2] );
if ( !strcmp( three[2], "bit" ) ) {
if ( nrrdEncodingAscii == nio->encoding ) {
fprintf( stderr, "%s: WARNING: \"bit\"-type data will be read in as "
"unsigned char\n", me );
nrrd->type = nrrdTypeUChar;
} else {
biffAddf( NRRD, "%s: can't read in \"bit\"-type data as BINARY", me );
return 1;
}
} else if ( !strcmp( three[2], "unsigned_char" ) ) {
nrrd->type = nrrdTypeUChar;
} else if ( !strcmp( three[2], "char" ) ) {
nrrd->type = nrrdTypeChar;
} else if ( !strcmp( three[2], "unsigned_short" ) ) {
nrrd->type = nrrdTypeUShort;
} else if ( !strcmp( three[2], "short" ) ) {
nrrd->type = nrrdTypeShort;
} else if ( !strcmp( three[2], "unsigned_int" ) ) {
nrrd->type = nrrdTypeUInt;
} else if ( !strcmp( three[2], "int" ) ) {
nrrd->type = nrrdTypeInt;
} else if ( !strcmp( three[2], "float" ) ) {
nrrd->type = nrrdTypeFloat;
} else if ( !strcmp( three[2], "double" ) ) {
nrrd->type = nrrdTypeDouble;
} else {
/* "unsigned_long" and "long" fall in here- I don't know what
the VTK people mean by these types, since always mean different
things on 32-bit versus 64-bit architectures */
biffAddf( NRRD, "%s: type \"%s\" not recognized", me, three[2] );
airMopError( mop ); return 1;
}
airToUpper( three[0] );
if ( !strncmp( "SCALARS", three[0], strlen( "SCALARS" ) ) ) {
GETLINE( LOOKUP_TABLE ); airToUpper( nio->line );
if ( strcmp( nio->line, "LOOKUP_TABLE DEFAULT" ) ) {
biffAddf( NRRD,
"%s: sorry, can only deal with default LOOKUP_TABLE", me );
airMopError( mop ); return 1;
}
nrrd->dim = 3;
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSize,
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSpacing, xs, ys, zs );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMin, xm, ym, zm );
} else if ( !strncmp( "VECTORS", three[0], strlen( "VECTORS" ) ) ) {
nrrd->dim = 4;
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSize,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm );
nrrd->axis[0].kind = nrrdKind3Vector;
} else if ( !strncmp( "TENSORS", three[0], strlen( "TENSORS" ) ) ) {
nrrd->dim = 4;
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSize,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, sz ) );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSpacing, AIR_NAN, xs, ys, zs );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMin, AIR_NAN, xm, ym, zm );
nrrd->axis[0].kind = nrrdKind3DMatrix;
} else {
biffAddf( NRRD,
"%s: sorry, can only deal with SCALARS, VECTORS, and TENSORS "
"currently, so couldn't parse attribute declaration \"%s\"",
me, nio->line );
airMopError( mop ); return 1;
}
if ( !nio->skipData ) {
if ( _nrrdCalloc( nrrd, nio, file ) ) {
biffAddf( NRRD, "%s: couldn't allocate memory for data", me );
return 1;
}
if ( nio->encoding->read( file, nrrd->data, nrrdElementNumber( nrrd ),
nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( 1 < nrrdElementSize( nrrd )
&& nio->encoding->endianMatters
&& airMyEndian( ) != airEndianBig ) {
/* encoding exposes endianness, and its big, but we aren't */
nrrdSwapEndian( nrrd );
}
} else {
nrrd->data = NULL;
}
285
airMopOkay( mop );
return 0;
}
/* this strongly assumes that nrrdFitsInFormat( ) was true */
int
_nrrdFormatVTK_write( FILE *file, const Nrrd *_nrrd, NrrdIoState *nio ) {
static const char me[]="_nrrdFormatVTK_write";
int i, sx, sy, sz, sax;
double xs, ys, zs, xm, ym, zm;
char type[AIR_STRLEN_MED], name[AIR_STRLEN_SMALL];
Nrrd *nrrd;
airArray *mop;
/* HEY: should this copy be done more conservatively */
mop = airMopNew( );
airMopAdd( mop, nrrd=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nrrd, _nrrd ) ) {
biffAddf( NRRD, "%s: couldn't make private copy", me );
airMopError( mop ); return 1;
}
if ( !( 3 == nrrd->dim ||
( 4 == nrrd->dim && ( 3 == nrrd->axis[0].size ||
9 == nrrd->axis[0].size ) ) ) ) {
biffAddf( NRRD, "%s: doesn't seem to be scalar, vector, or matrix", me );
airMopError( mop ); return 1;
}
sax = nrrd->dim - 3;
xs = nrrd->axis[sax+0].spacing;
ys = nrrd->axis[sax+1].spacing;
zs = nrrd->axis[sax+2].spacing;
if ( !( AIR_EXISTS( xs ) && AIR_EXISTS( ys ) && AIR_EXISTS( zs ) ) ) {
xs = ys = zs = 1.0;
}
xm = nrrd->axis[sax+0].min;
ym = nrrd->axis[sax+1].min;
zm = nrrd->axis[sax+2].min;
if ( !( AIR_EXISTS( xm ) && AIR_EXISTS( ym ) && AIR_EXISTS( zm ) ) ) {
xm = ym = zm = 0.0;
}
sx = AIR_CAST( int, nrrd->axis[sax+0].size );
sy = AIR_CAST( int, nrrd->axis[sax+1].size );
sz = AIR_CAST( int, nrrd->axis[sax+2].size );
switch( nrrd->type ) {
case nrrdTypeUChar:
strcpy( type, "unsigned_char" );
break;
case nrrdTypeChar:
strcpy( type, "char" );
break;
case nrrdTypeUShort:
strcpy( type, "unsigned_short" );
break;
case nrrdTypeShort:
strcpy( type, "short" );
break;
case nrrdTypeUInt:
strcpy( type, "unsigned_int" );
break;
case nrrdTypeInt:
strcpy( type, "int" );
break;
case nrrdTypeFloat:
strcpy( type, "float" );
break;
case nrrdTypeDouble:
strcpy( type, "double" );
break;
default:
biffAddf( NRRD, "%s: can't put %s-type nrrd into VTK", me,
airEnumStr( nrrdType, nrrd->type ) );
airMopError( mop ); return 1;
}
fprintf( file, "%s\n", MAGIC3 );
/* there is a file-format-imposed limit on the length of the "content" */
if ( nrrd->content ) {
/* when the "250" below was previously "255", vtk didn't deal */
for ( i=0; i<=250 && nrrd->content[i]; i++ ) {
fputc( nrrd->content[i], file );
}
fputc( '\n', file );
} else {
fprintf( file, NRRD_UNKNOWN "\n" );
}
if ( nrrdEncodingRaw == nio->encoding ) {
fprintf( file, "BINARY\n" );
} else {
fprintf( file, "ASCII\n" );
}
fprintf( file, "DATASET STRUCTURED_POINTS\n" );
fprintf( file, "DIMENSIONS %d %d %d\n", sx, sy, sz );
fprintf( file, "ORIGIN %g %g %g\n", xm, ym, zm );
fprintf( file, "SPACING %g %g %g\n", xs, ys, zs );
fprintf( file, "POINT_DATA %d\n", sx*sy*sz );
airSrandMT( AIR_CAST( unsigned int, airTime( ) ) );
sprintf( name, "nrrd%05d", airRandInt( 100000 ) );
if ( 3 == nrrd->dim ) {
fprintf( file, "SCALARS %s %s\n", name, type );
fprintf( file, "LOOKUP_TABLE default\n" );
} else {
/* 4 == nrrd->dim */
if ( 3 == nrrd->axis[0].size ) {
fprintf( file, "VECTORS %s %s\n", name, type );
} else {
fprintf( file, "TENSORS %s %s\n", name, type );
}
}
if ( 1 < nrrdElementSize( nrrd )
&& nio->encoding->endianMatters
&& airMyEndian( ) != airEndianBig ) {
/* encoding exposes endianness, and we're not big, as req.d by VTK */
nrrdSwapEndian( nrrd );
}
if ( nio->encoding->write( file, nrrd->data, nrrdElementNumber( nrrd ),
nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
const NrrdFormat
_nrrdFormatVTK = {
"VTK",
AIR_FALSE, /* isImage */
AIR_TRUE, /* readable */
AIR_FALSE, /* usesDIO */
_nrrdFormatVTK_available,
417 _nrrdFormatVTK_nameLooksLike,
_nrrdFormatVTK_fitsInto,
_nrrdFormatVTK_contentStartsLike,
_nrrdFormatVTK_read,
_nrrdFormatVTK_write
};
const NrrdFormat *const
nrrdFormatVTK = &_nrrdFormatVTK;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
This file is a modified version of the 'gzio.c' and 'zutil.h' source
files from the zlib 1.1.4 distribution.
zlib.h -- interface of the 'zlib' general purpose compression library
version 1.1.4, March 11th, 2002
Copyright ( C ) 1995-2002 Jean-loup Gailly and Mark Adler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jean-loup Gailly Mark Adler
jloup@gzip.org madler@alumni.caltech.edu
The data format used by the zlib library is described by RFCs ( Request for
Comments ) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
( zlib format ), rfc1951.txt ( deflate format ) and rfc1952.txt ( gzip format ).
*/
#if TEEM_ZLIB
#include "nrrd.h"
#include "privateNrrd.h"
#ifdef _WIN32 /* Window 95 & Windows NT */
# define _NRRD_OS_CODE 0x0b
#endif
#if defined( MACOS ) || defined( TARGET_OS_MAC ) || defined( __APPLE_CC__ )
# define _NRRD_OS_CODE 0x07
#endif
#ifndef _NRRD_OS_CODE
# define _NRRD_OS_CODE 0x03 /* assume Unix */
#endif
/* default memLevel */
#if MAX_MEM_LEVEL >= 8
# define _NRRD_DEF_MEM_LEVEL 8
#else
# define _NRRD_DEF_MEM_LEVEL MAX_MEM_LEVEL
#endif
/* stream buffer size */
#define _NRRD_Z_BUFSIZE 16 * 1024
/* gzip flag byte */
#define _NRRD_ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
#define _NRRD_HEAD_CRC 0x02 /* bit 1 set: header CRC present */
#define _NRRD_EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define _NRRD_ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define _NRRD_COMMENT 0x10 /* bit 4 set: file comment present */
#define _NRRD_RESERVED 0xE0 /* bits 5..7: reserved */
typedef struct _NrrdGzStream {
z_stream stream;
int z_err; /* error code for last stream operation */
int z_eof; /* set if end of input file */
FILE *file; /* .gz file */
Byte *inbuf; /* input buffer */
Byte *outbuf; /* output buffer */
uLong crc; /* crc32 of uncompressed data */
char *msg; /* error message */
int transparent; /* 1 if input file is not a .gz file */
char mode; /* 'w' or 'r' */
long startpos; /* start of compressed data in file ( header skipped ) */
} _NrrdGzStream;
static int _nrrdGzMagic[2] = {0x1f, 0x8b}; /* gzip magic header */
/* zlib error messages */
static const char *_nrrdGzErrMsg[10] = {
"need dictionary", /* Z_NEED_DICT 2 */
"stream end", /* Z_STREAM_END 1 */
"", /* Z_OK 0 */
"file error", /* Z_ERRNO ( -1 ) */
"stream error", /* Z_STREAM_ERROR ( -2 ) */
"data error", /* Z_DATA_ERROR ( -3 ) */
"insufficient memory", /* Z_MEM_ERROR ( -4 ) */
"buffer error", /* Z_BUF_ERROR ( -5 ) */
"incompatible version", /* Z_VERSION_ERROR ( -6 ) */
""};
#define _NRRD_GZ_ERR_MSG( err ) _nrrdGzErrMsg[Z_NEED_DICT-( err )]
/* some forward declarations for things in this file */
123 static void _nrrdGzCheckHeader( _NrrdGzStream *s );
124 static int _nrrdGzDestroy( _NrrdGzStream *s );
125 static int _nrrdGzDoFlush( gzFile file, int flush );
126 static void _nrrdGzPutLong( FILE *file, uLong x );
127 static uLong _nrrdGzGetLong( _NrrdGzStream *s );
/*
** _nrrdGzOpen( )
**
** Opens a gzip ( .gz ) file for reading or writing. The mode parameter
** is like in fopen ( "rb" or "wb" ). The file represented by the FILE* pointer
** should be open already with the same mode. The mode parameter can also be
** used to specify the compression level "[0-9]" and strategy "[f|h]".
**
** The compression level must be between 0 and 9: 1 gives best speed,
** 9 gives best compression, 0 gives no compression at all ( the input data
** is simply copied a block at a time ). The default level is 6.
**
** The strategy parameter is used to tune the compression algorithm. Use
** "f" for data produced by a filter ( or predictor ), or "h" to force Huffman
** encoding only ( no string match ). Filtered data consists mostly of small
** values with a somewhat random distribution. In this case, the compression
** algorithm is tuned to compress them better. The effect of "f" is to force
** more Huffman coding and less string matching; it is somewhat intermediate
** between the default and Huffman. The strategy parameter only affects the
** compression ratio but not the correctness of the compressed output even
** if it is not set appropriately.
**
** The complete syntax for the mode parameter is: "( r|w[a] )[0-9][f|h]".
**
** Returns Z_NULL if the file could not be opened or if there was
** insufficient memory to allocate the ( de )compression state; errno
** can be checked to distinguish the two cases ( if errno is zero, the
** zlib error is Z_MEM_ERROR ).
*/
gzFile
159 _nrrdGzOpen( FILE* fd, const char* mode ) {
static const char me[]="_nrrdGzOpen";
int error;
int level = Z_DEFAULT_COMPRESSION; /* compression level */
int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
const char *p = mode;
_NrrdGzStream *s;
char fmode[AIR_STRLEN_MED]; /* copy of mode, without the compression level */
char *m = fmode;
if ( !mode ) {
biffAddf( NRRD, "%s: no file mode specified", me );
return Z_NULL;
}
/* allocate stream struct */
s = ( _NrrdGzStream * )calloc( 1, sizeof( _NrrdGzStream ) );
if ( !s ) {
biffAddf( NRRD, "%s: failed to allocate stream buffer", me );
return Z_NULL;
}
/* initialize stream struct */
s->stream.zalloc = ( alloc_func )0;
s->stream.zfree = ( free_func )0;
s->stream.opaque = ( voidpf )0;
s->stream.next_in = s->inbuf = Z_NULL;
s->stream.next_out = s->outbuf = Z_NULL;
s->stream.avail_in = s->stream.avail_out = 0;
s->file = NULL;
s->z_err = Z_OK;
s->z_eof = 0;
s->crc = crc32( 0L, Z_NULL, 0 );
s->msg = NULL;
s->transparent = 0;
/* parse mode flag */
s->mode = '\0';
do {
if ( *p == 'r' ) s->mode = 'r';
if ( *p == 'w' || *p == 'a' ) s->mode = 'w';
if ( *p >= '0' && *p <= '9' ) {
level = *p - '0';
} else if ( *p == 'f' ) {
strategy = Z_FILTERED;
} else if ( *p == 'h' ) {
strategy = Z_HUFFMAN_ONLY;
} else {
*m++ = *p; /* copy the mode */
}
} while ( *p++ && m != fmode + sizeof( fmode ) );
if ( s->mode == '\0' ) {
biffAddf( NRRD, "%s: invalid file mode", me );
return _nrrdGzDestroy( s ), ( gzFile )Z_NULL;
}
if ( s->mode == 'w' ) {
error = deflateInit2( &( s->stream ), level,
Z_DEFLATED, -MAX_WBITS, _NRRD_DEF_MEM_LEVEL,
strategy );
/* windowBits is passed < 0 to suppress zlib header */
s->stream.next_out = s->outbuf = ( Byte* )calloc( 1, _NRRD_Z_BUFSIZE );
if ( error != Z_OK || s->outbuf == Z_NULL ) {
biffAddf( NRRD, "%s: stream init failed", me );
return _nrrdGzDestroy( s ), ( gzFile )Z_NULL;
}
} else {
s->stream.next_in = s->inbuf = ( Byte* )calloc( 1, _NRRD_Z_BUFSIZE );
error = inflateInit2( &( s->stream ), -MAX_WBITS );
/* windowBits is passed < 0 to tell that there is no zlib header.
* Note that in this case inflate *requires* an extra "dummy" byte
* after the compressed stream in order to complete decompression and
* return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
* present after the compressed stream.
*/
if ( error != Z_OK || s->inbuf == Z_NULL ) {
biffAddf( NRRD, "%s: stream init failed", me );
return _nrrdGzDestroy( s ), ( gzFile )Z_NULL;
}
}
s->stream.avail_out = _NRRD_Z_BUFSIZE;
errno = 0;
s->file = fd;
if ( s->file == NULL ) {
biffAddf( NRRD, "%s: null file pointer", me );
return _nrrdGzDestroy( s ), ( gzFile )Z_NULL;
}
if ( s->mode == 'w' ) {
/* Write a very simple .gz header: */
fprintf( s->file, "%c%c%c%c%c%c%c%c%c%c", _nrrdGzMagic[0], _nrrdGzMagic[1],
Z_DEFLATED,
0 /*flags*/,
0, 0, 0, 0 /*time*/,
0 /*xflags*/,
_NRRD_OS_CODE );
s->startpos = 10L;
/* We use 10L instead of ftell( s->file ) to because ftell causes an
* fflush on some systems. This version of the library doesn't use
* startpos anyway in write mode, so this initialization is not
* necessary.
*/
} else {
_nrrdGzCheckHeader( s ); /* skip the .gz header */
s->startpos = ( ftell( s->file ) - s->stream.avail_in );
}
return ( gzFile )s;
}
/*
** _nrrdGzClose( )
**
** Flushes all pending output if necessary, closes the compressed file
** and deallocates the ( de )compression state.
*/
int
273 _nrrdGzClose ( gzFile file ) {
static const char me[]="_nrrdGzClose";
int error;
_NrrdGzStream *s = ( _NrrdGzStream* )file;
if ( s == NULL ) {
biffAddf( NRRD, "%s: invalid stream", me );
return 1;
}
if ( s->mode == 'w' ) {
error = _nrrdGzDoFlush( file, Z_FINISH );
if ( error != Z_OK ) {
biffAddf( NRRD, "%s: failed to flush pending data", me );
return _nrrdGzDestroy( ( _NrrdGzStream* )file );
}
_nrrdGzPutLong( s->file, s->crc );
_nrrdGzPutLong( s->file, s->stream.total_in );
}
return _nrrdGzDestroy( ( _NrrdGzStream* )file );
}
/*
** _nrrdGzRead( )
**
** Reads the given number of uncompressed bytes from the compressed file.
** Returns the number of bytes actually read ( 0 for end of file ).
*/
int
301 _nrrdGzRead( gzFile file, void* buf, unsigned int len, unsigned int* didread ) {
static const char me[]="_nrrdGzRead";
_NrrdGzStream *s = ( _NrrdGzStream* )file;
Bytef *start = ( Bytef* )buf; /* starting point for crc computation */
Byte *next_out; /* == stream.next_out but not forced far ( for MSDOS ) */
if ( s == NULL || s->mode != 'r' ) {
biffAddf( NRRD, "%s: invalid stream or file mode", me );
*didread = 0;
return 1;
}
if ( s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO ) {
biffAddf( NRRD, "%s: data read error", me );
*didread = 0;
return 1;
}
if ( s->z_err == Z_STREAM_END ) {
*didread = 0;
return 0; /* EOF */
}
next_out = ( Byte* )buf;
s->stream.next_out = ( Bytef* )buf;
s->stream.avail_out = len;
while ( s->stream.avail_out != 0 ) {
if ( s->transparent ) {
/* Copy first the lookahead bytes: */
uInt n = s->stream.avail_in;
if ( n > s->stream.avail_out ) n = s->stream.avail_out;
if ( n > 0 ) {
memcpy( s->stream.next_out, s->stream.next_in, n );
next_out += n;
s->stream.next_out = next_out;
s->stream.next_in += n;
s->stream.avail_out -= n;
s->stream.avail_in -= n;
}
if ( s->stream.avail_out > 0 ) {
s->stream.avail_out -= ( uInt )fread( next_out, 1, s->stream.avail_out,
s->file );
}
len -= s->stream.avail_out;
s->stream.total_in += len;
s->stream.total_out += len;
if ( len == 0 ) s->z_eof = 1;
*didread = len;
return 0;
}
if ( s->stream.avail_in == 0 && !s->z_eof ) {
errno = 0;
s->stream.avail_in = ( uInt )fread( s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file );
if ( s->stream.avail_in == 0 ) {
s->z_eof = 1;
if ( ferror( s->file ) ) {
s->z_err = Z_ERRNO;
break;
}
}
s->stream.next_in = s->inbuf;
}
s->z_err = inflate( &( s->stream ), Z_NO_FLUSH );
if ( s->z_err == Z_STREAM_END ) {
/* Check CRC and original size */
s->crc = crc32( s->crc, start, ( uInt )( s->stream.next_out - start ) );
start = s->stream.next_out;
if ( _nrrdGzGetLong( s ) != s->crc ) {
s->z_err = Z_DATA_ERROR;
} else {
( void )_nrrdGzGetLong( s );
/* The uncompressed length returned by above getlong( ) may
* be different from s->stream.total_out ) in case of
* concatenated .gz files. Check for such files:
*/
_nrrdGzCheckHeader( s );
if ( s->z_err == Z_OK ) {
uLong total_in = s->stream.total_in;
uLong total_out = s->stream.total_out;
inflateReset( &( s->stream ) );
s->stream.total_in = total_in;
s->stream.total_out = total_out;
s->crc = crc32( 0L, Z_NULL, 0 );
}
}
}
if ( s->z_err != Z_OK || s->z_eof ) break;
}
s->crc = crc32( s->crc, start, ( uInt )( s->stream.next_out - start ) );
*didread = len - s->stream.avail_out;
return 0;
}
/*
** _nrrdGzWrite( )
**
** Writes the given number of uncompressed bytes into the compressed file.
** Returns the number of bytes actually written ( 0 in case of error ).
*/
int
408 _nrrdGzWrite( gzFile file, const void* buf, unsigned int len,
unsigned int* written ) {
static const char me[]="_nrrdGzWrite";
_NrrdGzStream *s = ( _NrrdGzStream* )file;
void *nonconstbuf;
if ( s == NULL || s->mode != 'w' ) {
biffAddf( NRRD, "%s: invalid stream or file mode", me );
*written = 0;
return 1;
}
/* If you google for "const correct zlib" or "zlib.h is not
const-correct" you'll find zlib mailing list discussions of how
zlib doesn't have all the consts that it should, and various code
examples of using multiple casts to hide the problem. Here's a
slow way that doesn't use mere casting to make the const go away */
memcpy( &nonconstbuf, &buf, sizeof( void* ) );
s->stream.next_in = ( Bytef* )nonconstbuf;
s->stream.avail_in = len;
while ( s->stream.avail_in != 0 ) {
if ( s->stream.avail_out == 0 ) {
s->stream.next_out = s->outbuf;
if ( fwrite( s->outbuf, 1, _NRRD_Z_BUFSIZE, s->file ) != _NRRD_Z_BUFSIZE ) {
s->z_err = Z_ERRNO;
biffAddf( NRRD, "%s: failed to write to file", me );
break;
}
s->stream.avail_out = _NRRD_Z_BUFSIZE;
}
s->z_err = deflate( &( s->stream ), Z_NO_FLUSH );
if ( s->z_err != Z_OK ) break;
}
s->crc = crc32( s->crc, ( const Bytef * )buf, len );
*written = len - s->stream.avail_in;
return 0;
}
/*
** _nrrdGzGetByte( )
**
** Reads a byte from a _NrrdGzStream. Updates next_in and avail_in.
** Returns EOF for end of file.
** IN assertion: the stream s has been sucessfully opened for reading.
*/
static int
456 _nrrdGzGetByte( _NrrdGzStream *s ) {
static const char me[]="_nrrdGzGetByte";
if ( s->z_eof ) return EOF;
if ( s->stream.avail_in == 0 ) {
errno = 0;
s->stream.avail_in = ( uInt )fread( s->inbuf, 1, _NRRD_Z_BUFSIZE, s->file );
if ( s->stream.avail_in == 0 ) {
s->z_eof = 1;
if ( ferror( s->file ) ) {
biffAddf( NRRD, "%s: failed to read from file", me );
s->z_err = Z_ERRNO;
}
return EOF;
}
s->stream.next_in = s->inbuf;
}
s->stream.avail_in--;
return *( s->stream.next_in )++;
}
/*
******** _nrrdGzCheckHeader( )
**
** Checks the gzip header of a _NrrdGzStream opened for reading. Sets
** the stream mode to transparent if the gzip magic header is not
** present; sets s->err to Z_DATA_ERROR if the magic header is present
** but the rest of the header is incorrect.
** IN assertion: the stream s has already been created sucessfully;
** s->stream.avail_in is zero for the first time, but may be non-zero
** for concatenated .gz files.
*/
static void
489 _nrrdGzCheckHeader( _NrrdGzStream *s ) {
static const char me[]="_nrrdGzCheckHeader";
int method; /* method byte */
int flags; /* flags byte */
uInt len;
int c;
/* Check the gzip magic header */
for ( len = 0; len < 2; len++ ) {
c = _nrrdGzGetByte( s );
if ( c != _nrrdGzMagic[len] ) {
if ( len != 0 ) s->stream.avail_in++, s->stream.next_in--;
if ( c != EOF ) {
s->stream.avail_in++, s->stream.next_in--;
s->transparent = 1;
}
s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
return;
}
}
method = _nrrdGzGetByte( s );
flags = _nrrdGzGetByte( s );
if ( method != Z_DEFLATED || ( flags & _NRRD_RESERVED ) != 0 ) {
biffAddf( NRRD, "%s: gzip compression method is not deflate", me );
s->z_err = Z_DATA_ERROR;
return;
}
/* Discard time, xflags and OS code: */
for ( len = 0; len < 6; len++ ) ( void )_nrrdGzGetByte( s );
if ( ( flags & _NRRD_EXTRA_FIELD ) != 0 ) { /* skip the extra field */
len = ( uInt )_nrrdGzGetByte( s );
len += ( ( uInt )_nrrdGzGetByte( s ) )<<8;
/* len is garbage if EOF but the loop below will quit anyway */
while ( len-- != 0 && _nrrdGzGetByte( s ) != EOF ) ;
}
if ( ( flags & _NRRD_ORIG_NAME ) != 0 ) { /* skip the original file name */
while ( ( c = _nrrdGzGetByte( s ) ) != 0 && c != EOF ) ;
}
if ( ( flags & _NRRD_COMMENT ) != 0 ) { /* skip the .gz file comment */
while ( ( c = _nrrdGzGetByte( s ) ) != 0 && c != EOF ) ;
}
if ( ( flags & _NRRD_HEAD_CRC ) != 0 ) { /* skip the header crc */
for ( len = 0; len < 2; len++ ) ( void )_nrrdGzGetByte( s );
}
s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
}
/*
** _nrrdGzDestroy( )
**
** Cleans up then free the given _NrrdGzStream. Returns a zlib error code.
** Try freeing in the reverse order of allocations. FILE* s->file is not
** closed. Because we didn't allocate it, we shouldn't delete it.
*/
static int
546 _nrrdGzDestroy( _NrrdGzStream *s ) {
static const char me[]="_nrrdGzDestroy";
int error = Z_OK;
if ( s == NULL ) {
biffAddf( NRRD, "%s: invalid stream", me );
return 1;
}
s->msg = ( char * )airFree( s->msg );
if ( s->stream.state != NULL ) {
if ( s->mode == 'w' ) {
error = deflateEnd( &( s->stream ) );
} else if ( s->mode == 'r' ) {
error = inflateEnd( &( s->stream ) );
}
}
if ( error != Z_OK ) {
biffAddf( NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG( error ) );
}
if ( s->z_err < 0 ) error = s->z_err;
if ( error != Z_OK ) {
biffAddf( NRRD, "%s: %s", me, _NRRD_GZ_ERR_MSG( error ) );
}
s->inbuf = ( Byte * )airFree( s->inbuf );
s->outbuf = ( Byte * )airFree( s->outbuf );
airFree( s ); /* avoiding unused value warnings, no NULL set */
return error != Z_OK;
}
/*
** _nrrdGzDoFlush( )
**
** Flushes all pending output into the compressed file. The parameter
** flush is the same as in the deflate( ) function.
*/
static int
582 _nrrdGzDoFlush( gzFile file, int flush ) {
static const char me[]="_nrrdGzDoFlush";
uInt len;
int done = 0;
_NrrdGzStream *s = ( _NrrdGzStream* )file;
if ( s == NULL || s->mode != 'w' ) {
biffAddf( NRRD, "%s: invalid stream or file mode", me );
return Z_STREAM_ERROR;
}
s->stream.avail_in = 0; /* should be zero already anyway */
for ( ;; ) {
len = _NRRD_Z_BUFSIZE - s->stream.avail_out;
if ( len != 0 ) {
if ( ( uInt )fwrite( s->outbuf, 1, len, s->file ) != len ) {
s->z_err = Z_ERRNO;
return Z_ERRNO;
}
s->stream.next_out = s->outbuf;
s->stream.avail_out = _NRRD_Z_BUFSIZE;
}
if ( done ) break;
s->z_err = deflate( &( s->stream ), flush );
/* Ignore the second of two consecutive flushes: */
if ( len == 0 && s->z_err == Z_BUF_ERROR ) s->z_err = Z_OK;
/* deflate has finished flushing only when it hasn't used up
* all the available space in the output buffer:
*/
done = ( s->stream.avail_out != 0 || s->z_err == Z_STREAM_END );
if ( s->z_err != Z_OK && s->z_err != Z_STREAM_END ) break;
}
return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
}
/*
** _nrrdGzPutLong( )
**
** Outputs a long in LSB order to the given file.
*/
static void
628 _nrrdGzPutLong( FILE* file, uLong x ) {
int n;
for ( n = 0; n < 4; n++ ) {
fputc( ( int )( x & 0xff ), file );
x >>= 8;
}
}
/*
** _nrrdGzGetLong( )
**
** Reads a long in LSB order from the given _NrrdGzStream.
** Sets z_err in case of error.
*/
static uLong
643 _nrrdGzGetLong( _NrrdGzStream *s ) {
uLong x = ( uLong )_nrrdGzGetByte( s );
int c;
x += ( ( uLong )_nrrdGzGetByte( s ) )<<8;
x += ( ( uLong )_nrrdGzGetByte( s ) )<<16;
c = _nrrdGzGetByte( s );
if ( c == EOF ) s->z_err = Z_DATA_ERROR;
x += ( ( uLong )c )<<24;
return x;
}
#endif /* TEEM_ZLIB */
/*
** random symbol to have in object file, even when Zlib not enabled
*/
int
661 _nrrdGzDummySymbol( void ) {
return 42;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/* ---------------------------- Nrrd ----------------------------- */
/*
** _nrrdHestNrrdParse( )
**
** Converts a filename into a nrrd for the sake of hest.
** There is no HestMaybeNrrdParse because this already does that:
** when we get an empty string, we give back a NULL pointer, and
** that is just fine
*/
int
38 _nrrdHestNrrdParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[] = "_nrrdHestNrrdParse", *nerr;
Nrrd **nrrdP;
airArray *mop;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
nrrdP = ( Nrrd ** )ptr;
if ( airStrlen( str ) ) {
mop = airMopNew( );
*nrrdP = nrrdNew( );
airMopAdd( mop, *nrrdP, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdLoad( *nrrdP, str, NULL ) ) {
airMopAdd( mop, nerr = biffGetDone( NRRD ), airFree, airMopOnError );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
airMopError( mop );
return ( strstr( err, "EOF" ) ? 2 : 1 );
}
airMopOkay( mop );
} else {
/* they gave us an empty string, we give back no nrrd,
but its not an error condition */
*nrrdP = NULL;
}
return 0;
}
hestCB
_nrrdHestNrrd = {
sizeof( Nrrd * ),
"nrrd",
_nrrdHestNrrdParse,
( airMopper )nrrdNuke
};
hestCB *
nrrdHestNrrd = &_nrrdHestNrrd;
/* ------------------------ NrrdKernelSpec -------------------------- */
int
81 _nrrdHestKernelSpecParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
NrrdKernelSpec **ksP;
char me[]="_nrrdHestKernelSpecParse", *nerr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
ksP = ( NrrdKernelSpec ** )ptr;
*ksP = nrrdKernelSpecNew( );
if ( nrrdKernelParse( &( ( *ksP )->kernel ), ( *ksP )->parm, str ) ) {
nerr = biffGetDone( NRRD );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
free( nerr );
return 1;
}
return 0;
}
hestCB
_nrrdHestKernelSpec = {
sizeof( NrrdKernelSpec* ),
"kernel specification",
_nrrdHestKernelSpecParse,
( airMopper )nrrdKernelSpecNix
};
hestCB *
nrrdHestKernelSpec = &_nrrdHestKernelSpec;
/* ------------------------ NrrdBoundarySpec -------------------------- */
int
114 _nrrdHestBoundarySpecParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
NrrdBoundarySpec **bsp;
char me[]="_nrrdHestBoundarySpecParse", *nerr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
bsp = ( NrrdBoundarySpec ** )ptr;
*bsp = nrrdBoundarySpecNew( );
if ( nrrdBoundarySpecParse( *bsp, str ) ) {
nerr = biffGetDone( NRRD );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
/* HEY: why not freeing bsp? */
free( nerr );
return 1;
}
return 0;
}
hestCB
_nrrdHestBoundarySpec = {
sizeof( NrrdBoundarySpec* ),
"boundary specification",
_nrrdHestBoundarySpecParse,
( airMopper )nrrdBoundarySpecNix
};
hestCB *
nrrdHestBoundarySpec = &_nrrdHestBoundarySpec;
/* --------------------------- NrrdIter ----------------------------- */
int
148 _nrrdLooksLikeANumber( char *str ) {
/* 0: -+ ( no restriction, but that's a little daft )
1: 0123456789 n > 0
2: . 0 <= n <= 1
3: eE 0 <= n <= 1
4: everything else 0 == n
*/
int count[5];
count[0] = count[1] = count[2] = count[3] = count[4] = 0;
while ( *str ) {
int lwc, cc = *str;
lwc = tolower( cc );
switch ( lwc ) {
case '-': case '+':
count[0]++;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
count[1]++;
break;
case '.':
count[2]++;
break;
case 'e':
count[3]++;
break;
default:
count[4]++;
break;
}
str++;
}
if ( count[1] > 0 &&
AIR_IN_CL( 0, count[2], 1 ) &&
AIR_IN_CL( 0, count[3], 1 ) &&
count[4] == 0 ) {
return AIR_TRUE;
} else {
return AIR_FALSE;
}
}
int
192 _nrrdHestIterParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="_nrrdHestIterParse", *nerr;
Nrrd *nrrd;
NrrdIter **iterP;
airArray *mop;
double val;
int ret;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
iterP = ( NrrdIter ** )ptr;
mop = airMopNew( );
*iterP = nrrdIterNew( );
airMopAdd( mop, *iterP, ( airMopper )nrrdIterNix, airMopOnError );
/* the challenge here is determining if a given string represents a
filename or a number. Obviously there are cases where it could
be both, so we'll assume its a filename first. Because: there
are different ways of writing the same number, such as "3" -->
"+3", "3.1" --> "3.10", so someone accidently using the file when
they mean to use the number has easy ways of changing the number
representation, since these trivial transformations will probably
not all result in valid filenames. Another problem is that one
really wants a general robust test to see if a given string is a
valid number representation AND NOTHING BUT THAT, and sscanf( ) is
not that test. In any case, if there are to be improved smarts
about this matter, they need to be implemented below and nowhere
else. */
nrrd = nrrdNew( );
ret = nrrdLoad( nrrd, str, NULL );
if ( !ret ) {
/* first attempt at nrrdLoad( ) was SUCCESSFUL */
nrrdIterSetOwnNrrd( *iterP, nrrd );
} else {
/* so it didn't load as a nrrd- if its because fopen( ) failed,
then we'll try it as a number. If its for another reason,
then we complain */
nrrdNuke( nrrd );
if ( 2 != ret ) {
/* it failed because of something besides the fopen( ), so complain */
nerr = biffGetDone( NRRD );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
airMopError( mop ); return 1;
} else {
/* fopen( ) failed, so it probably wasn't meant to be a filename */
free( biffGetDone( NRRD ) );
ret = airSingleSscanf( str, "%lf", &val );
if ( _nrrdLooksLikeANumber( str )
|| ( 1 == ret && ( !AIR_EXISTS( val )
|| AIR_ABS( AIR_PI - val ) < 0.0001
|| AIR_ABS( -AIR_PI - val ) < 0.0001 ) ) ) {
/* either it patently looks like a number, or,
it already parsed as a number and it is a special value */
if ( 1 == ret ) {
nrrdIterSetValue( *iterP, val );
} else {
/* oh, this is bad. */
fprintf( stderr, "%s: PANIC, is it a number or not?", me );
exit( 1 );
}
} else {
/* it doesn't look like a number, but the fopen failed, so
we'll let it fail again and pass back the error messages */
if ( nrrdLoad( nrrd = nrrdNew( ), str, NULL ) ) {
nerr = biffGetDone( NRRD );
airStrcpy( err, AIR_STRLEN_HUGE, nerr );
airMopError( mop ); return 1;
} else {
/* what the hell? */
fprintf( stderr, "%s: PANIC, is it a nrrd or not?", me );
exit( 1 );
}
}
}
}
airMopAdd( mop, iterP, ( airMopper )airSetNull, airMopOnError );
airMopOkay( mop );
return 0;
}
hestCB
_nrrdHestIter = {
sizeof( NrrdIter * ),
"nrrd/value",
_nrrdHestIterParse,
( airMopper )nrrdIterNix
};
hestCB *
nrrdHestIter = &_nrrdHestIter;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdHisto( )
**
** makes a 1D histogram of a given size and type
**
** pre-NrrdRange policy:
** this looks at nin->min and nin->max to see if they are both non-NaN.
** If so, it uses these as the range of the histogram, otherwise it
** finds the min and max present in the volume. If nin->min and nin->max
** are being used as the histogram range, then values which fall outside
** this are ignored ( they don't contribute to the histogram ).
**
** post-NrrdRange policy:
*/
int
42 nrrdHisto( Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
const Nrrd *nwght, size_t bins, int type ) {
static const char me[]="nrrdHisto", func[]="histo";
size_t I, num, idx;
airArray *mop;
NrrdRange *range;
double min, max, eps, val, count, incr, ( *lup )( const void *v, size_t I );
if ( !( nin && nout ) ) {
/* _range and nwght can be NULL */
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( !( bins > 0 ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: bins value ( %s ) invalid", me,
airSprintSize_t( stmp, bins ) );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) || nrrdTypeBlock == type ) {
biffAddf( NRRD, "%s: invalid nrrd type %d", me, type );
return 1;
}
if ( nwght ) {
if ( nout==nwght ) {
biffAddf( NRRD, "%s: nout==nwght disallowed", me );
return 1;
}
if ( nrrdTypeBlock == nwght->type ) {
biffAddf( NRRD, "%s: nwght type %s invalid", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !nrrdSameSize( nin, nwght, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: nwght size mismatch with nin", me );
return 1;
}
lup = nrrdDLookup[nwght->type];
} else {
lup = NULL;
}
if ( nrrdMaybeAlloc_va( nout, type, 1, bins ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: failed to alloc histo array ( len %s )", me,
airSprintSize_t( stmp, bins ) );
return 1;
}
mop = airMopNew( );
/* nout->axis[0].size set */
nout->axis[0].spacing = AIR_NAN;
nout->axis[0].thickness = AIR_NAN;
if ( nout && AIR_EXISTS( nout->axis[0].min ) && AIR_EXISTS( nout->axis[0].max ) ) {
/* HEY: total hack to externally nail down min and max of histogram:
use the min and max already set on axis[0] */
/* HEY: shouldn't this blatent hack be further restricted by also
checking the existence of range->min and range->max ? */
min = nout->axis[0].min;
max = nout->axis[0].max;
} else {
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
min = range->min;
max = range->max;
nout->axis[0].min = min;
nout->axis[0].max = max;
}
eps = ( min == max ? 1.0 : 0.0 );
nout->axis[0].center = nrrdCenterCell;
/* nout->axis[0].label set below */
/* make histogram */
num = nrrdElementNumber( nin );
for ( I=0; I<num; I++ ) {
val = nrrdDLookup[nin->type]( nin->data, I );
if ( AIR_EXISTS( val ) ) {
if ( val < min || val > max+eps ) {
/* value is outside range; ignore it */
continue;
}
if ( AIR_IN_CL( min, val, max ) ) {
idx = airIndex( min, val, max+eps, AIR_CAST( unsigned int, bins ) );
/*
printf( "!%s: %d: index( %g, %g, %g, %d ) = %d\n",
me, ( int )I, min, val, max, bins, idx );
*/
/* count is a double in order to simplify clamping the
hit values to the representable range for nout->type */
count = nrrdDLookup[nout->type]( nout->data, idx );
incr = nwght ? lup( nwght->data, I ) : 1;
count = nrrdDClamp[nout->type]( count + incr );
nrrdDInsert[nout->type]( nout->data, idx, count );
}
}
}
if ( nrrdContentSet_va( nout, func, nin, "%d", bins ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
nout->axis[0].label = ( char * )airFree( nout->axis[0].label );
nout->axis[0].label = ( char * )airStrdup( nout->content );
if ( !nrrdStateKindNoop ) {
nout->axis[0].kind = nrrdKindDomain;
}
airMopOkay( mop );
return 0;
}
int
162 nrrdHistoCheck( const Nrrd *nhist ) {
static const char me[]="nrrdHistoCheck";
if ( !nhist ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nhist->type ) {
biffAddf( NRRD, "%s: has non-scalar %s type",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nrrdHasNonExist( nhist ) ) {
biffAddf( NRRD, "%s: has non-existent values", me );
return 1;
}
if ( 1 != nhist->dim ) {
biffAddf( NRRD, "%s: dim == %u != 1",
me, nhist->dim );
return 1;
}
if ( !( nhist->axis[0].size > 1 ) ) {
biffAddf( NRRD, "%s: has single sample along sole axis", me );
return 1;
}
return 0;
}
int
192 nrrdHistoDraw( Nrrd *nout, const Nrrd *nin,
size_t sy, int showLog, double max ) {
static const char me[]="nrrdHistoDraw", func[]="dhisto";
char cmt[AIR_STRLEN_MED], stmp[AIR_STRLEN_SMALL];
unsigned int ki, numticks, *linY, *logY, tick, *ticks;
double hits, maxhits, usemaxhits;
unsigned char *pgmData;
airArray *mop;
int E;
size_t sx, xi, yi, maxhitidx;
if ( !( nin && nout && sy > 0 ) ) {
biffAddf( NRRD, "%s: invalid args", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( nrrdHistoCheck( nin ) ) {
biffAddf( NRRD, "%s: input nrrd not a histogram", me );
return 1;
}
sx = nin->axis[0].size;
nrrdBasicInfoInit( nout, NRRD_BASIC_INFO_DATA_BIT );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeUChar, 2, sx, sy ) ) {
biffAddf( NRRD, "%s: failed to allocate histogram image", me );
return 1;
}
/* perhaps I should be using nrrdAxisInfoCopy */
nout->axis[0].spacing = nout->axis[1].spacing = AIR_NAN;
nout->axis[0].thickness = nout->axis[1].thickness = AIR_NAN;
nout->axis[0].min = nin->axis[0].min;
nout->axis[0].max = nin->axis[0].max;
nout->axis[0].center = nout->axis[1].center = nrrdCenterCell;
nout->axis[0].label = ( char * )airStrdup( nin->axis[0].label );
nout->axis[1].label = ( char * )airFree( nout->axis[1].label );
pgmData = ( unsigned char * )nout->data;
maxhits = 0.0;
maxhitidx = 0;
for ( xi=0; xi<sx; xi++ ) {
hits = nrrdDLookup[nin->type]( nin->data, xi );
if ( maxhits < hits ) {
maxhits = hits;
maxhitidx = xi;
}
}
if ( AIR_EXISTS( max ) && max > 0 ) {
usemaxhits = max;
} else {
usemaxhits = maxhits;
}
nout->axis[1].min = usemaxhits;
nout->axis[1].max = 0;
numticks = AIR_CAST( unsigned int, log10( usemaxhits + 1 ) );
mop = airMopNew( );
ticks = AIR_CALLOC( numticks, unsigned int );
airMopMem( mop, &ticks, airMopAlways );
linY = AIR_CALLOC( sx, unsigned int );
airMopMem( mop, &linY, airMopAlways );
logY = AIR_CALLOC( sx, unsigned int );
airMopMem( mop, &logY, airMopAlways );
if ( !( ticks && linY && logY ) ) {
biffAddf( NRRD, "%s: failed to allocate temp arrays", me );
airMopError( mop ); return 1;
}
for ( ki=0; ki<numticks; ki++ ) {
ticks[ki] = airIndex( 0, log10( pow( 10, ki+1 ) + 1 ), log10( usemaxhits+1 ),
AIR_CAST( unsigned int, sy ) );
}
for ( xi=0; xi<sx; xi++ ) {
hits = nrrdDLookup[nin->type]( nin->data, xi );
linY[xi] = airIndex( 0, hits, usemaxhits,
AIR_CAST( unsigned int, sy ) );
logY[xi] = airIndex( 0, log10( hits+1 ), log10( usemaxhits+1 ),
AIR_CAST( unsigned int, sy ) );
/* printf( "%d -> %d, %d", x, linY[x], logY[x] ); */
}
for ( yi=0; yi<sy; yi++ ) {
tick = 0;
for ( ki=0; ki<numticks; ki++ )
tick |= ticks[ki] == yi;
for ( xi=0; xi<sx; xi++ ) {
pgmData[xi + sx*( sy-1-yi )] =
( 2 == showLog /* HACK: draw log curve, but not log tick marks */
? ( yi >= logY[xi]
? 0 /* above log curve */
: ( yi >= linY[xi]
? 128 /* below log curve, above normal curve */
: 255 /* below log curve, below normal curve */
)
)
: ( !showLog
? ( yi >= linY[xi] ? 0 : 255 )
: ( yi >= logY[xi] /* above log curve */
? ( !tick ? 0 /* not on tick mark */
: 255 ) /* on tick mark */
: ( yi >= linY[xi] /* below log curve, above normal curve */
? ( !tick ? 128 /* not on tick mark */
: 0 ) /* on tick mark */
:255 /* below log curve, below normal curve */
)
)
)
);
}
}
E = 0;
sprintf( cmt, "min value: %g\n", nout->axis[0].min );
if ( !E ) E |= nrrdCommentAdd( nout, cmt );
sprintf( cmt, "max value: %g\n", nout->axis[0].max );
if ( !E ) E |= nrrdCommentAdd( nout, cmt );
sprintf( cmt, "max hits: %g, in bin %s, around value %g\n", maxhits,
airSprintSize_t( stmp, maxhitidx ),
nrrdAxisInfoPos( nout, 0, AIR_CAST( double, maxhitidx ) ) );
if ( !E ) E |= nrrdCommentAdd( nout, cmt );
if ( !E ) E |= nrrdContentSet_va( nout, func, nin, "%s",
airSprintSize_t( stmp, sy ) );
if ( E ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* bye */
airMopOkay( mop );
return 0;
}
/*
******** nrrdHistoAxis
**
** replace scanlines along one scanline with a histogram of the scanline
**
** this looks at nin->min and nin->max to see if they are both non-NaN.
** If so, it uses these as the range of the histogram, otherwise it
** finds the min and max present in the volume
**
** By its very nature, and by the simplicity of this implemention,
** this can be a slow process due to terrible memory locality. User
** may want to permute axes before and after this, but that can be
** slow too...
*/
int
335 nrrdHistoAxis( Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
unsigned int hax, size_t bins, int type ) {
static const char me[]="nrrdHistoAxis", func[]="histax";
int map[NRRD_DIM_MAX];
unsigned int ai, hidx;
size_t szIn[NRRD_DIM_MAX], szOut[NRRD_DIM_MAX], size[NRRD_DIM_MAX],
coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX];
size_t I, hI, num;
double val, count;
airArray *mop;
NrrdRange *range;
if ( !( nin && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( !( bins > 0 ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: bins value ( %s ) invalid", me,
airSprintSize_t( stmp, bins ) );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) || nrrdTypeBlock == type ) {
biffAddf( NRRD, "%s: invalid nrrd type %d", me, type );
return 1;
}
if ( !( hax <= nin->dim-1 ) ) {
biffAddf( NRRD, "%s: axis %d is not in range [0, %d]",
me, hax, nin->dim-1 );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
size[hax] = bins;
if ( nrrdMaybeAlloc_nva( nout, type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: failed to alloc output nrrd", me );
airMopError( mop ); return 1;
}
/* copy axis information */
for ( ai=0; ai<nin->dim; ai++ ) {
map[ai] = ai != hax ? ( int )ai : -1;
}
nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_NONE );
/* axis hax now has to be set manually */
nout->axis[hax].size = bins;
nout->axis[hax].spacing = AIR_NAN; /* min and max convey the information */
nout->axis[hax].thickness = AIR_NAN;
nout->axis[hax].min = range->min;
nout->axis[hax].max = range->max;
nout->axis[hax].center = nrrdCenterCell;
if ( nin->axis[hax].label ) {
nout->axis[hax].label = AIR_CALLOC( strlen( "histax( )" )
+ strlen( nin->axis[hax].label )
+ 1, char );
if ( nout->axis[hax].label ) {
sprintf( nout->axis[hax].label, "histax( %s )", nin->axis[hax].label );
} else {
biffAddf( NRRD, "%s: couldn't allocate output label", me );
airMopError( mop ); return 1;
}
} else {
nout->axis[hax].label = NULL;
}
if ( !nrrdStateKindNoop ) {
nout->axis[hax].kind = nrrdKindDomain;
}
/* the skinny: we traverse the input samples in linear order, and
increment the bin in the histogram for the scanline we're in.
This is not terribly clever, and the memory locality is a
disaster */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, szIn );
nrrdAxisInfoGet_nva( nout, nrrdAxisInfoSize, szOut );
memset( coordIn, 0, NRRD_DIM_MAX*sizeof( size_t ) );
num = nrrdElementNumber( nin );
for ( I=0; I<num; I++ ) {
/* get input nrrd value and compute its histogram index */
val = nrrdDLookup[nin->type]( nin->data, I );
if ( AIR_EXISTS( val ) && AIR_IN_CL( range->min, val, range->max ) ) {
hidx = airIndex( range->min, val, range->max, AIR_CAST( unsigned int, bins ) );
memcpy( coordOut, coordIn, nin->dim*sizeof( size_t ) );
coordOut[hax] = ( unsigned int )hidx;
NRRD_INDEX_GEN( hI, coordOut, szOut, nout->dim );
count = nrrdDLookup[nout->type]( nout->data, hI );
count = nrrdDClamp[nout->type]( count + 1 );
nrrdDInsert[nout->type]( nout->data, hI, count );
}
NRRD_COORD_INCR( coordIn, szIn, nin->dim, 0 );
}
if ( nrrdContentSet_va( nout, func, nin, "%d, %d", hax, bins ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
nrrdBasicInfoInit( nout, ( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| 0 /* what? */ ) );
airMopOkay( mop );
return 0;
}
int
450 nrrdHistoJoint( Nrrd *nout, const Nrrd *const *nin,
const NrrdRange *const *_range, unsigned int numNin,
const Nrrd *nwght, const size_t *bins,
int type, const int *clamp ) {
static const char me[]="nrrdHistoJoint", func[]="jhisto";
int skip, hadContent;
double val, count, incr, ( *lup )( const void *v, size_t I ),
rmin[NRRD_DIM_MAX], rmax[NRRD_DIM_MAX];
size_t Iin, Iout, numEl, coord[NRRD_DIM_MAX], totalContentStrlen;
airArray *mop;
NrrdRange **range;
unsigned int nii, ai;
/* error checking */
/* nwght can be NULL -> weighting is constant 1.0 */
if ( !( nout && nin && bins && clamp ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, %p, %p )", me,
AIR_VOIDP( nout ), AIR_CVOIDP( nin ),
AIR_CVOIDP( bins ), AIR_CVOIDP( clamp ) );
return 1;
}
if ( !( numNin >= 1 ) ) {
biffAddf( NRRD, "%s: need numNin >= 1 ( not %d )", me, numNin );
return 1;
}
if ( numNin > NRRD_DIM_MAX ) {
biffAddf( NRRD, "%s: can only deal with up to %d nrrds ( not %d )", me,
NRRD_DIM_MAX, numNin );
return 1;
}
for ( nii=0; nii<numNin; nii++ ) {
if ( !( nin[nii] ) ) {
biffAddf( NRRD, "%s: input nrrd #%u NULL", me, nii );
return 1;
}
if ( nout==nin[nii] ) {
biffAddf( NRRD, "%s: nout==nin[%d] disallowed", me, nii );
return 1;
}
if ( nrrdTypeBlock == nin[nii]->type ) {
biffAddf( NRRD, "%s: nin[%d] type %s invalid", me, nii,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
}
if ( airEnumValCheck( nrrdType, type ) || nrrdTypeBlock == type ) {
biffAddf( NRRD, "%s: invalid nrrd type %d", me, type );
return 1;
}
mop = airMopNew( );
range = AIR_CALLOC( numNin, NrrdRange* );
airMopAdd( mop, range, airFree, airMopAlways );
for ( ai=0; ai<numNin; ai++ ) {
if ( _range && _range[ai] ) {
range[ai] = nrrdRangeCopy( _range[ai] );
nrrdRangeSafeSet( range[ai], nin[ai], nrrdBlind8BitRangeState );
} else {
range[ai] = nrrdRangeNewSet( nin[ai], nrrdBlind8BitRangeState );
}
airMopAdd( mop, range[ai], ( airMopper )nrrdRangeNix, airMopAlways );
}
for ( ai=0; ai<numNin; ai++ ) {
if ( !nin[ai] ) {
biffAddf( NRRD, "%s: input nrrd[%d] NULL", me, ai );
return 1;
}
if ( !( bins[ai] >= 1 ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: need bins[%u] >= 1 ( not %s )", me, ai,
airSprintSize_t( stmp, bins[ai] ) );
return 1;
}
if ( ai && !nrrdSameSize( nin[0], nin[ai], AIR_TRUE ) ) {
biffAddf( NRRD, "%s: nin[0] ( n1 ) mismatch with nin[%u] ( n2 )", me, ai );
return 1;
}
}
/* check nwght */
if ( nwght ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
if ( nout==nwght ) {
biffAddf( NRRD, "%s: nout==nwght disallowed", me );
return 1;
}
if ( nrrdTypeBlock == nwght->type ) {
biffAddf( NRRD, "%s: nwght type %s invalid", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nrrdElementNumber( nin[0] ) != nrrdElementNumber( nwght ) ) {
biffAddf( NRRD, "%s: element # in nwght %s != nin[0] %s", me,
airSprintSize_t( stmp2, nrrdElementNumber( nin[0] ) ),
airSprintSize_t( stmp1, nrrdElementNumber( nwght ) ) );
return 1;
}
lup = nrrdDLookup[nwght->type];
} else {
lup = NULL;
}
/* allocate output nrrd */
if ( nrrdMaybeAlloc_nva( nout, type, numNin, bins ) ) {
biffAddf( NRRD, "%s: couldn't allocate output histogram", me );
return 1;
}
hadContent = 0;
totalContentStrlen = 0;
for ( ai=0; ai<numNin; ai++ ) {
nout->axis[ai].size = bins[ai];
nout->axis[ai].spacing = AIR_NAN;
nout->axis[ai].thickness = AIR_NAN;
nout->axis[ai].min = range[ai]->min;
nout->axis[ai].max = range[ai]->max;
nout->axis[ai].center = nrrdCenterCell;
if ( !nrrdStateKindNoop ) {
nout->axis[ai].kind = nrrdKindDomain;
}
if ( nin[ai]->content ) {
hadContent = 1;
totalContentStrlen += strlen( nin[ai]->content );
nout->axis[ai].label = AIR_CALLOC( strlen( "histo( , )" )
+ strlen( nin[ai]->content )
+ 11
+ 1, char );
if ( nout->axis[ai].label ) {
char stmp[AIR_STRLEN_SMALL];
sprintf( nout->axis[ai].label, "histo( %s, %s )", nin[ai]->content,
airSprintSize_t( stmp, bins[ai] ) );
} else {
biffAddf( NRRD, "%s: couldn't allocate output label #%u", me, ai );
return 1;
}
} else {
nout->axis[ai].label = ( char * )airFree( nout->axis[ai].label );
totalContentStrlen += 2;
}
}
/* the skinny */
for ( ai=0; ai<numNin; ai++ ) {
if ( range[ai]->min <= range[ai]->max ) {
rmin[ai] = range[ai]->min;
rmax[ai] = range[ai]->max;
} else {
rmin[ai] = range[ai]->max;
rmax[ai] = range[ai]->min;
}
}
numEl = nrrdElementNumber( nin[0] );
for ( Iin=0; Iin<numEl; Iin++ ) {
skip = 0;
for ( ai=0; ai<numNin; ai++ ) {
val = nrrdDLookup[nin[ai]->type]( nin[ai]->data, Iin );
if ( !AIR_EXISTS( val ) ) {
/* coordinate d in the joint histo can't be determined
if nin[ai] has a non-existent value here */
skip = 1;
break;
}
if ( !AIR_IN_CL( rmin[ai], val, rmax[ai] ) ) {
if ( clamp[ai] ) {
val = AIR_CLAMP( rmin[ai], val, rmax[ai] );
} else {
skip = 1;
break;
}
}
coord[ai] = AIR_CAST( size_t, airIndexClampULL( range[ai]->min,
val,
range[ai]->max,
bins[ai] ) );
}
if ( skip ) {
continue;
}
NRRD_INDEX_GEN( Iout, coord, bins, numNin );
count = nrrdDLookup[nout->type]( nout->data, Iout );
incr = nwght ? lup( nwght->data, Iin ) : 1.0;
count = nrrdDClamp[nout->type]( count + incr );
nrrdDInsert[nout->type]( nout->data, Iout, count );
}
/* HEY: switch to nrrdContentSet_va? */
if ( hadContent ) {
nout->content = AIR_CALLOC( strlen( func ) + strlen( "( )" )
+ numNin*strlen( ", " )
+ totalContentStrlen
+ 1, char );
if ( nout->content ) {
size_t len;
sprintf( nout->content, "%s( ", func );
for ( ai=0; ai<numNin; ai++ ) {
len = strlen( nout->content );
strcpy( nout->content + len,
nin[ai]->content ? nin[ai]->content : "?" );
len = strlen( nout->content );
nout->content[len] = ai < numNin-1 ? ', ' : ' )';
}
nout->content[len+1] = '\0';
} else {
biffAddf( NRRD, "%s: couldn't allocate output content", me );
return 1;
}
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdHistoThresholdOtsu
**
** does simple Otsu tresholding of a histogram, with a variable exponont.
** When "expo" is 2.0, it computes variance; lower values probably represent
** greater insensitivities to outliers. Idea from ...
*/
int
668 nrrdHistoThresholdOtsu( double *threshP, const Nrrd *_nhist, double expo ) {
static const char me[]="nrrdHistoThresholdOtsu";
unsigned int histLen, histIdx, maxIdx;
Nrrd *nhist, *nbvar;
double *hist, *bvar, thresh, num0, num1, mean0, mean1,
onum0, onum1, omean0, omean1, max;
airArray *mop;
if ( !( threshP && _nhist ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdHistoCheck( _nhist ) ) {
biffAddf( NRRD, "%s: input nrrd not a histogram", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nhist = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nbvar = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nhist, _nhist, nrrdTypeDouble )
|| nrrdCopy( nbvar, nhist ) ) {
biffAddf( NRRD, "%s: making local copies", me );
airMopError( mop ); return 1;
}
hist = AIR_CAST( double*, nhist->data );
bvar = AIR_CAST( double*, nbvar->data );
histLen = AIR_CAST( unsigned int, nhist->axis[0].size );
num1 = mean1 = 0;
for ( histIdx=0; histIdx<histLen; histIdx++ ) {
num1 += hist[histIdx];
mean1 += hist[histIdx]*histIdx;
}
if ( num1 ) {
num0 = 0;
mean0 = 0;
mean1 /= num1;
for ( histIdx=0; histIdx<histLen; histIdx++ ) {
if ( histIdx ) {
onum0 = num0;
onum1 = num1;
omean0 = mean0;
omean1 = mean1;
num0 = onum0 + hist[histIdx-1];
num1 = onum1 - hist[histIdx-1];
mean0 = ( omean0*onum0 + hist[histIdx-1]*( histIdx-1 ) ) / num0;
mean1 = ( omean1*onum1 - hist[histIdx-1]*( histIdx-1 ) ) / num1;
}
bvar[histIdx] = num0*num1*airSgnPow( mean1 - mean0, expo );
}
max = bvar[0];
maxIdx = 0;
for ( histIdx=1; histIdx<histLen; histIdx++ ) {
if ( bvar[histIdx] > max ) {
max = bvar[histIdx];
maxIdx = histIdx;
}
}
thresh = maxIdx;
} else {
thresh = histLen/2;
}
if ( AIR_EXISTS( nhist->axis[0].min ) && AIR_EXISTS( nhist->axis[0].max ) ) {
thresh = NRRD_CELL_POS( nhist->axis[0].min, nhist->axis[0].max,
histLen, thresh );
}
*threshP = thresh;
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
NrrdIter *
28 nrrdIterNew( ) {
NrrdIter *iter;
if ( ( iter = ( NrrdIter * )calloc( 1, sizeof( NrrdIter ) ) ) ) {
iter->nrrd = NULL;
iter->ownNrrd = NULL;
iter->val = AIR_NAN;
iter->size = 0;
iter->data = NULL;
iter->left = 0;
iter->load = NULL;
}
return iter;
}
void
44 nrrdIterSetValue( NrrdIter *iter, double val ) {
if ( iter ) {
iter->nrrd = NULL;
iter->ownNrrd = iter->ownNrrd ? nrrdNuke( iter->ownNrrd ) : NULL;
iter->val = val;
iter->size = nrrdTypeSize[nrrdTypeDouble];
iter->data = ( char* )&( iter->val );
iter->left = 0;
iter->load = nrrdDLoad[nrrdTypeDouble];
}
return;
}
void
59 nrrdIterSetNrrd( NrrdIter *iter, const Nrrd *nrrd ) {
if ( iter && nrrd && nrrd->data ) {
if ( nrrdTypeBlock == nrrd->type ) {
/* we can't deal */
nrrdIterSetValue( iter, AIR_NAN );
return;
}
iter->nrrd = nrrd;
iter->ownNrrd = iter->ownNrrd ? nrrdNuke( iter->ownNrrd ) : NULL;
iter->val = AIR_NAN;
iter->size = nrrdTypeSize[nrrd->type];
iter->data = ( char * )nrrd->data;
iter->left = nrrdElementNumber( nrrd )-1;
iter->load = nrrdDLoad[nrrd->type];
}
return;
}
/*
** formerly known as nrrdIterSetNrrd
*/
void
82 nrrdIterSetOwnNrrd( NrrdIter *iter, Nrrd *nrrd ) {
if ( iter && nrrd && nrrd->data ) {
if ( nrrdTypeBlock == nrrd->type ) {
/* we can't deal */
nrrdIterSetValue( iter, AIR_NAN );
return;
}
iter->nrrd = NULL;
iter->ownNrrd = iter->ownNrrd ? nrrdNuke( iter->ownNrrd ) : NULL;
iter->ownNrrd = nrrd;
iter->val = AIR_NAN;
iter->size = nrrdTypeSize[nrrd->type];
iter->data = ( char * )nrrd->data;
iter->left = nrrdElementNumber( nrrd )-1;
iter->load = nrrdDLoad[nrrd->type];
}
return;
}
double
103 nrrdIterValue( NrrdIter *iter ) {
double ret = 0.0;
if ( iter ) {
ret = iter->load( iter->data );
if ( iter->nrrd || iter->ownNrrd ) {
iter->data += iter->size;
if ( iter->left ) {
iter->left -= 1;
} else {
iter->data = ( char * )( _NRRD_ITER_NRRD( iter )->data );
iter->left = nrrdElementNumber( _NRRD_ITER_NRRD( iter ) )-1;
}
}
}
return ret;
}
/*
******** nrrdIterContent( )
**
** ALLOCATES a string that is either the nrrd's content ( or
** nrrdStateUnknownContent ) or a string version of the value; useful
** for when you's use the "content" of a nrrd
*/
char *
129 nrrdIterContent( NrrdIter *iter ) {
char *ret, buff[AIR_STRLEN_SMALL];
ret = NULL;
if ( iter ) {
if ( _NRRD_ITER_NRRD( iter ) ) {
ret = _nrrdContentGet( _NRRD_ITER_NRRD( iter ) );
} else {
airSinglePrintf( NULL, buff, "%g", iter->val );
ret = airStrdup( buff );
}
}
return ret;
}
NrrdIter *
145 nrrdIterNix( NrrdIter *iter ) {
if ( iter ) {
if ( iter->ownNrrd ) {
iter->ownNrrd = nrrdNuke( iter->ownNrrd );
}
free( iter );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
/*
** summary of information about how the kernel parameter vector is set:
** Note that, annoyingly, nrrdKernelUsesScale ( at end of this file )
** has to be updated to record which kernels ( including their derivatives )
** use parm[0] for scale.
numParm parm[0] parm[1] parm[2]
nrrdKernelHann 2 scale cut-off
nrrdKernelBlackman 2 scale cut-off
nrrdKernelCatmullRom 0
nrrdKernelBSpline3 0
nrrdKernelBSpline3ApproxInverse 0
nrrdKernelBSpline5 0
nrrdKernelBSpline5ApproxInverse 0
nrrdKernelBSpline7 0
nrrdKernelBSpline7ApproxInverse 0
nrrdKernelZero 1 scale
nrrdKernelBox 1 scale
nrrdKernelCatmullRomSupportDebug 1 support
nrrdKernelBoxSupportDebug 1 support
nrrdKernelCos4SupportDebug 1 support
nrrdKernelCheap 1 scale
nrrdKernelHermiteScaleSpaceFlag 0
nrrdKernelTent 1 scale
nrrdKernelForwDiff 1 scale
nrrdKernelCentDiff 1 scale
nrrdKernelBCCubic 3 scale B C
nrrdKernelAQuartic 2 scale A
nrrdKernelC3Quintic 0
nrrdKernelC4Hexic 0
nrrdKernelC4HexicApproxInverse 0
nrrdKernelC5Septic 0
nrrdKernelGaussian 2 sigma cut-off
nrrdKernelDiscreteGaussian 2 sigma cut-off
nrrdKernelTMF[][][] 1 a
** Note that when parm[0] is named "scale", that parameter is optional,
** and the default is 1.0, when given in string form
** E.g. "tent" is understood as "tent:1",
** but "gauss:4" isn't complete and won't parse; while "gauss:1, 4" is good
** See note above about nrrdKernelUsesScale ( at end of this file )
*/
/* these functions replace what had been a lot of
identical functions for similar kernels */
static double
73 returnZero( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double
79 returnOne( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
static double
85 returnTwo( const double *parm ) {
AIR_UNUSED( parm );
return 2.0;
}
static double
91 returnThree( const double *parm ) {
AIR_UNUSED( parm );
return 3.0;
}
static double
97 returnFour( const double *parm ) {
AIR_UNUSED( parm );
return 4.0;
}
/* ------------------------------------------------------------ */
/* learned: if you copy/paste the macros for these kernels into
** other code, you *have* to make sure that the arguments for the
** kernels that are supposed to be reals, are not passed as an
** integral type ( had this problem trying to re-use _BCCUBIC
** with constant B="1" and C="0" and had trouble figuring out
** why the kernel was give garbage results
*/
/* the "zero" kernel is here more as a template than for anything else
( as well as when you need the derivative of nrrdKernelForwDiff or
any other piece-wise constant kernels )
In particular the support method is pretty silly. */
#define _ZERO( x ) 0
static double
120 _nrrdZeroSup( const double *parm ) {
double S;
S = parm[0];
return S;
}
static double
128 _nrrdZero1_d( double x, const double *parm ) {
double S;
S = parm[0];
x = AIR_ABS( x )/S;
return _ZERO( x )/S;
}
static float
137 _nrrdZero1_f( float x, const double *parm ) {
float S;
S = AIR_CAST( float, parm[0] );
x = AIR_ABS( x )/S;
return _ZERO( x )/S;
}
static void
146 _nrrdZeroN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t;
size_t i;
S = parm[0];
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = _ZERO( t )/S;
}
}
static void
159 _nrrdZeroN_f( float *f, const float *x, size_t len, const double *parm ) {
float t, S;
size_t i;
S = AIR_CAST( float, parm[0] );
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = _ZERO( t )/S;
}
}
static NrrdKernel
_nrrdKernelZero = {
"zero",
1, _nrrdZeroSup, returnZero,
_nrrdZero1_f, _nrrdZeroN_f, _nrrdZero1_d, _nrrdZeroN_d
};
176 NrrdKernel *const
nrrdKernelZero = &_nrrdKernelZero;
/* ------------------------------------------------------------ */
#define _BOX( x ) ( x > 0.5 ? 0 : ( x < 0.5 ? 1 : 0.5 ) )
static double
184 _nrrdBoxSup( const double *parm ) {
double S;
S = parm[0];
/* adding the 0.5 is to ensure that weights computed within the
support really do catch all the non-zero values */
return S/2 + 0.5;
}
static double
194 _nrrdBox1_d( double x, const double *parm ) {
double S;
S = parm[0];
x = AIR_ABS( x )/S;
return _BOX( x )/S;
}
static float
203 _nrrdBox1_f( float x, const double *parm ) {
float S;
S = AIR_CAST( float, parm[0] );
x = AIR_ABS( x )/S;
return AIR_CAST( float, _BOX( x )/S );
}
static void
212 _nrrdBoxN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t;
size_t i;
S = parm[0];
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = _BOX( t )/S;
}
}
static void
225 _nrrdBoxN_f( float *f, const float *x, size_t len, const double *parm ) {
float t, S;
size_t i;
S = AIR_CAST( float, parm[0] );
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = AIR_CAST( float, _BOX( t )/S );
}
}
static NrrdKernel
_nrrdKernelBox = {
"box",
1, _nrrdBoxSup, returnOne,
_nrrdBox1_f, _nrrdBoxN_f, _nrrdBox1_d, _nrrdBoxN_d
};
242 NrrdKernel *const
nrrdKernelBox = &_nrrdKernelBox;
/* ------------------------------------------------------------ */
static double
248 _nrrdBoxSDSup( const double *parm ) {
return parm[0];
}
static double
254 _nrrdBoxSD1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _BOX( x );
}
static float
261 _nrrdBoxSD1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _BOX( x ) );
}
static void
268 _nrrdBoxSDN_d( double *f, const double *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
double t;
t = AIR_ABS( x[i] );
f[i] = _BOX( t );
}
}
static void
279 _nrrdBoxSDN_f( float *f, const float *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
float t;
t = AIR_ABS( x[i] );
f[i] = AIR_CAST( float, _BOX( t ) );
}
}
static NrrdKernel
_nrrdKernelBoxSupportDebug = {
"boxsup",
1, _nrrdBoxSDSup, returnOne,
_nrrdBoxSD1_f, _nrrdBoxSDN_f, _nrrdBoxSD1_d, _nrrdBoxSDN_d
};
295 NrrdKernel *const
nrrdKernelBoxSupportDebug = &_nrrdKernelBoxSupportDebug;
/* ------------------------------------------------------------ */
#define COS4( x ) ( x > 0.5 \
? 0.0 \
: cos( AIR_PI*x )*cos( AIR_PI*x )*cos( AIR_PI*x )*cos( AIR_PI*x ) )
303
static double
_nrrdCos4SDInt( const double *parm ) {
AIR_UNUSED( parm );
return 3.0/8.0;
}
309
static double
_nrrdCos4SDSup( const double *parm ) {
return parm[0];
}
315
static double
_nrrdCos4SD1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return COS4( x );
}
322
static float
_nrrdCos4SD1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, COS4( x ) );
}
329
static void
_nrrdCos4SDN_d( double *f, const double *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
double t;
t = AIR_ABS( x[i] );
f[i] = COS4( t );
}
}
340
static void
_nrrdCos4SDN_f( float *f, const float *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
float t;
t = AIR_ABS( x[i] );
f[i] = AIR_CAST( float, COS4( t ) );
}
}
static NrrdKernel
_nrrdKernelCos4SupportDebug = {
"cos4sup",
1, _nrrdCos4SDSup, _nrrdCos4SDInt,
356 _nrrdCos4SD1_f, _nrrdCos4SDN_f, _nrrdCos4SD1_d, _nrrdCos4SDN_d
};
NrrdKernel *const
nrrdKernelCos4SupportDebug = &_nrrdKernelCos4SupportDebug;
/* ------------------------------------------------------------ */
#define DCOS4( x ) ( x > 0.5 \
364 ? 0.0 \
: -4*AIR_PI*( cos( AIR_PI*x )*cos( AIR_PI*x )*cos( AIR_PI*x ) \
*sin( AIR_PI*x ) ) )
static double
369 _nrrdDCos4SDSup( const double *parm ) {
return parm[0];
}
static double
_nrrdDCos4SD1_d( double x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
377 if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return sgn*DCOS4( x );
}
static float
_nrrdDCos4SD1_f( float x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
385 if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return AIR_CAST( float, sgn*DCOS4( x ) );
}
static void
_nrrdDCos4SDN_d( double *f, const double *x, size_t len, const double *parm ) {
int sgn;
double t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
398 f[i] = sgn*DCOS4( t );
}
}
static void
_nrrdDCos4SDN_f( float *f, const float *x, size_t len, const double *parm ) {
int sgn;
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*DCOS4( t ) );
}
}
static NrrdKernel
416 _nrrdKernelCos4SupportDebugD = {
"cos4supD",
1, _nrrdDCos4SDSup, returnZero,
_nrrdDCos4SD1_f, _nrrdDCos4SDN_f, _nrrdDCos4SD1_d, _nrrdDCos4SDN_d
};
NrrdKernel *const
nrrdKernelCos4SupportDebugD = &_nrrdKernelCos4SupportDebugD;
424 /* ------------------------------------------------------------ */
#define DDCOS4( x ) ( x > 0.5 \
? 0.0 \
: -2*AIR_PI*AIR_PI*( cos( AIR_PI*2*x ) + cos( AIR_PI*4*x ) ) )
429
static double
_nrrdDDCos4SDSup( const double *parm ) {
return parm[0];
}
static double
436 _nrrdDDCos4SD1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return DDCOS4( x );
}
static float
443 _nrrdDDCos4SD1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, DDCOS4( x ) );
}
static void
_nrrdDDCos4SDN_d( double *f, const double *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
454 double t;
t = AIR_ABS( x[i] );
f[i] = DDCOS4( t );
}
}
static void
_nrrdDDCos4SDN_f( float *f, const float *x, size_t len, const double *parm ) {
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
float t;
t = AIR_ABS( x[i] );
f[i] = AIR_CAST( float, DDCOS4( t ) );
}
}
470
static NrrdKernel
_nrrdKernelCos4SupportDebugDD = {
"cos4supDD",
1, _nrrdDDCos4SDSup, returnZero,
_nrrdDDCos4SD1_f, _nrrdDDCos4SDN_f, _nrrdDDCos4SD1_d, _nrrdDDCos4SDN_d
};
NrrdKernel *const
478 nrrdKernelCos4SupportDebugDD = &_nrrdKernelCos4SupportDebugDD;
/* ------------------------------------------------------------ */
#define DDDCOS4( x ) ( x > 0.5 \
483 ? 0.0 \
: 4*AIR_PI*AIR_PI*AIR_PI*( sin( 2*AIR_PI*x ) + 2*sin( 4*AIR_PI*x ) ) )
static double
_nrrdDDDCos4SDSup( const double *parm ) {
return parm[0];
}
491 static double
_nrrdDDDCos4SD1_d( double x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return sgn*DDDCOS4( x );
}
499 static float
_nrrdDDDCos4SD1_f( float x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return AIR_CAST( float, sgn*DDDCOS4( x ) );
}
static void
_nrrdDDDCos4SDN_d( double *f, const double *x, size_t len, const double *parm ) {
int sgn;
double t;
size_t i;
512 AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*DDDCOS4( t );
}
}
static void
_nrrdDDDCos4SDN_f( float *f, const float *x, size_t len, const double *parm ) {
int sgn;
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*DDDCOS4( t ) );
530 }
}
static NrrdKernel
_nrrdKernelCos4SupportDebugDDD = {
"cos4supDDD",
1, _nrrdDDDCos4SDSup, returnZero,
_nrrdDDDCos4SD1_f, _nrrdDDDCos4SDN_f, _nrrdDDDCos4SD1_d, _nrrdDDDCos4SDN_d
};
NrrdKernel *const
nrrdKernelCos4SupportDebugDDD = &_nrrdKernelCos4SupportDebugDDD;
542 /* ------------------------------------------------------------ */
/* The point here is that post-kernel-evaluation, we need to see
which sample is closest to the origin, and this is one way of
enabling that
SO: this kernel will not usefully report its integral or support! */
#define _CHEAP( x ) AIR_ABS( x )
static double
_nrrdCheapSup( const double *parm ) {
552 double S;
S = parm[0];
/* adding the 0.5 is to insure that weights computed within the
support really do catch all the non-zero values */
return S/2 + 0.5;
558 }
static double
_nrrdCheap1_d( double x, const double *parm ) {
return _CHEAP( x )/parm[0];
564 }
static float
_nrrdCheap1_f( float x, const double *parm ) {
return AIR_CAST( float, _CHEAP( x )/parm[0] );
}
static void
_nrrdCheapN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
575 size_t i;
for ( i=0; i<len; i++ ) {
t = x[i];
f[i] = _CHEAP( t )/parm[0];
}
}
static void
_nrrdCheapN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
for ( i=0; i<len; i++ ) {
t = x[i];
f[i] = AIR_CAST( float, _CHEAP( t )/parm[0] );
591 }
}
static NrrdKernel
_nrrdKernelCheap = {
"cheap",
1, _nrrdCheapSup, returnOne,
_nrrdCheap1_f, _nrrdCheapN_f, _nrrdCheap1_d, _nrrdCheapN_d
599 };
NrrdKernel *const
nrrdKernelCheap = &_nrrdKernelCheap;
/* ------------------------------------------------------------ */
#define _TENT( x ) ( x >= 1 ? 0 : 1 - x )
607 static double
_nrrdTentSup( const double *parm ) {
double S;
S = parm[0];
return S;
}
static double
616 _nrrdTent1_d( double x, const double *parm ) {
double S;
S = parm[0];
x = AIR_ABS( x )/S;
return S ? _TENT( x )/S : x == 0;
}
static float
625 _nrrdTent1_f( float x, const double *parm ) {
float S;
S = AIR_CAST( float, parm[0] );
x = AIR_ABS( x )/S;
return S ? _TENT( x )/S : x == 0;
}
static void
_nrrdTentN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t;
size_t i;
638
S = parm[0];
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = S ? _TENT( t )/S : t == 0;
}
}
static void
_nrrdTentN_f( float *f, const float *x, size_t len, const double *parm ) {
float t, S;
size_t i;
S = AIR_CAST( float, parm[0] );
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t )/S;
f[i] = S ? _TENT( t )/S : t == 0;
655 }
}
static NrrdKernel
_nrrdKernelTent = {
"tent",
1, _nrrdTentSup, returnOne,
_nrrdTent1_f, _nrrdTentN_f, _nrrdTent1_d, _nrrdTentN_d
};
NrrdKernel *const
nrrdKernelTent = &_nrrdKernelTent;
/* ------------------------------------------------------------ */
/*
** NOTE: THERE IS NOT REALLY A HERMITE KERNEL ( at least not yet,
** because it takes both values and derivatives as arguments, which
** the NrrdKernel currently can't handle ). This isn't really a
673 ** kernel, its mostly a flag ( hence the name ), but it also has the
** role of generating weights according to linear interpolation, which
** is useful for the eventual spline evaluation.
**
** This hack is in sinister collusion with gage, to enable Hermite
** interpolation for its stack reconstruction.
*/
680
static double
_nrrdHermite1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _TENT( x );
}
687
static float
_nrrdHermite1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _TENT( x );
}
static void
_nrrdHermiteN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
698 size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t );
f[i] = _TENT( t );
}
}
static void
_nrrdHermiteN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i]; t = AIR_ABS( t );
f[i] = _TENT( t );
}
}
716
/* HEY: should just re-use fields from nrrdKernelTent, instead
of creating new functions */
static NrrdKernel
_nrrdKernelHermiteScaleSpaceFlag = {
"hermiteSS",
0, returnOne, returnOne,
_nrrdHermite1_f, _nrrdHermiteN_f, _nrrdHermite1_d, _nrrdHermiteN_d
724 };
NrrdKernel *const
nrrdKernelHermiteScaleSpaceFlag = &_nrrdKernelHermiteScaleSpaceFlag;
/* ------------------------------------------------------------ */
#define _FORDIF( x ) ( x < -1 ? 0.0f : \
( x < 0 ? 1.0f : \
732 ( x < 1 ? -1.0f : 0.0f ) ) )
static double
_nrrdFDSup( const double *parm ) {
double S;
S = parm[0];
return S+0.0001; /* sigh */
}
741
static double
_nrrdFD1_d( double x, const double *parm ) {
double t, S;
S = parm[0];
t = x/S;
return _FORDIF( t )/( S*S );
}
750
static float
_nrrdFD1_f( float x, const double *parm ) {
float t, S;
S = AIR_CAST( float, parm[0] );
t = x/S;
return _FORDIF( t )/( S*S );
}
static void
_nrrdFDN_d( double *f, const double *x, size_t len, const double *parm ) {
762 double t, S;
size_t i;
S = parm[0];
for ( i=0; i<len; i++ ) {
t = x[i]/S;
f[i] = _FORDIF( t )/( S*S );
}
}
static void
_nrrdFDN_f( float *f, const float *x, size_t len, const double *parm ) {
float t, S;
size_t i;
S = AIR_CAST( float, parm[0] );
for ( i=0; i<len; i++ ) {
779 t = x[i]/S;
f[i] = _FORDIF( t )/( S*S );
}
}
static NrrdKernel
_nrrdKernelFD = {
"fordif",
787 1, _nrrdFDSup, returnZero,
_nrrdFD1_f, _nrrdFDN_f, _nrrdFD1_d, _nrrdFDN_d
};
NrrdKernel *const
nrrdKernelForwDiff = &_nrrdKernelFD;
/* ------------------------------------------------------------ */
795 #define _CENDIF( x ) ( x <= -2 ? 0 : \
( x <= -1 ? 0.5*x + 1 : \
( x <= 1 ? -0.5*x : \
( x <= 2 ? 0.5*x - 1 : 0 ) ) ) )
static double
_nrrdCDSup( const double *parm ) {
double S;
804 S = parm[0];
return 2*S;
}
static double
_nrrdCD1_d( double x, const double *parm ) {
double S;
S = parm[0];
813 x /= S;
return _CENDIF( x )/( S*S );
}
static float
_nrrdCD1_f( float x, const double *parm ) {
float S;
S = AIR_CAST( float, parm[0] );
x /= S;
return AIR_CAST( float, _CENDIF( x )/( S*S ) );
}
826 static void
_nrrdCDN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t;
size_t i;
S = parm[0];
for ( i=0; i<len; i++ ) {
t = x[i]/S;
f[i] = _CENDIF( t )/( S*S );
}
}
static void
_nrrdCDN_f( float *f, const float *x, size_t len, const double *parm ) {
float t, S;
size_t i;
843
S = AIR_CAST( float, parm[0] );
for ( i=0; i<len; i++ ) {
t = x[i]/S;
f[i] = AIR_CAST( float, _CENDIF( t )/( S*S ) );
}
}
851 static NrrdKernel
_nrrdKernelCD = {
"cendif",
1, _nrrdCDSup, returnZero,
_nrrdCD1_f, _nrrdCDN_f, _nrrdCD1_d, _nrrdCDN_d
};
NrrdKernel *const
nrrdKernelCentDiff = &_nrrdKernelCD;
859
/* ------------------------------------------------------------ */
#define _BCCUBIC( x, B, C ) \
( x >= 2.0 ? 0 : \
( x >= 1.0 \
? ( ( ( -B/6 - C )*x + B + 5*C )*x -2*B - 8*C )*x + 4*B/3 + 4*C \
: ( ( 2 - 3*B/2 - C )*x - 3 + 2*B + C )*x*x + 1 - B/3 ) )
static double
869 _nrrdBCSup( const double *parm ) {
double S;
S = parm[0];
return 2*S;
}
static double
_nrrdBC1_d( double x, const double *parm ) {
double S;
double B, C;
880
S = parm[0]; B = parm[1]; C = parm[2];
x = AIR_ABS( x )/S;
return _BCCUBIC( x, B, C )/S;
}
static float
_nrrdBC1_f( float x, const double *parm ) {
float B, C, S;
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
C = AIR_CAST( float, parm[2] );
x = AIR_ABS( x )/S;
894 return _BCCUBIC( x, B, C )/S;
}
static void
_nrrdBCN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, B, C;
size_t i;
S = parm[0]; B = parm[1]; C = parm[2];
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t )/S;
f[i] = _BCCUBIC( t, B, C )/S;
}
}
static void
_nrrdBCN_f( float *f, const float *x, size_t len, const double *parm ) {
float S, t, B, C;
914 size_t i;
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
C = AIR_CAST( float, parm[2] );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t )/S;
922 f[i] = _BCCUBIC( t, B, C )/S;
}
}
static NrrdKernel
_nrrdKernelBC = {
"BCcubic",
3, _nrrdBCSup, returnOne,
930 _nrrdBC1_f, _nrrdBCN_f, _nrrdBC1_d, _nrrdBCN_d
};
NrrdKernel *const
nrrdKernelBCCubic = &_nrrdKernelBC;
/* ------------------------------------------------------------ */
#define _DBCCUBIC( x, B, C ) \
( x >= 2.0 ? 0.0 : \
( x >= 1.0 \
? ( ( -B/2 - 3*C )*x + 2*B + 10*C )*x -2*B - 8*C \
: ( ( 6 - 9*B/2 - 3*C )*x - 6 + 4*B + 2*C )*x ) )
942
static double
_nrrdDBCSup( const double *parm ) {
double S;
S = parm[0];
return 2*S;
}
static double
_nrrdDBC1_d( double x, const double *parm ) {
double S;
double B, C;
955 int sgn = 1;
S = parm[0]; B = parm[1]; C = parm[2];
if ( x < 0 ) { x = -x; sgn = -1; }
x /= S;
return sgn*_DBCCUBIC( x, B, C )/( S*S );
}
static float
_nrrdDBC1_f( float x, const double *parm ) {
float B, C, S;
int sgn = 1;
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
970 C = AIR_CAST( float, parm[2] );
if ( x < 0 ) { x = -x; sgn = -1; }
x /= S;
return AIR_CAST( float, sgn*_DBCCUBIC( x, B, C )/( S*S ) );
}
static void
_nrrdDBCN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, B, C;
size_t i;
int sgn;
S = parm[0]; B = parm[1]; C = parm[2];
for ( i=0; i<len; i++ ) {
t = x[i]/S;
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DBCCUBIC( t, B, C )/( S*S );
}
}
991 static void
_nrrdDBCN_f( float *f, const float *x, size_t len, const double *parm ) {
float S, t, B, C;
int sgn;
size_t i;
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
999 C = AIR_CAST( float, parm[2] );
for ( i=0; i<len; i++ ) {
t = x[i]/S;
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DBCCUBIC( t, B, C )/( S*S ) );
}
}
1007 static NrrdKernel
_nrrdKernelDBC = {
"BCcubicD",
3, _nrrdDBCSup, returnZero,
_nrrdDBC1_f, _nrrdDBCN_f, _nrrdDBC1_d, _nrrdDBCN_d
};
NrrdKernel *const
nrrdKernelBCCubicD = &_nrrdKernelDBC;
/* ------------------------------------------------------------ */
1017
#define _DDBCCUBIC( x, B, C ) \
( x >= 2.0 ? 0 : \
( x >= 1.0 \
? ( -B - 6*C )*x + 2*B + 10*C \
: ( 12 - 9*B - 6*C )*x - 6 + 4*B + 2*C ) )
static double
_nrrdDDBCSup( const double *parm ) {
double S;
1028 S = parm[0];
return 2*S;
}
static double
_nrrdDDBC1_d( double x, const double *parm ) {
double S;
double B, C;
S = parm[0]; B = parm[1]; C = parm[2];
x = AIR_ABS( x )/S;
return _DDBCCUBIC( x, B, C )/( S*S*S );
}
1042 static float
_nrrdDDBC1_f( float x, const double *parm ) {
float B, C, S;
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
C = AIR_CAST( float, parm[2] );
x = AIR_ABS( x )/S;
return _DDBCCUBIC( x, B, C )/( S*S*S );
}
static void
_nrrdDDBCN_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, B, C;
size_t i;
S = parm[0]; B = parm[1]; C = parm[2];
for ( i=0; i<len; i++ ) {
t = x[i];
1062 t = AIR_ABS( t )/S;
f[i] = _DDBCCUBIC( t, B, C )/( S*S*S );
}
}
static void
_nrrdDDBCN_f( float *f, const float *x, size_t len, const double *parm ) {
float S, t, B, C;
size_t i;
1071
S = AIR_CAST( float, parm[0] );
B = AIR_CAST( float, parm[1] );
C = AIR_CAST( float, parm[2] );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t )/S;
1078 f[i] = _DDBCCUBIC( t, B, C )/( S*S*S );
}
}
static NrrdKernel
_nrrdKernelDDBC = {
"BCcubicDD",
1085 3, _nrrdDDBCSup, returnZero,
_nrrdDDBC1_f, _nrrdDDBCN_f, _nrrdDDBC1_d, _nrrdDDBCN_d
};
NrrdKernel *const
nrrdKernelBCCubicDD = &_nrrdKernelDDBC;
/* ------------------------------------------------------------ */
/* if you've got the definition already, why not use it */
#define _CTMR( x ) _BCCUBIC( x, 0.0, 0.5 )
static double
1097 _nrrdCTMR1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _CTMR( x );
}
static float
_nrrdCTMR1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _CTMR( x ) );
}
static void
_nrrdCTMRN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
1114 AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = _CTMR( t );
1119 }
}
static void
_nrrdCTMRN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
1130 f[i] = AIR_CAST( float, _CTMR( t ) );
}
}
static NrrdKernel
_nrrdKernelCatmullRom = {
"catmull-rom",
0, returnTwo, returnOne,
_nrrdCTMR1_f, _nrrdCTMRN_f, _nrrdCTMR1_d, _nrrdCTMRN_d
1139 };
NrrdKernel *const
nrrdKernelCatmullRom = &_nrrdKernelCatmullRom;
static double
_nrrdCtmrSDSup( const double *parm ) {
1147 return AIR_MAX( 2.0, parm[0] );
}
static NrrdKernel
_nrrdKernelCatmullRomSupportDebug = {
"ctmrsup",
1, _nrrdCtmrSDSup, returnOne,
_nrrdCTMR1_f, _nrrdCTMRN_f, _nrrdCTMR1_d, _nrrdCTMRN_d
1155 };
NrrdKernel *const
nrrdKernelCatmullRomSupportDebug = &_nrrdKernelCatmullRomSupportDebug;
/* ------------------------------------------------------------ */
/* if you've got the definition already, why not use it */
#define _DCTMR( x ) _DBCCUBIC( x, 0.0, 0.5 )
static double
_nrrdDCTMR1_d( double x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
1168 if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return sgn*_DCTMR( x );
}
static float
_nrrdDCTMR1_f( float x, const double *parm ) {
int sgn;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; } else { sgn = 1; }
return AIR_CAST( float, sgn*_DCTMR( x ) );
}
static void
_nrrdDCTMRN_d( double *f, const double *x, size_t len, const double *parm ) {
int sgn;
double t;
size_t i;
AIR_UNUSED( parm );
1186 for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DCTMR( t );
}
}
static void
_nrrdDCTMRN_f( float *f, const float *x, size_t len, const double *parm ) {
1195 int sgn;
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DCTMR( t ) );
}
1204 }
static NrrdKernel
_nrrdKernelCatmullRomD = {
"catmull-romD",
0, returnTwo, returnZero,
_nrrdDCTMR1_f, _nrrdDCTMRN_f, _nrrdDCTMR1_d, _nrrdDCTMRN_d
1211 };
NrrdKernel *const
nrrdKernelCatmullRomD = &_nrrdKernelCatmullRomD;
static NrrdKernel
_nrrdKernelCatmullRomSupportDebugD = {
"ctmrsupD",
1218 1, _nrrdCtmrSDSup, returnZero,
_nrrdDCTMR1_f, _nrrdDCTMRN_f, _nrrdDCTMR1_d, _nrrdDCTMRN_d
};
NrrdKernel *const
nrrdKernelCatmullRomSupportDebugD = &_nrrdKernelCatmullRomSupportDebugD;
/* ------------------------------------------------------------ */
/* if you've got the definition already, why not use it */
#define _DDCTMR( x ) _DDBCCUBIC( x, 0.0, 0.5 )
static double
1230 _nrrdDDCTMR1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _DDCTMR( x );
}
static float
_nrrdDDCTMR1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _DDCTMR( x ) );
}
static void
_nrrdDDCTMRN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
1247 AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = _DDCTMR( t );
}
}
static void
1256 _nrrdDDCTMRN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _DDCTMR( t ) );
1264 }
}
static NrrdKernel
_nrrdKernelCatmullRomDD = {
"catmull-romDD",
0, returnTwo, returnZero,
_nrrdDDCTMR1_f, _nrrdDDCTMRN_f, _nrrdDDCTMR1_d, _nrrdDDCTMRN_d
1272 };
NrrdKernel *const
nrrdKernelCatmullRomDD = &_nrrdKernelCatmullRomDD;
static NrrdKernel
_nrrdKernelCatmullRomSupportDebugDD = {
"ctmrsupDD",
1, _nrrdCtmrSDSup, returnZero,
_nrrdDDCTMR1_f, _nrrdDDCTMRN_f, _nrrdDDCTMR1_d, _nrrdDDCTMRN_d
};
1282 NrrdKernel *const
nrrdKernelCatmullRomSupportDebugDD = &_nrrdKernelCatmullRomSupportDebugDD;
/* ------------------------------------------------------------ */
#define _AQUARTIC( x, A ) \
( x >= 3.0 ? 0 : \
( x >= 2.0 \
? A*( -54 + x*( 81 + x*( -45 + x*( 11 - x ) ) ) ) \
1291 : ( x >= 1.0 \
? 4 - 6*A + x*( -10 + 25*A + x*( 9 - 33*A \
+ x*( -3.5 + 17*A + x*( 0.5 - 3*A ) ) ) ) \
: 1 + x*x*( -3 + 6*A + x*( ( 2.5 - 10*A ) + x*( -0.5 + 4*A ) ) ) ) ) )
static double
_nrrdA4Sup( const double *parm ) {
double S;
S = parm[0];
return 3*S;
}
static double
1305 _nrrdA41_d( double x, const double *parm ) {
double S;
double A;
S = parm[0]; A = parm[1];
x = AIR_ABS( x )/S;
return _AQUARTIC( x, A )/S;
}
static float
_nrrdA41_f( float x, const double *parm ) {
float A, S;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
x = AIR_ABS( x )/S;
return AIR_CAST( float, _AQUARTIC( x, A )/S );
}
1323 static void
_nrrdA4N_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, A;
size_t i;
S = parm[0]; A = parm[1];
for ( i=0; i<len; i++ ) {
1331 t = x[i];
t = AIR_ABS( t )/S;
f[i] = _AQUARTIC( t, A )/S;
}
}
static void
_nrrdA4N_f( float *f, const float *x, size_t len, const double *parm ) {
1339 float S, t, A;
size_t i;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t )/S;
f[i] = AIR_CAST( float, _AQUARTIC( t, A )/S );
}
}
static NrrdKernel
1351 _nrrdKernelA4 = {
"Aquartic",
2, _nrrdA4Sup, returnOne,
_nrrdA41_f, _nrrdA4N_f, _nrrdA41_d, _nrrdA4N_d
};
NrrdKernel *const
nrrdKernelAQuartic = &_nrrdKernelA4;
/* ------------------------------------------------------------ */
#define _DAQUARTIC( x, A ) \
1362 ( x >= 3.0 ? 0 : \
( x >= 2.0 \
? A*( 81 + x*( -90 + x*( 33 - 4*x ) ) ) \
: ( x >= 1.0 \
? -10 + 25*A + x*( 18 - 66*A + x*( -10.5 + 51*A + x*( 2 - 12*A ) ) ) \
: x*( -6 + 12*A + x*( 7.5 - 30*A + x*( -2 + 16*A ) ) ) ) ) )
static double
_nrrdDA4Sup( const double *parm ) {
double S;
S = parm[0];
return 3*S;
}
1377 static double
_nrrdDA41_d( double x, const double *parm ) {
double S;
double A;
int sgn = 1;
S = parm[0]; A = parm[1];
if ( x < 0 ) { x = -x; sgn = -1; }
x /= S;
return sgn*_DAQUARTIC( x, A )/( S*S );
}
static float
_nrrdDA41_f( float x, const double *parm ) {
float A, S;
int sgn = 1;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
if ( x < 0 ) { x = -x; sgn = -1; }
1396 x /= S;
return AIR_CAST( float, sgn*_DAQUARTIC( x, A )/( S*S ) );
}
static void
_nrrdDA4N_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, A;
1404 size_t i;
int sgn;
S = parm[0]; A = parm[1];
for ( i=0; i<len; i++ ) {
t = x[i]/S;
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DAQUARTIC( t, A )/( S*S );
1412 }
}
static void
_nrrdDA4N_f( float *f, const float *x, size_t len, const double *parm ) {
float S, t, A;
size_t i;
int sgn;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
1422 for ( i=0; i<len; i++ ) {
t = x[i]/S;
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DAQUARTIC( t, A )/( S*S ) );
}
}
static NrrdKernel
_nrrdKernelDA4 = {
1431 "AquarticD",
2, _nrrdDA4Sup, returnZero,
_nrrdDA41_f, _nrrdDA4N_f, _nrrdDA41_d, _nrrdDA4N_d
};
NrrdKernel *const
nrrdKernelAQuarticD = &_nrrdKernelDA4;
/* ------------------------------------------------------------ */
#define _DDAQUARTIC( x, A ) \
( x >= 3.0 ? 0 : \
( x >= 2.0 \
? A*( -90 + x*( 66 - x*12 ) ) \
: ( x >= 1.0 \
1445 ? 18 - 66*A + x*( -21 + 102*A + x*( 6 - 36*A ) ) \
: -6 + 12*A + x*( 15 - 60*A + x*( -6 + 48*A ) ) ) ) )
static double
_nrrdDDA4Sup( const double *parm ) {
double S;
S = parm[0];
return 3*S;
}
static double
_nrrdDDA41_d( double x, const double *parm ) {
double S;
double A;
S = parm[0]; A = parm[1];
x = AIR_ABS( x )/S;
1463 return _DDAQUARTIC( x, A )/( S*S*S );
}
static float
_nrrdDDA41_f( float x, const double *parm ) {
float S, A;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
x = AIR_ABS( x )/S;
return _DDAQUARTIC( x, A )/( S*S*S );
}
static void
_nrrdDDA4N_d( double *f, const double *x, size_t len, const double *parm ) {
double S;
double t, A;
size_t i;
S = parm[0]; A = parm[1];
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t )/S;
f[i] = _DDAQUARTIC( t, A )/( S*S*S );
}
}
static void
1490 _nrrdDDA4N_f( float *f, const float *x, size_t len, const double *parm ) {
float S, t, A;
size_t i;
S = AIR_CAST( float, parm[0] ); A = AIR_CAST( float, parm[1] );
for ( i=0; i<len; i++ ) {
t = x[i];
1497 t = AIR_ABS( t )/S;
f[i] = _DDAQUARTIC( t, A )/( S*S*S );
}
}
static NrrdKernel
_nrrdKernelDDA4 = {
1504 "AquarticDD",
2, _nrrdDDA4Sup, returnZero,
_nrrdDDA41_f, _nrrdDDA4N_f, _nrrdDDA41_d, _nrrdDDA4N_d
};
NrrdKernel *const
nrrdKernelAQuarticDD = &_nrrdKernelDDA4;
/* ------------------------------------------------------------ */
/*
** This is the unique, four-sample support, quintic, C^3 kernel, with 1st and
** 3rd derivatives zero at origin, which integrates to unity on [-2, 2], which
1516 ** exactly reconstructs 0th and 1st order polynomials. Unfortunately it does
** NOT reconstruct 2nd order polynomials, so its not very useful. It worse
** than "one step down" from c4hexic ( see below ), since while its support and
** polynomial power is one less than c4hexic, it cannot reconstruct
** parabolas; c4hexic can reconstruct cubics.
**
** The same kernel is also available as nrrdKernelTMF w/ D, C, A = -1, 3, 2 ==
** TMF_dn_c3_2ef == "tmf:n, 3, 2" == nrrdKernelTMF[0][4][2], the only advantage
** here being that you have access to the first and second derivatives of
** this quintic kernel as nrrdKernelC3QuinticD and nrrdKernelC3QuinticDD.
**
** By the way, TMF_d0_c3_3ef == TMF_dn_c3_3ef == "tmf:n, 3, 3", which can ( by
** definition ) reconstruct parabolas, has four-sample support, and has
** piecewise polynomial order *seven*
*/
#define _C3QUINTIC( x ) \
1533 ( x >= 2.0 ? 0 : \
( x >= 1.0 \
? 0.8 + x*x*( -2 + x*( 2 + x*( -0.75 + x*0.1 ) ) ) \
: 0.7 + x*x*( -1 + x*x*( 0.75 - x*0.3 ) ) ) )
static double
_c3quint1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
1541 x = AIR_ABS( x );
return _C3QUINTIC( x );
}
static float
_c3quint1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
1549 return AIR_CAST( float, _C3QUINTIC( x ) );
}
static void
_c3quintN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
AIR_UNUSED( parm );
1557 for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = _C3QUINTIC( t );
}
}
static void
_c3quintN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
1570 t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _C3QUINTIC( t ) );
}
}
static NrrdKernel
_c3quint = {
"C3Quintic",
0, returnTwo, returnOne,
_c3quint1_f, _c3quintN_f, _c3quint1_d, _c3quintN_d
};
NrrdKernel *const
nrrdKernelC3Quintic = &_c3quint;
/* ------------------------------------------------------------ */
#define _DC3QUINTIC( x ) \
1588 ( x >= 2.0 ? 0 : \
( x >= 1.0 \
? x*( -4 + x*( 6 + x*( -3 + x*0.5 ) ) ) \
: x*( -2 + x*x*( 3 - x*1.5 ) ) ) )
static double
_Dc3quint1_d( double x, const double *parm ) {
int sgn = 1;
1596 AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return sgn*_DC3QUINTIC( x );
}
static float
_Dc3quint1_f( float x, const double *parm ) {
1603 int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return AIR_CAST( float, sgn*_DC3QUINTIC( x ) );
}
static void
1610 _Dc3quintN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DC3QUINTIC( t );
}
}
1622 static void
_Dc3quintN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DC3QUINTIC( t ) );
}
}
static NrrdKernel
_nrrdKernelDC3Quintic = {
"C3QuinticD",
0, returnTwo, returnZero,
1639 _Dc3quint1_f, _Dc3quintN_f, _Dc3quint1_d, _Dc3quintN_d
};
NrrdKernel *const
nrrdKernelC3QuinticD = &_nrrdKernelDC3Quintic;
/* ------------------------------------------------------------ */
#define _DDC3QUINTIC( x ) \
( x >= 2.0 ? 0 : \
( x >= 1.0 \
? -4 + x*( 12 + x*( -9 + x*2 ) ) \
: -2 + x*x*( 9 - x*6 ) ) )
static double
_DDc3quint1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return _DDC3QUINTIC( x );
}
1658
static float
_DDc3quint1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _DDC3QUINTIC( x ) );
}
1665
static void
_DDc3quintN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
1672 t = x[i];
t = AIR_ABS( t );
f[i] = _DDC3QUINTIC( t );
}
}
static void
_DDc3quintN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
1684 t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _DDC3QUINTIC( t ) );
}
}
static NrrdKernel
_DDc3quint = {
"C3QuinticDD",
0, returnTwo, returnZero,
_DDc3quint1_f, _DDc3quintN_f, _DDc3quint1_d, _DDc3quintN_d
};
NrrdKernel *const
nrrdKernelC3QuinticDD = &_DDc3quint;
/* ------------------------------------------------------------ */
1701 /*
** This is the unique, 6-sample support, hexic, C^4 kernel, with 1st and 3rd
** derivatives zero at origin, which integrates to unity on [-3, 3], with 3rd
** order Taylor accuracy ( errors start showing up when reconstructing 4th
** order polynomials ) It doesn't interpolate, but its close, and it rings
** once.
**
** this is awfully close to, but not quite the same as, "tmf:n, 3, 4" ==
1709 ** TMF_dn_c3_4ef == nrrdKernelTMF[0][4][4], which is only C^3 smooth
*/
#define _C4HEXIC( x ) \
( x >= 3.0 \
? 0 \
: ( x >= 2.0 \
? 1539.0/160.0 + x*( -189.0/8.0 + x*( 747.0/32.0 + x*( -12.0 + x*( 109.0/32.0 + x*( -61.0/120.0 + x/32.0 ) ) ) ) ) \
1717 : ( x >= 1.0 \
? 3.0/160.0 + x*( 35.0/8.0 + x*( -341.0/32.0 + x*( 10.0 + x*( -147.0/32.0 + x*( 25.0/24.0 - x*3.0/32.0 ) ) ) ) ) \
: 69.0/80.0 + x*x*( -23.0/16.0 + x*x*( 19.0/16.0 + x*( -7.0/12.0 + x/16.0 ) ) ) ) ) )
static double
_c4hex1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
1725 return _C4HEXIC( x );
}
static float
_c4hex1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _C4HEXIC( x ) );
}
static void
_c4hexN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1738 size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = _C4HEXIC( t );
}
}
static void
_c4hexN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _C4HEXIC( t ) );
1756 }
}
static NrrdKernel
_c4hex = {
"C4Hexic",
0, returnThree, returnOne,
_c4hex1_f, _c4hexN_f, _c4hex1_d, _c4hexN_d
1764 };
NrrdKernel *const
nrrdKernelC4Hexic = &_c4hex;
/* ------------------------------------------------------------ */
#define _DC4HEXIC( x ) \
1771 ( x >= 3.0 \
? 0 \
: ( x >= 2.0 \
? -189.0/8.0 + x*( 747.0/16.0 + x*( -36.0 + x*( 109.0/8.0 + x*( -61.0/24.0 + x*( 3.0/16.0 ) ) ) ) ) \
: ( x >= 1.0 \
? 35.0/8.0 + x*( -341.0/16.0 + x*( 30 + x*( -147.0/8.0 + x*( 125.0/24.0 + x*( -9.0/16.0 ) ) ) ) ) \
: x*( -23.0/8.0 + x*x*( 19.0/4.0 + x*( -35.0/12.0 + x*( 3.0/8.0 ) ) ) ) ) ) )
1778
static double
_Dc4hex1_d( double x, const double *parm ) {
int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return sgn*_DC4HEXIC( x );
}
static float
_Dc4hex1_f( float x, const double *parm ) {
int sgn = 1;
1790 AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return AIR_CAST( float, sgn*_DC4HEXIC( x ) );
}
static void
_Dc4hexN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DC4HEXIC( t );
}
}
1807
static void
_Dc4hexN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
1815 t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DC4HEXIC( t ) );
}
}
static NrrdKernel
_nrrdKernelDC4hexic = {
1823 "C4HexicD",
0, returnThree, returnZero,
_Dc4hex1_f, _Dc4hexN_f, _Dc4hex1_d, _Dc4hexN_d
};
NrrdKernel *const
nrrdKernelC4HexicD = &_nrrdKernelDC4hexic;
/* ------------------------------------------------------------ */
1831
#define _DDC4HEXIC( x ) \
( x >= 3.0 \
? 0 \
: ( x >= 2.0 \
? 747.0/16.0 + x*( -72.0 + x*( 327.0/8.0 + x*( -61.0/6.0 + x*15.0/16.0 ) ) ) \
: ( x >= 1.0 \
? -341.0/16.0 + x*( 60 + x*( -441.0/8.0 + x*( 125.0/6.0 - x*45.0/16.0 ) ) ) \
: -23.0/8.0 + x*x*( 57.0/4.0 + x*( -35.0/3.0 + x*( 15.0/8.0 ) ) ) ) ) )
static double
_DDc4hex1_d( double x, const double *parm ) {
AIR_UNUSED( parm );
1844 x = AIR_ABS( x );
return _DDC4HEXIC( x );
}
static float
_DDc4hex1_f( float x, const double *parm ) {
AIR_UNUSED( parm );
x = AIR_ABS( x );
return AIR_CAST( float, _DDC4HEXIC( x ) );
}
static void
_DDc4hexN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
1862 t = AIR_ABS( t );
f[i] = _DDC4HEXIC( t );
}
}
static void
_DDc4hexN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _DDC4HEXIC( t ) );
}
}
static NrrdKernel
_DDc4hex = {
"C4HexicDD",
0, returnThree, returnZero,
_DDc4hex1_f, _DDc4hexN_f, _DDc4hex1_d, _DDc4hexN_d
};
1885 NrrdKernel *const
nrrdKernelC4HexicDD = &_DDc4hex;
/* ------------------------------------------------------------ */
#define _DDDC4HEXIC( x ) \
( x >= 3.0 \
? 0 \
1893 : ( x >= 2.0 \
? -72.0 + x*( 327.0/4.0 + x*( -61.0/2.0 + 15*x/4 ) ) \
: ( x >= 1.0 \
? 60 + x*( -441.0/4.0 + x*( 125.0/2.0 - 45*x/4 ) ) \
: x*( 57.0/2.0 + x*( -35 + 15*x/2 ) ) \
) ) )
static double
_DDDc4hex1_d( double x, const double *parm ) {
1902 int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return sgn*_DDDC4HEXIC( x );
}
static float
_DDDc4hex1_f( float x, const double *parm ) {
int sgn = 1;
1911 AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
return AIR_CAST( float, sgn*_DDDC4HEXIC( x ) );
}
static void
_DDDc4hexN_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
1923 t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DDDC4HEXIC( t );
}
}
static void
_DDDc4hexN_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DDDC4HEXIC( t ) );
}
}
1941
static NrrdKernel
_nrrdKernelDDDC4hexic = {
"C4HexicDDD",
0, returnThree, returnZero,
_DDDc4hex1_f, _DDDc4hexN_f, _DDDc4hex1_d, _DDDc4hexN_d
};
NrrdKernel *const
nrrdKernelC4HexicDDD = &_nrrdKernelDDDC4hexic;
/* ------------- approximate inverse of c4h ------------------------- */
/* see comments around "_bspl3_ANI" in bsplKernel.c */
static double
_c4hex_ANI_kvals[12] = {
1.1906949847244948336,
-0.13537708971729194940,
0.047024535491780434571,
1960 -0.0088462060502312555095,
0.0022443498051487024049,
-0.00048639680369511914410,
0.00011421848629250278186,
-0.000025727759438407893986,
5.9204264168395963454e-6,
-1.3468552403175349134e-6,
3.0637649910394681441e-7,
-5.5762487950611026674e-8};
1970 static double
_c4hex_ANI_sup( const double *parm ) {
AIR_UNUSED( parm );
return 12.5;
}
#define C4HEX_ANI( ret, tmp, x ) \
tmp = AIR_CAST( unsigned int, x+0.5 ); \
if ( tmp < 12 ) { \
ret = _c4hex_ANI_kvals[tmp]; \
1980 } else { \
ret = 0.0; \
}
static double
_c4hex_ANI_1d( double x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
C4HEX_ANI( r, tmp, ax );
return r;
}
static float
_c4hex_ANI_1f( float x, const double *parm ) {
1995 double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
C4HEX_ANI( r, tmp, ax );
return AIR_CAST( float, r );
}
static void
_c4hex_ANI_Nd( double *f, const double *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
C4HEX_ANI( r, tmp, ax );
f[i] = r;
}
}
static void
2015 _c4hex_ANI_Nf( float *f, const float *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
C4HEX_ANI( r, tmp, ax );
f[i] = AIR_CAST( float, r );
}
}
2025
static NrrdKernel
_nrrdKernelC4HexicApproxInverse = {
"C4HexicAI", 0,
_c4hex_ANI_sup, returnOne,
_c4hex_ANI_1f, _c4hex_ANI_Nf,
_c4hex_ANI_1d, _c4hex_ANI_Nd
};
NrrdKernel *const
nrrdKernelC4HexicApproxInverse = &_nrrdKernelC4HexicApproxInverse;
2036 /* ------------------------- c5septic ------------------------------ */
/*
** This is the unique, 8-sample support, C^5 kernel, piecewise order-7
** with 4th order Taylor accuracy ( errors start showing up when
** reconstructing 5th order polynomials ). Coincidentally, it has zero
** 1st and 3rd deriv at the origin, and it integrates to unity on
** [-4, 4]. It doesn't interpolate, but its close; it rings twice. */
#define _C5SEPT0( x ) ( 0.9379776601998824 + x*x*( -1.654320987654321 + x*x*( 1.073045267489712 + x*x*( -0.44997427983539096 + x*0.13978909465020575 ) ) ) )
#define _C5SEPT1( x ) ( 0.04651675485008818 + x*( -0.7377829218106996 + x*( 0.9699074074074074 + x*( 0.18531378600823045 + x*( -0.7839506172839507 + x*( 0.2357253086419753 + x*( 0.12021604938271604 - x*0.054552469135802466 ) ) ) ) ) ) )
2047 #define _C5SEPT2( x ) ( -0.01860670194003527 + x*( 0.14022633744855967 + x*( -0.16296296296296298 + x*( -0.09825102880658436 + x*( 0.28858024691358025 + x*( -0.18858024691358025 + x*( 0.04405864197530864 - x*0.0013631687242798354 ) ) ) ) ) ) )
#define _C5SEPT3( x ) ( 0.003101116990005879 + x*( -0.014223251028806585 + x*( 0.02021604938271605 + x*( 0.003729423868312757 + x*( -0.0411522633744856 + x*( 0.04714506172839506 + x*( -0.023199588477366254 + x*0.004383450911228689 ) ) ) ) ) ) )
#define _C5SEPT( i, x ) \
( 0 == i ? _C5SEPT0( x ) \
: ( 1 == i ? _C5SEPT1( x ) \
: ( 2 == i ? _C5SEPT2( x ) \
: ( 3 == i ? _C5SEPT3( x ) \
: 0.0 ) ) ) )
static double
_c5sept1_d( double x, const double *parm ) {
unsigned int xi;
AIR_UNUSED( parm );
x = AIR_ABS( x );
xi = AIR_CAST( unsigned int, x );
x -= xi;
2063 return _C5SEPT( xi, x );
}
static float
_c5sept1_f( float x, const double *parm ) {
unsigned int xi;
AIR_UNUSED( parm );
x = AIR_ABS( x );
xi = AIR_CAST( unsigned int, x );
x -= AIR_CAST( float, xi );
return AIR_CAST( float, _C5SEPT( xi, x ) );
}
static void
_c5septN_d( double *f, const double *x, size_t len, const double *parm ) {
unsigned int ti;
double t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
2084 t = AIR_ABS( t );
ti = AIR_CAST( unsigned int, t );
t -= ti;
f[i] = _C5SEPT( ti, t );
}
}
static void
_c5septN_f( float *f, const float *x, size_t len, const double *parm ) {
unsigned int ti;
2094 float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
ti = AIR_CAST( unsigned int, t );
t -= AIR_CAST( float, ti );
f[i] = AIR_CAST( float, _C5SEPT( ti, t ) );
}
2104 }
static NrrdKernel
_c5sept = {
"C5Septic",
0, returnFour, returnOne,
_c5sept1_f, _c5septN_f, _c5sept1_d, _c5septN_d
};
NrrdKernel *const
nrrdKernelC5Septic = &_c5sept;
2114
#define _DC5SEPT0( x ) ( x*( -3.308641975308642 + x*x*( 4.292181069958848 + x*x*( -2.6998456790123457 + x*0.9785236625514403 ) ) ) )
#define _DC5SEPT1( x ) ( -0.7377829218106996 + x*( 1.9398148148148149 + x*( 0.5559413580246914 + x*( -3.1358024691358026 + x*( 1.1786265432098766 + x*( 0.7212962962962963 - x*0.3818672839506173 ) ) ) ) ) )
#define _DC5SEPT2( x ) ( 0.14022633744855967 + x*( -0.32592592592592595 + x*( -0.29475308641975306 + x*( 1.154320987654321 + x*( -0.9429012345679012 + x*( 0.26435185185185184 - x*0.009542181069958848 ) ) ) ) ) )
#define _DC5SEPT3( x ) ( -0.014223251028806585 + x*( 0.0404320987654321 + x*( 0.011188271604938271 + x*( -0.1646090534979424 + x*( 0.2357253086419753 + x*( -0.13919753086419753 + x*0.03068415637860082 ) ) ) ) ) )
#define _DC5SEPT( i, x ) \
( 0 == i ? _DC5SEPT0( x ) \
: ( 1 == i ? _DC5SEPT1( x ) \
: ( 2 == i ? _DC5SEPT2( x ) \
: ( 3 == i ? _DC5SEPT3( x ) \
: 0.0 ) ) ) )
static double
_dc5sept1_d( double x, const double *parm ) {
unsigned int xi;
2129 int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
xi = AIR_CAST( unsigned int, x );
x -= xi;
return sgn*_DC5SEPT( xi, x );
}
static float
_dc5sept1_f( float x, const double *parm ) {
unsigned int xi;
int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
xi = AIR_CAST( unsigned int, x );
x -= AIR_CAST( float, xi );
return AIR_CAST( float, sgn*_DC5SEPT( xi, x ) );
}
static void
2149 _dc5septN_d( double *f, const double *x, size_t len, const double *parm ) {
unsigned int ti;
double t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
ti = AIR_CAST( unsigned int, t );
2159 t -= ti;
f[i] = sgn*_DC5SEPT( ti, t );
}
}
static void
_dc5septN_f( float *f, const float *x, size_t len, const double *parm ) {
unsigned int ti;
float t;
size_t i;
int sgn = 1;
2170 AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
ti = AIR_CAST( unsigned int, t );
t -= AIR_CAST( float, ti );
f[i] = AIR_CAST( float, sgn*_DC5SEPT( ti, t ) );
}
}
static NrrdKernel
2181 _dc5sept = {
"C5SepticD",
0, returnFour, returnZero,
_dc5sept1_f, _dc5septN_f, _dc5sept1_d, _dc5septN_d
};
NrrdKernel *const
nrrdKernelC5SepticD = &_dc5sept;
#define _DDC5SEPT0( x ) ( -3.308641975308642 + x*x*( 12.876543209876543 + x*x*( -13.499228395061728 + x*5.871141975308642 ) ) )
#define _DDC5SEPT1( x ) ( 1.9398148148148149 + x*( 1.1118827160493827 + x*( -9.407407407407407 + x*( 4.714506172839506 + x*( 3.6064814814814814 - x*2.2912037037037036 ) ) ) ) )
#define _DDC5SEPT2( x ) ( -0.32592592592592595 + x*( -0.5895061728395061 + x*( 3.462962962962963 + x*( -3.771604938271605 + x*( 1.3217592592592593 - x*0.05725308641975309 ) ) ) ) )
#define _DDC5SEPT3( x ) ( 0.0404320987654321 + x*( 0.022376543209876542 + x*( -0.49382716049382713 + x*( 0.9429012345679012 + x*( -0.6959876543209876 + x*0.18410493827160493 ) ) ) ) )
#define _DDC5SEPT( i, x ) \
( 0 == i ? _DDC5SEPT0( x ) \
: ( 1 == i ? _DDC5SEPT1( x ) \
: ( 2 == i ? _DDC5SEPT2( x ) \
2197 : ( 3 == i ? _DDC5SEPT3( x ) \
: 0.0 ) ) ) )
static double
_ddc5sept1_d( double x, const double *parm ) {
unsigned int xi;
AIR_UNUSED( parm );
x = AIR_ABS( x );
xi = AIR_CAST( unsigned int, x );
x -= xi;
return _DDC5SEPT( xi, x );
}
static float
_ddc5sept1_f( float x, const double *parm ) {
unsigned int xi;
AIR_UNUSED( parm );
x = AIR_ABS( x );
xi = AIR_CAST( unsigned int, x );
x -= AIR_CAST( float, xi );
return AIR_CAST( float, _DDC5SEPT( xi, x ) );
2218 }
static void
_ddc5septN_d( double *f, const double *x, size_t len, const double *parm ) {
unsigned int ti;
double t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
ti = AIR_CAST( unsigned int, t );
t -= ti;
f[i] = _DDC5SEPT( ti, t );
}
}
static void
_ddc5septN_f( float *f, const float *x, size_t len, const double *parm ) {
unsigned int ti;
float t;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
ti = AIR_CAST( unsigned int, t );
t -= AIR_CAST( float, ti );
f[i] = AIR_CAST( float, _DDC5SEPT( ti, t ) );
}
}
static NrrdKernel
_ddc5sept = {
"C5SepticDD",
0, returnFour, returnZero,
_ddc5sept1_f, _ddc5septN_f, _ddc5sept1_d, _ddc5septN_d
};
NrrdKernel *const
2257 nrrdKernelC5SepticDD = &_ddc5sept;
#define _DDDC5SEPT0( x ) ( x*( 25.75308641975309 + x*x*( -53.99691358024691 + x*29.35570987654321 ) ) )
#define _DDDC5SEPT1( x ) ( 1.111882716049383 + x*( -18.81481481481481 + x*( 14.14351851851852 + x*( 14.42592592592593 - x*11.45601851851852 ) ) ) )
#define _DDDC5SEPT2( x ) ( -0.5895061728395062 + x*( 6.925925925925926 + x*( -11.31481481481481 + x*( 5.287037037037037 - x*0.2862654320987654 ) ) ) )
#define _DDDC5SEPT3( x ) ( 0.02237654320987654 + x*( -0.9876543209876543 + x*( 2.828703703703704 + x*( -2.783950617283951 + x*0.9205246913580247 ) ) ) )
2263 #define _DDDC5SEPT( i, x ) \
( 0 == i ? _DDDC5SEPT0( x ) \
: ( 1 == i ? _DDDC5SEPT1( x ) \
: ( 2 == i ? _DDDC5SEPT2( x ) \
: ( 3 == i ? _DDDC5SEPT3( x ) \
: 0.0 ) ) ) )
static double
2271 _dddc5sept1_d( double x, const double *parm ) {
unsigned int xi;
int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
xi = AIR_CAST( unsigned int, x );
x -= xi;
return sgn*_DDDC5SEPT( xi, x );
}
2281 static float
_dddc5sept1_f( float x, const double *parm ) {
unsigned int xi;
int sgn = 1;
AIR_UNUSED( parm );
if ( x < 0 ) { x = -x; sgn = -1; }
xi = AIR_CAST( unsigned int, x );
x -= AIR_CAST( float, xi );
return AIR_CAST( float, sgn*_DDDC5SEPT( xi, x ) );
}
2291
static void
_dddc5septN_d( double *f, const double *x, size_t len, const double *parm ) {
unsigned int ti;
double t;
size_t i;
int sgn;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
ti = AIR_CAST( unsigned int, t );
t -= ti;
2304 f[i] = sgn*_DDDC5SEPT( ti, t );
}
}
static void
_dddc5septN_f( float *f, const float *x, size_t len, const double *parm ) {
unsigned int ti;
float t;
size_t i;
int sgn = 1;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
ti = AIR_CAST( unsigned int, t );
t -= AIR_CAST( float, ti );
f[i] = AIR_CAST( float, sgn*_DDDC5SEPT( ti, t ) );
}
}
2323
static NrrdKernel
_dddc5sept = {
"C5SepticDDD",
0, returnFour, returnZero,
_dddc5sept1_f, _dddc5septN_f, _dddc5sept1_d, _dddc5septN_d
};
NrrdKernel *const
2331 nrrdKernelC5SepticDDD = &_dddc5sept;
/* note that this implies a much more accurate inverse than is given
for the splines or other kernels; this is a consequence of GLK
re-purposing the Mathematica expressions for the bpsln7 inverse,
which is unfortunate: c5septic is nearly interpolating, so far
fewer terms would suffice */
#define C5SEPT_AI_LEN 26
2339 static double
_c5sept_ANI_kvals[C5SEPT_AI_LEN] = {
1.072662863909143543451743,
-0.05572032001443187610952953,
0.02453993146603215267432700,
-0.005922375635530229254855750,
0.0009781882769025851448681918,
-0.0002499281491108793120774480,
0.00005196973116762945530292666,
2348 -0.00001090007030248955413371701,
2.425581976693179040189747e-6,
-5.143328756144314306529358e-7,
1.109572595055083858393193e-7,
-2.400323559797703961855318e-8,
5.151959978856239469272136e-9,
-1.111431289951609447815300e-9,
2.394624249806782312051293e-10,
-5.155654818408366273965675e-11,
1.111091879440261302739584e-11,
2358 -2.393303168472914213194646e-12,
5.155527554392687415035171e-13,
-1.110707782883835600110634e-13,
2.392644268109899456937863e-14,
-5.154377464414088544322752e-15,
1.110376957658466603291262e-15,
-2.391459139266885907929865e-16,
5.137528165538909945741180e-17,
-9.024576392408067896802608e-18};
2368 static double
_c5sept_ANI_sup( const double *parm ) {
AIR_UNUSED( parm );
return C5SEPT_AI_LEN + 0.5;
}
static double
_c5sept_ANI_int( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
#define C5SEPT_ANI( ret, tmp, x ) \
tmp = AIR_CAST( unsigned int, x+0.5 ); \
2382 if ( tmp < C5SEPT_AI_LEN ) { \
ret = _c5sept_ANI_kvals[tmp]; \
} else { \
ret = 0.0; \
}
static double
_c5sept_ANI_1d( double x, const double *parm ) {
double ax, r; unsigned int tmp;
AIR_UNUSED( parm );
ax = AIR_ABS( x );
C5SEPT_ANI( r, tmp, ax );
return r;
}
static float
_c5sept_ANI_1f( float x, const double *parm ) {
double ax, r; unsigned int tmp;
2401 AIR_UNUSED( parm );
ax = AIR_ABS( x );
C5SEPT_ANI( r, tmp, ax );
return AIR_CAST( float, r );
}
static void
_c5sept_ANI_Nd( double *f, const double *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
C5SEPT_ANI( r, tmp, ax );
f[i] = r;
2418 }
}
static void
_c5sept_ANI_Nf( float *f, const float *x, size_t len, const double *parm ) {
double ax, r; unsigned int tmp;
size_t i;
AIR_UNUSED( parm );
for ( i=0; i<len; i++ ) {
ax = x[i]; ax = AIR_ABS( ax );
C5SEPT_ANI( r, tmp, ax );
2430 f[i] = AIR_CAST( float, r );
}
}
static NrrdKernel
_nrrdKernelC5SepticApproxInverse = {
"C5SepticAI", 0,
_c5sept_ANI_sup, _c5sept_ANI_int,
_c5sept_ANI_1f, _c5sept_ANI_Nf,
_c5sept_ANI_1d, _c5sept_ANI_Nd
2440 };
NrrdKernel *const
nrrdKernelC5SepticApproxInverse = &_nrrdKernelC5SepticApproxInverse;
/* ------------------------------------------------------------ */
#define _GAUSS( x, sig, cut ) ( \
x >= sig*cut ? 0 \
: exp( -x*x/( 2.0*sig*sig ) )/( sig*2.50662827463100050241 ) )
static double
_nrrdGInt( const double *parm ) {
double cut;
2454 cut = parm[1];
return airErf( cut/sqrt( 2.0 ) );
}
static double
_nrrdGSup( const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
2464 return sig*cut;
}
static double
_nrrdG1_d( double x, const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
x = AIR_ABS( x );
return _GAUSS( x, sig, cut );
}
static float
2478 _nrrdG1_f( float x, const double *parm ) {
float sig, cut;
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
x = AIR_ABS( x );
return AIR_CAST( float, _GAUSS( x, sig, cut ) );
}
static void
_nrrdGN_d( double *f, const double *x, size_t len, const double *parm ) {
double sig, cut, t;
size_t i;
sig = parm[0];
cut = parm[1];
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
2497 f[i] = _GAUSS( t, sig, cut );
}
}
static void
_nrrdGN_f( float *f, const float *x, size_t len, const double *parm ) {
float sig, cut, t;
size_t i;
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _GAUSS( t, sig, cut ) );
}
}
static NrrdKernel
2516 _nrrdKernelG = {
"gauss",
2, _nrrdGSup, _nrrdGInt,
_nrrdG1_f, _nrrdGN_f, _nrrdG1_d, _nrrdGN_d
};
NrrdKernel *const
2522 nrrdKernelGaussian = &_nrrdKernelG;
/* ------------------------------------------------------------ */
#define _DISCRETEGAUSS( xx, sig, abscut ) \
( sig > 0 \
? ( xx > abscut \
? 0 \
: airBesselInExpScaled( AIR_CAST( int, xx + 0.5 ), sig*sig ) ) \
2531 : xx <= 0.5 )
/* the last line used to be AIR_MAX( 0.5, ( ret ) ), but the problem was
that even for reasonable cutoffs, like sigma=6, there would be a
sudden change in the non-zero values at the edge of the kernel with
slowly increasing sigma. The real solution is to have a smarter way
of determining where to cut-off this particular kernel, the
discrete gaussian, when the values are at the low level one would
expect with "sigma=6" when talking about a continuous Gaussian */
#define _DGABSCUT( ret, sig, cut ) \
( ret ) = 0.5 + ceil( ( sig )*( cut ) ); \
2542 ( ret ) = AIR_MAX( 2.5, ( ret ) )
static double
_nrrdDiscGaussianSup( const double *parm ) {
double ret;
_DGABSCUT( ret, parm[0], parm[1] );
return ret;
}
/* the functions are out of their usual definition order because we
2553 use the function evaluation to determine the integral, rather than
trying to do it analytically */
static double
_nrrdDiscGaussian1_d( double xx, const double *parm ) {
double sig, cut;
sig = parm[0];
_DGABSCUT( cut, sig, parm[1] );
xx = AIR_ABS( xx );
return _DISCRETEGAUSS( xx, sig, cut );
}
static double
_nrrdDiscGaussianInt( const double *parm ) {
2568 double sum, cut;
int ii, supp;
_DGABSCUT( cut, parm[0], parm[1] );
supp = AIR_CAST( int, cut );
sum = 0.0;
for ( ii=-supp; ii<=supp; ii++ ) {
sum += _nrrdDiscGaussian1_d( ii, parm );
}
return sum;
}
static float
_nrrdDiscGaussian1_f( float xx, const double *parm ) {
double sig, cut;
sig = parm[0];
_DGABSCUT( cut, sig, parm[1] );
xx = AIR_ABS( xx );
return AIR_CAST( float, _DISCRETEGAUSS( xx, sig, cut ) );
2588 }
static void
_nrrdDiscGaussianN_d( double *f, const double *x,
size_t len, const double *parm ) {
double sig, cut, tt;
size_t ii;
2596 sig = parm[0];
_DGABSCUT( cut, sig, parm[1] );
for ( ii=0; ii<len; ii++ ) {
tt = AIR_ABS( x[ii] );
f[ii] = _DISCRETEGAUSS( tt, sig, cut );
}
}
static void
2605 _nrrdDiscGaussianN_f( float *f, const float *x, size_t len, const double *parm ) {
double sig, cut, tt;
size_t ii;
sig = parm[0];
_DGABSCUT( cut, sig, parm[1] );
for ( ii=0; ii<len; ii++ ) {
tt = AIR_ABS( x[ii] );
f[ii] = AIR_CAST( float, _DISCRETEGAUSS( tt, sig, cut ) );
2614 }
}
static NrrdKernel
_nrrdKernelDiscreteGaussian = {
"discretegauss", 2,
_nrrdDiscGaussianSup, _nrrdDiscGaussianInt,
_nrrdDiscGaussian1_f, _nrrdDiscGaussianN_f,
_nrrdDiscGaussian1_d, _nrrdDiscGaussianN_d
};
2624 NrrdKernel *const
nrrdKernelDiscreteGaussian = &_nrrdKernelDiscreteGaussian;
/* The current implementation of nrrdKernelDiscreteGaussian, with the current
implementation of airBesselInExpScaled, has problems for large
sigma. Until those are fixed, this is a suggested limit on how big sigma
( kparm[0] ) can be and still have an accurate blurring kernel. This
problem can be avoided completely by doing the blurring in frequency
space, which is implemented in teem/src/gage/stackBlur.c's
_stackBlurDiscreteGaussFFT( ), although that has its own problem: in places
2634 where a signal really should zero, the FFT can produce some very
low-amplitude noise ( and hence new extrema ) */
const double nrrdKernelDiscreteGaussianGoodSigmaMax = 6.0;
/* ------------------------------------------------------------ */
#define _DGAUSS( x, sig, cut ) ( \
x >= sig*cut ? 0 \
: -exp( -x*x/( 2.0*sig*sig ) )*x/( sig*sig*sig*2.50662827463100050241 ) )
static double
_nrrdDGInt( const double *parm ) {
AIR_UNUSED( parm );
return 0;
2648 }
static double
_nrrdDGSup( const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
return sig*cut;
}
static double
_nrrdDG1_d( double x, const double *parm ) {
double sig, cut;
int sgn = 1;
sig = parm[0];
cut = parm[1];
if ( x < 0 ) { x = -x; sgn = -1; }
2667 return sgn*_DGAUSS( x, sig, cut );
}
static float
_nrrdDG1_f( float x, const double *parm ) {
float sig, cut;
int sgn = 1;
2674
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
if ( x < 0 ) { x = -x; sgn = -1; }
return AIR_CAST( float, sgn*_DGAUSS( x, sig, cut ) );
}
static void
_nrrdDGN_d( double *f, const double *x, size_t len, const double *parm ) {
double sig, cut, t;
size_t i;
int sgn;
sig = parm[0];
cut = parm[1];
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = sgn*_DGAUSS( t, sig, cut );
}
}
static void
_nrrdDGN_f( float *f, const float *x, size_t len, const double *parm ) {
float sig, cut, t;
size_t i;
int sgn;
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
for ( i=0; i<len; i++ ) {
t = x[i];
if ( t < 0 ) { t = -t; sgn = -1; } else { sgn = 1; }
f[i] = AIR_CAST( float, sgn*_DGAUSS( t, sig, cut ) );
}
}
static NrrdKernel
_nrrdKernelDG = {
"gaussD",
2, _nrrdDGSup, _nrrdDGInt,
_nrrdDG1_f, _nrrdDGN_f, _nrrdDG1_d, _nrrdDGN_d
};
NrrdKernel *const
nrrdKernelGaussianD = &_nrrdKernelDG;
/* ------------------------------------------------------------ */
#define _DDGAUSS( x, sig, cut ) ( \
x >= sig*cut ? 0 \
: exp( -x*x/( 2.0*sig*sig ) )*( x*x-sig*sig ) / \
( sig*sig*sig*sig*sig*2.50662827463100050241 ) )
static double
_nrrdDDGInt( const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
return -0.79788456080286535587*cut*exp( -cut*cut/2 )/( sig*sig );
}
static double
_nrrdDDGSup( const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
return sig*cut;
}
static double
_nrrdDDG1_d( double x, const double *parm ) {
double sig, cut;
sig = parm[0];
cut = parm[1];
x = AIR_ABS( x );
return _DDGAUSS( x, sig, cut );
}
static float
_nrrdDDG1_f( float x, const double *parm ) {
float sig, cut;
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
x = AIR_ABS( x );
return AIR_CAST( float, _DDGAUSS( x, sig, cut ) );
}
static void
_nrrdDDGN_d( double *f, const double *x, size_t len, const double *parm ) {
double sig, cut, t;
size_t i;
sig = parm[0];
cut = parm[1];
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = _DDGAUSS( t, sig, cut );
}
}
static void
_nrrdDDGN_f( float *f, const float *x, size_t len, const double *parm ) {
float sig, cut, t;
size_t i;
sig = AIR_CAST( float, parm[0] );
cut = AIR_CAST( float, parm[1] );
for ( i=0; i<len; i++ ) {
t = x[i];
t = AIR_ABS( t );
f[i] = AIR_CAST( float, _DDGAUSS( t, sig, cut ) );
}
}
static NrrdKernel
_nrrdKernelDDG = {
"gaussDD",
2, _nrrdDDGSup, _nrrdDDGInt,
_nrrdDDG1_f, _nrrdDDGN_f, _nrrdDDG1_d, _nrrdDDGN_d
};
NrrdKernel *const
nrrdKernelGaussianDD = &_nrrdKernelDDG;
/* ------------------------------------------------------------ */
NrrdKernel *
_nrrdKernelStrToKern( char *str ) {
if ( !strcmp( "zero", str ) ) return nrrdKernelZero;
if ( !strcmp( "box", str ) ) return nrrdKernelBox;
if ( !strcmp( "boxsup", str ) ) return nrrdKernelBoxSupportDebug;
if ( !strcmp( "cos4sup", str ) ) return nrrdKernelCos4SupportDebug;
if ( !strcmp( "cos4supd", str ) ) return nrrdKernelCos4SupportDebugD;
if ( !strcmp( "cos4supdd", str ) ) return nrrdKernelCos4SupportDebugDD;
if ( !strcmp( "cos4supddd", str ) ) return nrrdKernelCos4SupportDebugDDD;
if ( !strcmp( "cheap", str ) ) return nrrdKernelCheap;
if ( !strcmp( "hermiteflag", str ) ) return nrrdKernelHermiteScaleSpaceFlag;
if ( !strcmp( "hermite", str ) ) return nrrdKernelHermiteScaleSpaceFlag;
if ( !strcmp( "hermitess", str ) ) return nrrdKernelHermiteScaleSpaceFlag;
if ( !strcmp( "herm", str ) ) return nrrdKernelHermiteScaleSpaceFlag;
if ( !strcmp( "tent", str ) ) return nrrdKernelTent;
if ( !strcmp( "tentd", str ) ) return nrrdKernelForwDiff;
if ( !strcmp( "forwdiff", str ) ) return nrrdKernelForwDiff;
if ( !strcmp( "fordif", str ) ) return nrrdKernelForwDiff;
if ( !strcmp( "centdiff", str ) ) return nrrdKernelCentDiff;
2825 if ( !strcmp( "cendif", str ) ) return nrrdKernelCentDiff;
if ( !strcmp( "bccubic", str ) ) return nrrdKernelBCCubic;
if ( !strcmp( "cubic", str ) ) return nrrdKernelBCCubic;
if ( !strcmp( "bccubicd", str ) ) return nrrdKernelBCCubicD;
if ( !strcmp( "cubicd", str ) ) return nrrdKernelBCCubicD;
if ( !strcmp( "bccubicdd", str ) ) return nrrdKernelBCCubicDD;
if ( !strcmp( "cubicdd", str ) ) return nrrdKernelBCCubicDD;
if ( !strcmp( "ctmr", str ) ) return nrrdKernelCatmullRom;
if ( !strcmp( "catmull-rom", str ) ) return nrrdKernelCatmullRom;
if ( !strcmp( "ctmrd", str ) ) return nrrdKernelCatmullRomD;
if ( !strcmp( "catmull-romd", str ) ) return nrrdKernelCatmullRomD;
if ( !strcmp( "ctmrdd", str ) ) return nrrdKernelCatmullRomDD;
if ( !strcmp( "catmull-romdd", str ) ) return nrrdKernelCatmullRomDD;
if ( !strcmp( "ctmrsup", str ) ) return nrrdKernelCatmullRomSupportDebug;
if ( !strcmp( "ctmrsupd", str ) ) return nrrdKernelCatmullRomSupportDebugD;
2840 if ( !strcmp( "ctmrsupdd", str ) ) return nrrdKernelCatmullRomSupportDebugDD;
if ( !strcmp( "aquartic", str ) ) return nrrdKernelAQuartic;
if ( !strcmp( "quartic", str ) ) return nrrdKernelAQuartic;
if ( !strcmp( "aquarticd", str ) ) return nrrdKernelAQuarticD;
if ( !strcmp( "quarticd", str ) ) return nrrdKernelAQuarticD;
if ( !strcmp( "aquarticdd", str ) ) return nrrdKernelAQuarticDD;
if ( !strcmp( "quarticdd", str ) ) return nrrdKernelAQuarticDD;
if ( !strcmp( "c3quintic", str ) ) return nrrdKernelC3Quintic;
if ( !strcmp( "c3q", str ) ) return nrrdKernelC3Quintic;
if ( !strcmp( "c3quinticd", str ) ) return nrrdKernelC3QuinticD;
if ( !strcmp( "c3qd", str ) ) return nrrdKernelC3QuinticD;
if ( !strcmp( "c3quinticdd", str ) ) return nrrdKernelC3QuinticDD;
if ( !strcmp( "c3qdd", str ) ) return nrrdKernelC3QuinticDD;
if ( !strcmp( "c4hexic", str ) ) return nrrdKernelC4Hexic;
if ( !strcmp( "c4hai", str ) ) return nrrdKernelC4HexicApproxInverse;
if ( !strcmp( "c4hexicai", str ) ) return nrrdKernelC4HexicApproxInverse;
if ( !strcmp( "c4h", str ) ) return nrrdKernelC4Hexic;
if ( !strcmp( "c4hexicd", str ) ) return nrrdKernelC4HexicD;
if ( !strcmp( "c4hd", str ) ) return nrrdKernelC4HexicD;
if ( !strcmp( "c4hexicdd", str ) ) return nrrdKernelC4HexicDD;
if ( !strcmp( "c4hdd", str ) ) return nrrdKernelC4HexicDD;
if ( !strcmp( "c4hexicddd", str ) ) return nrrdKernelC4HexicDDD;
if ( !strcmp( "c4hddd", str ) ) return nrrdKernelC4HexicDDD;
if ( !strcmp( "c5septic", str ) ) return nrrdKernelC5Septic;
if ( !strcmp( "c5septicd", str ) ) return nrrdKernelC5SepticD;
if ( !strcmp( "c5septicdd", str ) ) return nrrdKernelC5SepticDD;
if ( !strcmp( "c5septicddd", str ) )return nrrdKernelC5SepticDDD;
if ( !strcmp( "c5septicai", str ) ) return nrrdKernelC5SepticApproxInverse;
if ( !strcmp( "gaussian", str ) ) return nrrdKernelGaussian;
if ( !strcmp( "gauss", str ) ) return nrrdKernelGaussian;
if ( !strcmp( "gaussiand", str ) ) return nrrdKernelGaussianD;
if ( !strcmp( "gaussd", str ) ) return nrrdKernelGaussianD;
if ( !strcmp( "gd", str ) ) return nrrdKernelGaussianD;
if ( !strcmp( "gaussiandd", str ) ) return nrrdKernelGaussianDD;
if ( !strcmp( "gaussdd", str ) ) return nrrdKernelGaussianDD;
if ( !strcmp( "gdd", str ) ) return nrrdKernelGaussianDD;
if ( !strcmp( "ds", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "dscale", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "dg", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "dgauss", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "dgaussian", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "discretegauss", str ) ) return nrrdKernelDiscreteGaussian;
if ( !strcmp( "hann", str ) ) return nrrdKernelHann;
if ( !strcmp( "hannd", str ) ) return nrrdKernelHannD;
if ( !strcmp( "hanndd", str ) ) return nrrdKernelHannDD;
if ( !strcmp( "bkmn", str ) ) return nrrdKernelBlackman;
if ( !strcmp( "black", str ) ) return nrrdKernelBlackman;
if ( !strcmp( "blackman", str ) ) return nrrdKernelBlackman;
if ( !strcmp( "bkmnd", str ) ) return nrrdKernelBlackmanD;
if ( !strcmp( "blackd", str ) ) return nrrdKernelBlackmanD;
if ( !strcmp( "blackmand", str ) ) return nrrdKernelBlackmanD;
if ( !strcmp( "bkmndd", str ) ) return nrrdKernelBlackmanDD;
if ( !strcmp( "blackdd", str ) ) return nrrdKernelBlackmanDD;
if ( !strcmp( "blackmandd", str ) ) return nrrdKernelBlackmanDD;
if ( !strcmp( "bspl1", str ) ) return nrrdKernelBSpline1;
if ( !strcmp( "bspln1", str ) ) return nrrdKernelBSpline1;
if ( !strcmp( "bspl1d", str ) ) return nrrdKernelBSpline1D;
if ( !strcmp( "bspln1d", str ) ) return nrrdKernelBSpline1D;
if ( !strcmp( "bspl2", str ) ) return nrrdKernelBSpline2;
if ( !strcmp( "bspln2", str ) ) return nrrdKernelBSpline2;
if ( !strcmp( "bspl2d", str ) ) return nrrdKernelBSpline2D;
if ( !strcmp( "bspln2d", str ) ) return nrrdKernelBSpline2D;
if ( !strcmp( "bspl2dd", str ) ) return nrrdKernelBSpline2DD;
if ( !strcmp( "bspln2dd", str ) ) return nrrdKernelBSpline2DD;
if ( !strcmp( "bspl3", str ) ) return nrrdKernelBSpline3;
if ( !strcmp( "bspln3", str ) ) return nrrdKernelBSpline3;
if ( !strcmp( "bspl3ai", str ) ) return nrrdKernelBSpline3ApproxInverse;
if ( !strcmp( "bspln3ai", str ) ) return nrrdKernelBSpline3ApproxInverse;
if ( !strcmp( "bspl3d", str ) ) return nrrdKernelBSpline3D;
if ( !strcmp( "bspln3d", str ) ) return nrrdKernelBSpline3D;
if ( !strcmp( "bspl3dd", str ) ) return nrrdKernelBSpline3DD;
if ( !strcmp( "bspln3dd", str ) ) return nrrdKernelBSpline3DD;
if ( !strcmp( "bspl3ddd", str ) ) return nrrdKernelBSpline3DDD;
if ( !strcmp( "bspln3ddd", str ) ) return nrrdKernelBSpline3DDD;
if ( !strcmp( "bspl4", str ) ) return nrrdKernelBSpline4;
if ( !strcmp( "bspln4", str ) ) return nrrdKernelBSpline4;
if ( !strcmp( "bspl4d", str ) ) return nrrdKernelBSpline4D;
if ( !strcmp( "bspln4d", str ) ) return nrrdKernelBSpline4D;
if ( !strcmp( "bspl4dd", str ) ) return nrrdKernelBSpline4DD;
if ( !strcmp( "bspln4dd", str ) ) return nrrdKernelBSpline4DD;
if ( !strcmp( "bspl4ddd", str ) ) return nrrdKernelBSpline4DDD;
if ( !strcmp( "bspln4ddd", str ) ) return nrrdKernelBSpline4DDD;
if ( !strcmp( "bspl5", str ) ) return nrrdKernelBSpline5;
if ( !strcmp( "bspln5", str ) ) return nrrdKernelBSpline5;
if ( !strcmp( "bspl5ai", str ) ) return nrrdKernelBSpline5ApproxInverse;
if ( !strcmp( "bspln5ai", str ) ) return nrrdKernelBSpline5ApproxInverse;
if ( !strcmp( "bspl5d", str ) ) return nrrdKernelBSpline5D;
if ( !strcmp( "bspln5d", str ) ) return nrrdKernelBSpline5D;
if ( !strcmp( "bspl5dd", str ) ) return nrrdKernelBSpline5DD;
if ( !strcmp( "bspln5dd", str ) ) return nrrdKernelBSpline5DD;
if ( !strcmp( "bspl5ddd", str ) ) return nrrdKernelBSpline5DDD;
if ( !strcmp( "bspln5ddd", str ) ) return nrrdKernelBSpline5DDD;
if ( !strcmp( "bspl6", str ) ) return nrrdKernelBSpline6;
if ( !strcmp( "bspln6", str ) ) return nrrdKernelBSpline6;
if ( !strcmp( "bspl6d", str ) ) return nrrdKernelBSpline6D;
if ( !strcmp( "bspln6d", str ) ) return nrrdKernelBSpline6D;
if ( !strcmp( "bspl6dd", str ) ) return nrrdKernelBSpline6DD;
if ( !strcmp( "bspln6dd", str ) ) return nrrdKernelBSpline6DD;
if ( !strcmp( "bspl6ddd", str ) ) return nrrdKernelBSpline6DDD;
if ( !strcmp( "bspln6ddd", str ) ) return nrrdKernelBSpline6DDD;
if ( !strcmp( "bspl7", str ) ) return nrrdKernelBSpline7;
if ( !strcmp( "bspln7", str ) ) return nrrdKernelBSpline7;
if ( !strcmp( "bspl7ai", str ) ) return nrrdKernelBSpline7ApproxInverse;
if ( !strcmp( "bspln7ai", str ) ) return nrrdKernelBSpline7ApproxInverse;
if ( !strcmp( "bspl7d", str ) ) return nrrdKernelBSpline7D;
if ( !strcmp( "bspln7d", str ) ) return nrrdKernelBSpline7D;
if ( !strcmp( "bspl7dd", str ) ) return nrrdKernelBSpline7DD;
if ( !strcmp( "bspln7dd", str ) ) return nrrdKernelBSpline7DD;
if ( !strcmp( "bspl7ddd", str ) ) return nrrdKernelBSpline7DDD;
if ( !strcmp( "bspln7ddd", str ) ) return nrrdKernelBSpline7DDD;
return NULL;
}
/* this returns a number between -1 and max;
it does NOT do the increment-by-one;
it does NOT do range checking */
int
_nrrdKernelParseTMFInt( int *val, char *str ) {
static const char me[]="nrrdKernelParseTMFInt";
if ( !strcmp( "n", str ) ) {
*val = -1;
} else {
if ( 1 != sscanf( str, "%d", val ) ) {
biffAddf( NRRD, "%s: couldn't parse \"%s\" as int", me, str );
return 1;
}
}
return 0;
}
int
nrrdKernelParse( const NrrdKernel **kernelP,
double *parm, const char *_str ) {
static const char me[]="nrrdKernelParse";
char str[AIR_STRLEN_HUGE],
kstr[AIR_STRLEN_MED], *_pstr=NULL, *pstr,
*tmfStr[4] = {NULL, NULL, NULL, NULL};
int tmfD, tmfC, tmfA;
unsigned int jj, haveParm, needParm;
airArray *mop;
if ( !( kernelP && parm && _str ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
/* [jorik] ( if i understood this correctly ) parm is always of length
** NRRD_KERNEL_PARMS_NUM. We have to clear all parameters here, since
** nrrdKernelSet copies all arguments into its own array later, and
** copying uninitialised memory is bad ( it traps my memory debugger ).
*/
for ( jj=0; jj<NRRD_KERNEL_PARMS_NUM; jj++ ) {
parm[jj] = 0;
}
airStrcpy( str, AIR_STRLEN_HUGE, _str );
strcpy( kstr, "" );
pstr = NULL;
pstr = strchr( str, ':' );
if ( pstr ) {
*pstr = '\0';
_pstr = ++pstr;
}
strcpy( kstr, str );
airToLower( kstr );
mop = airMopNew( );
/* first see if its a TMF, then try parsing it as the other stuff */
if ( kstr == strstr( kstr, "tmf" ) ) {
3009 if ( 4 == airParseStrS( tmfStr, pstr, ", ", 4 ) ) {
airMopAdd( mop, tmfStr[0], airFree, airMopAlways );
airMopAdd( mop, tmfStr[1], airFree, airMopAlways );
airMopAdd( mop, tmfStr[2], airFree, airMopAlways );
airMopAdd( mop, tmfStr[3], airFree, airMopAlways );
/* a TMF with a parameter: D, C, A, a */
if ( 1 != airSingleSscanf( tmfStr[3], "%lg", parm ) ) {
biffAddf( NRRD, "%s: couldn't parse TMF parameter \"%s\" as double",
me, tmfStr[3] );
airMopError( mop ); return 1;
}
} else if ( 3 == airParseStrS( tmfStr, pstr, ", ", 3 ) ) {
airMopAdd( mop, tmfStr[0], airFree, airMopAlways );
airMopAdd( mop, tmfStr[1], airFree, airMopAlways );
airMopAdd( mop, tmfStr[2], airFree, airMopAlways );
/* a TMF without a parameter: D, C, A ==> a=0.0 */
parm[0] = 0.0;
} else {
biffAddf( NRRD, "%s: TMF kernels require 3 arguments D, C, A "
"in the form tmf:D, C, A", me );
airMopError( mop ); return 1;
}
3031 if ( _nrrdKernelParseTMFInt( &tmfD, tmfStr[0] )
|| _nrrdKernelParseTMFInt( &tmfC, tmfStr[1] )
|| _nrrdKernelParseTMFInt( &tmfA, tmfStr[2] ) ) {
biffAddf( NRRD, "%s: problem parsing \"%s, %s, %s\" as D, C, A "
"for TMF kernel", me, tmfStr[0], tmfStr[1], tmfStr[2] );
airMopError( mop ); return 1;
}
if ( !AIR_IN_CL( -1, tmfD, ( int )nrrdKernelTMF_maxD ) ) {
biffAddf( NRRD, "%s: derivative value %d outside range [-1, %d]",
me, tmfD, nrrdKernelTMF_maxD );
airMopError( mop ); return 1;
}
if ( !AIR_IN_CL( -1, tmfC, ( int )nrrdKernelTMF_maxC ) ) {
biffAddf( NRRD, "%s: continuity value %d outside range [-1, %d]",
me, tmfC, nrrdKernelTMF_maxC );
airMopError( mop ); return 1;
}
if ( !AIR_IN_CL( 1, tmfA, ( int )nrrdKernelTMF_maxA ) ) {
biffAddf( NRRD, "%s: accuracy value %d outside range [1, %d]",
me, tmfA, nrrdKernelTMF_maxA );
airMopError( mop ); return 1;
}
/*
fprintf( stderr, "!%s: D, C, A = %d, %d, %d --> %d, %d, %d\n", me,
tmfD, tmfC, tmfA, tmfD+1, tmfC+1, tmfA );
*/
*kernelP = nrrdKernelTMF[tmfD+1][tmfC+1][tmfA];
} else {
/* its not a TMF */
if ( !( *kernelP = _nrrdKernelStrToKern( kstr ) ) ) {
biffAddf( NRRD, "%s: kernel \"%s\" not recognized", me, kstr );
airMopError( mop ); return 1;
}
if ( ( *kernelP )->numParm > NRRD_KERNEL_PARMS_NUM ) {
biffAddf( NRRD, "%s: kernel \"%s\" requests %d parameters > max %d",
me, kstr, ( *kernelP )->numParm, NRRD_KERNEL_PARMS_NUM );
airMopError( mop ); return 1;
}
if ( *kernelP == nrrdKernelGaussian ||
*kernelP == nrrdKernelGaussianD ||
*kernelP == nrrdKernelGaussianDD ||
*kernelP == nrrdKernelDiscreteGaussian ||
*kernelP == nrrdKernelBoxSupportDebug ||
*kernelP == nrrdKernelCos4SupportDebug ||
*kernelP == nrrdKernelCos4SupportDebugD ||
*kernelP == nrrdKernelCos4SupportDebugDD ||
*kernelP == nrrdKernelCos4SupportDebugDDD ) {
/* for these kernels, we need all the parameters given explicitly */
needParm = ( *kernelP )->numParm;
} else {
/* For everything else ( note that TMF kernels are handled
separately ), we can make do with one less than the required,
by using the default spacing */
needParm = ( ( *kernelP )->numParm > 0
? ( *kernelP )->numParm - 1
: 0 );
}
if ( needParm > 0 && !pstr ) {
biffAddf( NRRD, "%s: didn't get any of %d required doubles after "
3090 "colon in \"%s\"",
me, needParm, kstr );
airMopError( mop ); return 1;
}
for ( haveParm=0; haveParm<( *kernelP )->numParm; haveParm++ ) {
if ( !pstr )
break;
if ( 1 != airSingleSscanf( pstr, "%lg", parm+haveParm ) ) {
biffAddf( NRRD, "%s: trouble parsing \"%s\" as double ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
}
if ( ( pstr = strchr( pstr, ', ' ) ) ) {
pstr++;
if ( !*pstr ) {
biffAddf( NRRD, "%s: nothing after last comma in \"%s\" ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
3108 }
}
}
/* haveParm is now the number of parameters that were parsed. */
if ( haveParm < needParm ) {
biffAddf( NRRD, "%s: parsed only %d of %d required doubles "
"from \"%s\" ( in \"%s\" )",
me, haveParm, needParm, _pstr, _str );
airMopError( mop ); return 1;
} else if ( haveParm == needParm &&
needParm == ( *kernelP )->numParm-1 ) {
/* shift up parsed values, and set parm[0] to default */
for ( jj=haveParm; jj>=1; jj-- ) {
parm[jj] = parm[jj-1];
}
parm[0] = nrrdDefaultKernelParm0;
} else {
if ( pstr ) {
biffAddf( NRRD, "%s: \"%s\" ( in \"%s\" ) has more than %d doubles",
me, _pstr, _str, ( *kernelP )->numParm );
airMopError( mop ); return 1;
}
}
}
/*
fprintf( stderr, "%s: %g %g %g %g %g\n", me,
parm[0], parm[1], parm[2], parm[3], parm[4] );
*/
airMopOkay( mop );
return 0;
}
int
nrrdKernelSpecParse( NrrdKernelSpec *ksp, const char *str ) {
static const char me[]="nrrdKernelSpecParse";
const NrrdKernel *kern;
double kparm[NRRD_KERNEL_PARMS_NUM];
if ( !( ksp && str ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdKernelParse( &kern, kparm, str ) ) {
biffAddf( NRRD, "%s: ", me );
return 1;
}
nrrdKernelSpecSet( ksp, kern, kparm );
return 0;
}
/*
3159 ** note that the given string has to be allocated for a certain size
** which is plenty big
*/
int
nrrdKernelSpecSprint( char str[AIR_STRLEN_LARGE], const NrrdKernelSpec *ksp ) {
static const char me[]="nrrdKernelSpecSprint";
unsigned int warnLen = AIR_STRLEN_LARGE/3;
char stmp[AIR_STRLEN_LARGE];
if ( !( str && ksp ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( strlen( ksp->kernel->name ) > warnLen ) {
biffAddf( NRRD, "%s: kernel name ( len %s ) might lead to overflow", me,
airSprintSize_t( stmp, strlen( ksp->kernel->name ) ) );
return 1;
}
if ( strstr( ksp->kernel->name, "TMF" ) ) {
/* these are handled differently; the identification of the
kernel is actually packaged as kernel parameters */
if ( !( ksp->kernel->name == strstr( ksp->kernel->name, "TMF" ) ) ) {
biffAddf( NRRD, "%s: TMF kernel name %s didn't start with TMF",
me, ksp->kernel->name );
return 1;
}
/* 0123456789012 */
/* TMF_dX_cX_Xef */
if ( !( 13 == strlen( ksp->kernel->name )
&& '_' == ksp->kernel->name[3]
&& '_' == ksp->kernel->name[6]
&& '_' == ksp->kernel->name[9] ) ) {
biffAddf( NRRD, "%s: sorry, expected strlen( %s ) = 13 with 3 _s",
me, ksp->kernel->name );
return 1;
}
sprintf( str, "tmf:%c, %c, %c",
ksp->kernel->name[5],
ksp->kernel->name[8],
ksp->kernel->name[10] );
/* see if the single parm should be added on */
if ( 0.0 != ksp->parm[0] ) {
sprintf( stmp, ", %.17g", ksp->parm[0] );
strcat( str, stmp );
}
} else {
strcpy( str, ksp->kernel->name );
if ( ksp->kernel->numParm ) {
unsigned int pi;
for ( pi=0; pi<ksp->kernel->numParm; pi++ ) {
sprintf( stmp, "%c%.17g", ( !pi ? ':' : ', ' ), ksp->parm[pi] );
if ( strlen( str ) + strlen( stmp ) > warnLen ) {
biffAddf( NRRD, "%s: kernel parm %u could overflow", me, pi );
return 1;
}
strcat( str, stmp );
}
}
}
return 0;
}
int
nrrdKernelSprint( char str[AIR_STRLEN_LARGE], const NrrdKernel *kernel,
const double kparm[NRRD_KERNEL_PARMS_NUM] ) {
static const char me[]="nrrdKernelSprint";
NrrdKernelSpec ksp;
nrrdKernelSpecSet( &ksp, kernel, kparm );
3228 if ( nrrdKernelSpecSprint( str, &ksp ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
** This DOES make an effort to set *differ based on "ordering" ( -1 or +1 )
** but HEY that's very contrived; why bother?
*/
int
nrrdKernelCompare( const NrrdKernel *kernA,
const double parmA[NRRD_KERNEL_PARMS_NUM],
const NrrdKernel *kernB,
const double parmB[NRRD_KERNEL_PARMS_NUM],
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdKernelCompare";
unsigned int pnum, pidx;
if ( !( kernA && kernB && differ ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, or %p )", me,
AIR_CVOIDP( kernA ), AIR_CVOIDP( kernB ), AIR_VOIDP( differ ) );
return 1;
}
if ( kernA != kernB ) {
*differ = kernA < kernB ? -1 : 1;
if ( explain ) {
sprintf( explain, "kernA %s kernB", *differ < 0 ? "<" : ">" );
}
return 0;
}
pnum = kernA->numParm;
if ( !pnum ) {
/* actually, we're done: no parms and kernels are equal */
*differ = 0;
return 0;
}
if ( !( parmA && parmB ) ) {
biffAddf( NRRD, "%s: kernel %s needs %u parms but got NULL parm vectors",
me, kernA->name ? kernA->name : "( unnamed )", pnum );
return 0;
}
for ( pidx=0; pidx<pnum; pidx++ ) {
if ( parmA[pidx] != parmB[pidx] ) {
*differ = parmA[pidx] < parmB[pidx] ? -1 : 1;
if ( explain ) {
sprintf( explain, "parmA[%u]=%f %s parmB[%u]=%f", pidx, parmA[pidx],
*differ < 0 ? "<" : ">", pidx, parmB[pidx] );
}
return 0;
}
}
/* so far nothing unequal */
*differ = 0;
return 0;
}
/*
** This DOES NOT make an effort to set *differ based on "ordering";
*/
int
nrrdKernelSpecCompare( const NrrdKernelSpec *aa,
const NrrdKernelSpec *bb,
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdKernelSpecCompare";
char subexplain[AIR_STRLEN_LARGE];
if ( !( differ ) ) {
biffAddf( NRRD, "%s: got NULL differ", me );
return 1;
}
if ( !!aa != !!bb ) {
if ( explain ) {
sprintf( explain, "different NULL-ities of kspec itself %s != %s",
aa ? "non-NULL" : "NULL",
bb ? "non-NULL" : "NULL" );
}
*differ = 1; return 0;
}
if ( !aa ) {
/* got two NULL kernel specs ==> equal */
*differ = 0; return 0;
}
if ( !!aa->kernel != !!bb->kernel ) {
if ( explain ) {
sprintf( explain, "different NULL-ities of kspec->kernel %s != %s",
aa->kernel ? "non-NULL" : "NULL",
bb->kernel ? "non-NULL" : "NULL" );
}
*differ = 1; return 0;
}
if ( !aa->kernel ) {
/* both kernels NULL, can't do anything informative with parms */
*differ = 0; return 0;
}
if ( nrrdKernelCompare( aa->kernel, aa->parm,
bb->kernel, bb->parm,
differ, subexplain ) ) {
biffAddf( NRRD, "%s: trouble comparing kernels", me );
return 1;
}
if ( *differ ) {
if ( explain ) {
sprintf( explain, "kern/parm pairs differ: %s", subexplain );
}
*differ = 1; /* losing ordering info ( of dubious value ) */
return 0;
}
*differ = 0;
return 0;
}
/*
******** nrrdKernelCheck
**
** Makes sure a given kernel is behaving as expected
**
** Tests:
** nrrdKernelSprint
** nrrdKernelParse
** nrrdKernelCompare
** nrrdKernelSpecNew
** nrrdKernelSpecNix
** nrrdKernelSpecSet
** nrrdKernelSpecSprint
** nrrdKernelSpecParse
** and also exercises all the ways of evaluating the kernel and
** makes sure they all agree, and agree with the integral kernel, if given
*/
int
nrrdKernelCheck( const NrrdKernel *kern,
const double parm[NRRD_KERNEL_PARMS_NUM],
size_t evalNum, double epsilon,
unsigned int diffOkEvalMax,
unsigned int diffOkIntglMax,
const NrrdKernel *ikern,
const double iparm[NRRD_KERNEL_PARMS_NUM] ) {
const NrrdKernel *parsedkern;
double parsedparm[NRRD_KERNEL_PARMS_NUM], supp, integral;
static const char me[]="nrrdKernelCheck";
char kstr[AIR_STRLEN_LARGE], kspstr[AIR_STRLEN_LARGE],
explain[AIR_STRLEN_LARGE], stmp[AIR_STRLEN_SMALL];
int differ;
size_t evalIdx;
double *dom_d, *ran_d, wee;
float *dom_f, *ran_f;
unsigned int diffOkEvalNum, diffOkIntglNum;
NrrdKernelSpec *kspA, *kspB;
airArray *mop;
mop = airMopNew( );
if ( !kern ) {
biffAddf( NRRD, "%s: got NULL kernel", me );
airMopError( mop ); return 1;
}
if ( !( evalNum > 20 ) ) {
biffAddf( NRRD, "%s: need evalNum > 20", me );
airMopError( mop ); return 1;
}
if ( !( kern->support && kern->integral
&& kern->eval1_f && kern->evalN_f
&& kern->eval1_d && kern->evalN_d ) ) {
biffAddf( NRRD, "%s: kernel has NULL fields ( %d, %d, %d, %d, %d, %d )", me,
!!( kern->support ), !!( kern->integral ),
!!( kern->eval1_f ), !!( kern->evalN_f ),
!!( kern->eval1_d ), !!( kern->evalN_d ) );
airMopError( mop ); return 1;
}
kspA = nrrdKernelSpecNew( );
airMopAdd( mop, kspA, ( airMopper )nrrdKernelSpecNix, airMopAlways );
kspB = nrrdKernelSpecNew( );
airMopAdd( mop, kspB, ( airMopper )nrrdKernelSpecNix, airMopAlways );
nrrdKernelSpecSet( kspA, kern, parm );
if ( nrrdKernelSprint( kstr, kern, parm )
|| nrrdKernelSpecSprint( kspstr, kspA ) ) {
biffAddf( NRRD, "%s: trouble", me );
airMopError( mop ); return 1;
}
if ( strcmp( kstr, kspstr ) ) {
biffAddf( NRRD, "%s: sprinted kernel |%s| != kspec |%s|", me, kstr, kspstr );
airMopError( mop ); return 1;
}
if ( nrrdKernelParse( &parsedkern, parsedparm, kstr )
|| nrrdKernelSpecParse( kspB, kstr ) ) {
biffAddf( NRRD, "%s: trouble parsing |%s| back to kern/parm pair or kspec",
me, kstr );
airMopError( mop ); return 1;
}
if ( nrrdKernelCompare( kern, parm, parsedkern, parsedparm,
&differ, explain ) ) {
biffAddf( NRRD, "%s: trouble comparing kern/parm pairs", me );
airMopError( mop ); return 1;
}
if ( differ ) {
biffAddf( NRRD, "%s: given and re-parsed kernels differ: %s", me, explain );
airMopError( mop ); return 1;
}
if ( nrrdKernelCompare( kspA->kernel, kspA->parm,
kspB->kernel, kspB->parm,
&differ, explain ) ) {
biffAddf( NRRD, "%s: trouble comparing kspecs", me );
airMopError( mop ); return 1;
}
if ( differ ) {
biffAddf( NRRD, "%s: given and re-parsed kspecs differ: %s", me, explain );
airMopError( mop ); return 1;
}
supp = kern->support( parm );
/* wee is the step between evaluation points */
wee = 2*supp/AIR_CAST( double, evalNum );
if ( ( kern->eval1_d )( supp+wee/1000, parm ) ||
( kern->eval1_d )( supp+wee, parm ) ||
( kern->eval1_d )( supp+10*wee, parm ) ||
( kern->eval1_d )( -supp-wee/1000, parm ) ||
( kern->eval1_d )( -supp-wee, parm ) ||
( kern->eval1_d )( -supp-10*wee, parm ) ) {
if ( nrrdKernelCheap != kern ) {
/* the "cheap" kernel alone gets a pass on reporting its support */
biffAddf( NRRD, "%s: kern %s is non-zero outside support %g",
me, kstr, supp );
airMopError( mop ); return 1;
}
}
/* allocate domain and range for both float and double */
dom_d = AIR_CALLOC( evalNum, double );
airMopAdd( mop, dom_d, airFree, airMopAlways );
ran_d = AIR_CALLOC( evalNum, double );
airMopAdd( mop, ran_d, airFree, airMopAlways );
dom_f = AIR_CALLOC( evalNum, float );
airMopAdd( mop, dom_f, airFree, airMopAlways );
3461 ran_f = AIR_CALLOC( evalNum, float );
airMopAdd( mop, ran_f, airFree, airMopAlways );
if ( !( dom_d && ran_d && dom_f && ran_f ) ) {
biffAddf( NRRD, "%s: couldn't alloc buffers for %s values for %s",
me, airSprintSize_t( stmp, evalNum ), kstr );
airMopError( mop ); return 1;
}
for ( evalIdx=0; evalIdx<evalNum; evalIdx++ ) {
dom_d[evalIdx] = AIR_AFFINE( -0.5, evalIdx,
AIR_CAST( double, evalNum )-0.5,
-supp, supp );
dom_f[evalIdx] = AIR_CAST( float, dom_d[evalIdx] );
}
/* do the vector evaluations */
kern->evalN_f( ran_f, dom_f, evalNum, parm );
kern->evalN_d( ran_d, dom_d, evalNum, parm );
/*
for ( evalIdx=0; evalIdx<evalNum; evalIdx++ ) {
fprintf( stderr, "%u %g --> %g\n", AIR_CAST( unsigned int, evalIdx ),
dom_d[evalIdx], ran_d[evalIdx] );
}
*/
/* compare evaluations ( and maybe derivatives ) and numerically compute
integral */
diffOkEvalNum = 0;
diffOkIntglNum = 0;
integral = 0.0;
for ( evalIdx=0; evalIdx<evalNum; evalIdx++ ) {
double single_f, single_d;
single_f = kern->eval1_f( dom_f[evalIdx], parm );
single_d = kern->eval1_d( dom_d[evalIdx], parm );
integral += single_d;
/* single float vs vector float */
if ( nrrdKernelForwDiff == kern
|| nrrdKernelBCCubic == kern
|| nrrdKernelBCCubicDD == kern
|| nrrdKernelAQuarticDD == kern ) {
/* HEY this is crazy: need a special epsilon for these kernels;
WHY WHY do these kernels evaluate to different things in the
single versus the vector case? */
float specEps;
if ( nrrdKernelForwDiff == kern ) {
specEps = 5e-9f;
} else if ( nrrdKernelBCCubic == kern ) {
specEps = 5e-8f;
} else if ( nrrdKernelBCCubicDD == kern ) {
specEps = 5e-8f;
} else if ( nrrdKernelAQuarticDD == kern ) {
specEps = 5e-8f;
} else {
specEps = 0.0;
}
if ( fabs( single_f - ran_f[evalIdx] ) > specEps ) {
biffAddf( NRRD, "%s: %s ( eval1_f( %.17g )=%.17g ) != "
"( evalN_f( %.17g )=%.17g ) by %.17g > %.17g",
me, kstr, dom_f[evalIdx], single_f,
dom_f[evalIdx], ran_f[evalIdx],
fabs( single_f - ran_f[evalIdx] ), specEps );
airMopError( mop ); return 1;
}
} else {
if ( single_f != ran_f[evalIdx] ) {
biffAddf( NRRD, "%s: %s ( eval1_f( %.17g )=%.17g ) != "
"( evalN_f( %.17g )=%.17g )",
me, kstr, dom_f[evalIdx], single_f,
dom_f[evalIdx], ran_f[evalIdx] );
airMopError( mop ); return 1;
}
}
/* single double vs vector double */
if ( single_d != ran_d[evalIdx] ) {
biffAddf( NRRD, "%s: %s ( eval1_d( %.17g )=%.17g ) != ( evalN_d( %.17g )=%.17g )",
me, kstr, dom_d[evalIdx], single_d,
dom_d[evalIdx], ran_d[evalIdx] );
airMopError( mop ); return 1;
}
/* single float vs single double */
if ( fabs( single_f - single_d ) > epsilon ) {
diffOkEvalNum++;
if ( diffOkEvalNum > diffOkEvalMax ) {
biffAddf( NRRD,
"%s: %s |eval1_f( %.17g )=%.17g ) - ( eval1_d( %.17g )=%.17g )|"
" %.17g > epsilon %.17g too many times ( %u > %u )", me,
kstr, dom_f[evalIdx], single_f, dom_d[evalIdx], single_d,
fabs( single_f - single_d ), epsilon,
diffOkEvalNum, diffOkEvalMax );
airMopError( mop ); return 1;
}
}
/* check whether we're the derivative of ikern */
if ( ikern ) {
double forw, back, ndrv;
forw = ikern->eval1_d( dom_d[evalIdx] + wee/2, iparm );
back = ikern->eval1_d( dom_d[evalIdx] - wee/2, iparm );
ndrv = ( forw - back )/wee;
if ( fabs( ndrv - single_d ) > epsilon ) {
diffOkIntglNum++;
if ( diffOkIntglNum > diffOkIntglMax ) {
biffAddf( NRRD, "%s: %s( %.17g ) |num deriv( %s ) %.17g - %.17g| "
"%.17g > %.17g too many times ( %u > %u )",
me, kstr, dom_d[evalIdx], ikern->name, ndrv, single_d,
fabs( ndrv - single_d ), epsilon,
diffOkIntglNum, diffOkIntglMax );
airMopError( mop ); return 1;
}
}
}
}
integral *= 2*supp/( AIR_CAST( double, evalNum ) );
/* the "cheap" kernel alone gets a pass on reporting its integral */
if ( nrrdKernelCheap != kern ) {
double hackeps=10;
/* hackeps is clearly a hack to permit the integral to have greater
error than any single evaluation; there must be a more principled
way to set this */
if ( fabs( integral - kern->integral( parm ) ) > hackeps*epsilon ) {
biffAddf( NRRD, "%s: %s |numerical integral %.17g - claimed %.17g| "
"%.17g > %.17g", me, kstr, integral, kern->integral( parm ),
fabs( integral - kern->integral( parm ) ), hackeps*epsilon );
airMopError( mop ); return 1;
}
}
/* HEY check being derivative of ikern/iparm */
AIR_UNUSED( ikern );
AIR_UNUSED( iparm );
airMopOkay( mop );
return 0;
}
int
nrrdKernelParm0IsScale( const NrrdKernel *kern ) {
int ret;
if ( !kern ) {
ret = 0;
} else if ( nrrdKernelHann == kern ||
nrrdKernelHannD == kern ||
nrrdKernelHannDD == kern ||
nrrdKernelBlackman == kern ||
nrrdKernelBlackmanD == kern ||
nrrdKernelBlackmanDD == kern ||
nrrdKernelZero == kern ||
nrrdKernelBox == kern ||
nrrdKernelCheap == kern ||
nrrdKernelTent == kern ||
nrrdKernelForwDiff == kern ||
nrrdKernelCentDiff == kern ||
nrrdKernelBCCubic == kern ||
nrrdKernelBCCubicD == kern ||
nrrdKernelBCCubicDD == kern ||
nrrdKernelAQuartic == kern ||
nrrdKernelAQuarticD == kern ||
nrrdKernelAQuarticDD == kern ||
nrrdKernelGaussian == kern ||
nrrdKernelGaussianD == kern ||
nrrdKernelGaussianDD == kern ||
nrrdKernelDiscreteGaussian == kern ) {
ret = 1;
} else {
ret = 0;
}
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/***
**** NONE of the nrrdKeyValue functions use biff.
**** They don't use them now, and they never should.
**** Unless I change my mind.
***/
/*
******** nrrdKeyValueSize
**
** returns the number of key/value pairs in a nrrd
*/
unsigned int
39 nrrdKeyValueSize( const Nrrd *nrrd ) {
if ( !nrrd ) {
return 0;
}
return nrrd->kvpArr->len;
}
/*
******** nrrdKeyValueIndex
**
** given an int in [0 .. #key/value pairs - 1], sets *keyP and *valueP
** to put to the corresponding key and value.
**
** NOTE: whether or not *keyP and *valueP are set to pointers to memory
** "inside" the nrrd struct ( pointers which you had better not free( )! )
** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults
** to AIR_FALSE
*/
void
59 nrrdKeyValueIndex( const Nrrd *nrrd, char **keyP, char **valueP,
unsigned int ki ) {
if ( !( nrrd && keyP && valueP && ki < nrrd->kvpArr->len ) ) {
if ( keyP ) {
*keyP = NULL;
}
if ( valueP ) {
*valueP = NULL;
}
return;
}
if ( nrrdStateKeyValueReturnInternalPointers ) {
*keyP = nrrd->kvp[0 + 2*ki];
*valueP = nrrd->kvp[1 + 2*ki];
} else {
*keyP = airStrdup( nrrd->kvp[0 + 2*ki] );
*valueP = airStrdup( nrrd->kvp[1 + 2*ki] );
}
return;
}
static unsigned int
82 _kvpIdxFind( const Nrrd *nrrd, const char *key, int *found ) {
unsigned int nk, ki, ret;
nk = nrrd->kvpArr->len;
for ( ki=0; ki<nk; ki++ ) {
if ( !strcmp( nrrd->kvp[0 + 2*ki], key ) ) {
break;
}
}
if ( ki<nk ) {
ret = ki;
*found = AIR_TRUE;
} else {
ret = UINT_MAX;
*found = AIR_FALSE;
}
return ret;
}
void
102 nrrdKeyValueClear( Nrrd *nrrd ) {
unsigned int nk, ki;
if ( !nrrd ) {
return;
}
nk = nrrd->kvpArr->len;
for ( ki=0; ki<nk; ki++ ) {
nrrd->kvp[0 + 2*ki] = ( char * )airFree( nrrd->kvp[0 + 2*ki] );
nrrd->kvp[1 + 2*ki] = ( char * )airFree( nrrd->kvp[1 + 2*ki] );
}
airArrayLenSet( nrrd->kvpArr, 0 );
return;
}
int
120 nrrdKeyValueErase( Nrrd *nrrd, const char *key ) {
unsigned int nk, ki;
int found;
if ( !( nrrd && key ) ) {
/* got NULL pointer */
return 1;
}
ki = _kvpIdxFind( nrrd, key, &found );
if ( !found ) {
return 0;
}
nrrd->kvp[0 + 2*ki] = ( char * )airFree( nrrd->kvp[0 + 2*ki] );
nrrd->kvp[1 + 2*ki] = ( char * )airFree( nrrd->kvp[1 + 2*ki] );
nk = nrrd->kvpArr->len;
for ( ; ki<nk-1; ki++ ) {
nrrd->kvp[0 + 2*ki] = nrrd->kvp[0 + 2*( ki+1 )];
nrrd->kvp[1 + 2*ki] = nrrd->kvp[1 + 2*( ki+1 )];
}
airArrayLenIncr( nrrd->kvpArr, -1 );
return 0;
}
/*
******** nrrdKeyValueAdd
**
** This will COPY the given strings, and so does not depend on
** them existing past the return of this function
**
** NOTE: Despite what might be most logical, there is no effort made
** here to cleanup key or value, including any escaping or filtering
** that might be warranted for white space other than \n
**
** does NOT use BIFF
*/
int
157 nrrdKeyValueAdd( Nrrd *nrrd, const char *key, const char *value ) {
unsigned int ki;
int found;
if ( !( nrrd && key && value ) ) {
/* got NULL pointer */
return 1;
}
if ( !strlen( key ) ) {
/* reject empty keys */
return 1;
}
ki = _kvpIdxFind( nrrd, key, &found );
if ( found ) {
/* over-writing value for an existing key, so have to free old value */
airFree( nrrd->kvp[1 + 2*ki] );
nrrd->kvp[1 + 2*ki] = airStrdup( value );
} else {
/* adding value for a new key */
ki = airArrayLenIncr( nrrd->kvpArr, 1 );
nrrd->kvp[0 + 2*ki] = airStrdup( key );
nrrd->kvp[1 + 2*ki] = airStrdup( value );
}
return 0;
}
/*
******** nrrdKeyValueGet
**
** NOTE: whether or not *keyP and *valueP are set to pointers to memory
** "inside" the nrrd struct ( pointers which you had better not free( )! )
** is controlled by nrrdStateKeyValueReturnInternalPointers, which defaults
** to AIR_FALSE
**
** does NOT use BIFF
*/
char *
194 nrrdKeyValueGet( const Nrrd *nrrd, const char *key ) {
char *ret;
unsigned int ki;
int found;
if ( !( nrrd && key ) ) {
/* got NULL pointer */
return NULL;
}
ki = _kvpIdxFind( nrrd, key, &found );
if ( found ) {
if ( nrrdStateKeyValueReturnInternalPointers ) {
ret = nrrd->kvp[1 + 2*ki];
} else {
ret = airStrdup( nrrd->kvp[1 + 2*ki] );
}
} else {
ret = NULL;
}
return ret;
}
/*
** Does the escaping of special characters in a string that
** is being written either to "FILE *file" or "char *dst"
** ( WHICH IS ASSUMED to be allocated to be big enough! )
** Which characters to escape should be put in string "toescape"
** currently supported: \n \ "
** Also, converts characters in "tospace" to a space. Being in
** toescape trumps being in tospace, so tospace can be harmlessly
** set to, say, AIR_WHITESPACE.
**
** accident of history that this function is in this file
*/
void
229 _nrrdWriteEscaped( FILE *file, char *dst, const char *str,
const char *toescape, const char *tospace ) {
/* static const char me[]="_nrrdWriteEscaped"; */
size_t ci, gslen; /* given strlen */
gslen = strlen( str );
for ( ci=0; ci<gslen; ci++ ) {
char cc;
cc = str[ci];
if ( strchr( toescape, cc ) ) {
switch( cc ) {
case '\n':
if ( file ) {
fprintf( file, "\\n" );
} else {
strcat( dst, "\\n" );
}
break;
case '\\':
if ( file ) {
fprintf( file, "\\\\" );
} else {
strcat( dst, "\\\\" );
}
break;
case '"':
if ( file ) {
fprintf( file, "\\\"" );
} else {
strcat( dst, "\\\"" );
}
break;
}
} else {
if ( strchr( tospace, cc ) ) {
cc = ' ';
}
if ( file ) {
fputc( cc, file );
} else {
size_t dsln;
dsln = strlen( dst );
dst[dsln++] = cc;
dst[dsln] = '\0';
}
}
}
return;
}
/*
** _nrrdKeyValueWrite
**
** writes a given key and value to a file, starting with the given
** prefix ( if non-NULL ), and ending with "\n"
*/
int
286 _nrrdKeyValueWrite( FILE *file, char **stringP, const char *prefix,
const char *key, const char *value ) {
if ( !( ( file || stringP ) && key && value ) ) {
return 1;
}
if ( stringP ) {
/* 2*strlen( ) because at worst all characters will be escaped */
*stringP = AIR_CALLOC( airStrlen( prefix ) + 2*airStrlen( key )
+ strlen( ":=" ) + 2*airStrlen( value )
+ strlen( "\n" ) + 1, char );
/* HEY error checking? */
}
if ( prefix ) {
if ( file ) {
fprintf( file, "%s", prefix );
} else {
strcat( *stringP, prefix );
}
}
if ( file ) {
_nrrdWriteEscaped( file, NULL, key, "\n\\", _NRRD_WHITESPACE_NOTAB );
fprintf( file, ":=" );
_nrrdWriteEscaped( file, NULL, value, "\n\\", _NRRD_WHITESPACE_NOTAB );
fprintf( file, "\n" );
} else {
_nrrdWriteEscaped( NULL, *stringP, key, "\n\\", _NRRD_WHITESPACE_NOTAB );
strcat( *stringP, ":=" );
_nrrdWriteEscaped( NULL, *stringP, value, "\n\\", _NRRD_WHITESPACE_NOTAB );
strcat( *stringP, "\n" );
}
return 0;
}
/*
******** nrrdKeyValueCopy( )
**
** copies key/value pairs from one nrrd to another
** Existing key/value pairs in nout are blown away
*/
int
327 nrrdKeyValueCopy( Nrrd *nout, const Nrrd *nin ) {
char *key, *value;
unsigned int ki;
if ( !( nout && nin ) ) {
/* got NULL pointer */
return 1;
}
if ( nout == nin ) {
/* can't satisfy semantics of copying with nout==nin */
return 2;
}
nrrdKeyValueClear( nout );
for ( ki=0; ki<nin->kvpArr->len; ki++ ) {
key = nin->kvp[0 + 2*ki];
value = nin->kvp[1 + 2*ki];
if ( nrrdKeyValueAdd( nout, key, value ) ) {
return 3;
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** RETIRED: nrrdMinMaxSet( )
**
** Sets nrrd->min and nrrd->max to the extremal ( existent ) values in
** the given nrrd, by calling the appropriate member of nrrdMinMaxExactFind[]
**
** calling this function will result in nrrd->hasNonExist being set
** ( because of the nrrdFindMinMax[] functions )
**
** decided NOT to use biff, so that this is a more distinct alternative to
** nrrdMinMaxCleverSet( ).
void
nrrdMinMaxSet( Nrrd *nrrd ) {
NRRD_TYPE_BIGGEST _min, _max;
if ( nrrd ) {
if ( !airEnumValCheck( nrrdType, nrrd->type )
&& nrrdTypeBlock != nrrd->type ) {
nrrdFindMinMax[nrrd->type]( &_min, &_max, nrrd );
nrrd->min = nrrdDLoad[nrrd->type]( &_min );
nrrd->max = nrrdDLoad[nrrd->type]( &_max );
} else {
nrrd->min = nrrd->max = AIR_NAN;
}
}
return;
}
*/
/*
** RETIRED: nrrdMinMaxCleverSet( )
**
** basically a wrapper around nrrdMinMaxSet( ), with bells + whistles:
** 1 ) will call nrrdMinMaxSet only when one of nrrd->min and nrrd->max
** are non-existent, with the end result that only the non-existent
** values are over-written
** 2 ) obeys the nrrdStateClever8BitMinMax global state to short-cut
** finding min and max for 8-bit data. Values for nrrd->min or
** nrrd->max which were existent to start with are untouched.
** 3 ) reports error if there are no existent values in nrrd ( AIR_EXISTS( )
** fails on every value )
**
** Like nrrdMinMaxSet( ), this will always set nrrd->hasNonExist.
**
** Uses biff.
int
nrrdMinMaxCleverSet( Nrrd *nrrd ) {
static const char me[]="nrrdMinMaxCleverSet";
double min, max;
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, nrrd->type ) ) {
biffAddf( NRRD, "%s: input nrrd has invalid type ( %d )", me, nrrd->type );
return 1;
}
if ( nrrdTypeBlock == nrrd->type ) {
sprintf( err, "%s: can't find min/max of type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
}
if ( AIR_EXISTS( nrrd->min ) && AIR_EXISTS( nrrd->max ) ) {
/ * both of min and max already set, so we won't look for those, but
we have to comply with stated behavior of always setting hasNonExist * /
nrrdHasNonExistSet( nrrd );
return 0;
}
if ( nrrdStateClever8BitMinMax
&& ( nrrdTypeChar == nrrd->type || nrrdTypeUChar == nrrd->type ) ) {
if ( nrrdTypeChar == nrrd->type ) {
if ( !AIR_EXISTS( nrrd->min ) )
nrrd->min = SCHAR_MIN;
if ( !AIR_EXISTS( nrrd->max ) )
nrrd->max = SCHAR_MAX;
} else {
if ( !AIR_EXISTS( nrrd->min ) )
nrrd->min = 0;
if ( !AIR_EXISTS( nrrd->max ) )
nrrd->max = UCHAR_MAX;
}
nrrdHasNonExistSet( nrrd );
return 0;
}
/ * at this point we need to find either min and/or max ( at least
one of them was non-existent on the way in ) * /
/ * save incoming values in case they exist * /
min = nrrd->min;
max = nrrd->max;
/ * this will set nrrd->min, nrrd->max, and hasNonExist * /
nrrdMinMaxSet( nrrd );
if ( !( AIR_EXISTS( nrrd->min ) && AIR_EXISTS( nrrd->max ) ) ) {
biffAddf( NRRD, "%s: no existent values!", me );
return 1;
}
/ * re-enstate the existent incoming min and/or max values * /
if ( AIR_EXISTS( min ) )
nrrd->min = min;
if ( AIR_EXISTS( max ) )
nrrd->max = max;
return 0;
}
*/
static int
137 clampRoundConvert( Nrrd *nout, const Nrrd *nin, int type,
int doClamp, int roundDir ) {
static const char me[]="clampRoundConvert";
char typeS[AIR_STRLEN_SMALL];
size_t num, size[NRRD_DIM_MAX];
if ( !( nin && nout
&& !nrrdCheck( nin )
&& !airEnumValCheck( nrrdType, type ) ) ) {
biffAddf( NRRD, "%s: invalid args", me );
return 1;
}
if ( nin->type == nrrdTypeBlock || type == nrrdTypeBlock ) {
biffAddf( NRRD, "%s: can't convert to or from nrrd type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type] ) {
biffAddf( NRRD, "%s: nout==nin but input, output type sizes unequal", me );
return 1;
}
if ( nrrdStateDisallowIntegerNonExist
&& !nrrdTypeIsIntegral[nin->type] && nrrdTypeIsIntegral[type] ) {
/* there's a risk of non-existent values getting converted to
non-sensical integral values */
if ( nrrdHasNonExist( nin ) ) {
biffAddf( NRRD, "%s: can't convert to integral values ( %s ) with "
"non-existent values in input", me,
airEnumStr( nrrdType, type ) );
return 1;
}
}
/* if we're actually converting to the same type, just do a copy */
if ( type == nin->type ) {
if ( nout == nin ) {
/* nout == nin is allowed if the input and output type are
of the same size, which will certainly be the case if the
input and output types are identical, so there's actually
no work to do */
} else {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: couldn't copy input to output", me );
return 1;
}
}
} else {
/* allocate space if necessary */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
/* MUST be nrrdMaybeAlloc_nva ( not nrrd Alloc_nva ) because we allow
nout==nin if type sizes match */
if ( nrrdMaybeAlloc_nva( nout, type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: failed to allocate output", me );
return 1;
}
/* call the appropriate converter */
num = nrrdElementNumber( nin );
if ( roundDir ) {
_nrrdCastClampRound[nout->type][nin->type]( nout->data, nin->data, num,
doClamp, roundDir );
} else if ( doClamp ) {
_nrrdClampConv[nout->type][nin->type]( nout->data, nin->data, num );
} else {
_nrrdConv[nout->type][nin->type]( nout->data, nin->data, num );
}
nout->blockSize = 0;
/* copy peripheral information */
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
sprintf( typeS, "( %s )", airEnumStr( nrrdType, nout->type ) );
if ( nrrdContentSet_va( nout, typeS, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* the min and max have probably changed if there was a conversion
to integral values, or to a lower precision representation */
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
return 0;
}
/*
******** nrrdConvert( )
**
** copies values from one type of nrrd to another, without any
** transformation, except what you get with a cast. The point is to
** make available on Nrrds the exact same behavior as you have in C
** with casts and assignments.
*/
int
240 nrrdConvert( Nrrd *nout, const Nrrd *nin, int type ) {
static const char me[]="nrrdConvert";
if ( clampRoundConvert( nout, nin, type,
AIR_FALSE /* clamp */,
0 /* round */ ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdClampConvert( )
**
** same as nrrdConvert, but with clamping to output value representation range
** so as to avoid wrap-around artifacts
**
** HEY: WARNING: may have loss of data when processing long long
** ( either signed or unsigned )
*/
int
262 nrrdClampConvert( Nrrd *nout, const Nrrd *nin, int type ) {
static const char me[]="nrrdClampConvert";
if ( clampRoundConvert( nout, nin, type,
AIR_TRUE /* clamp */,
0 /* round */ ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdCastClampRound( )
**
** For ( maybe ) doing rounding to integer, then ( maybe ) clamping
** and then casting. The maybe-ness of rounding and clamping
** in this function is inconsistent with the certainty of clamping
** nrrdClampConvert, so this function name may be regretted.
**
** NOTE! Rounding is not performed when outType is for float
** or double! That logic is implemented here.
**
** And warning same as above:
** HEY: WARNING: may have loss of data when processing long long
** ( either signed or unsigned )
*/
int
290 nrrdCastClampRound( Nrrd *nout, const Nrrd *nin, int outType,
int doClamp, int roundDir ) {
static const char me[]="nrrdCastClampRound";
if ( clampRoundConvert( nout, nin, outType, doClamp,
nrrdTypeIsIntegral[outType] ? roundDir : 0 ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdQuantize( )
**
** convert values to 8, 16, or 32 bit unsigned quantities
** by mapping the value range delimited by the nrrd's min
** and max to the representable range
**
** NOTE: for the time being, this uses a "double" as the intermediate
** value holder, which may mean needless loss of precision
*/
int
313 nrrdQuantize( Nrrd *nout, const Nrrd *nin, const NrrdRange *_range,
unsigned int bits ) {
static const char me[]="nrrdQuantize", func[]="quantize";
double valIn, minIn, maxIn, eps;
int type=nrrdTypeUnknown;
size_t I, num, size[NRRD_DIM_MAX];
unsigned char *outUC;
unsigned short *outUS;
unsigned int *outUI;
airArray *mop;
NrrdRange *range;
if ( !( nin && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can't quantize type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
/* determine nrrd type from number of bits */
switch ( bits ) {
case 8: type = nrrdTypeUChar; break;
case 16: type = nrrdTypeUShort; break;
case 32: type = nrrdTypeUInt; break;
default:
biffAddf( NRRD, "%s: bits has to be 8, 16, or 32 ( not %d )", me, bits );
return 1;
}
if ( nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type] ) {
biffAddf( NRRD, "%s: nout==nin but input, output type sizes unequal", me );
return 1;
}
mop = airMopNew( );
if ( _range ) {
range = nrrdRangeCopy( _range );
nrrdRangeSafeSet( range, nin, nrrdBlind8BitRangeState );
} else {
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
}
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( nrrdStateDisallowIntegerNonExist && range->hasNonExist ) {
biffAddf( NRRD, "%s: can't quantize non-existent values "
"( NaN, +/-inf )", me );
airMopError( mop ); return 1;
}
/* allocate space if necessary */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
/* MUST be nrrdMaybeAlloc_nva ( not nrrd Alloc_nva ) because we allow
nout==nin if type sizes match */
if ( nrrdMaybeAlloc_nva( nout, type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: failed to create output", me );
airMopError( mop ); return 1;
}
/* the skinny */
num = nrrdElementNumber( nin );
minIn = range->min;
maxIn = range->max;
eps = ( minIn == maxIn ? 1.0 : 0.0 );
outUC = ( unsigned char* )nout->data;
outUS = ( unsigned short* )nout->data;
outUI = ( unsigned int* )nout->data;
switch( bits ) {
case 8:
for ( I=0; I<num; I++ ) {
valIn = nrrdDLookup[nin->type]( nin->data, I );
outUC[I] = airIndexClamp( minIn, valIn, maxIn+eps, 1 << 8 );
}
break;
case 16:
for ( I=0; I<num; I++ ) {
valIn = nrrdDLookup[nin->type]( nin->data, I );
outUS[I] = airIndexClamp( minIn, valIn, maxIn+eps, 1 << 16 );
}
break;
case 32:
for ( I=0; I<num; I++ ) {
valIn = nrrdDLookup[nin->type]( nin->data, I );
outUI[I] = AIR_CAST( unsigned int,
airIndexClampULL( minIn, valIn, maxIn+eps,
AIR_ULLONG( 1 ) << 32 ) );
}
break;
}
/* set information in new volume */
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
if ( nrrdContentSet_va( nout, func, nin, "%d", bits ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
nout->oldMin = minIn;
nout->oldMax = maxIn;
nout->blockSize = 0;
airMopOkay( mop );
return 0;
}
/*
** _nrrdTypeNumberOfValues[]
**
** only meaningful for integral values, and only correct for
** 32-bit values; tells the number of different integral values that
** can be represented by the type
**
** HEY: this used to be public, but that was stopped when it was clear
** that only the following function was using the array. The reason
** for the array is questionable, and the implementation below
** should be re-evaluated.
*/
static const double
_nrrdTypeNumberOfValues[NRRD_TYPE_MAX+1] = {
0, /* unknown */
UCHAR_MAX+1, /* char */
UCHAR_MAX+1, /* unsigned char */
USHRT_MAX+1, /* short */
USHRT_MAX+1, /* unsigned short */
( double )UINT_MAX+1, /* int */
( double )UINT_MAX+1, /* unsigned int */
( double )NRRD_ULLONG_MAX+1, /* long long */
( double )NRRD_ULLONG_MAX+1, /* unsigned long long */
0, /* float */
0, /* double */
0 /* punt */
};
/*
******** nrrdUnquantize( )
**
** try to recover floating point values from a quantized nrrd,
** using the oldMin and oldMax values, if they exist. If they
** don't exist, the output range will be 0.0 to 1.0. However,
** because we're using NRRD_CELL_POS to recover values,
** the output values will never be exactly 0.0 to 1.0 ( or oldMin
** to oldMax ). In unsigned char data, for instance, the value
** V will be mapped to:
** NRRD_CELL_POS( 0.0, 1.0, 256, V ) ==
** AIR_AFFINE( 0, V + 0.5, 256, 0.0, 1.0 ) ==
** ( ( double )( 1.0 )-( 0.0 ) )*( ( double )( V+0.5 )-( 0 ) )/( ( double )( 256 )-( 0 ) ) + ( 0.0 ) ==
** ( 1.0 )*( V+0.5 ) / ( 256.0 ) + ( 0.0 ) ==
** ( V+0.5 )/256
** so a 0 will be mapped to 1/512 = 0.00195
*/
int
479 nrrdUnquantize( Nrrd *nout, const Nrrd *nin, int type ) {
static const char me[]="nrrdUnquantize", func[]="unquantize";
float *outF;
double *outD, minIn, numValIn, minOut, maxOut, valIn;
size_t NN, II, size[NRRD_DIM_MAX];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: don't recognize type %d\n", me, type );
return 1;
}
if ( !( type == nrrdTypeFloat || type == nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: output type must be %s or %s ( not %s )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, type ) );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can't unquantize type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !nrrdTypeIsIntegral[nin->type] ) {
biffAddf( NRRD, "%s: can only unquantize integral types, not %s", me,
airEnumStr( nrrdType, nin->type ) );
return 1;
}
if ( nout == nin && nrrdTypeSize[type] != nrrdTypeSize[nin->type] ) {
biffAddf( NRRD, "%s: nout==nin but input, output type sizes unequal", me );
return 1;
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( nrrdMaybeAlloc_nva( nout, type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: failed to create output", me );
return 1;
}
minIn = nrrdTypeMin[nin->type];
numValIn = _nrrdTypeNumberOfValues[nin->type];
if ( AIR_EXISTS( nin->oldMin ) && AIR_EXISTS( nin->oldMax ) ) {
minOut = nin->oldMin;
maxOut = nin->oldMax;
} else {
minOut = 0.0;
maxOut = 1.0;
}
outF = ( float* )nout->data;
outD = ( double* )nout->data;
NN = nrrdElementNumber( nin );
switch( type ) {
case nrrdTypeFloat:
for ( II=0; II<NN; II++ ) {
valIn = minIn + nrrdDLookup[nin->type]( nin->data, II );
outF[II] = AIR_CAST( float,
NRRD_CELL_POS( minOut, maxOut, numValIn, valIn ) );
}
break;
case nrrdTypeDouble:
for ( II=0; II<NN; II++ ) {
valIn = minIn + nrrdDLookup[nin->type]( nin->data, II );
outD[II] = NRRD_CELL_POS( minOut, maxOut, numValIn, valIn );
}
break;
}
/* set information in new volume */
if ( nout != nin ) {
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
}
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_OLDMIN_BIT
| NRRD_BASIC_INFO_OLDMAX_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
nout->oldMin = nout->oldMax = AIR_NAN;
nout->blockSize = 0;
return 0;
}
/*
** _nrrdHistoEqCompare( )
**
** used by nrrdHistoEq in smart mode to sort the "steady" array
** in _descending_ order
*/
int
584 _nrrdHistoEqCompare( const void *a, const void *b ) {
return *( ( const unsigned int* )b ) - *( ( const unsigned int* )a );
}
/*
******** nrrdHistoEq( )
**
** performs histogram equalization on given nrrd, treating it as a
** big one-dimensional array. The procedure is as follows:
** - create a histogram of nrrd ( using "bins" bins )
** - integrate the histogram, and normalize and shift this so it is
** a monotonically increasing function from min to max, where
** ( min, max ) is the range of values in the nrrd
** - map the values in the nrrd through the adjusted histogram integral
**
** If the histogram of the given nrrd is already as flat as can be,
** the histogram integral will increase linearly, and the adjusted
** histogram integral should be close to the identity function, so
** the values shouldn't change much.
**
** If the nhistP arg is non-NULL, then it is set to point to
** the histogram that was used for calculation. Otherwise this
** histogram is deleted on return.
**
** This is all that is done normally, when "smart" is == 0. In
** "smart" mode ( activated by setting "smart" to something greater
** than 0 ), the histogram is analyzed during its creation to detect if
** there are a few bins which keep getting hit with the same value
** over and over. It may be desirable to ignore these bins in the
** histogram integral because they may not contain any useful
** information, and so they should not effect how values are
** re-mapped. The value of "smart" is the number of bins that will be
** ignored. For instance, use the value 1 if the problem with naive
** histogram equalization is a large amount of background ( which is
** exactly one fixed value ).
*/
int
622 nrrdHistoEq( Nrrd *nout, const Nrrd *nin, Nrrd **nmapP,
unsigned int bins, unsigned int smart, float amount ) {
static const char me[]="nrrdHistoEq", func[]="heq";
Nrrd *nhist, *nmap;
double val, min, max, *last = NULL, *ycoord = NULL;
int *respect = NULL, lort;
unsigned int *hist, *steady = NULL, idx, hirt;
size_t I, num;
airArray *mop;
NrrdRange *range;
unsigned bii;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( NRRD, "%s: can't histogram equalize type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( bins > 4 ) ) {
biffAddf( NRRD, "%s: need # bins > 4 ( not %d )", me, bins );
return 1;
}
/* we start by simply copying, because the only thing we're
changing is the values themselves, and all peripheral
information is unchanged by this value remapping */
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
mop = airMopNew( );
if ( nmapP ) {
airMopAdd( mop, nmapP, ( airMopper )airSetNull, airMopOnError );
}
num = nrrdElementNumber( nin );
if ( smart <= 0 ) {
nhist = nrrdNew( );
if ( nrrdHisto( nhist, nin, NULL, NULL, bins, nrrdTypeUInt ) ) {
biffAddf( NRRD, "%s: failed to create histogram", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
hist = ( unsigned int* )nhist->data;
min = nhist->axis[0].min;
max = nhist->axis[0].max;
} else {
/* for "smart" mode, we have to some extra work while creating the
histogram to look for bins incessantly hit with the exact same
value */
if ( nrrdMaybeAlloc_va( nhist=nrrdNew( ), nrrdTypeUInt, 1,
AIR_CAST( size_t, bins ) ) ) {
biffAddf( NRRD, "%s: failed to allocate histogram", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
hist = ( unsigned int* )nhist->data;
nhist->axis[0].size = bins;
/* allocate the respect, steady, and last arrays */
respect = ( int* )calloc( bins, sizeof( int ) );
steady = ( unsigned int* )calloc( 2*bins, sizeof( unsigned int ) );
last = ( double* )calloc( bins, sizeof( double ) );
airMopMem( mop, &respect, airMopAlways );
airMopMem( mop, &steady, airMopAlways );
airMopMem( mop, &last, airMopAlways );
if ( !( respect && steady && last ) ) {
biffAddf( NRRD, "%s: couldn't allocate smart arrays", me );
airMopError( mop ); return 1;
}
/* steady[0 + 2*bii] == how many times has bin bii seen the same value
steady[1 + 2*bii] == bii ( steady will be rearranged by qsort( ) ) */
for ( bii=0; bii<bins; bii++ ) {
last[bii] = AIR_NAN;
respect[bii] = 1;
steady[1 + 2*bii] = bii;
}
/* now create the histogram */
range = nrrdRangeNewSet( nin, nrrdBlind8BitRangeState );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( range->min == range->max ) {
biffAddf( NRRD, "%s: invalid min and max in nrrd. "
"Min and max are equivalent ( min, max = %g ).", me, range->min );
airMopError( mop ); return 1;
}
min = range->min;
max = range->max;
for ( I=0; I<num; I++ ) {
val = nrrdDLookup[nin->type]( nin->data, I );
if ( AIR_EXISTS( val ) ) {
idx = airIndex( min, val, max, bins );
++hist[idx];
if ( AIR_EXISTS( last[idx] ) ) {
steady[0 + 2*idx] = ( last[idx] == val
? 1 + steady[0 + 2*idx]
: 0 );
}
last[idx] = val;
}
}
/* now sort the steady array */
qsort( steady, bins, 2*sizeof( unsigned int ), _nrrdHistoEqCompare );
/* we ignore some of the bins according to "smart" arg */
for ( bii=0; bii<smart; bii++ ) {
respect[steady[1+2*bii]] = 0;
/* printf( "%s: disrespecting bin %d\n", me, steady[1+2*bii] ); */
}
}
if ( nrrdMaybeAlloc_va( nmap=nrrdNew( ), nrrdTypeDouble, 1,
AIR_CAST( size_t, bins+1 ) ) ) {
biffAddf( NRRD, "%s: failed to create map nrrd", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nmap, ( airMopper )nrrdNuke,
nmapP ? airMopOnError : airMopAlways );
ycoord = AIR_CAST( double*, nmap->data );
nmap->axis[0].min = min;
nmap->axis[0].max = max;
/* integrate the histogram then normalize it */
for ( bii=0; bii<=bins; bii++ ) {
if ( bii == 0 ) {
ycoord[bii] = 0;
} else {
ycoord[bii] = ycoord[bii-1] + hist[bii-1]*( smart
? respect[bii-1]
: 1 );
}
}
/* if we've done smart, the integral will have little flat spots
where we ignored hits in the histogram. That means the mapping
will actually be lower at that value than it should be. In
truth, we should be using an irregular mapping for this, and the
control points at the ignored bins should just be missing. So we
have to do this silliness to raise those control points in the
regular map. */
if ( smart ) {
/* there are bins+1 control points, with indices 0 to bins.
We'll fix control points 1 to bins-1. ycoord[bii] is too low
if hist[bii-1] was not respected ( !respect[bii-1] ) */
for ( bii=1; bii<=bins-1; bii++ ) {
if ( !respect[bii-1] ) {
/* lort and hirt will bracket the index of the bad control point
with points corresponding either to respected bins or the
endpoints of the histogram */
for ( lort=bii; lort>=1 && !respect[lort-1]; lort-- )
;
for ( hirt=bii; hirt<bins && !respect[hirt-1]; hirt++ )
;
ycoord[bii] = AIR_AFFINE( lort, bii, hirt,
ycoord[lort], ycoord[hirt] );
}
}
/* the very last control point has to be handled differently */
if ( !respect[bins-1] ) {
ycoord[bins] += ycoord[bins-1] - ycoord[bins-2];
}
}
/* rescale the histogram integration to span the original
value range, and affect the influence of "amount" */
for ( bii=0; bii<=bins; bii++ ) {
ycoord[bii] = AIR_AFFINE( 0.0, ycoord[bii], ycoord[bins], min, max );
ycoord[bii] = AIR_AFFINE( 0.0, amount, 1.0,
AIR_AFFINE( 0, bii, bins, min, max ),
ycoord[bii] );
}
/* map the nrrd values through the normalized histogram integral */
if ( nrrdApply1DRegMap( nout, nin, NULL, nmap, nin->type, AIR_FALSE ) ) {
biffAddf( NRRD, "%s: problem remapping", me );
airMopError( mop ); return 1;
}
/*
for ( I=0; I<num; I++ ) {
val = nrrdDLookup[nin->type]( nin->data, I );
if ( AIR_EXISTS( val ) ) {
AIR_INDEX( min, val, max, bins, idx );
val = AIR_AFFINE( xcoord[idx], val, xcoord[idx+1],
ycoord[idx], ycoord[idx+1] );
}
nrrdDInsert[nout->type]( nout->data, I, val );
}
*/
/* if user is interested, set pointer to map nrrd,
otherwise it will be nixed by airMop */
if ( nmapP ) {
*nmapP = nmap;
}
/* fiddling with content is the only thing we'll do */
if ( nrrdContentSet_va( nout, func, nin, "%d, %d", bins, smart ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/* the non-histogram measures assume that there will be no NaNs in data */
void
29 _nrrdMeasureUnknown( void *ans, int ansType,
const void *line, int lineType,
size_t len, double axmin, double axmax ) {
static const char me[]="_nrrdMeasureUnknown";
AIR_UNUSED( line );
AIR_UNUSED( lineType );
AIR_UNUSED( len );
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
fprintf( stderr, "%s: Need To Specify A Measure !!! \n", me );
nrrdDStore[ansType]( ans, AIR_NAN );
}
void
44 _nrrdMeasureMin( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, M, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
M = lup( line, 0 );
for ( ii=1; ii<len; ii++ ) {
val = lup( line, ii );
M = AIR_MIN( M, val );
}
} else {
M = AIR_NAN;
for ( ii=0; !AIR_EXISTS( M ) && ii<len; ii++ ) {
M = lup( line, ii );
}
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
M = AIR_MIN( M, val );
}
}
}
nrrdDStore[ansType]( ans, M );
}
void
76 _nrrdMeasureMax( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, M, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
M = lup( line, 0 );
for ( ii=1; ii<len; ii++ ) {
val = lup( line, ii );
M = AIR_MAX( M, val );
}
} else {
M = AIR_NAN;
for ( ii=0; !AIR_EXISTS( M ) && ii<len; ii++ ) {
M = lup( line, ii );
}
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
M = AIR_MAX( M, val );
}
}
}
nrrdDStore[ansType]( ans, M );
}
void
107 _nrrdMeasureProduct( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, P, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
P = 1.0;
for ( ii=0; ii<len; ii++ ) {
P *= lup( line, ii );
}
} else {
P = AIR_NAN;
/* the point of this is to ensure that that if there are NO
existent values, then the return is NaN */
for ( ii=0; !AIR_EXISTS( P ) && ii<len; ii++ ) {
P = lup( line, ii );
}
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
P *= val;
}
}
}
nrrdDStore[ansType]( ans, P );
}
void
139 _nrrdMeasureSum( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
S = 0.0;
for ( ii=0; ii<len; ii++ ) {
S += lup( line, ii );
}
} else {
S = AIR_NAN;
for ( ii=0; !AIR_EXISTS( S ) && ii<len; ii++ )
S = lup( line, ii );
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
S += val;
}
}
}
nrrdDStore[ansType]( ans, S );
}
void
168 _nrrdMeasureMean( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, M, ( *lup )( const void*, size_t );
size_t ii, count;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
S = 0.0;
for ( ii=0; ii<len; ii++ ) {
S += lup( line, ii );
}
M = S/len;
} else {
S = AIR_NAN;
for ( ii=0; !AIR_EXISTS( S ) && ii<len; ii++ ) {
S = lup( line, ii );
}
if ( AIR_EXISTS( S ) ) {
/* there was an existent value */
count = 1;
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
count++;
S += val;
}
}
M = S/count;
} else {
/* there were NO existent values */
M = AIR_NAN;
}
}
nrrdDStore[ansType]( ans, M );
}
/* stupid little forward declaration */
void
209 _nrrdMeasureHistoMode( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax );
void
214 _nrrdMeasureMode( void *ans, int ansType,
const void *_line, int lineType, size_t len,
double axmin, double axmax ) {
Nrrd *nline, *nhist;
void *line;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
line = calloc( len, nrrdTypeSize[lineType] );
if ( line ) {
memcpy( line, _line, len*nrrdTypeSize[lineType] );
nline = nrrdNew( );
if ( nrrdWrap_va( nline, line, lineType, 1, len ) ) {
free( biffGetDone( NRRD ) );
nrrdNix( nline );
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
nhist = nrrdNew( );
if ( nrrdHisto( nhist, nline, NULL, NULL,
nrrdStateMeasureModeBins, nrrdTypeInt ) ) {
free( biffGetDone( NRRD ) );
nrrdNuke( nhist );
nrrdNix( nline );
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
/* now we pass this histogram off to histo-mode */
_nrrdMeasureHistoMode( ans, ansType,
nhist->data, nrrdTypeInt, nrrdStateMeasureModeBins,
nhist->axis[0].min, nhist->axis[0].max );
nrrdNuke( nhist );
nrrdNix( nline );
} else {
nrrdDStore[ansType]( ans, 0 );
}
return;
}
void
256 _nrrdMeasureMedian( void *ans, int ansType,
const void *_line, int lineType, size_t len,
double axmin, double axmax ) {
double M=0, ( *lup )( const void*, size_t );
size_t ii, mid;
void *line;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
line = calloc( len, nrrdTypeSize[lineType] );
if ( line ) {
memcpy( line, _line, len*nrrdTypeSize[lineType] );
/* yes, I know, this is not the fastest median. I'll get to it ... */
qsort( line, len, nrrdTypeSize[lineType], nrrdValCompare[lineType] );
M = AIR_NAN;
for ( ii=0; !AIR_EXISTS( M ) && ii<len; ii++ ) {
M = lup( line, ii );
}
if ( AIR_EXISTS( M ) ) {
/* i is index AFTER first existent value */
ii--;
len -= ii;
mid = len/2;
if ( len % 2 ) {
/* len is odd, there is a middle value, its at mid */
M = lup( line, ii+mid );
} else {
/* len is even, two middle values are at mid-1 and mid */
M = ( lup( line, ii+mid-1 ) + lup( line, ii+mid ) )/2;
}
}
}
nrrdDStore[ansType]( ans, M );
}
void
295 _nrrdMeasureL1( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
S = 0.0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
S += AIR_ABS( val );
}
} else {
S = AIR_NAN;
for ( ii=0; !AIR_EXISTS( S ) && ii<len; ii++ ) {
S = lup( line, ii );
}
S = AIR_ABS( S );
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
S += AIR_ABS( val );
}
}
}
nrrdDStore[ansType]( ans, S );
}
#define L2_BODY( FOUR ) \
327 AIR_UNUSED( axmin ); \
328 AIR_UNUSED( axmax ); \
lup = nrrdDLookup[lineType]; \
if ( nrrdTypeIsIntegral[lineType] ) { \
S = 0.0; \
count = len; \
for ( ii=0; ii<len; ii++ ) { \
val = lup( line, ii ); \
S += ( FOUR ) ? val*val*val*val : val*val; \
} \
} else { \
S = AIR_NAN; \
count = 0; \
for ( ii=0; !AIR_EXISTS( S ) && ii<len; ii++ ) { \
S = lup( line, ii ); \
} \
if ( AIR_EXISTS( S ) ) { \
/* there's at least one existing value */ \
count = 1; \
S *= ( FOUR ) ? S*S*S : S ; \
for ( ; ii<len; ii++ ) { \
val = lup( line, ii ); \
if ( AIR_EXISTS( val ) ) { \
count++; \
S += ( FOUR ) ? val*val*val*val : val*val; \
} \
} \
} \
}
void
_nrrdMeasureL2( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, aa, ( *lup )( const void*, size_t );
size_t ii, count;
L2_BODY( AIR_FALSE );
if ( AIR_EXISTS( S ) ) {
aa = sqrt( S );
} else {
aa = AIR_NAN;
}
nrrdDStore[ansType]( ans, aa );
}
void
_nrrdMeasureL4( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, aa, ( *lup )( const void*, size_t );
size_t ii, count;
L2_BODY( AIR_TRUE );
if ( AIR_EXISTS( S ) ) {
aa = sqrt( sqrt( S ) );
} else {
aa = AIR_NAN;
}
nrrdDStore[ansType]( ans, aa );
}
void
_nrrdMeasureNormalizedL2( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, aa, ( *lup )( const void*, size_t );
size_t ii, count;
L2_BODY( AIR_FALSE );
if ( AIR_EXISTS( S ) ) {
aa = sqrt( S )/count;
} else {
aa = AIR_NAN;
}
nrrdDStore[ansType]( ans, aa );
}
void
_nrrdMeasureRootMeanSquare( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, aa, ( *lup )( const void*, size_t );
size_t ii, count;
L2_BODY( AIR_FALSE );
if ( AIR_EXISTS( S ) ) {
aa = sqrt( S/count );
} else {
aa = AIR_NAN;
}
nrrdDStore[ansType]( ans, aa );
}
void
_nrrdMeasureLinf( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, M, ( *lup )( const void*, size_t );
size_t ii;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
val = lup( line, 0 );
M = AIR_ABS( val );
for ( ii=1; ii<len; ii++ ) {
val = lup( line, ii );
val = AIR_ABS( val );
M = AIR_MAX( M, val );
}
} else {
M = AIR_NAN;
for ( ii=0; !AIR_EXISTS( M ) && ii<len; ii++ ) {
M = lup( line, ii );
}
M = AIR_ABS( M );
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
val = AIR_ABS( val );
M = AIR_MAX( M, val );
}
}
}
nrrdDStore[ansType]( ans, M );
}
/* ========================================================== */
#if 0 /* two variance functions:
0 for new two-pass ( more accurate )
1 for old single-pass */
void
_nrrdMeasureVariance( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, SS, ( *lup )( const void*, size_t );
size_t ii, count;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
SS = S = 0.0;
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
S += val;
SS += val*val;
}
S /= len;
SS /= len;
} else {
count = 0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
count++;
S += val;
SS += val*val;
}
}
if ( count ) {
S /= count;
SS /= count;
} else {
S = SS = AIR_NAN;
}
}
/* HEY: the AIR_MAX is needed because precision errors
can produce a negative value for SS - S*S;
this may be a sign of the false economy of doing
the variance calculation in a single pass */
nrrdDStore[ansType]( ans, AIR_MAX( 0.0, SS - S*S ) );
}
#else /* ========================================================== */
void
_nrrdMeasureVariance( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double vari, mean, val, ( *lup )( const void*, size_t );
size_t ii, count;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
mean = vari = 0.0;
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
for ( ii=0; ii<len; ii++ ) {
mean += lup( line, ii );
}
mean /= len;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
vari += ( val-mean )*( val-mean );
}
vari /= len;
} else {
count = 0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
count++;
mean += val;
}
}
if ( count ) {
mean /= count;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
vari += ( val-mean )*( val-mean );
}
}
vari /= count;
} else {
vari = AIR_NAN;
}
}
nrrdDStore[ansType]( ans, vari );
}
#endif
/* ========================================================== */
void
_nrrdMeasureSD( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double var;
_nrrdMeasureVariance( ans, ansType, line, lineType, len, axmin, axmax );
var = nrrdDLoad[ansType]( ans );
nrrdDStore[ansType]( ans, sqrt( var ) );
}
void
_nrrdMeasureCoV( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, S, M, ( *lup )( const void*, size_t ), diff, stdv;
size_t ii, count;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
S = 0.0;
for ( ii=0; ii<len; ii++ ) {
S += lup( line, ii );
}
M = S/len;
diff = 0.0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
diff += ( M-val )*( M-val );
}
stdv = sqrt( diff/len );
} else {
S = AIR_NAN;
for ( ii=0; !AIR_EXISTS( S ) && ii<len; ii++ ) {
S = lup( line, ii );
}
if ( AIR_EXISTS( S ) ) {
/* there was an existent value */
count = 1;
for ( ; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
count++;
S += val;
}
}
M = S/count;
diff = 0.0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
diff += ( M-val )*( M-val );
}
}
stdv = sqrt( diff/count );
} else {
/* there were NO existent values */
M = stdv = AIR_NAN;
}
}
nrrdDStore[ansType]( ans, stdv/M );
}
void
_nrrdMeasureLineFit( double *intc, double *slope,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double x, y, xi=0, yi=0, xiyi=0, xisq=0, det, ( *lup )( const void*, size_t );
size_t ii;
lup = nrrdDLookup[lineType];
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = 0;
axmax = AIR_CAST( double, len-1 );
}
if ( 1 == len ) {
*slope = 0;
*intc = lup( line, 0 );
} else {
for ( ii=0; ii<len; ii++ ) {
x = NRRD_NODE_POS( axmin, axmax, len, ii );
y = lup( line, ii );
xi += x;
yi += y;
xiyi += x*y;
xisq += x*x;
}
det = len*xisq - xi*xi;
*slope = ( len*xiyi - xi*yi )/det;
*intc = ( -xi*xiyi + xisq*yi )/det;
}
}
void
_nrrdMeasureLineSlope( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double slope, intc;
_nrrdMeasureLineFit( &intc, &slope, line, lineType, len, axmin, axmax );
nrrdDStore[ansType]( ans, slope );
}
void
_nrrdMeasureLineIntercept( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double slope, intc;
_nrrdMeasureLineFit( &intc, &slope, line, lineType, len, axmin, axmax );
nrrdDStore[ansType]( ans, intc );
}
void
_nrrdMeasureLineError( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double x, y, slope, intc, tmp, err=0, ( *lup )( const void*, size_t );
size_t ii;
_nrrdMeasureLineFit( &intc, &slope, line, lineType, len, axmin, axmax );
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = 0;
axmax = AIR_CAST( double, len-1 );
}
lup = nrrdDLookup[lineType];
for ( ii=0; ii<len; ii++ ) {
x = NRRD_NODE_POS( axmin, axmax, len, ii );
y = lup( line, ii );
tmp = slope*x + intc - y;
err += tmp*tmp;
}
nrrdDStore[ansType]( ans, err );
}
void
_nrrdMeasureSkew( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, diff, mean, vari, third, ( *lup )( const void*, size_t );
size_t ii, count;
AIR_UNUSED( axmin );
AIR_UNUSED( axmax );
/* we don't try to do any one-pass short-cuts */
/* find the mean */
mean = 0;
lup = nrrdDLookup[lineType];
if ( nrrdTypeIsIntegral[lineType] ) {
count = len;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
mean += val;
}
} else {
count = 0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
count++;
mean += val;
}
}
}
if ( 0 == count ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
mean /= count;
/* find the variance and third moment */
vari = third = 0;
if ( nrrdTypeIsIntegral[lineType] ) {
for ( ii=0; ii<len; ii++ ) {
diff = lup( line, ii ) - mean;
vari += diff*diff;
third += diff*diff*diff;
}
} else {
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
diff = val - mean;
vari += diff*diff;
third += diff*diff*diff;
}
}
}
if ( 0 == vari ) {
/* why not have an existent value ... */
nrrdDStore[ansType]( ans, 0 );
return;
}
vari /= count;
third /= count;
nrrdDStore[ansType]( ans, third/( vari*sqrt( vari ) ) );
}
/*
** one thing which ALL the _nrrdMeasureHisto measures assume is that,
** being a histogram, the input array will not have any non-existent
** values. It can be floating point, because it is plausible to have
** some histogram composed of fractionally weighted hits, but there is
** no way that it is reasonable to have NaN in a bin, and it is extremely
** unlikely that Inf could actually be created in a floating point
** histogram.
**
** Values in the histogram can be positive or negative, but negative
** values are always ignored.
**
** All the the _nrrdMeasureHisto measures assume that if not both
** axmin and axmax are existent, then ( axmin, axmax ) = ( -0.5, len-0.5 ).
** Exercise for the reader: Show that
**
** i == NRRD_POS( nrrdCenterCell, 0, len-1, len, i )
**
** This justifies that fact that when axmin and axmax are not both
** existent, then we can simply calculate the answer in index space,
** and not have to do any shifting or scaling at the end to account
** for the fact that we assume ( axmin, axmax ) = ( -0.5, len-0.5 ).
*/
void
_nrrdMeasureHistoMedian( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double sum, tmp, half, ansD, ( *lup )( const void*, size_t );
size_t ii;
lup = nrrdDLookup[lineType];
sum = 0;
for ( ii=0; ii<len; ii++ ) {
tmp = lup( line, ii );
sum += ( tmp > 0 ? tmp : 0 );
}
if ( !sum ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
/* else there was something in the histogram */
half = sum/2;
sum = 0;
for ( ii=0; ii<len; ii++ ) {
tmp = lup( line, ii );
sum += ( tmp > 0 ? tmp : 0 );
if ( sum >= half ) {
break;
}
}
ansD = AIR_CAST( double, ii );
if ( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) {
ansD = NRRD_CELL_POS( axmin, axmax, len, ansD );
}
nrrdDStore[ansType]( ans, ansD );
}
void
_nrrdMeasureHistoMode( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, max, idxsum, ansD, ( *lup )( const void*, size_t );
size_t ii, idxcount;
lup = nrrdDLookup[lineType];
max = -DBL_MAX;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( AIR_EXISTS( val ) ) {
max = AIR_MAX( max, val );
}
}
if ( -DBL_MAX == max ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
/* else there was something in the histogram */
/* we assume that there may be multiple bins which reach the maximum
height, and we average all those indices. This may well be
bone-headed, and is subject to change. 19 July 03: with the
addition of the final "type" argument to nrrdProject, the
bone-headedness has been alleviated somewhat, since you can pass
nrrdTypeFloat or nrrdTypeDouble to get an accurate answer */
idxsum = 0;
idxcount = 0;
for ( ii=0; ii<len; ii++ ) {
val = lup( line, ii );
if ( val == max ) {
idxcount++;
idxsum += ii;
}
}
if ( max == 0 && len == idxcount ) {
/* entire histogram was zeros => empty distribution => no mode */
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
ansD = idxsum/idxcount;
/*
printf( "idxsum = %g; idxcount = %d --> ansD = %g --> ",
( float )idxsum, idxcount, ansD );
*/
if ( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) {
ansD = NRRD_CELL_POS( axmin, axmax, len, ansD );
}
/*
printf( "%g\n", ansD );
*/
nrrdDStore[ansType]( ans, ansD );
}
void
_nrrdMeasureHistoMean( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double count, hits, ansD, ( *lup )( const void*, size_t );
size_t ii;
lup = nrrdDLookup[lineType];
ansD = count = 0;
for ( ii=0; ii<len; ii++ ) {
hits = lup( line, ii );
hits = AIR_MAX( hits, 0 );
count += hits;
ansD += hits*ii;
}
if ( !count ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
ansD /= count;
if ( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) {
ansD = NRRD_CELL_POS( axmin, axmax, len, ansD );
}
nrrdDStore[ansType]( ans, ansD );
}
void
_nrrdMeasureHistoVariance( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double S, SS, count, hits, val, ( *lup )( const void*, size_t );
size_t ii;
lup = nrrdDLookup[lineType];
count = 0;
SS = S = 0.0;
/* we fix axmin, axmax now because GK is better safe than sorry */
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
for ( ii=0; ii<len; ii++ ) {
val = NRRD_CELL_POS( axmin, axmax, len, ii );
hits = lup( line, ii );
hits = AIR_MAX( hits, 0 );
count += hits;
S += hits*val;
SS += hits*val*val;
}
if ( !count ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
S /= count;
SS /= count;
/* HEY: the AIR_MAX is needed because precision errors
can produce a negative value for SS - S*S;
this may be a sign of the false economy of doing
the variance calculation in a single pass */
nrrdDStore[ansType]( ans, AIR_MAX( 0.0, SS - S*S ) );
}
void
_nrrdMeasureHistoSD( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double var;
_nrrdMeasureHistoVariance( ans, ansType, line, lineType, len, axmin, axmax );
var = nrrdDLoad[ansType]( ans );
nrrdDStore[ansType]( ans, sqrt( var ) );
}
void
_nrrdMeasureHistoProduct( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, product, count, hits, ( *lup )( const void*, size_t );
size_t ii;
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
lup = nrrdDLookup[lineType];
product = 1.0;
count = 0;
for ( ii=0; ii<len; ii++ ) {
val = NRRD_CELL_POS( axmin, axmax, len, ii );
hits = lup( line, ii );
hits = AIR_MAX( hits, 0 );
count += hits;
product *= pow( val, hits );
}
if ( !count ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
nrrdDStore[ansType]( ans, product );
}
void
_nrrdMeasureHistoSum( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double sum, hits, val, ( *lup )( const void*, size_t );
size_t ii;
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
lup = nrrdDLookup[lineType];
sum = 0;
for ( ii=0; ii<len; ii++ ) {
val = NRRD_CELL_POS( axmin, axmax, len, ii );
hits = lup( line, ii );
hits = AIR_MAX( hits, 0 );
sum += hits*val;
}
nrrdDStore[ansType]( ans, sum );
}
void
_nrrdMeasureHistoL2( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double l2, count, hits, val, ( *lup )( const void*, size_t );
size_t ii;
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
lup = nrrdDLookup[lineType];
l2 = count = 0;
for ( ii=0; ii<len; ii++ ) {
val = NRRD_CELL_POS( axmin, axmax, len, ii );
hits = lup( line, ii );
hits = AIR_MAX( hits, 0 );
count += hits;
l2 += hits*val*val;
}
if ( !count ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
nrrdDStore[ansType]( ans, l2 );
}
void
_nrrdMeasureHistoMax( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, ( *lup )( const void*, size_t );
size_t ii;
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
lup = nrrdDLookup[lineType];
/* we're using ii-1 as index to avoid wrap-around with size_t index */
for ( ii=len; ii>0; ii-- ) {
if ( lup( line, ii-1 ) > 0 ) {
break;
}
}
if ( ii==0 ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
val = NRRD_CELL_POS( axmin, axmax, len, ii-1 );
nrrdDStore[ansType]( ans, val );
}
void
_nrrdMeasureHistoMin( void *ans, int ansType,
const void *line, int lineType, size_t len,
double axmin, double axmax ) {
double val, ( *lup )( const void*, size_t );
size_t ii;
if ( !( AIR_EXISTS( axmin ) && AIR_EXISTS( axmax ) ) ) {
axmin = -0.5;
axmax = len-0.5;
}
lup = nrrdDLookup[lineType];
for ( ii=0; ii<len; ii++ ) {
if ( lup( line, ii ) > 0 ) {
break;
}
}
if ( ii==len ) {
nrrdDStore[ansType]( ans, AIR_NAN );
return;
}
val = NRRD_CELL_POS( axmin, axmax, len, ii );
nrrdDStore[ansType]( ans, val );
}
void ( *
nrrdMeasureLine[NRRD_MEASURE_MAX+1] )( void *, int,
const void *, int, size_t,
double, double ) = {
_nrrdMeasureUnknown,
_nrrdMeasureMin,
_nrrdMeasureMax,
_nrrdMeasureMean,
_nrrdMeasureMedian,
_nrrdMeasureMode,
_nrrdMeasureProduct,
_nrrdMeasureSum,
_nrrdMeasureL1,
_nrrdMeasureL2,
_nrrdMeasureL4,
_nrrdMeasureNormalizedL2,
_nrrdMeasureRootMeanSquare,
_nrrdMeasureLinf,
_nrrdMeasureVariance,
_nrrdMeasureSD,
_nrrdMeasureCoV,
_nrrdMeasureSkew,
_nrrdMeasureLineSlope,
_nrrdMeasureLineIntercept,
_nrrdMeasureLineError,
_nrrdMeasureHistoMin,
_nrrdMeasureHistoMax,
_nrrdMeasureHistoMean,
_nrrdMeasureHistoMedian,
_nrrdMeasureHistoMode,
_nrrdMeasureHistoProduct,
_nrrdMeasureHistoSum,
_nrrdMeasureHistoL2,
_nrrdMeasureHistoVariance,
_nrrdMeasureHistoSD
};
int
_nrrdMeasureType( const Nrrd *nin, int measr ) {
static const char me[]="_nrrdMeasureType";
int type=nrrdTypeUnknown;
switch( measr ) {
case nrrdMeasureMin:
case nrrdMeasureMax:
case nrrdMeasureMedian:
case nrrdMeasureMode:
type = nin->type;
break;
case nrrdMeasureMean:
/* the rational for this is that if you're after the average value
along a scanline, you probably want it in the same format as
what you started with, and if you really want an exact answer
than you can always use nrrdMeasureSum and then divide. This may
well be bone-headed, so is subject to change */
type = nin->type;
break;
case nrrdMeasureProduct:
case nrrdMeasureSum:
case nrrdMeasureL1:
case nrrdMeasureL2:
case nrrdMeasureL4:
case nrrdMeasureNormalizedL2:
case nrrdMeasureRootMeanSquare:
case nrrdMeasureLinf:
case nrrdMeasureVariance:
case nrrdMeasureSD:
case nrrdMeasureCoV:
case nrrdMeasureSkew:
case nrrdMeasureLineSlope:
case nrrdMeasureLineIntercept:
case nrrdMeasureLineError:
type = nrrdStateMeasureType;
break;
case nrrdMeasureHistoMin:
case nrrdMeasureHistoMax:
case nrrdMeasureHistoProduct:
case nrrdMeasureHistoSum:
case nrrdMeasureHistoL2:
case nrrdMeasureHistoMean:
case nrrdMeasureHistoMedian:
case nrrdMeasureHistoMode:
case nrrdMeasureHistoVariance:
case nrrdMeasureHistoSD:
/* We ( currently ) don't keep track of the type of the original
values which generated the histogram, and we may not even
have access to that information. So we end up choosing one
type for all these histogram-based measures */
type = nrrdStateMeasureHistoType;
break;
default:
fprintf( stderr, "%s: PANIC: measr %d not handled\n", me, measr );
exit( 1 );
}
return type;
}
int
nrrdProject( Nrrd *nout, const Nrrd *cnin, unsigned int axis,
int measr, int type ) {
static const char me[]="nrrdProject", func[]="project";
int iType, oType, axmap[NRRD_DIM_MAX];
unsigned int ai, ei;
size_t iElSz, oElSz, iSize[NRRD_DIM_MAX], oSize[NRRD_DIM_MAX], linLen,
rowIdx, rowNum, colIdx, colNum, colStep;
const char *ptr, *iData;
char *oData, *line;
double axmin, axmax;
Nrrd *nin;
airArray *mop;
if ( !( cnin && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == cnin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( nrrdTypeBlock == cnin->type ) {
biffAddf( NRRD, "%s: can't project nrrd type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !AIR_IN_OP( nrrdMeasureUnknown, measr, nrrdMeasureLast ) ) {
biffAddf( NRRD, "%s: measure %d not recognized", me, measr );
return 1;
}
if ( 1 == cnin->dim ) {
if ( 0 != axis ) {
biffAddf( NRRD, "%s: axis must be 0, not %u, for 1-D array", me, axis );
return 1;
}
} else {
if ( !( axis <= cnin->dim-1 ) ) {
biffAddf( NRRD, "%s: axis %u not in range [0, %d]", me, axis, cnin->dim-1 );
return 1;
}
}
if ( nrrdTypeDefault != type ) {
if ( !( AIR_IN_OP( nrrdTypeUnknown, type, nrrdTypeLast ) ) ) {
biffAddf( NRRD, "%s: got invalid target type %d", me, type );
return 1;
}
}
mop = airMopNew( );
if ( 1 == cnin->dim ) {
/* There are more efficient ways of dealing with this case; this way is
easy to implement because it leaves most of the established code below
only superficially changed; uniformly replacing nin with ( nin ? nin :
cnin ), even if pointlessly so; this expression that can't be assigned
to a new variable because of the difference in const. */
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesInsert( nin, cnin, 1 ) ) {
biffAddf( NRRD, "%s: trouble inserting axis on 1-D array", me );
airMopError( mop ); return 1;
}
} else {
nin = NULL;
}
iType = ( nin ? nin : cnin )->type;
oType = ( nrrdTypeDefault != type
? type
: _nrrdMeasureType( ( nin ? nin : cnin ), measr ) );
iElSz = nrrdTypeSize[iType];
oElSz = nrrdTypeSize[oType];
nrrdAxisInfoGet_nva( ( nin ? nin : cnin ), nrrdAxisInfoSize, iSize );
colNum = rowNum = 1;
for ( ai=0; ai<( nin ? nin : cnin )->dim; ai++ ) {
if ( ai < axis ) {
colNum *= iSize[ai];
} else if ( ai > axis ) {
rowNum *= iSize[ai];
}
}
linLen = iSize[axis];
colStep = linLen*colNum;
for ( ai=0; ai<=( nin ? nin : cnin )->dim-2; ai++ ) {
axmap[ai] = ai + ( ai >= axis );
}
for ( ai=0; ai<=( nin ? nin : cnin )->dim-2; ai++ ) {
oSize[ai] = iSize[axmap[ai]];
}
if ( nrrdMaybeAlloc_nva( nout, oType, ( nin ? nin : cnin )->dim-1, oSize ) ) {
biffAddf( NRRD, "%s: failed to create output", me );
airMopError( mop ); return 1;
}
/* allocate a scanline buffer */
if ( !( line = AIR_CALLOC( linLen*iElSz, char ) ) ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't calloc( %s, %s ) scanline buffer", me,
airSprintSize_t( stmp1, linLen ),
airSprintSize_t( stmp2, iElSz ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, line, airFree, airMopAlways );
/* the skinny */
axmin = ( nin ? nin : cnin )->axis[axis].min;
axmax = ( nin ? nin : cnin )->axis[axis].max;
iData = AIR_CAST( char *, ( nin ? nin : cnin )->data );
oData = AIR_CAST( char *, nout->data );
for ( rowIdx=0; rowIdx<rowNum; rowIdx++ ) {
for ( colIdx=0; colIdx<colNum; colIdx++ ) {
ptr = iData + iElSz*( colIdx + rowIdx*colStep );
for ( ei=0; ei<linLen; ei++ ) {
memcpy( line + ei*iElSz, ptr + ei*iElSz*colNum, iElSz );
}
nrrdMeasureLine[measr]( oData, oType, line, iType, linLen,
axmin, axmax );
oData += oElSz;
}
}
/* copy the peripheral information */
if ( nrrdAxisInfoCopy( nout, ( nin ? nin : cnin ), axmap, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nrrdContentSet_va( nout, func, cnin /* hide possible axinsert */,
"%d, %s", axis, airEnumStr( nrrdMeasure, measr ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* this will copy the space origin over directly, which is reasonable */
if ( nrrdBasicInfoCopy( nout, ( nin ? nin : cnin ),
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
Wed Sep 14 05:55:40 EDT 2005: these are no longer used
void
nrrdPeripheralInit( Nrrd *nrrd ) {
nrrdBasicInfoInit( nrrd,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT );
return;
}
int
nrrdPeripheralCopy( Nrrd *nout, const Nrrd *nin ) {
nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT );
return 0;
}
*/
/* ---- BEGIN non-NrrdIO */
const int
nrrdPresent = 42;
/* ------------------------------------------------------------ */
NrrdBoundarySpec *
65 nrrdBoundarySpecNew( void ) {
NrrdBoundarySpec *ret;
ret = AIR_CALLOC( 1, NrrdBoundarySpec );
if ( ret ) {
ret->boundary = nrrdBoundaryUnknown;
ret->padValue = AIR_NAN;
}
return ret;
}
NrrdBoundarySpec *
77 nrrdBoundarySpecNix( NrrdBoundarySpec *bspec ) {
return airFree( bspec );
}
/* NOTE: this doesn't do a validity check! */
NrrdBoundarySpec *
84 nrrdBoundarySpecCopy( const NrrdBoundarySpec *bspec ) {
NrrdBoundarySpec *ret;
if ( bspec ) {
ret = nrrdBoundarySpecNew( );
ret->boundary = bspec->boundary;
ret->padValue = bspec->padValue;
} else {
ret = NULL;
}
return ret;
}
int
98 nrrdBoundarySpecCheck( const NrrdBoundarySpec *bspec ) {
static const char me[]="nrrdBoundarySpecCheck";
if ( !bspec ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdBoundary, bspec->boundary ) ) {
biffAddf( NRRD, "%s: %d is not a valid %s value", me,
bspec->boundary, nrrdBoundary->name );
return 1;
}
if ( nrrdBoundaryPad == bspec->boundary ) {
if ( !AIR_EXISTS( bspec->padValue ) ) {
biffAddf( NRRD, "%s: need existing pad value ( not %g ) with %s %s",
me, bspec->padValue, nrrdBoundary->name,
airEnumStr( nrrdBoundary, nrrdBoundaryPad ) );
return 1;
}
}
return 0;
}
int
122 nrrdBoundarySpecParse( NrrdBoundarySpec *bspec, const char *_str ) {
static const char me[]="nrrdBoundarySpecParse";
char *str, *parm;
airArray *mop;
if ( !( bspec && _str ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
str = airStrdup( _str );
if ( !str ) {
biffAddf( NRRD, "%s: couldn't copy string", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, str, airFree, airMopAlways );
parm = strchr( str, ':' );
if ( parm ) {
*parm = '\0';
parm++;
}
bspec->boundary = airEnumVal( nrrdBoundary, str );
if ( nrrdBoundaryUnknown == bspec->boundary ) {
biffAddf( NRRD, "%s: couldn't parse %s as a %s", me,
str, nrrdBoundary->name );
airMopError( mop ); return 1;
}
if ( parm ) {
if ( nrrdBoundaryPad != bspec->boundary ) {
biffAddf( NRRD, "%s: can only have parms for %s ( not %s )", me,
airEnumStr( nrrdBoundary, nrrdBoundaryPad ),
airEnumStr( nrrdBoundary, bspec->boundary ) );
airMopError( mop ); return 1;
}
if ( 1 != sscanf( parm, "%lg", &( bspec->padValue ) ) ) {
biffAddf( NRRD, "%s: couldn't parse \"%s\" as double", me, parm );
airMopError( mop ); return 1;
}
if ( !AIR_EXISTS( bspec->padValue ) ) {
biffAddf( NRRD, "%s: need existant pad value ( not %g )", me,
bspec->padValue );
airMopError( mop ); return 1;
}
} else {
if ( nrrdBoundaryPad == bspec->boundary ) {
biffAddf( NRRD, "%s: need padValue parm for %s", me,
airEnumStr( nrrdBoundary, nrrdBoundaryPad ) );
airMopError( mop ); return 1;
}
bspec->padValue = AIR_NAN;
}
airMopOkay( mop );
return 0;
}
int
178 nrrdBoundarySpecSprint( char str[AIR_STRLEN_LARGE],
const NrrdBoundarySpec *bspec ) {
static const char me[]="nrrdBoundarySpecSprint";
char *out;
if ( !( str && bspec ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdBoundarySpecCheck( bspec ) ) {
biffAddf( NRRD, "%s: problem", me );
return 1;
}
out = str;
sprintf( out, "%s", airEnumStr( nrrdBoundary, bspec->boundary ) );
out += strlen( out );
if ( nrrdBoundaryPad == bspec->boundary ) {
sprintf( out, ":%.17g", bspec->padValue );
}
return 0;
}
int
201 nrrdBoundarySpecCompare( const NrrdBoundarySpec *aa,
const NrrdBoundarySpec *bb,
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdBoundarySpecEqual";
if ( !differ ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !!aa != !!bb ) {
if ( explain ) {
sprintf( explain, "NULL-ities differ: %s != %s",
aa ? "non-NULL" : "NULL",
bb ? "non-NULL" : "NULL" );
}
*differ = 1; return 0;
}
if ( !aa ) {
/* got two NULL boundary specs ==> equal */
*differ = 0; return 0;
}
if ( aa->boundary != bb->boundary ) {
if ( explain ) {
sprintf( explain, "boundaries differ: %s != %s",
airEnumStr( nrrdBoundary, aa->boundary ),
airEnumStr( nrrdBoundary, bb->boundary ) );
}
*differ = 1; return 0;
}
if ( nrrdBoundaryPad == aa->boundary ) {
if ( aa->padValue != bb->padValue ) {
if ( explain ) {
sprintf( explain, "padValue differ: %.17g != %.17g",
aa->padValue, bb->padValue );
}
*differ = 1; return 0;
}
}
*differ = 0;
return 0;
}
/* ---- END non-NrrdIO */
/* ------------------------------------------------------------ */
void
247 nrrdIoStateInit( NrrdIoState *nio ) {
if ( nio ) {
nio->path = ( char * )airFree( nio->path );
nio->base = ( char * )airFree( nio->base );
nio->line = ( char * )airFree( nio->line );
nio->dataFNFormat = ( char * )airFree( nio->dataFNFormat );
/* the way IO to/from strings works, I don't think this should be freed */
nio->headerStringRead = NULL;
nio->headerStringWrite = NULL;
airArrayLenSet( nio->dataFNArr, 0 );
airArrayLenSet( nio->dataFSkipArr, 0 );
/* closing this is always someone else's responsibility */
nio->headerFile = NULL;
nio->dataFile = NULL;
nio->dataFileDim = 0;
nio->dataFNMin = 0;
nio->dataFNMax = 0;
nio->dataFNStep = 0;
nio->dataFNIndex = 0;
nio->lineLen = 0;
nio->pos = 0;
nio->endian = airEndianUnknown;
nio->lineSkip = 0;
nio->headerStrlen = 0;
nio->headerStrpos = 0;
nio->byteSkip = 0;
memset( nio->seen, 0, ( NRRD_FIELD_MAX+1 )*sizeof( int ) );
nio->detachedHeader = AIR_FALSE;
nio->bareText = nrrdDefaultWriteBareText;
nio->charsPerLine = nrrdDefaultWriteCharsPerLine;
nio->valsPerLine = nrrdDefaultWriteValsPerLine;
nio->skipData = AIR_FALSE;
nio->skipFormatURL = AIR_FALSE;
nio->keepNrrdDataFileOpen = AIR_FALSE;
nio->zlibLevel = -1;
nio->zlibStrategy = nrrdZlibStrategyDefault;
nio->bzip2BlockSize = -1;
nio->learningHeaderStrlen = AIR_FALSE;
nio->oldData = NULL;
nio->oldDataSize = 0;
nio->format = nrrdFormatUnknown;
nio->encoding = nrrdEncodingUnknown;
}
return;
}
NrrdIoState *
295 nrrdIoStateNew( void ) {
NrrdIoState *nio;
nio = ( NrrdIoState * )calloc( 1, sizeof( NrrdIoState ) );
if ( nio ) {
airPtrPtrUnion appu;
nio->path = NULL;
nio->base = NULL;
nio->line = NULL;
nio->dataFNFormat = NULL;
nio->dataFN = NULL;
nio->headerStringRead = NULL;
nio->headerStringWrite = NULL;
appu.cp = &( nio->dataFN );
nio->dataFNArr = airArrayNew( appu.v, NULL,
sizeof( char * ), NRRD_FILENAME_INCR );
airArrayPointerCB( nio->dataFNArr, airNull, airFree );
nio->dataFSkip = NULL;
appu.li = &( nio->dataFSkip );
nio->dataFSkipArr = airArrayNew( appu.v, NULL,
sizeof( long int ), NRRD_FILENAME_INCR );
nio->format = nrrdFormatUnknown;
nio->encoding = nrrdEncodingUnknown;
nrrdIoStateInit( nio );
}
return nio;
}
NrrdIoState *
325 nrrdIoStateNix( NrrdIoState *nio ) {
nio->path = ( char * )airFree( nio->path );
nio->base = ( char * )airFree( nio->base );
nio->line = ( char * )airFree( nio->line );
nio->dataFNFormat = ( char * )airFree( nio->dataFNFormat );
nio->dataFNArr = airArrayNuke( nio->dataFNArr );
nio->dataFSkipArr = airArrayNuke( nio->dataFSkipArr );
/* the NrrdIoState never owned nio->oldData; we don't free it */
airFree( nio ); /* no NULL assignment, else compile warnings */
return NULL;
}
/* ---- BEGIN non-NrrdIO */
/* ------------------------------------------------------------ */
void
343 _nrrdResampleInfoInit( NrrdResampleInfo *info ) {
int i, d;
for ( d=0; d<NRRD_DIM_MAX; d++ ) {
info->kernel[d] = NULL;
info->samples[d] = 0;
info->parm[d][0] = nrrdDefaultKernelParm0;
for ( i=1; i<NRRD_KERNEL_PARMS_NUM; i++ )
info->parm[d][i] = AIR_NAN;
info->min[d] = info->max[d] = AIR_NAN;
}
info->boundary = nrrdDefaultResampleBoundary;
info->type = nrrdDefaultResampleType;
info->renormalize = nrrdDefaultResampleRenormalize;
info->round = nrrdDefaultResampleRound;
info->clamp = nrrdDefaultResampleClamp;
info->cheap = nrrdDefaultResampleCheap;
info->padValue = nrrdDefaultResamplePadValue;
}
NrrdResampleInfo *
364 nrrdResampleInfoNew( void ) {
NrrdResampleInfo *info;
info = ( NrrdResampleInfo* )( calloc( 1, sizeof( NrrdResampleInfo ) ) );
if ( info ) {
/* explicitly sets pointers to NULL */
_nrrdResampleInfoInit( info );
}
return info;
}
NrrdResampleInfo *
376 nrrdResampleInfoNix( NrrdResampleInfo *info ) {
info = ( NrrdResampleInfo * )airFree( info );
return NULL;
}
/* ------------------------------------------------------------ */
NrrdKernelSpec *
385 nrrdKernelSpecNew( void ) {
NrrdKernelSpec *ksp;
int i;
ksp = ( NrrdKernelSpec * )calloc( 1, sizeof( NrrdKernelSpec ) );
if ( ksp ) {
ksp->kernel = NULL;
for ( i=0; i<NRRD_KERNEL_PARMS_NUM; i++ ) {
ksp->parm[i] = airNaN( ); /* valgrind complained about AIR_NAN at -O2 */
}
}
return ksp;
}
NrrdKernelSpec *
400 nrrdKernelSpecCopy( const NrrdKernelSpec *oldKsp ) {
NrrdKernelSpec *ksp=NULL;
if ( oldKsp ) {
ksp = ( NrrdKernelSpec * )calloc( 1, sizeof( NrrdKernelSpec ) );
if ( ksp ) {
memcpy( ksp, oldKsp, sizeof( NrrdKernelSpec ) );
}
}
return ksp;
}
NrrdKernelSpec *
413 nrrdKernelSpecNix( NrrdKernelSpec *ksp ) {
ksp = ( NrrdKernelSpec * )airFree( ksp );
return NULL;
}
void
420 nrrdKernelSpecSet( NrrdKernelSpec *ksp, const NrrdKernel *k,
const double kparm[NRRD_KERNEL_PARMS_NUM] ) {
unsigned int p;
if ( ksp && k && kparm ) {
ksp->kernel = k;
for ( p=0; p<( k->numParm ); p++ ) {
ksp->parm[p] = kparm[p];
}
}
}
void
433 nrrdKernelParmSet( const NrrdKernel **kP, double kparm[NRRD_KERNEL_PARMS_NUM],
NrrdKernelSpec *ksp ) {
int p;
if ( kP && kparm && ksp ) {
*kP = ksp->kernel;
for ( p=0; p<NRRD_KERNEL_PARMS_NUM; p++ ) {
kparm[p] = ksp->parm[p];
}
}
}
/* ---- END non-NrrdIO */
/* ------------------------------------------------------------ */
/* see axis.c for axis-specific "methods" */
/* ------------------------------------------------------------ */
/*
******** nrrdBasicInfoInit
**
** resets "basic" ( per-array ) information
** formerly nrrdPeripheralInit
**
** the bitflag communicates which fields should *not* be initialized
*/
void
462 nrrdBasicInfoInit( Nrrd *nrrd, int bitflag ) {
int dd, ee;
if ( !nrrd ) {
return;
}
if ( !( NRRD_BASIC_INFO_DATA_BIT & bitflag ) ) {
nrrd->data = airFree( nrrd->data );
}
if ( !( NRRD_BASIC_INFO_TYPE_BIT & bitflag ) ) {
nrrd->type = nrrdTypeUnknown;
}
if ( !( NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag ) ) {
nrrd->blockSize = 0;
}
if ( !( NRRD_BASIC_INFO_DIMENSION_BIT & bitflag ) ) {
nrrd->dim = 0;
}
if ( !( NRRD_BASIC_INFO_CONTENT_BIT & bitflag ) ) {
nrrd->content = ( char * )airFree( nrrd->content );
}
if ( !( NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag ) ) {
nrrd->sampleUnits = ( char * )airFree( nrrd->sampleUnits );
}
if ( !( NRRD_BASIC_INFO_SPACE_BIT & bitflag ) ) {
nrrd->space = nrrdSpaceUnknown;
nrrd->spaceDim = 0;
}
if ( !( NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag ) ) {
nrrd->space = nrrdSpaceUnknown;
nrrd->spaceDim = 0;
}
if ( !( NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag ) ) {
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
nrrd->spaceUnits[dd] = ( char * )airFree( nrrd->spaceUnits[dd] );
}
}
if ( !( NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag ) ) {
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
nrrd->spaceOrigin[dd] = AIR_NAN;
}
}
if ( !( NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag ) ) {
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
for ( ee=0; ee<NRRD_SPACE_DIM_MAX; ee++ ) {
nrrd->measurementFrame[dd][ee] = AIR_NAN;
}
}
}
if ( !( NRRD_BASIC_INFO_OLDMIN_BIT & bitflag ) ) {
nrrd->oldMin = AIR_NAN;
}
if ( !( NRRD_BASIC_INFO_OLDMAX_BIT & bitflag ) ) {
nrrd->oldMax = AIR_NAN;
}
if ( !( NRRD_BASIC_INFO_COMMENTS_BIT & bitflag ) ) {
nrrdCommentClear( nrrd );
}
if ( !( NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag ) ) {
nrrdKeyValueClear( nrrd );
}
return;
}
/*
******** nrrdBasicInfoCopy
**
** copies "basic" ( per-array ) information
** formerly known as nrrdPeripheralCopy, which was not used consistently
**
** the bitflag communicates which fields should *not* be copied
*/
int
536 nrrdBasicInfoCopy( Nrrd *dest, const Nrrd *src, int bitflag ) {
static const char me[]="nrrdBasicInfoCopy";
unsigned int dd, ee;
if ( !( dest && src ) )
return 0;
if ( dest == src ) {
/* nothing to do */
return 0;
}
if ( !( NRRD_BASIC_INFO_DATA_BIT & bitflag ) ) {
dest->data = src->data;
}
if ( !( NRRD_BASIC_INFO_TYPE_BIT & bitflag ) ) {
dest->type = src->type;
}
if ( !( NRRD_BASIC_INFO_BLOCKSIZE_BIT & bitflag ) ) {
dest->blockSize = src->blockSize;
}
if ( !( NRRD_BASIC_INFO_DIMENSION_BIT & bitflag ) ) {
dest->dim = src->dim;
}
if ( !( NRRD_BASIC_INFO_CONTENT_BIT & bitflag ) ) {
dest->content = ( char * )airFree( dest->content );
dest->content = airStrdup( src->content );
if ( src->content && !dest->content ) {
biffAddf( NRRD, "%s: couldn't copy content", me );
return 1;
}
}
if ( !( NRRD_BASIC_INFO_SAMPLEUNITS_BIT & bitflag ) ) {
dest->sampleUnits = ( char * )airFree( dest->sampleUnits );
dest->sampleUnits = airStrdup( src->sampleUnits );
if ( src->sampleUnits && !dest->sampleUnits ) {
biffAddf( NRRD, "%s: couldn't copy sampleUnits", me );
return 1;
}
}
if ( !( NRRD_BASIC_INFO_SPACE_BIT & bitflag ) ) {
dest->space = src->space;
}
if ( !( NRRD_BASIC_INFO_SPACEDIMENSION_BIT & bitflag ) ) {
dest->spaceDim = src->spaceDim;
}
if ( !( NRRD_BASIC_INFO_SPACEUNITS_BIT & bitflag ) ) {
for ( dd=0; dd<src->spaceDim; dd++ ) {
dest->spaceUnits[dd] = ( char * )airFree( dest->spaceUnits[dd] );
dest->spaceUnits[dd] = airStrdup( src->spaceUnits[dd] );
if ( src->spaceUnits[dd] && !dest->spaceUnits[dd] ) {
biffAddf( NRRD, "%s: couldn't copy spaceUnits[%d]", me, dd );
return 1;
}
}
for ( dd=src->spaceDim; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
dest->spaceUnits[dd] = ( char * )airFree( dest->spaceUnits[dd] );
}
}
if ( !( NRRD_BASIC_INFO_SPACEORIGIN_BIT & bitflag ) ) {
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
if ( dd <= src->spaceDim-1 ) {
dest->spaceOrigin[dd] = src->spaceOrigin[dd];
} else {
dest->spaceOrigin[dd] = AIR_NAN;
}
}
}
if ( !( NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT & bitflag ) ) {
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
for ( ee=0; ee<NRRD_SPACE_DIM_MAX; ee++ ) {
if ( dd <= src->spaceDim-1 && ee <= src->spaceDim-1 ) {
dest->measurementFrame[dd][ee] = src->measurementFrame[dd][ee];
} else {
dest->measurementFrame[dd][ee] = AIR_NAN;
}
}
}
for ( dd=src->spaceDim; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
dest->spaceOrigin[dd] = AIR_NAN;
}
}
if ( !( NRRD_BASIC_INFO_OLDMIN_BIT & bitflag ) ) {
dest->oldMin = src->oldMin;
}
if ( !( NRRD_BASIC_INFO_OLDMAX_BIT & bitflag ) ) {
dest->oldMax = src->oldMax;
}
if ( !( NRRD_BASIC_INFO_COMMENTS_BIT & bitflag ) ) {
if ( nrrdCommentCopy( dest, src ) ) {
biffAddf( NRRD, "%s: trouble copying comments", me );
return 1;
}
}
if ( !( NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT & bitflag ) ) {
if ( nrrdKeyValueCopy( dest, src ) ) {
biffAddf( NRRD, "%s: trouble copying key/value pairs", me );
return 1;
}
}
return 0;
}
/*
******* nrrdInit
**
** initializes a nrrd to default state. All nrrd functions in the
** business of initializing a nrrd struct use this function. Mostly
** just sets values to 0, NaN, "", NULL, or Unknown
*/
void
646 nrrdInit( Nrrd *nrrd ) {
int ii;
if ( nrrd ) {
nrrdBasicInfoInit( nrrd, NRRD_BASIC_INFO_NONE );
for ( ii=0; ii<NRRD_DIM_MAX; ii++ ) {
_nrrdAxisInfoInit( nrrd->axis + ii );
}
}
return;
}
/*
******** nrrdNew( )
**
** creates and initializes a Nrrd
**
** this does NOT use biff
*/
Nrrd *
666 nrrdNew( void ) {
int ii;
Nrrd *nrrd;
airPtrPtrUnion appu;
nrrd = ( Nrrd* )( calloc( 1, sizeof( Nrrd ) ) );
if ( !nrrd ) {
return NULL;
}
/* explicitly set pointers to NULL, since calloc isn't officially
guaranteed to do that. */
nrrd->data = NULL;
for ( ii=0; ii<NRRD_DIM_MAX; ii++ ) {
_nrrdAxisInfoNewInit( nrrd->axis + ii );
}
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
nrrd->spaceUnits[ii] = NULL;
}
nrrd->content = NULL;
nrrd->sampleUnits = NULL;
/* create comment airArray ( even though it starts empty ) */
nrrd->cmt = NULL;
appu.cp = &( nrrd->cmt );
nrrd->cmtArr = airArrayNew( appu.v, NULL, sizeof( char * ), NRRD_COMMENT_INCR );
if ( !nrrd->cmtArr ) {
return NULL;
}
airArrayPointerCB( nrrd->cmtArr, airNull, airFree );
/* create key/value airArray ( even thought it starts empty ) */
nrrd->kvp = NULL;
appu.cp = &( nrrd->kvp );
nrrd->kvpArr = airArrayNew( appu.v, NULL,
2*sizeof( char * ), NRRD_KEYVALUE_INCR );
if ( !nrrd->kvpArr ) {
return NULL;
}
/* key/value airArray uses no callbacks for now */
/* finish initializations */
nrrdInit( nrrd );
return nrrd;
}
/*
******** nrrdNix( )
**
** does nothing with the array data inside, just does whatever is needed
** to free the nrrd itself
**
** returns NULL
**
** this does NOT use biff
*/
Nrrd *
724 nrrdNix( Nrrd *nrrd ) {
int ii;
if ( nrrd ) {
for ( ii=0; ii<NRRD_DIM_MAX; ii++ ) {
_nrrdAxisInfoInit( &( nrrd->axis[ii] ) );
}
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
nrrd->spaceUnits[ii] = ( char * )airFree( nrrd->spaceUnits[ii] );
}
nrrd->content = ( char * )airFree( nrrd->content );
nrrd->sampleUnits = ( char * )airFree( nrrd->sampleUnits );
nrrdCommentClear( nrrd );
nrrd->cmtArr = airArrayNix( nrrd->cmtArr );
nrrdKeyValueClear( nrrd );
nrrd->kvpArr = airArrayNix( nrrd->kvpArr );
airFree( nrrd );
}
return NULL;
}
/*
******** nrrdEmpty( )
**
** frees data inside nrrd AND resets all its state, so its the
** same as what comes from nrrdNew( ). This includes free( )ing
** any comments.
*/
Nrrd *
753 nrrdEmpty( Nrrd *nrrd ) {
if ( nrrd ) {
nrrd->data = airFree( nrrd->data );
nrrdInit( nrrd );
}
return nrrd;
}
/*
******** nrrdNuke( )
**
** blows away the nrrd and everything inside
**
** always returns NULL
*/
Nrrd *
770 nrrdNuke( Nrrd *nrrd ) {
if ( nrrd ) {
nrrdEmpty( nrrd );
nrrdNix( nrrd );
}
return NULL;
}
/* ------------------------------------------------------------ */
int
782 _nrrdSizeCheck( const size_t *size, unsigned int dim, int useBiff ) {
static const char me[]="_nrrdSizeCheck";
size_t num, pre;
unsigned int ai;
pre = num = 1;
for ( ai=0; ai<dim; ai++ ) {
if ( !size[ai] ) {
biffMaybeAddf( useBiff, NRRD, "%s: axis %u size is zero!", me, ai );
return 1;
}
num *= size[ai];
if ( num/size[ai] != pre ) {
biffMaybeAddf( useBiff, NRRD,
"%s: total # of elements too large to be represented in "
"type size_t, so too large for current architecture", me );
return 1;
}
pre *= size[ai];
}
return 0;
}
/*
******** nrrdWrap_nva( )
**
** wraps a given Nrrd around a given array
**
** we don't touch any of the peripheral information ( content, comments,
** blocksize, min/max ) because it is entirely reasonable to be setting
** this before or after this call. "type" could be passed as
** nrrdTypeBlock, in which case it is the user's responsibility to
** set nrrd->blockSize at some other time.
*/
int
817 nrrdWrap_nva( Nrrd *nrrd, void *data, int type,
unsigned int dim, const size_t *size ) {
static const char me[]="nrrdWrap_nva";
if ( !( nrrd && size ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
nrrd->data = data;
nrrd->type = type;
nrrd->dim = dim;
if ( _nrrdSizeCheck( size, dim, AIR_TRUE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSize, size );
return 0;
}
/*
******** nrrdWrap_va( )
**
** Minimal var args wrapper around nrrdWrap_nva, with the advantage of
** taking all the axes sizes as the var args.
**
** This is THE BEST WAY to wrap a nrrd around existing raster data,
** assuming that the dimension is known at compile time.
**
** If successful, returns 0, otherwise, 1.
** This does use biff.
*/
int
849 nrrdWrap_va( Nrrd *nrrd, void *data, int type, unsigned int dim, ... ) {
static const char me[]="nrrdWrap_va";
va_list ap;
size_t size[NRRD_DIM_MAX];
unsigned int ai;
if ( !( nrrd && data ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
va_start( ap, dim );
for ( ai=0; ai<dim; ai++ ) {
size[ai] = va_arg( ap, size_t );
}
va_end( ap );
return nrrdWrap_nva( nrrd, data, type, dim, size );
}
/*
void
_nrrdTraverse( Nrrd *nrrd ) {
char *test, tval;
size_t I, N;
int S;
N = nrrdElementNumber( nrrd );
S = nrrdElementSize( nrrd );
tval = 0;
test = nrrd->data;
for ( I=0; I<N*S; I++ ) {
tval += test[I];
}
}
*/
int
886 _nrrdCopy( Nrrd *nout, const Nrrd *nin, int bitflag ) {
static const char me[]="_nrrdCopy";
size_t size[NRRD_DIM_MAX];
if ( !( nin && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
/* its not the case that we have nothing to do- the semantics of
copying cannot be achieved if the input and output nrrd are
the same; this is an error */
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: input nrrd reports zero element size!", me );
return 1;
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( nin->data ) {
if ( nrrdMaybeAlloc_nva( nout, nin->type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: couldn't allocate data", me );
return 1;
}
memcpy( nout->data, nin->data,
nrrdElementNumber( nin )*nrrdElementSize( nin ) );
} else {
/* someone is trying to copy structs without data, fine fine fine */
if ( nrrdWrap_nva( nout, NULL, nin->type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: couldn't wrap NULL data", me );
return 1;
}
}
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT );
/* if nin->data non-NULL ( second branch above ), this will
harmlessly unset and set type and dim */
nrrdBasicInfoInit( nout, NRRD_BASIC_INFO_DATA_BIT | bitflag );
if ( nrrdBasicInfoCopy( nout, nin, NRRD_BASIC_INFO_DATA_BIT | bitflag ) ) {
biffAddf( NRRD, "%s: trouble copying basic info", me );
return 1;
}
return 0;
}
/*
******** nrrdCopy
**
** copy method for nrrds. nout will end up as an "exact" copy of nin.
** New space for data is allocated here, and output nrrd points to it.
** Comments from old are added to comments for new, so these are also
** newly allocated. nout->ptr is not set, nin->ptr is not read.
*/
int
941 nrrdCopy( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="nrrdCopy";
if ( _nrrdCopy( nout, nin, NRRD_BASIC_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdAlloc_nva( )
**
** allocates data array and sets information. If this is a block type
** nrrd, it is necessary to set nrrd->blockSize PRIOR to calling
** this function.
**
** This function will always allocate more memory ( via calloc ), but
** it will free( ) nrrd->data if it is non-NULL when passed in.
**
** This function takes the same "don't mess with peripheral information"
** attitude as nrrdWrap( ).
**
** Note to Gordon: don't get clever and change ANY axis-specific
** information here. It may be very convenient to set that before
** nrrdAlloc or nrrdMaybeAlloc
**
** Note: This function DOES use biff
*/
int
971 nrrdAlloc_nva( Nrrd *nrrd, int type, unsigned int dim, const size_t *size ) {
static const char me[]="nrrdAlloc_nva";
size_t num, esize;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nrrd && size ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: type ( %d ) is invalid", me, type );
return 1;
}
if ( nrrdTypeBlock == type ) {
if ( !( 0 < nrrd->blockSize ) ) {
biffAddf( NRRD, "%s: given nrrd->blockSize %s invalid", me,
airSprintSize_t( stmp[0], nrrd->blockSize ) );
return 1;
}
}
if ( !AIR_IN_CL( 1, dim, NRRD_DIM_MAX ) ) {
biffAddf( NRRD, "%s: dim ( %d ) not in valid range [1, %d]",
me, dim, NRRD_DIM_MAX );
return 1;
}
nrrd->data = airFree( nrrd->data );
if ( nrrdWrap_nva( nrrd, NULL, type, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1 ;
}
num = nrrdElementNumber( nrrd );
esize = nrrdElementSize( nrrd );
nrrd->data = calloc( num, esize );
if ( !( nrrd->data ) ) {
biffAddf( NRRD, "%s: calloc( %s, %s ) failed", me,
airSprintSize_t( stmp[0], num ),
airSprintSize_t( stmp[1], esize ) );
return 1 ;
}
return 0;
}
/*
******** nrrdAlloc_va( )
**
** Handy wrapper around nrrdAlloc_nva, which takes, as its vararg list,
** all the axes sizes.
*/
int
1022 nrrdAlloc_va( Nrrd *nrrd, int type, unsigned int dim, ... ) {
static const char me[]="nrrdAlloc_va";
size_t size[NRRD_DIM_MAX];
unsigned int ai;
va_list ap;
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
va_start( ap, dim );
for ( ai=0; ai<dim; ai++ ) {
size[ai] = va_arg( ap, size_t );
}
va_end( ap );
if ( nrrdAlloc_nva( nrrd, type, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
** _nrrdMaybeAllocMaybeZero_nva
**
** New implementation of nrrdMaybeAlloc_nva, but now with ability
** to control whether or not to zero out when re-allocation wasn't needed
**
** HEY: should consider making this a public function, but GLK couldn't
** think of a name that wasn't silly
*/
int
1055 _nrrdMaybeAllocMaybeZero_nva( Nrrd *nrrd, int type,
unsigned int dim, const size_t *size,
int zeroWhenNoAlloc ) {
static const char me[]="nrrdMaybeAllocMaybeZero_nva";
size_t sizeWant, sizeHave, numWant, elementSizeWant;
int need;
unsigned int ai;
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: type ( %d ) is invalid", me, type );
return 1;
}
if ( nrrdTypeBlock == type ) {
if ( nrrdTypeBlock == nrrd->type ) {
biffAddf( NRRD, "%s: can't change from one block nrrd to another", me );
return 1;
}
if ( !( 0 < nrrd->blockSize ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: given nrrd->blockSize %s invalid", me,
airSprintSize_t( stmp, nrrd->blockSize ) );
return 1;
}
elementSizeWant = nrrd->blockSize;
} else {
elementSizeWant = nrrdTypeSize[type];
}
if ( _nrrdSizeCheck( size, dim, AIR_TRUE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( !( nrrd->data ) ) {
need = 1;
} else {
numWant = 1;
for ( ai=0; ai<dim; ai++ ) {
numWant *= size[ai];
}
if ( !nrrdElementSize( nrrd ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
sizeHave = nrrdElementNumber( nrrd ) * nrrdElementSize( nrrd );
/* fprintf( stderr, "##%s: sizeHave = %d * %d = %d\n", me,
( int )( nrrdElementNumber( nrrd ) ),
( int )( nrrdElementSize( nrrd ) ), ( int )sizeHave ); */
sizeWant = numWant * elementSizeWant;
/* fprintf( stderr, "##%s: sizeWant = %d * %d = %d\n", me,
( int )( numWant ),
( int )( elementSizeWant ), ( int )sizeWant ); */
need = sizeHave != sizeWant;
/* fprintf( stderr, "##%s: need = %d\n", me, need ); */
}
if ( need ) {
if ( nrrdAlloc_nva( nrrd, type, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
} else {
/* size is already exactly what we want */
if ( nrrdWrap_nva( nrrd, nrrd->data, type, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* but we may have to initialize memory */
if ( zeroWhenNoAlloc ) {
memset( nrrd->data, 0, nrrdElementNumber( nrrd )*nrrdElementSize( nrrd ) );
}
}
return 0;
}
/*
******** nrrdMaybeAlloc_nva
**
** NOTE: this is now just a wrapper around _nrrdMaybeAllocMaybeZero_nva;
** below info referred to original implementation.
**
** calls nrrdAlloc_nva if the requested space is different than
** what is currently held
**
** also subscribes to the "don't mess with peripheral information" philosophy
*/
int
1145 nrrdMaybeAlloc_nva( Nrrd *nrrd, int type,
unsigned int dim, const size_t *size ) {
static const char me[]="nrrdMaybeAlloc_nva";
int ret;
ret = _nrrdMaybeAllocMaybeZero_nva( nrrd, type, dim, size,
AIR_TRUE );
if ( ret ) {
biffAddf( NRRD, "%s: trouble", me );
}
return ret;
}
/*
******** nrrdMaybeAlloc_va( )
**
** Handy wrapper around nrrdAlloc, which takes, as its vararg list
** all the axes sizes, thereby calculating the total number.
*/
int
1164 nrrdMaybeAlloc_va( Nrrd *nrrd, int type, unsigned int dim, ... ) {
static const char me[]="nrrdMaybeAlloc_va";
size_t size[NRRD_DIM_MAX];
unsigned int ai;
va_list ap;
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
va_start( ap, dim );
for ( ai=0; ai<dim; ai++ ) {
size[ai] = va_arg( ap, size_t );
}
va_end( ap );
if ( nrrdMaybeAlloc_nva( nrrd, type, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/* ---- BEGIN non-NrrdIO */
/*
** nrrdCompare
**
** walks through all fields of the two nrrds to see if they contain
** the same information. So, this doesn't compare any pointer values,
** only the contents of things.
**
** Unlike strcmp( ), the return value from this can't be the indication
** of the difference, because we'd have to return a value even in the
** case of an error, which would be be strange. GLK briefly tried
** created a new #define _NRRD_ERROR_RETURN for this purpose ( with
** leet-speak value 32202 ), but its just a bad idea.
**
** NOTE: the structure of this code is very similar to that of
** nrrdAxisInfoCompare, and any improvements here should be reflected there
*/
int
1205 nrrdCompare( const Nrrd *ninA, const Nrrd *ninB,
int onlyData, double epsilon,
int *differ, char explain[AIR_STRLEN_LARGE] ) {
static const char me[]="nrrdCompare";
size_t numA, numB;
unsigned int axi, saxi;
if ( !( ninA && ninB && differ ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, or %p )", me,
AIR_CVOIDP( ninA ), AIR_CVOIDP( ninB ), AIR_VOIDP( differ ) );
return 1;
}
if ( explain ) {
strcpy( explain, "" );
}
if ( !onlyData ) {
if ( ninA->dim != ninB->dim ) {
*differ = ninA->dim < ninB->dim ? -1 : 1;
if ( explain ) {
sprintf( explain, "nin{A, B}->dim %u %s %u",
ninA->dim, *differ < 0 ? "<" : ">", ninB->dim );
}
return 0;
}
}
if ( ninA->type != ninB->type ) {
*differ = ninA->type < ninB->type ? -1 : 1;
if ( explain ) {
sprintf( explain, "nin{A, B}->type %s %s %s",
airEnumStr( nrrdType, ninA->type ), *differ < 0 ? "<" : ">",
airEnumStr( nrrdType, ninB->type ) );
}
return 0;
}
numA = nrrdElementNumber( ninA );
numB = nrrdElementNumber( ninB );
if ( numA != numB ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
*differ = numA < numB ? -1 : 1;
sprintf( explain, "element # {A, B} %s %s %s",
airSprintSize_t( stmp1, numA ), *differ < 0 ? "<" : ">",
airSprintSize_t( stmp2, numB ) );
return 0;
}
/* this will always set *differ */
if ( nrrdArrayCompare( ninA->type, ninA->data, ninB->data, numA,
epsilon, differ, explain ) ) {
biffAddf( NRRD, "%s: problem comparing values", me );
return 1;
}
if ( onlyData || *differ ) {
/* If caller only cared about data values,
we're done whether or not they were different.
else, if the data values are different, we're done.
The explanation of any difference in values is
already in "explain" ( if non-NULL ) */
return 0;
}
for ( axi=0; axi<ninA->dim; axi++ ) {
/* this always sets *differ */
if ( nrrdAxisInfoCompare( ninA->axis + axi, ninB->axis + axi,
differ, explain ) ) {
biffAddf( NRRD, "%s: problem comparing axis %u", me, axi );
return 1;
}
if ( *differ ) {
char tmpexplain[AIR_STRLEN_LARGE];
/* the explanation, if wanted, is in "explain", but we add context */
sprintf( tmpexplain, "( axis %u ) %s", axi, explain );
airStrcpy( explain, AIR_STRLEN_LARGE, tmpexplain );
return 0;
}
}
#define STRING_COMPARE( VAL, STR ) \
*differ = airStrcmp( ninA->VAL, ninB->VAL ); \
if ( *differ ) { \
if ( explain ) { \
/* can't print whole string because of fixed-size of explain */ \
sprintf( explain, "ninA->%s %s ninB->%s", \
STR, *differ < 0 ? "<" : ">", STR ); \
} \
return 0; \
}
STRING_COMPARE( content, "content" );
STRING_COMPARE( sampleUnits, "sampleUnits" );
if ( ninA->space != ninB->space ) {
*differ = ninA->space < ninB->space ? -1 : 1;
if ( explain ) {
sprintf( explain, "ninA->space %s %s ninB->space %s",
airEnumStr( nrrdSpace, ninA->space ),
*differ < 0 ? "<" : ">",
airEnumStr( nrrdSpace, ninB->space ) );
}
return 0;
}
if ( ninA->spaceDim != ninB->spaceDim ) {
*differ = ninA->spaceDim < ninB->spaceDim ? -1 : 1;
if ( explain ) {
sprintf( explain, "ninA->spaceDim %u %s ninB->spaceDim %u",
ninA->spaceDim, *differ < 0 ? "<" : ">", ninB->spaceDim );
}
return 0;
}
if ( ninA->blockSize != ninB->blockSize ) {
*differ = ninA->blockSize < ninB->blockSize ? -1 : 1;
if ( explain ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
sprintf( explain, "ninA->blockSize %s %s ninB->blockSize %s",
airSprintSize_t( stmp1, ninA->blockSize ),
*differ < 0 ? "<" : ">",
airSprintSize_t( stmp2, ninB->blockSize ) );
}
return 0;
}
#define DOUBLE_COMPARE( VAL, STR ) \
*differ = _nrrdDblcmp( ninA->VAL, ninB->VAL ); \
if ( *differ ) { \
if ( explain ) { \
sprintf( explain, "ninA->%s %.17g %s ninB->%s %.17g", \
STR, ninA->VAL, *differ < 0 ? "<" : ">", \
STR, ninB->VAL ); \
} \
return 0; \
}
for ( saxi=0; saxi<NRRD_SPACE_DIM_MAX; saxi++ ) {
char stmp[AIR_STRLEN_SMALL];
unsigned int saxj;
sprintf( stmp, "spaceOrigin[%u]", saxi );
DOUBLE_COMPARE( spaceOrigin[saxi], stmp );
sprintf( stmp, "spaceUnits[%u]", saxi );
STRING_COMPARE( spaceUnits[saxi], stmp );
for ( saxj=0; saxj<NRRD_SPACE_DIM_MAX; saxj++ ) {
sprintf( stmp, "measurementFrame[%u][%u]", saxi, saxj );
DOUBLE_COMPARE( measurementFrame[saxi][saxj], stmp );
}
}
DOUBLE_COMPARE( oldMin, "oldMin" );
DOUBLE_COMPARE( oldMax, "oldMax" );
#undef DOUBLE_COMPARE
if ( ninA->cmtArr->len != ninB->cmtArr->len ) {
*differ = ninA->cmtArr->len < ninB->cmtArr->len ? -1 : 1;
if ( explain ) {
sprintf( explain, "ninA # comments %u %s ninB # comments %u",
ninA->cmtArr->len, *differ < 0 ? "<" : ">", ninB->cmtArr->len );
}
return 0;
} else {
unsigned int ii;
char stmp[AIR_STRLEN_SMALL];
for ( ii=0; ii<ninA->cmtArr->len; ii++ ) {
sprintf( stmp, "comment[%u]", ii );
STRING_COMPARE( cmt[ii], stmp );
}
}
if ( ninA->kvpArr->len != ninB->kvpArr->len ) {
*differ = ninA->kvpArr->len < ninB->kvpArr->len ? -1 : 1;
if ( explain ) {
sprintf( explain, "ninA # key/values %u %s ninB # key/values %u",
ninA->kvpArr->len, *differ < 0 ? "<" : ">", ninB->kvpArr->len );
}
return 0;
} else {
unsigned int ii;
char stmp[AIR_STRLEN_SMALL];
for ( ii=0; ii<ninA->kvpArr->len; ii++ ) {
sprintf( stmp, "key/value key[%u]", ii );
STRING_COMPARE( kvp[2*ii + 0], stmp );
sprintf( stmp, "key/value value[%u]", ii );
STRING_COMPARE( kvp[2*ii + 1], stmp );
}
}
#undef STRING_COMPARE
/* ninA->ptr and ninB->ptr are not to be accessed */
/* if we've gotten this far, all fields were equal */
*differ = 0;
return 0;
}
/*
******** nrrdPPM( )
**
** for making a nrrd suitable for holding PPM data
**
** "don't mess with peripheral information"
*/
int
nrrdPPM( Nrrd *ppm, size_t sx, size_t sy ) {
static const char me[]="nrrdPPM";
char stmp[2][AIR_STRLEN_SMALL];
if ( nrrdMaybeAlloc_va( ppm, nrrdTypeUChar, 3,
AIR_CAST( size_t, 3 ), sx, sy ) ) {
biffAddf( NRRD, "%s: couldn't allocate %s x %s 24-bit image", me,
airSprintSize_t( stmp[0], sx ),
airSprintSize_t( stmp[1], sy ) );
return 1;
}
return 0;
}
/*
******** nrrdPGM( )
**
** for making a nrrd suitable for holding PGM data
**
** "don't mess with peripheral information"
*/
int
nrrdPGM( Nrrd *pgm, size_t sx, size_t sy ) {
static const char me[]="nrrdPGM";
char stmp[2][AIR_STRLEN_SMALL];
if ( nrrdMaybeAlloc_va( pgm, nrrdTypeUChar, 2,
sx, sy ) ) {
biffAddf( NRRD, "%s: couldn't allocate %s x %s 8-bit image", me,
airSprintSize_t( stmp[0], sx ),
airSprintSize_t( stmp[1], sy ) );
return 1;
}
return 0;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** _nrrdReadNrrdParseField( )
**
** This is for parsing the stuff BEFORE the colon
*/
int
33 _nrrdReadNrrdParseField( NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParseField";
char *next, *buff, *colon, *keysep;
int ret, fld=nrrdField_unknown, noField, badField=AIR_FALSE;
next = nio->line + nio->pos;
/* determining if the line is a comment is simple */
if ( NRRD_COMMENT_CHAR == next[0] ) {
return nrrdField_comment;
}
if ( !( buff = airStrdup( next ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't allocate buffer!", me );
return nrrdField_unknown;
}
/* #1: "...if you see a colon, then look for an equal sign..." */
/* Look for colon: if no colon, or failed to parse as a field, look for
* equal sign, if that failed then error */
/* Let the separator be := */
/* Escape \n */
colon = strstr( buff, ": " );
noField = !colon;
if ( colon ) {
*colon = '\0';
badField = ( nrrdField_unknown == ( fld = airEnumVal( nrrdField, buff ) ) );
}
if ( noField || badField ) {
keysep = strstr( buff, ":=" );
if ( !keysep ) {
if ( noField ) {
biffMaybeAddf( useBiff, NRRD,
"%s: didn't see \": \" or \":=\" in line",
me );
} else {
biffMaybeAddf( useBiff, NRRD,
"%s: failed to parse \"%s\" as field identifier",
me, buff );
}
free( buff ); return nrrdField_unknown;
}
free( buff );
ret = nrrdField_keyvalue;
} else {
/* *colon = '\0'; */
/* else we successfully parsed a field identifier */
next += strlen( buff ) + 2;
free( buff );
/* skip whitespace prior to start of first field descriptor */
next += strspn( next, _nrrdFieldSep );
nio->pos = AIR_CAST( int, next - nio->line );
ret = fld;
}
return ret;
}
/*
** NOTE: it is a common but unfortunate property of these parsers that
** they set values in the nrrd first, and then check their validity
** later. The reason for this is mostly the desire to centralize
** validity checking in one place, and right now that's in the
** _nrrdFieldCheck[] array of checkers
*/
static int
106 _nrrdReadNrrdParse_nonfield( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
AIR_UNUSED( useBiff );
/*
char c;
c= 10; write( 2, &c, 1 ); c= 69; write( 2, &c, 1 ); c=108; write( 2, &c, 1 );
c= 32; write( 2, &c, 1 ); c= 67; write( 2, &c, 1 ); c=104; write( 2, &c, 1 );
c=101; write( 2, &c, 1 ); c= 32; write( 2, &c, 1 ); c= 86; write( 2, &c, 1 );
c=105; write( 2, &c, 1 ); c=118; write( 2, &c, 1 ); c=101; write( 2, &c, 1 );
c= 33; write( 2, &c, 1 ); c= 10; write( 2, &c, 1 ); c= 10; write( 2, &c, 1 );
*/
return 0;
}
static int
125 _nrrdReadNrrdParse_comment( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_comment";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
/* this skips the '#' at nio->line[nio->pos] and any other ' ' and '#' */
if ( nrrdCommentAdd( nrrd, info ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble adding comment", me );
return 1;
}
return 0;
}
static int
141 _nrrdReadNrrdParse_content( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_content";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( strlen( info ) && !( nrrd->content = airStrdup( info ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't strdup( ) content", me );
return 1;
}
return 0;
}
static int
156 _nrrdReadNrrdParse_number( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
/*
static const char me[]="_nrrdReadNrrdParse_number";
char *info;
info = nio->line + nio->pos;
if ( 1 != sscanf( info, NRRD_BIG_INT_PRINTF, &( nrrd->num ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse number \"%s\"", me, info ); return 1;
}
*/
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
AIR_UNUSED( useBiff );
/* It was decided to just completely ignore this field. "number" is
** entirely redundant with the ( required ) sizes field, and there is no
** need to save it to, or learn it from, the header. In fact the "num"
** field was eliminated from the Nrrd struct some time ago, in favor of
** the nrrdElementNumber( ) function. It may seem odd or unfortunate that
**
** number: Hank Hill sells propane and propane accessories
**
** is a valid field specification, but at least Peggy is proud ...
*/
return 0;
}
static int
188 _nrrdReadNrrdParse_type( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_type";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( !( nrrd->type = airEnumVal( nrrdType, info ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't parse type \"%s\"", me, info );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_type]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
#define _PARSE_ONE_VAL( FIELD, CONV, TYPE ) \
if ( 1 != airSingleSscanf( info, CONV, &( FIELD ) ) ) { \
biffMaybeAddf( useBiff, NRRD, "%s: couldn't parse " TYPE \
209 " from \"%s\"", me, info ); \
return 1; \
}
static int
_nrrdReadNrrdParse_block_size( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_block_size";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( 1 != airSingleSscanf( info, "%z", &( nrrd->blockSize ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't parse size_t"
" from \"%s\"", me, info );
}
/* because blockSize and type fields may appear in any order,
226 we can't use _nrrdFieldCheck[] */
return 0;
}
static int
_nrrdReadNrrdParse_dimension( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_dimension";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_PARSE_ONE_VAL( nrrd->dim, "%u", "unsigned int" );
if ( _nrrdFieldCheck[nrrdField_dimension]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
** checking nrrd->dim against zero is valid because it is initialized
** to zero, and, _nrrdReadNrrdParse_dimension( ) won't allow it to be
** set to anything outside the range [1, NRRD_DIM_MAX]
*/
#define _CHECK_HAVE_DIM \
if ( 0 == nrrd->dim ) { \
253 biffMaybeAddf( useBiff, NRRD, \
"%s: don't yet have a valid dimension", me ); \
return 1; \
}
#define _CHECK_HAVE_SPACE_DIM \
if ( 0 == nrrd->spaceDim ) { \
biffMaybeAddf( useBiff, NRRD, \
"%s: don't yet have a valid space dimension", me ); \
return 1; \
}
#define _CHECK_GOT_ALL_VALUES \
if ( nrrd->dim != ret ) { \
biffMaybeAddf( useBiff, NRRD, \
"%s: parsed %d values, but dimension is %d", \
me, ret, nrrd->dim ); \
return 1; \
}
static int
_nrrdReadNrrdParse_sizes( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_sizes";
unsigned int ret;
size_t val[NRRD_DIM_MAX];
char *info;
281 AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
ret = airParseStrZ( val, info, _nrrdFieldSep, nrrd->dim );
_CHECK_GOT_ALL_VALUES;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSize, val );
/* HEY: this is a very imperfect check of excess info */
if ( nrrd->dim+1 == airParseStrZ( val, info, _nrrdFieldSep, nrrd->dim+1 ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d sizes",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_sizes]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_spacings( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_spacings";
unsigned int ret;
double val[NRRD_DIM_MAX];
char *info;
309 AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
ret = airParseStrD( val, info, _nrrdFieldSep, nrrd->dim );
_CHECK_GOT_ALL_VALUES;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSpacing, val );
/* HEY: this is a very imperfect check of excess info */
if ( nrrd->dim+1 == airParseStrD( val, info, _nrrdFieldSep, nrrd->dim+1 ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d spacings",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_spacings]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_thicknesses( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_thicknesses";
unsigned int ret;
double val[NRRD_DIM_MAX];
char *info;
337 AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
ret = airParseStrD( val, info, _nrrdFieldSep, nrrd->dim );
_CHECK_GOT_ALL_VALUES;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoThickness, val );
/* HEY: this is a very imperfect check of excess info */
if ( nrrd->dim+1 == airParseStrD( val, info, _nrrdFieldSep, nrrd->dim+1 ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d thicknesses",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_thicknesses]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_axis_mins( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_axis_mins";
unsigned int ret;
double val[NRRD_DIM_MAX];
char *info;
365 AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
ret = airParseStrD( val, info, _nrrdFieldSep, nrrd->dim );
_CHECK_GOT_ALL_VALUES;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoMin, val );
/* HEY: this is a very imperfect check of excess info */
if ( nrrd->dim+1 == airParseStrD( val, info, _nrrdFieldSep, nrrd->dim+1 ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d axis mins",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_axis_mins]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_axis_maxs( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_axis_maxs";
unsigned int ret;
double val[NRRD_DIM_MAX];
char *info;
393 AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
ret = airParseStrD( val, info, _nrrdFieldSep, nrrd->dim );
_CHECK_GOT_ALL_VALUES;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoMax, val );
/* HEY: this is a very imperfect check of excess info */
if ( nrrd->dim+1 == airParseStrD( val, info, _nrrdFieldSep, nrrd->dim+1 ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d axis maxs",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_axis_maxs]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdSpaceVectorParse( double val[NRRD_SPACE_DIM_MAX],
char **hhP, unsigned int spaceDim, int useBiff ) {
static const char me[]="_nrrdSpaceVectorParse";
char *hh, *buff, sep[]=", )";
airArray *mop;
unsigned int ret, dd;
size_t length;
mop = airMopNew( );
hh = *hhP;
/* skip past space */
length = strspn( hh, _nrrdFieldSep );
hh += length;
/* make sure we have something */
if ( !*hh ) {
biffMaybeAddf( useBiff, NRRD,
"%s: hit end of string before seeing ( ", me );
airMopError( mop ); return 1;
}
/* first, see if we're getting the non-vector */
if ( ( strstr( hh, _nrrdNoSpaceVector ) == hh ) ) {
if ( !hh[strlen( _nrrdNoSpaceVector )]
|| strchr( _nrrdFieldSep, hh[strlen( _nrrdNoSpaceVector )] ) ) {
/* yes, we got the non-vector */
for ( dd=0; dd<spaceDim; dd++ ) {
val[dd] = AIR_NAN;
}
length += strlen( _nrrdNoSpaceVector );
} else {
/* we got something that started out looking like the non-vector */
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse non-vector \"%s\"", me, hh );
airMopError( mop ); return 1;
}
} else {
/* this isn't a non-vector */
/* make sure we have an open paren */
if ( '( ' != *hh ) {
biffMaybeAddf( useBiff, NRRD,
"%s: first vector in \"%s\" didn't start with '( '",
me, hh );
airMopError( mop ); return 1;
}
/* copy string ( including open paren ) for local fiddling */
if ( !( buff = airStrdup( hh ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't allocate local buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, buff, airFree, airMopAlways );
/* scan for close paren */
hh = buff+1;
while ( *hh ) {
if ( ' )' == *hh ) {
break;
} else {
hh++;
}
}
if ( ' )' != *hh ) {
biffMaybeAddf( useBiff, NRRD,
"%s: didn't see ' )' at end of first vector in \"%s\"",
me, hh );
airMopError( mop ); return 1;
}
/* terminate at end paren */
*( hh+1 ) = 0;
length += strlen( buff );
/* see if we have too many fields */
ret = airStrntok( buff+1, sep );
if ( ret > spaceDim ) {
biffMaybeAddf( useBiff, NRRD,
"%s: space dimension is %d, but seem to have %d "
"coefficients", me, spaceDim, ret );
airMopError( mop ); return 1;
}
/* try to parse the values */
ret = airParseStrD( val, buff+1, ", ", spaceDim );
if ( spaceDim != ret ) {
biffMaybeAddf( useBiff, NRRD,
"%s: parsed %d values, but space dimension is %d",
me, ret, spaceDim );
airMopError( mop ); return 1;
}
}
/* probably not useful */
for ( dd=spaceDim; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
val[dd] = AIR_NAN;
}
/* make sure all coefficients exist or not together */
for ( dd=1; dd<spaceDim; dd++ ) {
if ( !!AIR_EXISTS( val[0] ) ^ !!AIR_EXISTS( val[dd] ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: existance of all space vector "
"coefficients must be consistent ( val[0] not like "
"val[%d] )", me, dd );
airMopError( mop ); return 1;
}
}
514 for ( dd=0; dd<spaceDim; dd++ ) {
if ( airIsInf_d( val[dd] ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: vector coefficient %d can't be infinite",
me, dd );
airMopError( mop ); return 1;
}
}
*hhP += length;
airMopOkay( mop );
return 0;
}
/*
** public version of _nrrdSpaceVectorParse, which might not really be
** needed, but given how _nrrdSpaceVectorParse currently wants a
** char**, so it can move the pointer to point to the next space
** vector to parse in a non-const string, this seems like a sane and
** minimal effort option
*/
int
nrrdSpaceVectorParse( double dir[NRRD_SPACE_DIM_MAX],
const char *_str, unsigned int spaceDim, int useBiff ) {
537 static const char me[]="nrrdSpaceVectorParse";
airArray *mop;
char *str;
mop = airMopNew( );
str = airStrdup( _str );
airMopAdd( mop, str, airFree, airMopAlways );
if ( !( dir && _str ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
if ( _nrrdSpaceVectorParse( dir, &str, spaceDim, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble parsing", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
static int
_nrrdReadNrrdParse_space_directions( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_space_directions";
unsigned int dd;
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_DIM;
_CHECK_HAVE_SPACE_DIM;
for ( dd=0; dd<nrrd->dim; dd++ ) {
if ( _nrrdSpaceVectorParse( nrrd->axis[dd].spaceDirection,
571 &info, nrrd->spaceDim, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: trouble getting space vector %d of %d",
me, dd+1, nrrd->dim );
return 1;
}
}
if ( strlen( info ) != strspn( info, _nrrdFieldSep ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d directions",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_space_directions]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_centers( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_centers";
unsigned int ai;
char *tok, *info, *last;
airArray *mop;
AIR_UNUSED( file );
mop = airMopNew( );
info = airStrdup( nio->line + nio->pos );
airMopAdd( mop, info, airFree, airMopAlways );
_CHECK_HAVE_DIM;
for ( ai=0; ai<nrrd->dim; ai++ ) {
tok = airStrtok( !ai ? info : NULL, _nrrdFieldSep, &last );
if ( !tok ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't extract string for center %d of %d",
me, ai+1, nrrd->dim );
airMopError( mop ); return 1;
}
if ( !strcmp( tok, NRRD_UNKNOWN ) ) {
nrrd->axis[ai].center = nrrdCenterUnknown;
continue;
}
if ( !strcmp( tok, NRRD_NONE ) ) {
nrrd->axis[ai].center = nrrdCenterUnknown;
continue;
}
if ( !( nrrd->axis[ai].center = airEnumVal( nrrdCenter, tok ) ) ) {
621 biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse center \"%s\" for axis %d",
me, tok, ai );
airMopError( mop ); return 1;
}
}
if ( airStrtok( !ai ? info : NULL, _nrrdFieldSep, &last ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d centers",
me, nrrd->dim );
airMopError( mop ); return 1;
}
if ( _nrrdFieldCheck[nrrdField_centers]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
static int
_nrrdReadNrrdParse_kinds( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_kinds";
unsigned int ai;
char *info, *tok, *last;
airArray *mop;
AIR_UNUSED( file );
mop = airMopNew( );
info = airStrdup( nio->line + nio->pos );
airMopAdd( mop, info, airFree, airMopAlways );
_CHECK_HAVE_DIM;
for ( ai=0; ai<nrrd->dim; ai++ ) {
tok = airStrtok( !ai ? info : NULL, _nrrdFieldSep, &last );
if ( !tok ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't extract string for kind %d of %d",
me, ai+1, nrrd->dim );
airMopError( mop ); return 1;
}
if ( !strcmp( tok, NRRD_UNKNOWN ) ) {
nrrd->axis[ai].kind = nrrdKindUnknown;
continue;
}
if ( !strcmp( tok, NRRD_NONE ) ) {
nrrd->axis[ai].center = nrrdKindUnknown;
continue;
}
if ( !( nrrd->axis[ai].kind = airEnumVal( nrrdKind, tok ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse \"%s\" kind %d of %d",
me, tok, ai+1, nrrd->dim );
airMopError( mop ); return 1;
675 }
}
if ( airStrtok( !ai ? info : NULL, _nrrdFieldSep, &last ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d kinds",
me, nrrd->dim );
airMopError( mop ); return 1;
}
/* can't run this now because kinds can come before sizes, in which
case the kind/size check in _nrrdFieldCheck_kinds will incorrectly
flag an error ...
if ( _nrrdFieldCheck[nrrdField_kinds]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
airMopError( mop ); return 1;
}
*/
airMopOkay( mop );
return 0;
}
static char *
_nrrdGetQuotedString( char **hP, int useBiff ) {
static const char me[]="_nrrdGetQuotedString";
char *h, *buff, *ret;
airArray *buffArr;
unsigned int pos;
airPtrPtrUnion appu;
h = *hP;
/* skip past space */
/* printf( "!%s: h |%s|\n", me, h );*/
h += strspn( h, _nrrdFieldSep );
/* printf( "!%s: h |%s|\n", me, h );*/
/* make sure we have something */
if ( !*h ) {
biffMaybeAddf( useBiff, NRRD,
"%s: hit end of string before seeing opening \"", me );
return NULL;
}
/* make sure we have a starting quote */
if ( '"' != *h ) {
biffMaybeAddf( useBiff, NRRD, "%s: didn't start with \"", me );
return NULL;
}
h++;
/* parse string until end quote */
buff = NULL;
appu.c = &buff;
buffArr = airArrayNew( appu.v, NULL, sizeof( char ), 2 );
if ( !buffArr ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't create airArray", me );
return NULL;
}
pos = airArrayLenIncr( buffArr, 1 ); /* pos should get 0 */
while ( h[pos] ) {
/* printf( "!%s: h+%d |%s|\n", me, pos, h+pos ); */
if ( '\"' == h[pos] ) {
break;
}
736 if ( '\\' == h[pos] && '\"' == h[pos+1] ) {
h += 1;
}
buff[pos] = h[pos];
pos = airArrayLenIncr( buffArr, 1 );
}
if ( '\"' != h[pos] ) {
biffMaybeAddf( useBiff, NRRD, "%s: didn't see ending \" soon enough", me );
return NULL;
}
h += pos + 1;
buff[pos] = 0;
ret = airStrdup( buff );
airArrayNuke( buffArr );
*hP = h;
return ret;
}
static int
_nrrdReadNrrdParse_labels( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_labels";
char *h; /* this is the "here" pointer which gradually progresses
through all the labels ( for all axes ) */
unsigned int ai;
char *info;
AIR_UNUSED( file );
/* because we have to correctly interpret quote marks, we
can't simply rely on airParseStrS */
info = nio->line + nio->pos;
/* printf( "!%s: info |%s|\n", me, info ); */
_CHECK_HAVE_DIM;
h = info;
772 for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( nrrd->axis[ai].label = _nrrdGetQuotedString( &h, useBiff ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't get get label %d of %d\n",
me, ai+1, nrrd->dim );
return 1;
}
}
if ( strlen( h ) != strspn( h, _nrrdFieldSep ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d labels",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_labels]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_units( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_units";
char *h; /* this is the "here" pointer which gradually progresses
through all the units ( for all axes ) */
unsigned int ai;
char *info;
AIR_UNUSED( file );
/* because we have to correctly interpret quote marks, we
can't simply rely on airParseStrS */
info = nio->line + nio->pos;
/* printf( "!%s: info |%s|\n", me, info ); */
_CHECK_HAVE_DIM;
h = info;
808 for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( nrrd->axis[ai].units = _nrrdGetQuotedString( &h, useBiff ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't get get unit %d of %d\n",
me, ai+1, nrrd->dim );
return 1;
}
}
if ( strlen( h ) != strspn( h, _nrrdFieldSep ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d units",
me, nrrd->dim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_units]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
825 return 0;
}
static int
_nrrdReadNrrdParse_min( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
AIR_UNUSED( useBiff );
/* This field is no longer assumed to be anything meaningful,
because nrrd->min no longer exists with the advent of NrrdRange.
839 But, having the field is not an error, to not trip on older
NRRD00.01 and NRRD0001 files which ( legitimately ) used it */
return 0;
}
static int
_nrrdReadNrrdParse_max( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
AIR_UNUSED( nio );
AIR_UNUSED( useBiff );
/* nrrd->max no longer exists, see above */
855
return 0;
}
static int
_nrrdReadNrrdParse_old_min( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_old_min";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_PARSE_ONE_VAL( nrrd->oldMin, "%lg", "double" );
if ( _nrrdFieldCheck[nrrdField_old_min]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
871 }
return 0;
}
static int
_nrrdReadNrrdParse_old_max( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_old_max";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_PARSE_ONE_VAL( nrrd->oldMax, "%lg", "double" );
if ( _nrrdFieldCheck[nrrdField_old_max]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
888 return 0;
}
static int
_nrrdReadNrrdParse_endian( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_endian";
char *info;
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
info = nio->line + nio->pos;
if ( !( nio->endian = airEnumVal( airEndian, info ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse endian \"%s\"", me, info );
return 1;
}
return 0;
}
908 static int
_nrrdReadNrrdParse_encoding( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_encoding";
char *info;
int etype;
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
info = nio->line + nio->pos;
if ( !( etype = airEnumVal( nrrdEncodingType, info ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse encoding \"%s\"", me, info );
return 1;
}
nio->encoding = nrrdEncodingArray[etype];
return 0;
}
928 static int
_nrrdReadNrrdParse_line_skip( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_line_skip";
char *info;
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
info = nio->line + nio->pos;
_PARSE_ONE_VAL( nio->lineSkip, "%u", "unsigned int" );
/* now that its unsigned, what error checking can I do?
if ( !( 0 <= nio->lineSkip ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: lineSkip value %d invalid", me, nio->lineSkip );
return 1;
}
*/
return 0;
}
static int
_nrrdReadNrrdParse_byte_skip( FILE *file, Nrrd *nrrd,
950 NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_byte_skip";
char *info;
AIR_UNUSED( file );
AIR_UNUSED( nrrd );
info = nio->line + nio->pos;
_PARSE_ONE_VAL( nio->byteSkip, "%ld", "long int" );
/* this check is being removed to enable the undocumented
( in the file format spec ) ability to say "byte skip: -N-1"
in order to skip backwards from EOF by N bytes
** if ( !( -1 <= nio->byteSkip ) ) {
** biffMaybeAddf( useBiff, NRRD,
** "%s: byteSkip value %ld invalid", me, nio->byteSkip );
** return 1;
** }
*/
return 0;
}
static int
_nrrdReadNrrdParse_keyvalue( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_keyvalue";
char *keysep, *line, *key, *value;
AIR_UNUSED( file );
/* we know this will find something */
line = airStrdup( nio->line + nio->pos );
if ( !line ) {
biffMaybeAddf( useBiff, NRRD, "%s: can't allocate parse line", me );
return 1;
}
keysep = strstr( line, ":=" );
if ( !keysep ) {
985 biffMaybeAddf( useBiff, NRRD,
"%s: didn't see \":=\" key/value delimiter in \"%s\"",
me, line );
free( line ); return 1;
}
keysep[0] = 0;
keysep[1] = 0;
key = line;
value = keysep+2;
/* convert escape sequences */
airUnescape( key );
airUnescape( value );
nrrdKeyValueAdd( nrrd, key, value );
free( line );
return 0;
}
static int
1006 _nrrdReadNrrdParse_sample_units( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_sample_units";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( strlen( info ) && !( nrrd->sampleUnits = airStrdup( info ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't strdup( ) sampleUnits", me );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_sample_units]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_space( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_space";
char *info;
int space;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( nio->seen[nrrdField_space_dimension] ) {
biffMaybeAddf( useBiff, NRRD,
1037 "%s: can't specify space after specifying "
"space dimension ( %d )", me, nrrd->spaceDim );
return 1;
}
if ( !( space = airEnumVal( nrrdSpace, info ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse space \"%s\"", me, info );
return 1;
}
if ( nrrdSpaceSet( nrrd, space ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_space]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_space_dimension( FILE *file, Nrrd *nrrd,
1059 NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_space_dimension";
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
if ( nio->seen[nrrdField_space] ) {
biffMaybeAddf( useBiff, NRRD,
"%s: can't specify space dimension after specifying "
"space ( %s )", me, airEnumStr( nrrdSpace, nrrd->space ) );
return 1;
}
_PARSE_ONE_VAL( nrrd->spaceDim, "%u", "unsigned int" );
if ( _nrrdFieldCheck[nrrdField_space_dimension]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_space_units( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_space_units";
char *h; /* this is the "here" pointer which gradually progresses
through all the units ( for all axes ) */
unsigned int ai;
char *info;
AIR_UNUSED( file );
/* because we have to correctly interpret quote marks, we
can't simply rely on airParseStrS */
info = nio->line + nio->pos;
/* printf( "!%s: info |%s|\n", me, info ); */
_CHECK_HAVE_SPACE_DIM;
h = info;
1095 for ( ai=0; ai<nrrd->spaceDim; ai++ ) {
if ( !( nrrd->spaceUnits[ai] = _nrrdGetQuotedString( &h, useBiff ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't get get space unit %d of %d",
me, ai+1, nrrd->spaceDim );
return 1;
}
}
if ( _nrrdGetQuotedString( &h, AIR_FALSE ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seemed to have more than expected %d space units",
me, nrrd->spaceDim );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_space_units]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_space_origin( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_space_origin";
1119 char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_SPACE_DIM;
if ( _nrrdSpaceVectorParse( nrrd->spaceOrigin, &info,
nrrd->spaceDim, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse origin \"%s\"", me, info );
return 1;
}
if ( _nrrdFieldCheck[nrrdField_space_origin]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
static int
_nrrdReadNrrdParse_measurement_frame( FILE *file, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_measurement_frame";
double colvec[NRRD_SPACE_DIM_MAX];
unsigned int dd, ii;
char *info;
AIR_UNUSED( file );
info = nio->line + nio->pos;
_CHECK_HAVE_SPACE_DIM;
for ( dd=0; dd<nrrd->spaceDim; dd++ ) {
/* we are going through the *columns* of the mf matrix */
if ( _nrrdSpaceVectorParse( colvec, &info, nrrd->spaceDim, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: trouble getting space vector %d of %d",
me, dd+1, nrrd->spaceDim );
return 1;
}
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
nrrd->measurementFrame[dd][ii] = ( ii < nrrd->spaceDim
? colvec[ii]
: AIR_NAN );
1164 }
}
if ( strlen( info ) != strspn( info, _nrrdFieldSep ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: seem to have more than expected %d directions",
me, nrrd->spaceDim );
return 1;
}
for ( dd=nrrd->spaceDim; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
nrrd->measurementFrame[dd][ii] = AIR_NAN;
}
}
if ( _nrrdFieldCheck[nrrdField_measurement_frame]( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
int
_nrrdContainsPercentThisAndMore( const char *str, char thss ) {
const char *hh, *tmp;
tmp = str;
do {
1190 hh = strchr( tmp, '%' );
if ( !( hh && hh[1] ) ) {
return 0;
}
if ( '%' == hh[1] ) {
/* its an escaped % */
tmp = hh + 2;
} else {
break;
}
} while ( tmp[0] );
hh++;
hh += strspn( hh, "0123456789" );
if ( !( hh[0] == thss ) ) {
return 0;
}
hh += strcspn( hh, _nrrdFieldSep );
return !!hh;
}
unsigned int
_nrrdDataFNNumber( NrrdIoState *nio ) {
unsigned int ret;
int ii;
if ( nio->dataFNFormat ) {
/* datafiles given in iterator form; count number of values */
ret = 0;
for ( ii = nio->dataFNMin;
1219 ( ( nio->dataFNStep > 0 && ii <= nio->dataFNMax )
|| ( nio->dataFNStep < 0 && ii >= nio->dataFNMax ) );
ii += nio->dataFNStep ) {
ret += 1;
}
} else if ( nio->dataFNArr->len ) {
/* datafiles given as an explicit list, or as a single file name,
and in either case, nrrdDataFNAdd( ) is used to add them to
the dataFNArr */
ret = nio->dataFNArr->len;
} else {
/* datafile is same as ( attached ) header file */
ret = 1;
}
return ret;
}
/*
** this always requires that the per-axis size fields have been set
*/
int
_nrrdDataFNCheck( NrrdIoState *nio, Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdDataFNCheck";
size_t pieceSize, pieceNum;
char stmp[AIR_STRLEN_SMALL];
if ( !nio->seen[nrrdField_sizes] ) {
biffMaybeAddf( useBiff, NRRD, "%s: sorry, currently can't handle "
"multiple detached data files without first knowing "
"the \"%s\" field",
me, airEnumStr( nrrdField, nrrdField_sizes ) );
return 1;
}
if ( nio->dataFileDim < nrrd->dim ) {
/* this requires that the per-axis size fields have been set */
_nrrdSplitSizes( &pieceSize, &pieceNum, nrrd, nio->dataFileDim );
if ( pieceNum != _nrrdDataFNNumber( nio ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: expected %s filenames ( of %u-D pieces ) "
"but got %u", me,
airSprintSize_t( stmp, pieceNum ), nio->dataFileDim,
_nrrdDataFNNumber( nio ) );
return 1;
}
} else {
/* we're getting data in "slabs" with the same dimension as the
nrrd, so for simplicity we assume that they're all equal size */
if ( _nrrdDataFNNumber( nio ) > nrrd->axis[nrrd->dim-1].size ) {
biffMaybeAddf( useBiff, NRRD,
"%s: can't have more pieces ( %u ) than axis %u "
"slices ( %s ) when nrrd dimension and "
"datafile dimension are both %u", me,
_nrrdDataFNNumber( nio ),
nrrd->dim-1,
airSprintSize_t( stmp, nrrd->axis[nrrd->dim-1].size ),
nrrd->dim );
return 1;
}
if ( ( double )nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber( nio )
!= nrrd->axis[nrrd->dim-1].size/_nrrdDataFNNumber( nio ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: number of datafiles ( %d ) doesn't divide into "
1281 "number of axis %u slices ( %s )", me,
( int )_nrrdDataFNNumber( nio ), nrrd->dim-1,
airSprintSize_t( stmp, nrrd->axis[nrrd->dim-1].size ) );
return 1;
}
}
return 0;
}
/*
** Sat Jan 29 16:44:50 EST 2005: this used to "open the separate
** datafile, and set the FILE* in nio->dataFile, which otherwise will
** stay NULL", but now we support multiple detached data files. So.
**
** The job of this function is to map the "data file" specification to
** one or more filenames that can be passed direction to fopen for
** reading in the data. This involves parsing the various formats for
** identifying multiple data files, and possibly prefixing them with
** nio->path.
*/
static int
_nrrdReadNrrdParse_data_file( FILE *ffile, Nrrd *nrrd,
NrrdIoState *nio, int useBiff ) {
static const char me[]="_nrrdReadNrrdParse_data_file";
char *info, *nums;
unsigned int linelen, tmp;
airArray *mop;
mop = airMopNew( );
info = airStrdup( nio->line + nio->pos );
if ( !info ) {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't copy line!", me );
return 1;
}
airMopAdd( mop, info, airFree, airMopAlways );
/* HEY: this change should be made someday
if ( _nrrdContainsPercentThisAndMore( info, 'd' )
|| _nrrdContainsPercentThisAndMore( info, 'u' ) ) { */
if ( _nrrdContainsPercentThisAndMore( info, 'd' ) ) {
/* ---------------------------------------------------------- */
/* --------- format.%d <min> <max> <step> [<dim>] ----------- */
/* ---------------------------------------------------------- */
size_t sspn;
_CHECK_HAVE_DIM;
nums = info + strcspn( info, _nrrdFieldSep );
sspn = strspn( nums, _nrrdFieldSep );
nums[0] = 0; /* terminate so that format is now in info */
nums += sspn;
if ( !( 3 == sscanf( nums, "%d %d %d", &( nio->dataFNMin ),
&( nio->dataFNMax ), &( nio->dataFNStep ) ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse three ints ( min, max, step ) after "
"data filename template", me );
airMopError( mop ); return 1;
}
if ( 4 == sscanf( nums, "%d %d %d %u", &( nio->dataFNMin ),
&( nio->dataFNMax ), &( nio->dataFNStep ),
&( nio->dataFileDim ) ) ) {
if ( !AIR_IN_CL( 1, nio->dataFileDim, nrrd->dim ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: datafile dimension %u outside valid range [1, %u]",
me, nio->dataFileDim, nrrd->dim );
airMopError( mop ); return 1;
}
} else {
nio->dataFileDim = nrrd->dim-1;
}
if ( 0 == nio->dataFNStep ) {
biffMaybeAddf( useBiff, NRRD,
"%s: file number step must be non-zero", me );
airMopError( mop ); return 1;
}
if ( ( nio->dataFNMax - nio->dataFNMin )*( nio->dataFNStep ) < 0 ) {
biffMaybeAddf( useBiff, NRRD,
"%s: file number max %d not approached from min %d "
"by step %d", me,
nio->dataFNMax, nio->dataFNMin, nio->dataFNStep );
airMopError( mop ); return 1;
}
if ( !( nio->dataFNFormat = airStrdup( info ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't copy data filename format", me );
airMopError( mop ); return 1;
}
if ( _nrrdDataFNCheck( nio, nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: trouble with number of datafiles", me );
airMopError( mop ); return 1;
}
} else if ( !strncmp( info, NRRD_LIST_FLAG, strlen( NRRD_LIST_FLAG ) ) ||
!strncmp( info, NRRD_SKIPLIST_FLAG, strlen( NRRD_SKIPLIST_FLAG ) ) ) {
int skiplist;
unsigned int lineidx;
/* ---------------------------------------------------------- */
/* -------------------- LIST or SKIPLIST -------------------- */
/* ---------------------------------------------------------- */
_CHECK_HAVE_DIM;
skiplist = !strncmp( info, NRRD_SKIPLIST_FLAG, strlen( NRRD_SKIPLIST_FLAG ) );
if ( _nrrdHeaderCheck( nrrd, nio, AIR_TRUE ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: NRRD header is incomplete. "
"\"%s\" data file specification must be "
"contiguous with end of header!", me,
skiplist ? NRRD_SKIPLIST_FLAG : NRRD_LIST_FLAG );
airMopError( mop ); return 1;
}
info += strlen( skiplist ? NRRD_SKIPLIST_FLAG : NRRD_LIST_FLAG );
if ( info[0] ) {
if ( 1 == sscanf( info, "%u", &( nio->dataFileDim ) ) ) {
if ( !AIR_IN_CL( 1, nio->dataFileDim, nrrd->dim ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: datafile dimension %u outside "
"valid range [1, %u]",
me, nio->dataFileDim, nrrd->dim );
airMopError( mop ); return 1;
}
} else {
biffMaybeAddf( useBiff, NRRD, "%s: couldn't parse info after "
"\"%s\" as an int", me,
skiplist ? NRRD_SKIPLIST_FLAG : NRRD_LIST_FLAG );
airMopError( mop ); return 1;
}
} else {
/* nothing after NRRD_LIST_FLAG or NRRD_SKIPLIST_FLAG,
so dataFileDim is implicit */
nio->dataFileDim = nrrd->dim-1;
}
/* read in all the datafile names */
lineidx = 0;
do {
/* yes, nio->line is re-used/over-written here, but I don't
think that's a problem */
if ( _nrrdOneLine( &linelen, nio, ffile ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: trouble getting file name line %u", me, lineidx );
airMopError( mop ); return 1;
}
if ( linelen > 0 ) {
/* we got a non-empty line */
if ( skiplist ) {
char *lhere;
long int oneskip;
if ( 1 != airSingleSscanf( nio->line, "%ld", &oneskip ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: couldn't parse skip on list line %u",
me, lineidx );
airMopError( mop ); return 1;
}
lhere = strchr( nio->line, ' ' );
if ( !lhere ) {
biffMaybeAddf( useBiff, NRRD, "%s: didn't see space after "
"skip on list line %u", me, lineidx );
airMopError( mop ); return 1;
}
lhere++;
if ( !( lhere[0] ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: didn't see filename after "
"skip and space on list line %u", me, lineidx );
airMopError( mop ); return 1;
}
airArrayLenIncr( nio->dataFSkipArr, 1 );
nio->dataFSkip[lineidx] = oneskip;
airArrayLenIncr( nio->dataFNArr, 1 );
nio->dataFN[lineidx] = airStrdup( lhere );
} else {
airArrayLenIncr( nio->dataFNArr, 1 );
nio->dataFN[lineidx] = airStrdup( nio->line );
}
}
++lineidx;
} while ( linelen > 0 );
if ( _nrrdDataFNCheck( nio, nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: trouble with number of datafiles", me );
1454 airMopError( mop ); return 1;
}
} else {
/* ---------------------------------------------------------- */
/* -------------------- ( single filename ) ------------------- */
/* ---------------------------------------------------------- */
/* there is apparently only a single detached data file; for
this its okay to not yet know nrrd->dim */
tmp = airArrayLenIncr( nio->dataFNArr, 1 );
nio->dataFN[tmp] = airStrdup( info );
nio->dataFileDim = 0;
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdFieldInfoParse[NRRD_FIELD_MAX+1]( )
**
** These are all for parsing the stuff AFTER the colon
*/
int
( *nrrdFieldInfoParse[NRRD_FIELD_MAX+1] )( FILE *, Nrrd *,
NrrdIoState *, int ) = {
_nrrdReadNrrdParse_nonfield,
_nrrdReadNrrdParse_comment,
_nrrdReadNrrdParse_content,
_nrrdReadNrrdParse_number,
_nrrdReadNrrdParse_type,
_nrrdReadNrrdParse_block_size,
_nrrdReadNrrdParse_dimension,
_nrrdReadNrrdParse_space,
_nrrdReadNrrdParse_space_dimension,
_nrrdReadNrrdParse_sizes,
_nrrdReadNrrdParse_spacings,
_nrrdReadNrrdParse_thicknesses,
_nrrdReadNrrdParse_axis_mins,
_nrrdReadNrrdParse_axis_maxs,
_nrrdReadNrrdParse_space_directions,
_nrrdReadNrrdParse_centers,
_nrrdReadNrrdParse_kinds,
_nrrdReadNrrdParse_labels,
_nrrdReadNrrdParse_units,
_nrrdReadNrrdParse_min,
_nrrdReadNrrdParse_max,
_nrrdReadNrrdParse_old_min,
_nrrdReadNrrdParse_old_max,
_nrrdReadNrrdParse_endian,
_nrrdReadNrrdParse_encoding,
_nrrdReadNrrdParse_line_skip,
_nrrdReadNrrdParse_byte_skip,
_nrrdReadNrrdParse_keyvalue,
_nrrdReadNrrdParse_sample_units,
_nrrdReadNrrdParse_space_units,
_nrrdReadNrrdParse_space_origin,
_nrrdReadNrrdParse_measurement_frame,
_nrrdReadNrrdParse_data_file
};
/* kernel parsing is all in kernel.c */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
NrrdRange *
28 nrrdRangeNew( double min, double max ) {
NrrdRange *range;
range = ( NrrdRange * )calloc( 1, sizeof( NrrdRange ) );
if ( range ) {
range->min = min;
range->max = max;
range->hasNonExist = nrrdHasNonExistUnknown;
}
return range;
}
NrrdRange *
41 nrrdRangeCopy( const NrrdRange *rin ) {
NrrdRange *rout=NULL;
if ( rin ) {
rout = nrrdRangeNew( rin->min, rin->max );
rout->hasNonExist = rin->hasNonExist;
}
return rout;
}
NrrdRange *
52 nrrdRangeNix( NrrdRange *range ) {
airFree( range );
return NULL;
}
void
59 nrrdRangeReset( NrrdRange *range ) {
if ( range ) {
range->min = AIR_NAN;
range->max = AIR_NAN;
range->hasNonExist = nrrdHasNonExistUnknown;
}
}
/*
** not using biff ( obviously )
*/
void
72 nrrdRangeSet( NrrdRange *range, const Nrrd *nrrd, int blind8BitRange ) {
NRRD_TYPE_BIGGEST _min, _max;
int blind;
if ( !range ) {
return;
}
if ( nrrd
&& !airEnumValCheck( nrrdType, nrrd->type )
&& nrrdTypeBlock != nrrd->type ) {
blind = ( nrrdBlind8BitRangeTrue == blind8BitRange
|| ( nrrdBlind8BitRangeState == blind8BitRange
&& nrrdStateBlind8BitRange ) );
if ( blind && 1 == nrrdTypeSize[nrrd->type] ) {
if ( nrrdTypeChar == nrrd->type ) {
range->min = SCHAR_MIN;
range->max = SCHAR_MAX;
} else {
range->min = 0;
range->max = UCHAR_MAX;
}
range->hasNonExist = nrrdHasNonExistFalse;
} else {
nrrdMinMaxExactFind[nrrd->type]( &_min, &_max, &( range->hasNonExist ),
nrrd );
range->min = nrrdDLoad[nrrd->type]( &_min );
range->max = nrrdDLoad[nrrd->type]( &_max );
}
} else {
range->min = range->max = AIR_NAN;
range->hasNonExist = nrrdHasNonExistUnknown;
}
return;
}
/*
******** nrrdRangePercentileSet
**
** this is called when information about the range of values in the
** nrrd is requested; and the learned information is put into "range"
** ( overwriting whatever is there! )
**
** uses biff
*/
int
117 nrrdRangePercentileSet( NrrdRange *range, const Nrrd *nrrd,
double minPerc, double maxPerc,
unsigned int hbins, int blind8BitRange ) {
static const char me[]="nrrdRangePercentileSet";
airArray *mop;
Nrrd *nhist;
double allmin, allmax, minval, maxval, *hist, sum, total, sumPerc;
unsigned int hi;
if ( !( range && nrrd ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 0;
}
nrrdRangeSet( range, nrrd, blind8BitRange );
/* range->min and range->max
are the full range of nrrd ( except maybe for blind8 ) */
if ( !minPerc && !maxPerc ) {
/* wanted full range; there is nothing more to do */
return 0;
}
if ( !hbins ) {
biffAddf( NRRD, "%s: sorry, non-histogram-based percentiles not "
"currently implemented ( need hbins > 0 )", me );
return 1;
}
if ( !( hbins >= 5 ) ) {
biffAddf( NRRD, "%s: # histogram bins %u unreasonably small", me, hbins );
return 1;
}
if ( range->hasNonExist ) {
biffAddf( NRRD, "%s: sorry, can currently do histogram-based percentiles "
"only in arrays with no non-existent values", me );
return 1;
}
mop = airMopNew( );
allmin = range->min;
allmax = range->max;
nhist = nrrdNew( );
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
/* the histogram is over the entire range of values */
if ( nrrdHisto( nhist, nrrd, range, NULL, hbins, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: trouble making histogram", me );
airMopError( mop );
return 1;
}
hist = AIR_CAST( double *, nhist->data );
total = AIR_CAST( double, nrrdElementNumber( nrrd ) );
if ( minPerc ) {
minval = AIR_NAN;
sumPerc = AIR_ABS( minPerc )*total/100.0;
sum = hist[0];
for ( hi=1; hi<hbins; hi++ ) {
sum += hist[hi];
if ( sum >= sumPerc ) {
minval = AIR_AFFINE( 0, hi-1, hbins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( hi == hbins || !AIR_EXISTS( minval ) ) {
biffAddf( NRRD, "%s: failed to find lower %g-percentile value",
me, minPerc );
airMopError( mop );
return 1;
}
range->min = ( minPerc > 0
? minval
: 2*allmin - minval );
}
if ( maxPerc ) {
maxval = AIR_NAN;
sumPerc = AIR_ABS( maxPerc )*total/100.0;
sum = hist[hbins-1];
for ( hi=hbins-1; hi; hi-- ) {
sum += hist[hi-1];
if ( sum >= sumPerc ) {
maxval = AIR_AFFINE( 0, hi, hbins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( !hi || !AIR_EXISTS( maxval ) ) {
biffAddf( NRRD, "%s: failed to find upper %g-percentile value", me,
maxPerc );
airMopError( mop );
return 1;
}
range->max = ( maxPerc > 0
? maxval
: 2*allmax - maxval );
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdRangePercentileFromStringSet
**
** Implements smarts of figuring out a range from no info ( "nan" ),
** a known percentile ( e.g. "3%" ) or a known explicit value ( e.g. "3" ),
** for both min and max. Used by "unu quantize" and others.
*/
int
223 nrrdRangePercentileFromStringSet( NrrdRange *range, const Nrrd *nrrd,
const char *_minStr, const char *_maxStr,
unsigned int hbins, int blind8BitRange ) {
static const char me[]="nrrdRangePercentileFromStringSet";
double minVal, maxVal, minPerc, maxPerc;
char *minStr, *maxStr;
unsigned int mmIdx;
airArray *mop;
if ( !( range && nrrd && _minStr && _maxStr ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
minStr = airStrdup( _minStr );
airMopAdd( mop, minStr, airFree, airMopAlways );
maxStr = airStrdup( _maxStr );
airMopAdd( mop, maxStr, airFree, airMopAlways );
/* parse min and max */
minVal = maxVal = minPerc = maxPerc = AIR_NAN;
for ( mmIdx=0; mmIdx<=1; mmIdx++ ) {
int percwant;
double val, *mmv, *mmp;
char *mmStr;
if ( 0 == mmIdx ) {
mmv = &minVal;
mmp = &minPerc;
mmStr = minStr;
} else {
mmv = &maxVal;
mmp = &maxPerc;
mmStr = maxStr;
}
if ( airEndsWith( mmStr, NRRD_MINMAX_PERC_SUFF ) ) {
percwant = AIR_TRUE;
mmStr[strlen( mmStr )-strlen( NRRD_MINMAX_PERC_SUFF )] = '\0';
} else {
percwant = AIR_FALSE;
}
if ( 1 != airSingleSscanf( mmStr, "%lf", &val ) ) {
biffAddf( NRRD, "%s: couldn't parse \"%s\" for %s", me,
!mmIdx ? _minStr : _maxStr,
!mmIdx ? "minimum" : "maximum" );
airMopError( mop );
return 1;
}
if ( percwant ) {
if ( !AIR_EXISTS( val ) ) {
biffAddf( NRRD, "%s: %s percentile must exist", me,
!mmIdx ? "minimum" : "maximum" );
airMopError( mop );
return 1;
}
/* setting a finite percentile */
*mmp = val;
} else if ( !AIR_EXISTS( val ) ) {
/* don't want a percentile, and given value is nan
=> same as full range == zero percentile */
*mmp = 0.0;
} else {
/* value exists: given explicitly */
*mmv = val;
}
}
/* so whenever one end of the range is not given explicitly,
it has been mapped to a statement about the percentile,
which does require learning about the nrrd's values */
if ( AIR_EXISTS( minPerc ) || AIR_EXISTS( maxPerc ) ) {
if ( nrrdRangePercentileSet( range, nrrd,
AIR_EXISTS( minPerc ) ? minPerc : 0.0,
AIR_EXISTS( maxPerc ) ? maxPerc : 0.0,
hbins, blind8BitRange ) ) {
biffAddf( NRRD, "%s: trouble finding percentile range", me );
airMopError( mop );
return 1;
}
}
/* whatever explicit values were given are now stored */
if ( AIR_EXISTS( minVal ) ) {
range->min = minVal;
}
if ( AIR_EXISTS( maxVal ) ) {
range->max = maxVal;
}
airMopOkay( mop );
return 0;
}
/*
** wrapper around nrrdRangeSet that ( effectively ) sets range->min
** and range->min only if they didn't already exist
*/
void
318 nrrdRangeSafeSet( NrrdRange *range, const Nrrd *nrrd, int blind8BitRange ) {
double minIn, maxIn;
if ( !range ) {
return;
}
minIn = range->min;
maxIn = range->max;
nrrdRangeSet( range, nrrd, blind8BitRange );
if ( AIR_EXISTS( minIn ) ) {
range->min = minIn;
}
if ( AIR_EXISTS( maxIn ) ) {
range->max = maxIn;
}
return;
}
/*
** does not use biff
*/
NrrdRange *
340 nrrdRangeNewSet( const Nrrd *nrrd, int blind8BitRange ) {
NrrdRange *range;
range = nrrdRangeNew( 0, 0 ); /* doesn't matter what values are used here */
nrrdRangeSet( range, nrrd, blind8BitRange );
return range;
}
/*
******** nrrdHasNonExist
**
** returns the nrrdHasNonExist* enum value appropriate for a given nrrd.
** By cleverness, this value can be used as a regular C boolean, so that
** the function will act as you expect.
**
** ( the existence of this function implies that I'll never have an airEnum
** of the same name, which would be the usual thing to do with a C enum,
** but I don't think an airEnum for this would be useful )
*/
int
360 nrrdHasNonExist( const Nrrd *nrrd ) {
NRRD_TYPE_BIGGEST _min, _max;
int ret;
if ( nrrd
&& !airEnumValCheck( nrrdType, nrrd->type )
&& nrrdTypeBlock != nrrd->type ) {
if ( nrrdTypeIsIntegral[nrrd->type] ) {
ret = nrrdHasNonExistFalse;
} else {
/* HEY: this could be optimized by being more specialized */
nrrdMinMaxExactFind[nrrd->type]( &_min, &_max, &ret, nrrd );
}
} else {
ret = nrrdHasNonExistUnknown;
}
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#if TEEM_BZIP2
#include <bzlib.h>
#endif
/* The "/ *Teem:" ( without space ) comments in here are an experiment */
char _nrrdRelativePathFlag[] = "./";
char _nrrdFieldSep[] = " \t";
char _nrrdLineSep[] = "\r\n";
char _nrrdNoSpaceVector[] = "none";
char _nrrdTextSep[] = " , \t";
/*
** return length of next "line" in nio->headerStringRead
*/
unsigned int
43 _nrrdHeaderStringOneLineStrlen( NrrdIoState *nio ) {
return AIR_CAST( unsigned int,
strcspn( nio->headerStringRead + nio->headerStrpos, _nrrdLineSep ) );
}
/*
** read next "line" in nio->headerStringRead
*/
unsigned int
53 _nrrdHeaderStringOneLine( NrrdIoState *nio ) {
unsigned int len1, len2;
len1 = _nrrdHeaderStringOneLineStrlen( nio );
strncpy( nio->line, nio->headerStringRead + nio->headerStrpos, len1 );
nio->line[len1] = '\0';
nio->headerStrpos += len1;
len2 = AIR_CAST( unsigned int,
strspn( nio->headerStringRead + nio->headerStrpos, _nrrdLineSep ) );
nio->headerStrpos += len2;
return len1;
}
/*
** _nrrdOneLine
**
** wrapper around airOneLine; does re-allocation of line buffer
** ( "line" ) in the NrrdIoState if needed. The return value semantics
** are similar, except that what airOneLine would return, we put
** in *lenP. If there is an error ( airOneLine returned 0,
** something couldn't be allocated ), *lenP is set to 0, and
** we return 1. HITTING EOF IS NOT ACTUALLY AN ERROR, see code
** below. Otherwise we return 0.
**
** Does use biff
*/
int
80 _nrrdOneLine( unsigned int *lenP, NrrdIoState *nio, FILE *file ) {
static const char me[]="_nrrdOneLine";
char **line;
airArray *mop, *lineArr;
airPtrPtrUnion appu;
unsigned int lineIdx, len, needLen;
if ( !( lenP && nio && ( file || nio->headerStringRead ) ) ) {
biffAddf( NRRD, "%s: got NULL pointer ( %p, %p, %p/%p )", me,
AIR_CAST( void*, lenP ), AIR_CAST( void*, nio ),
AIR_CAST( void*, file ), nio->headerStringRead );
return 1;
}
if ( 0 == nio->lineLen ) {
/* nio->line hasn't been allocated for anything */
nio->lineLen = 3;
nio->line = ( char* )malloc( nio->lineLen );
if ( !nio->line ) {
biffAddf( NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen );
*lenP = 0; return 1;
}
}
if ( file ) {
len = airOneLine( file, nio->line, nio->lineLen );
} else {
/* NOTE: NULL-ity error check above makes this safe */
needLen = _nrrdHeaderStringOneLineStrlen( nio );
if ( needLen+1 > nio->lineLen ) {
nio->lineLen = needLen+1;
airFree( nio->line ); /* lose previous allocated line */
nio->line = ( char* )malloc( nio->lineLen );
if ( !nio->line ) {
biffAddf( NRRD, "%s: couldn't alloc %d-char line\n",
me, nio->lineLen );
*lenP = 0; return 1;
}
}
len = _nrrdHeaderStringOneLine( nio );
}
if ( len <= nio->lineLen ) {
/* otherwise we hit EOF ( or end of nio->headerStringRead ) before a
newline, or the line ( possibly empty ) fit within the nio->line,
neither of which is an error here */
*lenP = len;
} else {
/* line didn't fit in buffer, so we have to increase line
buffer size and put the line together in pieces */
/* NOTE: this will never happen when reading from nio->headerStringRead */
appu.cp = &line;
lineArr = airArrayNew( appu.v, NULL, sizeof( char * ), 1 );
if ( !lineArr ) {
biffAddf( NRRD, "%s: couldn't allocate airArray", me );
*lenP = 0; return 1;
}
airArrayPointerCB( lineArr, airNull, airFree );
mop = airMopNew( );
airMopAdd( mop, lineArr, ( airMopper )airArrayNuke, airMopAlways );
while ( len == nio->lineLen+1 ) {
lineIdx = airArrayLenIncr( lineArr, 1 );
if ( !lineArr->data ) {
biffAddf( NRRD, "%s: couldn't increment line buffer array", me );
*lenP = 0; airMopError( mop ); return 1;
}
line[lineIdx] = nio->line;
nio->lineLen *= 2;
nio->line = ( char* )malloc( nio->lineLen );
if ( !nio->line ) {
biffAddf( NRRD, "%s: couldn't alloc %d-char line\n",
me, nio->lineLen );
*lenP = 0; airMopError( mop ); return 1;
}
len = airOneLine( file, nio->line, nio->lineLen );
}
/* last part did fit in nio->line buffer, also save this into line[] */
lineIdx = airArrayLenIncr( lineArr, 1 );
if ( !lineArr->data ) {
biffAddf( NRRD, "%s: couldn't increment line buffer array", me );
*lenP = 0; airMopError( mop ); return 1;
}
line[lineIdx] = nio->line;
nio->lineLen *= 3; /* for good measure */
nio->line = ( char* )malloc( nio->lineLen );
if ( !nio->line ) {
biffAddf( NRRD, "%s: couldn't alloc %d-char line\n", me, nio->lineLen );
*lenP = 0; airMopError( mop ); return 1;
}
/* now concatenate everything into a new nio->line */
strcpy( nio->line, "" );
for ( lineIdx=0; lineIdx<lineArr->len; lineIdx++ ) {
strcat( nio->line, line[lineIdx] );
}
/* HEY: API is bad: *lenP should be a size_t pointer! */
*lenP = AIR_UINT( strlen( nio->line ) ) + 1;
airMopError( mop );
}
return 0;
}
/*
** _nrrdCalloc( )
**
** allocates the data for the array, but only if necessary ( as informed by
** nio->oldData and nio->oldDataSize ).
**
** as a recent feature, this will handle the extra work of allocating
** memory in the special way required for direct IO, if possible. For
** this to work, though, the FILE *file has to be passed. Since file
** is not otherwise needed, it can be passed as NULL for non-direct-IO
** situations. In any case, if the directIO-compatible allocation fails
** its not error, and we revert to regular allocation.
**
** NOTE: this assumes the checking that is done by _nrrdHeaderCheck
*/
int
194 _nrrdCalloc( Nrrd *nrrd, NrrdIoState *nio, FILE *file ) {
static const char me[]="_nrrdCalloc";
size_t needDataSize;
int fd;
needDataSize = nrrdElementNumber( nrrd )*nrrdElementSize( nrrd );
if ( nio->oldData && needDataSize == nio->oldDataSize ) {
/* re-use old data */
nrrd->data = nio->oldData;
/* its not an error to have a directIO-incompatible pointer, so
there's no other error checking to do here */
} else {
nrrd->data = airFree( nrrd->data );
fd = file ? fileno( file ) : -1;
if ( nrrdEncodingRaw == nio->encoding
&& -1 != fd
&& airNoDio_okay == airDioTest( fd, NULL, needDataSize ) ) {
nrrd->data = airDioMalloc( needDataSize, fd );
}
if ( !nrrd->data ) {
/* directIO-compatible allocation wasn't tried, or it failed */
nrrd->data = malloc( needDataSize );
}
if ( !nrrd->data ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't allocate %s things of size %s", me,
airSprintSize_t( stmp1, nrrdElementNumber( nrrd ) ),
airSprintSize_t( stmp2, nrrdElementSize( nrrd ) ) );
return 1;
}
}
/* make it look like it came from calloc( ), as used by nrrdNew( ) */
memset( nrrd->data, 0, needDataSize );
return 0;
}
/*
******** nrrdLineSkip
**
** public for the sake of things like "unu make"
** uses the NrrdIoState for its line buffer ( used by _nrrdOneLine )
*/
int
237 nrrdLineSkip( FILE *dataFile, NrrdIoState *nio ) {
static const char me[]="nrrdLineSkip";
unsigned int lsi, skipRet;
/* For compressed data: If you don't actually have ascii headers on
top of your gzipped data then you will potentially huge lines
while _nrrdOneLine looks for line terminations. Quoting Gordon:
"Garbage in, Garbage out." */
if ( !( dataFile && nio ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
for ( lsi=0; lsi<nio->lineSkip; lsi++ ) {
if ( _nrrdOneLine( &skipRet, nio, dataFile ) ) {
biffAddf( NRRD, "%s: error skipping line %u of %u",
me, lsi+1, nio->lineSkip );
return 1;
}
if ( !skipRet ) {
biffAddf( NRRD, "%s: hit EOF skipping line %u of %u",
me, lsi+1, nio->lineSkip );
return 1;
}
}
return 0;
}
int
267 _nrrdByteSkipSkip( FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio, long int byteSkip ) {
static const char me[]="nrrdByteSkipSkip";
int skipRet;
size_t bsize;
if ( !( dataFile && nrrd && nio ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nio->encoding->isCompression ) {
biffAddf( NRRD, "%s: this function can't work with compressed "
"encoding %s", me, nio->encoding->name );
return 1;
}
if ( byteSkip < 0 ) {
long backwards;
if ( nrrdEncodingRaw != nio->encoding ) {
biffAddf( NRRD, "%s: this function can do backwards byte skip only "
"in %s encoding, not %s", me,
nrrdEncodingRaw->name, nio->encoding->name );
return 1;
}
if ( stdin == dataFile ) {
biffAddf( NRRD, "%s: can't fseek on stdin", me );
return 1;
}
bsize = nrrdElementNumber( nrrd )/_nrrdDataFNNumber( nio );
bsize *= nrrdElementSize( nrrd );
/* backwards is ( positive ) number of bytes AFTER data that we ignore */
backwards = -byteSkip - 1;
/* HEY what if bsize fits in size_t but not in ( signed ) long? */
if ( fseek( dataFile, -AIR_CAST( long, bsize ) - backwards, SEEK_END ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: failed to fseek( dataFile, %s, SEEK_END )", me,
airSprintSize_t( stmp, bsize ) );
return 1;
}
if ( nrrdStateVerboseIO >= 2 ) {
fprintf( stderr, "( %s: actually skipped %d bytes )\n",
me, ( int )ftell( dataFile ) );
}
} else {
if ( ( stdin == dataFile ) || ( -1==fseek( dataFile, byteSkip, SEEK_CUR ) ) ) {
long skipi;
/* fseek failed, perhaps because we're reading stdin, so
we revert to consuming the input one byte at a time */
for ( skipi=0; skipi<byteSkip; skipi++ ) {
skipRet = fgetc( dataFile );
if ( EOF == skipRet ) {
biffAddf( NRRD, "%s: hit EOF skipping byte %ld of %ld",
me, skipi, byteSkip );
return 1;
}
}
}
}
return 0;
}
/*
******** nrrdByteSkip
**
** public for the sake of things like "unu make"
** uses nio for information about how much data should actually be skipped
** with negative byteSkip
*/
int
334 nrrdByteSkip( FILE *dataFile, Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="nrrdByteSkip";
if ( !( dataFile && nrrd && nio ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
/* HEY: with the advent of NRRD0006 per-file skips, maybe this is
the function that should be public */
if ( _nrrdByteSkipSkip( dataFile, nrrd, nio, nio->byteSkip ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
** _nrrdRead( )
**
** read in nrrd from a given file *OR* given string. The main job of
** this function is to start reading the file/string, to determine the
** format, and then call the appropriate format's reader. This means
** that the various encoding ( data ) readers can assume that
** nio->format is usefully set.
**
** If ( file ), the only input information that nio is used for is
** nio->path, so that detached header-relative data files can be
** found. If ( string ), the headerStr-related fields in the _nio will
** be set/used
*/
int
366 _nrrdRead( Nrrd *nrrd, FILE *file, const char *string, NrrdIoState *_nio ) {
static const char me[]="_nrrdRead";
unsigned int llen;
NrrdIoState *nio;
int nfi;
airArray *mop;
/* sanity check, for good measure */
if ( !nrrdSanity( ) ) {
biffAddf( NRRD, "%s: sanity check FAILED: have to fix and re-compile",
me );
return 1;
}
if ( !( ( file || string ) && nrrd ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( file && string ) {
biffAddf( NRRD, "%s: can't read from both file and string", me );
return 1;
}
mop = airMopNew( );
if ( _nio ) {
nio = _nio;
} else {
nio = nrrdIoStateNew( );
if ( !nio ) {
biffAddf( NRRD, "%s: couldn't alloc I/O struct", me );
return 1;
}
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
}
/* remember old data pointer and allocated size. Whether or not to
free( ) this memory will be decided later */
nio->oldData = nrrd->data;
nio->oldDataSize = ( nio->oldData
? nrrdElementNumber( nrrd )*nrrdElementSize( nrrd )
: 0 );
/*
fprintf( stderr, "!%s: nio->oldData = %p, oldDataSize = %d\n", me,
nio->oldData, ( int )( nio->oldDataSize ) );
*/
nrrd->data = NULL;
/* initialize given nrrd ( but we have thwarted freeing existing memory ) */
nrrdInit( nrrd );
/* tell the nio where to find the string to read from */
nio->headerStringRead = string;
if ( _nrrdOneLine( &llen, nio, file ) ) {
biffAddf( NRRD, "%s: error getting first line ( containing \"magic\" )",
me );
airMopError( mop ); return 1;
}
if ( !llen ) {
biffAddf( NRRD, "%s: immediately hit EOF", me );
airMopError( mop ); return 1;
}
nio->format = nrrdFormatUnknown;
for ( nfi = nrrdFormatTypeUnknown+1;
nfi < nrrdFormatTypeLast;
nfi++ ) {
if ( nrrdFormatArray[nfi]->contentStartsLike( nio ) ) {
nio->format = nrrdFormatArray[nfi];
break;
}
}
if ( nrrdFormatUnknown == nio->format ) {
char linestart[AIR_STRLEN_SMALL], stmp[AIR_STRLEN_SMALL];
airStrcpy( linestart, AIR_STRLEN_SMALL, nio->line );
if ( strlen( linestart ) != strlen( nio->line ) ) {
biffAddf( NRRD, "%s: couldn't parse ( length %s ) line starting "
"with \"%s\" as magic or beginning of any recognized format",
me, airSprintSize_t( stmp, strlen( nio->line ) ), linestart );
} else {
biffAddf( NRRD, "%s: couldn't parse \"%s\" as magic or beginning "
"of any recognized format", me, nio->line );
}
airMopError( mop ); return 1;
}
if ( string && nrrdFormatNRRD != nio->format ) {
biffAddf( NRRD, "%s: sorry, can only read %s files from strings ( not %s )",
me, nrrdFormatNRRD->name, nio->format->name );
airMopError( mop ); return 1;
}
/* try to read the file */
if ( nio->format->read( file, nrrd, nio ) ) {
biffAddf( NRRD, "%s: trouble reading %s file", me, nio->format->name );
airMopError( mop ); return 1;
}
/* reshape up grayscale images, if desired */
if ( nio->format->isImage && 2 == nrrd->dim && nrrdStateGrayscaleImage3D ) {
if ( nrrdAxesInsert( nrrd, nrrd, 0 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
/* free prior memory if we didn't end up using it */
/* HEY: could actually do a check on the nio to refine this */
if ( nio->oldData != nrrd->data ) {
nio->oldData = airFree( nio->oldData );
nio->oldDataSize = 0;
}
/* finally, make sure that what we're returning isn't malformed somehow,
except that we ( probably stupidly ) allow nrrd->data to be NULL, given
the possibility of using nio->skipData */
if ( _nrrdCheck( nrrd, AIR_FALSE, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: problem with nrrd after reading", me );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdRead( )
**
** now just a wrapper around _nrrdRead( ); reads a NRRD from a FILE *
*/
int
495 nrrdRead( Nrrd *nrrd, FILE *file, NrrdIoState *_nio ) {
static const char me[]="nrrdRead";
if ( _nrrdRead( nrrd, file, NULL, _nio ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdStringRead( )
**
** also a wrapper around _nrrdRead( ); reads a NRRD from a char *.
**
** Because the same underlying _nrrdRead( ) is used, the same semantics
** about using existing nrrd->data when possible applies, as does the
** action of nrrdStateGrayscaleImage3D
*/
int
515 nrrdStringRead( Nrrd *nrrd, const char *string, NrrdIoState *_nio ) {
static const char me[]="nrrdRead";
if ( _nrrdRead( nrrd, NULL, string, _nio ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
** _nrrdSplitName( )
**
** splits a file name into a path and a base filename. The path
** separator is '/', but there is a hack ( thanks Torsten Rohlfing )
** which allows '\' to work on Windows. The division between the path
** and the base is the last path separator in the file name. The path
** is everything prior to this, and base is everything after ( so the
** base does NOT start with the path separator ). If there is not a
** '/' in the name, or if a path separator appears as the last
** character, then the path is set to ".", and the name is copied into
** base.
*/
void
539 _nrrdSplitName( char **dirP, char **baseP, const char *name ) {
char *where;
if ( dirP ) {
*dirP = ( char * )airFree( *dirP );
}
if ( baseP ) {
*baseP = ( char * )airFree( *baseP );
}
where = strrchr( name, '/' );
#ifdef _WIN32
/* Deal with Windows "\" path separators; thanks to Torsten Rohlfing */
if ( !where || ( strrchr( name, '\\' ) > where ) ) {
where = strrchr( name, '\\' );
}
#endif
/* we found a valid break if the last directory character
is somewhere in the string except the last character */
if ( where && airStrlen( where ) > 1 ) {
if ( dirP ) {
*dirP = airStrdup( name );
( *dirP )[where - name] = 0;
}
if ( baseP ) {
*baseP = airStrdup( where + 1 );
}
} else {
/* if the name had no slash, its in the current directory, which
means that we need to explicitly store "." as the header
directory in case we have header-relative data. */
if ( dirP ) {
*dirP = airStrdup( "." );
}
if ( baseP ) {
*baseP = airStrdup( name );
}
}
return;
}
/*
******** nrrdLoad( )
**
**
**
** call tree for this, to help figure out what's going on
**
** read.c/nrrdLoad
** | read.c/_nrrdSplitName
** | read.c/nrrdRead
** | nio->format->read
** = formatNRRD.c/_nrrdFormatNRRD_read:
** | read.c/_nrrdOneLine
** | parseNrrd.c/_nrrdReadNrrdParseField
** | parseNrrd.c/nrrdFieldInfoParse[]
** = parseNrrd.c/_nrrdReadNrrdParse_data_file
** | fopen( dataName )
** | formatNRRD.c/_nrrdHeaderCheck
** | read.c/nrrdLineSkip
** | read.c/nrrdByteSkip
** | nio->encoding->read
** = encodingRaw.c/_nrrdEncodingRaw_read
** | read.c/_nrrdCalloc
** | formatNRRD.c/nrrdSwapEndian
** | miscAir.c/airFclose
**
** ( more documentation here )
**
** sneakiness: returns 2 if the reason for problem was a failed fopen( ).
**
*/
int /*Teem: biff if ( ret ) */
611 nrrdLoad( Nrrd *nrrd, const char *filename, NrrdIoState *nio ) {
static const char me[]="nrrdLoad";
FILE *file;
airArray *mop;
if ( !( nrrd && filename ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( !nio ) {
nio = nrrdIoStateNew( );
if ( !nio ) {
biffAddf( NRRD, "%s: couldn't alloc I/O struct", me );
return 1;
}
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
}
/* we save the directory of the filename given to us so that if it turns
out that this is a detached header with a header-relative data file,
then we will know how to find the data file */
_nrrdSplitName( &( nio->path ), NULL, filename );
/* printf( "!%s: |%s|%s|\n", me, nio->dir, nio->base ); */
if ( !( file = airFopen( filename, stdin, "rb" ) ) ) {
biffAddf( NRRD, "%s: fopen( \"%s\", \"rb\" ) failed: %s",
me, filename, strerror( errno ) );
airMopError( mop ); return 2;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopOnError );
/* non-error exiting is handled below */
if ( nrrdRead( nrrd, file, nio ) ) {
biffAddf( NRRD, "%s: trouble reading \"%s\"", me, filename );
airMopError( mop ); return 1;
}
if ( nrrdFormatNRRD == nio->format
&& nio->keepNrrdDataFileOpen
&& file == nio->dataFile ) {
/* we have to keep the datafile open. If was attached, we can't
close file, because that is the datafile. If was detached,
file != nio->dataFile, so we can close file. */
} else {
/* always close non-NRRD files */
airFclose( file );
}
airMopOkay( mop );
return 0;
}
int
665 nrrdLoadMulti( Nrrd *const *nin, unsigned int ninLen,
const char *fnameFormat,
unsigned int numStart, NrrdIoState *nio ) {
static const char me[]="nrrdLoadMulti";
char *fname;
airArray *mop;
unsigned int nii;
if ( !( nin && fnameFormat ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( _nrrdContainsPercentThisAndMore( fnameFormat, 'u' ) ) ) {
biffAddf( NRRD, "%s: given format \"%s\" doesn't seem to "
"have the \"%%u\" conversion specification to sprintf "
"an unsigned int\n", me, fnameFormat );
return 1;
}
mop = airMopNew( );
/* should be big enough for the number replacing the format sequence */
fname = AIR_CAST( char *, malloc( strlen( fnameFormat ) + 128 ) );
if ( !( fname ) ) {
biffAddf( NRRD, "%s: couldn't allocate local fname buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, fname, airFree, airMopAlways );
for ( nii=0; nii<ninLen; nii++ ) {
unsigned int num;
num = numStart + nii;
sprintf( fname, fnameFormat, num );
if ( nrrdLoad( nin[nii], fname, nio ) ) {
biffAddf( NRRD, "%s: trouble loading nin[%u] from %s", me, nii, fname );
airMopError( mop ); return 1;
}
/* HEY: GLK hopes that the nio doesn't have any state that needs
resetting, but we can't call nrrdIoStateInit( ) because that
would negate the purpose of sending in the nio for all but the
first saved nrrd */
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdInvertPerm( )
**
** given an array ( p ) which represents a permutation of n elements,
** compute the inverse permutation ip. The value of this function
** is not its core functionality, but all the error checking it
** provides.
*/
int
36 nrrdInvertPerm( unsigned int *invp, const unsigned int *pp, unsigned int nn ) {
static const char me[]="nrrdInvertPerm";
int problem;
unsigned int ii;
if ( !( invp && pp && nn > 0 ) ) {
biffAddf( NRRD, "%s: got NULL pointer or non-positive nn ( %d )", me, nn );
return 1;
}
/* use the given array "invp" as a temp buffer for validity checking */
memset( invp, 0, nn*sizeof( unsigned int ) );
for ( ii=0; ii<nn; ii++ ) {
if ( !( pp[ii] <= nn-1 ) ) {
biffAddf( NRRD,
"%s: permutation element #%d == %d out of bounds [0, %d]",
me, ii, pp[ii], nn-1 );
return 1;
}
invp[pp[ii]]++;
}
/* for some reason when this code was written ( revision 2700 Sun Jul
3 04:18:33 2005 UTC ) it was decided that all problems with the
permutation would be reported with a pile of error messages in
biff; rather than bailing at the first problem. Not clear if
this is a good idea. */
problem = AIR_FALSE;
for ( ii=0; ii<nn; ii++ ) {
if ( 1 != invp[ii] ) {
biffAddf( NRRD, "%s: element #%d mapped to %d times ( should be once )",
me, ii, invp[ii] );
problem = AIR_TRUE;
}
}
if ( problem ) {
return 1;
}
/* the skinny */
for ( ii=0; ii<nn; ii++ ) {
invp[pp[ii]] = ii;
}
return 0;
}
/*
******** nrrdAxesInsert
**
** like reshape, but preserves axis information on old axes, and
** this is only for adding a "stub" axis with length 1. All other
** axis attributes are initialized as usual.
*/
int
90 nrrdAxesInsert( Nrrd *nout, const Nrrd *nin, unsigned int axis ) {
static const char me[]="nrrdAxesInsert", func[]="axinsert";
unsigned int ai;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( axis <= nin->dim ) ) {
biffAddf( NRRD, "%s: given axis ( %d ) outside valid range [0, %d]",
me, axis, nin->dim );
return 1;
}
if ( NRRD_DIM_MAX == nin->dim ) {
biffAddf( NRRD, "%s: given nrrd already at NRRD_DIM_MAX ( %d )",
me, NRRD_DIM_MAX );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
nout->dim = 1 + nin->dim;
for ( ai=nin->dim; ai>axis; ai-- ) {
_nrrdAxisInfoCopy( &( nout->axis[ai] ), &( nin->axis[ai-1] ),
NRRD_AXIS_INFO_NONE );
}
/* the ONLY thing we can say about the new axis is its size */
_nrrdAxisInfoInit( &( nout->axis[axis] ) );
if ( !nrrdStateKindNoop ) {
/* except maybe the kind */
nout->axis[axis].kind = nrrdKindStub;
}
nout->axis[axis].size = 1;
if ( nrrdContentSet_va( nout, func, nin, "%d", axis ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* all basic info has already been copied by nrrdCopy( ) above */
return 0;
}
/*
******** nrrdAxesPermute
**
** changes the scanline ordering of the data in a nrrd
**
** The basic means by which data is moved around is with memcpy( ).
** The goal is to call memcpy( ) as few times as possible, on memory
** segments as large as possible. Currently, this is done by
** detecting how many of the low-index axes are left untouched by
** the permutation- this constitutes a "scanline" which can be
** copied around as a unit. For permuting the y and z axes of a
** matrix-x-y-z order matrix volume, this optimization produced a
** factor of 5 speed up ( exhaustive multi-platform tests, of course ).
**
** The axes[] array determines the permutation of the axes.
** axis[i] = j means: axis i in the output will be the input's axis j
** ( axis[i] answers: "what do I put here", from the standpoint of the output,
** not "where do I put this", from the standpoint of the input )
*/
int
157 nrrdAxesPermute( Nrrd *nout, const Nrrd *nin, const unsigned int *axes ) {
static const char me[]="nrrdAxesPermute", func[]="permute";
char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL];
size_t idxOut, idxInA=0, /* indices for input and output scanlines */
lineSize, /* size of block of memory which can be
moved contiguously from input to output,
thought of as a "scanline" */
numLines, /* how many "scanlines" there are to permute */
szIn[NRRD_DIM_MAX], *lszIn,
szOut[NRRD_DIM_MAX], *lszOut,
cIn[NRRD_DIM_MAX],
cOut[NRRD_DIM_MAX];
char *dataIn, *dataOut;
int axmap[NRRD_DIM_MAX];
unsigned int
ai, /* running index along dimensions */
lowPax, /* lowest axis which is "p"ermutated */
ldim, /* nin->dim - lowPax */
ip[NRRD_DIM_MAX+1], /* inverse of permutation in "axes" */
laxes[NRRD_DIM_MAX+1]; /* copy of axes[], but shifted down by lowPax
elements, to remove i such that i == axes[i] */
airArray *mop;
mop = airMopNew( );
if ( !( nin && nout && axes ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
/* we don't actually need ip[], computing it is for error checking */
if ( nrrdInvertPerm( ip, axes, nin->dim ) ) {
biffAddf( NRRD, "%s: couldn't compute axis permutation inverse", me );
airMopError( mop ); return 1;
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
airMopError( mop ); return 1;
}
for ( ai=0; ai<nin->dim && axes[ai] == ai; ai++ )
;
lowPax = ai;
/* allocate output by initial copy */
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: trouble copying input", me );
airMopError( mop ); return 1;
}
dataIn = ( char* )nin->data;
} else {
dataIn = ( char* )calloc( nrrdElementNumber( nin ), nrrdElementSize( nin ) );
if ( !dataIn ) {
biffAddf( NRRD, "%s: couldn't create local copy of data", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, dataIn, airFree, airMopAlways );
memcpy( dataIn, nin->data, nrrdElementNumber( nin )*nrrdElementSize( nin ) );
}
if ( lowPax < nin->dim ) {
/* if lowPax == nin->dim, then we were given the identity permutation, so
there's nothing to do other than the copy already done. Otherwise,
here we are ( actually, lowPax < nin->dim-1 ) */
for ( ai=0; ai<nin->dim; ai++ ) {
axmap[ai] = AIR_INT( axes[ai] );
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, szIn );
if ( nrrdAxisInfoCopy( nout, nin, axmap, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
nrrdAxisInfoGet_nva( nout, nrrdAxisInfoSize, szOut );
/* the skinny */
lineSize = 1;
for ( ai=0; ai<lowPax; ai++ ) {
lineSize *= szIn[ai];
}
numLines = nrrdElementNumber( nin )/lineSize;
lineSize *= nrrdElementSize( nin );
lszIn = szIn + lowPax;
lszOut = szOut + lowPax;
ldim = nin->dim - lowPax;
memset( laxes, 0, sizeof( laxes ) );
for ( ai=0; ai<ldim; ai++ ) {
laxes[ai] = axes[ai+lowPax]-lowPax;
}
dataOut = AIR_CAST( char *, nout->data );
memset( cIn, 0, sizeof( cIn ) );
memset( cOut, 0, sizeof( cOut ) );
for ( idxOut=0; idxOut<numLines; idxOut++ ) {
/* in our representation of the coordinates of the start of the
scanlines that we're copying, we are not even storing all the
zeros in the coordinates prior to lowPax, and when we go to
a linear index for the memcpy( ), we multiply by lineSize */
for ( ai=0; ai<ldim; ai++ ) {
cIn[laxes[ai]] = cOut[ai];
}
NRRD_INDEX_GEN( idxInA, cIn, lszIn, ldim );
memcpy( dataOut + idxOut*lineSize, dataIn + idxInA*lineSize, lineSize );
NRRD_COORD_INCR( cOut, lszOut, ldim, 0 );
}
/* set content */
strcpy( buff1, "" );
for ( ai=0; ai<nin->dim; ai++ ) {
sprintf( buff2, "%s%d", ( ai ? ", " : "" ), axes[ai] );
strcat( buff1, buff2 );
}
if ( nrrdContentSet_va( nout, func, nin, "%s", buff1 ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nout != nin ) {
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdShuffle
**
** rearranges hyperslices of a nrrd along a given axis according to
** given permutation. This could be used to on a 4D array,
** representing a 3D volume of vectors, to re-order the vector
** components.
**
** the given permutation array must allocated for at least as long as
** the input nrrd along the chosen axis. perm[j] = i means that the
** value at position j in the _new_ array should come from position i
** in the _old_array. The standpoint is from the new, looking at
** where to find the values amid the old array ( perm answers "what do
** I put here", not "where do I put this" ). This allows multiple
** positions in the new array to copy from the same old position, and
** insures that there is an source for all positions along the new
** array.
*/
int
307 nrrdShuffle( Nrrd *nout, const Nrrd *nin, unsigned int axis,
const size_t *perm ) {
static const char me[]="nrrdShuffle", func[]="shuffle";
char buff2[AIR_STRLEN_SMALL];
/* Sun Feb 8 13:13:58 CST 2009: There was a memory bug here caused
by using the same buff1[NRRD_DIM_MAX*30] declaration that had
worked fine for nrrdAxesPermute and nrrdReshape, but does NOT
work here because now samples along an axes are re-ordered, not
axes, so its often not allocated for long enough to hold the
string that's printed to it. Ideally there'd be another argument
that says whether to document the shuffle in the content string,
which would mean an API change. Or, we can use a secret
heuristic ( or maybe later a nrrdState variable ) for determining
when an axis is short enough to make documenting the shuffle
interesting. This is useful since functions like nrrdFlip( )
probably do *not* need the shuffle ( the sample reversal ) to be
documented for long axes */
#define LONGEST_INTERESTING_AXIS 42
char buff1[LONGEST_INTERESTING_AXIS*30];
unsigned int ai, ldim, len;
size_t idxInB=0, idxOut, lineSize, numLines, size[NRRD_DIM_MAX], *lsize,
cIn[NRRD_DIM_MAX+1], cOut[NRRD_DIM_MAX+1];
char *dataIn, *dataOut;
if ( !( nin && nout && perm ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( !( axis < nin->dim ) ) {
biffAddf( NRRD, "%s: axis %d outside valid range [0, %d]",
me, axis, nin->dim-1 );
return 1;
}
len = AIR_CAST( unsigned int, nin->axis[axis].size );
for ( ai=0; ai<len; ai++ ) {
if ( !( perm[ai] < len ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: perm[%d] ( %s ) outside valid range [0, %d]", me, ai,
airSprintSize_t( stmp, perm[ai] ), len-1 );
return 1;
}
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
/* set information in new volume */
nout->blockSize = nin->blockSize;
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
if ( nrrdMaybeAlloc_nva( nout, nin->type, nin->dim, size ) ) {
biffAddf( NRRD, "%s: failed to allocate output", me );
return 1;
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* the min and max along the shuffled axis are now meaningless */
nout->axis[axis].min = nout->axis[axis].max = AIR_NAN;
/* do the safe thing first */
nout->axis[axis].kind = _nrrdKindAltered( nin->axis[axis].kind, AIR_FALSE );
/* try cleverness */
if ( !nrrdStateKindNoop ) {
if ( 0 == nrrdKindSize( nin->axis[axis].kind )
|| nrrdKindStub == nin->axis[axis].kind
|| nrrdKindScalar == nin->axis[axis].kind
|| nrrdKind2Vector == nin->axis[axis].kind
|| nrrdKind3Color == nin->axis[axis].kind
|| nrrdKind4Color == nin->axis[axis].kind
|| nrrdKind3Vector == nin->axis[axis].kind
|| nrrdKind3Gradient == nin->axis[axis].kind
|| nrrdKind3Normal == nin->axis[axis].kind
|| nrrdKind4Vector == nin->axis[axis].kind ) {
/* these kinds have no intrinsic ordering */
nout->axis[axis].kind = nin->axis[axis].kind;
}
}
/* the skinny */
lineSize = 1;
for ( ai=0; ai<axis; ai++ ) {
lineSize *= nin->axis[ai].size;
}
numLines = nrrdElementNumber( nin )/lineSize;
lineSize *= nrrdElementSize( nin );
lsize = size + axis;
ldim = nin->dim - axis;
dataIn = AIR_CAST( char *, nin->data );
dataOut = AIR_CAST( char *, nout->data );
memset( cIn, 0, sizeof( cIn ) );
memset( cOut, 0, sizeof( cOut ) );
for ( idxOut=0; idxOut<numLines; idxOut++ ) {
memcpy( cIn, cOut, sizeof( cIn ) );
cIn[0] = perm[cOut[0]];
NRRD_INDEX_GEN( idxInB, cIn, lsize, ldim );
NRRD_INDEX_GEN( idxOut, cOut, lsize, ldim );
memcpy( dataOut + idxOut*lineSize, dataIn + idxInB*lineSize, lineSize );
NRRD_COORD_INCR( cOut, lsize, ldim, 0 );
}
/* Set content. The LONGEST_INTERESTING_AXIS hack avoids the
previous array out-of-bounds bug */
if ( len <= LONGEST_INTERESTING_AXIS ) {
strcpy( buff1, "" );
for ( ai=0; ai<len; ai++ ) {
char stmp[AIR_STRLEN_SMALL];
sprintf( buff2, "%s%s", ( ai ? ", " : "" ), airSprintSize_t( stmp, perm[ai] ) );
strcat( buff1, buff2 );
}
if ( nrrdContentSet_va( nout, func, nin, "%s", buff1 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
} else {
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
#undef LONGEST_INTERESTING_AXIS
}
/* ---- BEGIN non-NrrdIO */
/*
******** nrrdAxesSwap( )
**
** for when you just want to switch the order of two axes, without
** going through the trouble of creating the permutation array
** needed to call nrrdAxesPermute( )
*/
int
458 nrrdAxesSwap( Nrrd *nout, const Nrrd *nin, unsigned int ax1, unsigned int ax2 ) {
static const char me[]="nrrdAxesSwap", func[]="swap";
unsigned int ai, axmap[NRRD_DIM_MAX];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( ax1 < nin->dim && ax2 < nin->dim ) ) {
biffAddf( NRRD, "%s: ax1 ( %d ) or ax2 ( %d ) out of bounds [0, %d]",
me, ax1, ax2, nin->dim-1 );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
axmap[ai] = ai;
}
axmap[ax2] = ax1;
axmap[ax1] = ax2;
if ( nrrdAxesPermute( nout, nin, axmap )
|| nrrdContentSet_va( nout, func, nin, "%d, %d", ax1, ax2 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* basic info already copied by nrrdAxesPermute */
return 0;
}
/*
******** nrrdFlip( )
**
** reverse the order of slices along the given axis.
** Actually, just a wrapper around nrrdShuffle( ) ( with some
** extra setting of axis info )
*/
int
494 nrrdFlip( Nrrd *nout, const Nrrd *nin, unsigned int axis ) {
static const char me[]="nrrdFlip", func[]="flip";
size_t *perm, si;
airArray *mop;
unsigned int axisIdx;
mop = airMopNew( );
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
if ( !( axis < nin->dim ) ) {
biffAddf( NRRD, "%s: given axis ( %d ) is outside valid range ( [0, %d] )",
me, axis, nin->dim-1 );
airMopError( mop ); return 1;
}
if ( !( perm = ( size_t* )calloc( nin->axis[axis].size, sizeof( size_t ) ) ) ) {
biffAddf( NRRD, "%s: couldn't alloc permutation array", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, perm, airFree, airMopAlways );
for ( si=0; si<nin->axis[axis].size; si++ ) {
perm[si] = nin->axis[axis].size-1-si;
}
/* nrrdBasicInfoCopy called by nrrdShuffle( ) */
if ( nrrdShuffle( nout, nin, axis, perm )
|| nrrdContentSet_va( nout, func, nin, "%d", axis ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
_nrrdAxisInfoCopy( &( nout->axis[axis] ), &( nin->axis[axis] ),
NRRD_AXIS_INFO_SIZE_BIT
| NRRD_AXIS_INFO_KIND_BIT );
/* HEY: ( Tue Jan 18 00:28:26 EST 2005 ) there's a basic question to
be answered here: do we want to keep the "location" of the
samples fixed, while changing their ordering, or do want to flip
the location of the samples? In the former, the position
information has to be flipped to cancel the flipping of the the
sample order, so that samples maintain location. In the latter,
the position information is copied verbatim from the original. */
/* ( Tue Sep 13 09:59:12 EDT 2005 ) answer: we keep the "location" of
the samples fixed, while changing their ordering. This is the
low-level thing to do, so for a nrrd function, its the right thing
to do. You don't need a nrrd function to simply manipulate
per-axis meta-information */
nout->axis[axis].min = nin->axis[axis].max;
nout->axis[axis].max = nin->axis[axis].min;
/* HEY: Fri Jan 14 02:53:30 EST 2005: isn't spacing supposed to be
the step from one sample to the next? So its a signed quantity.
If min and max can be flipped ( so min > max ), then spacing can
be negative, right? */
nout->axis[axis].spacing = -nin->axis[axis].spacing;
/* HEY: Fri Jan 14 02:53:30 EST 2005: but not thickness */
nout->axis[axis].thickness = nin->axis[axis].thickness;
/* need to set general orientation info too */
for ( axisIdx=0; axisIdx<NRRD_SPACE_DIM_MAX; axisIdx++ ) {
nout->axis[axis].spaceDirection[axisIdx] =
-nin->axis[axis].spaceDirection[axisIdx];
}
/* modify origin only if we flipped a spatial axis */
if ( AIR_EXISTS( nin->axis[axis].spaceDirection[0] ) ) {
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0,
nin->spaceOrigin,
AIR_CAST( double, nin->axis[axis].size-1 ),
nin->axis[axis].spaceDirection );
} else {
nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
}
airMopOkay( mop );
return 0;
}
/*
**
** NOTE: this seems to destroy all space/orientation info. What
** should be done?
*/
int
573 nrrdJoin( Nrrd *nout, const Nrrd *const *nin, unsigned int ninNum,
unsigned int axis, int incrDim ) {
static const char me[]="nrrdJoin";
unsigned int ni, ai, mindim, maxdim, outdim,
permute[NRRD_DIM_MAX], ipermute[NRRD_DIM_MAX];
int diffdim, axmap[NRRD_DIM_MAX];
size_t outlen, outnum, chunksize, size[NRRD_DIM_MAX];
char *dataPerm;
Nrrd *ntmpperm, /* axis-permuted version of output */
**ninperm;
airArray *mop;
char stmp[2][AIR_STRLEN_SMALL];
/* error checking */
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( ninNum >= 1 ) ) {
biffAddf( NRRD, "%s: ninNum ( %d ) must be >= 1", me, ninNum );
return 1;
}
for ( ni=0; ni<ninNum; ni++ ) {
if ( !( nin[ni] ) ) {
biffAddf( NRRD, "%s: input nrrd #%d NULL", me, ni );
return 1;
}
if ( nout==nin[ni] ) {
biffAddf( NRRD, "%s: nout==nin[%d] disallowed", me, ni );
return 1;
}
}
mop = airMopNew( );
ninperm = AIR_CALLOC( ninNum, Nrrd * );
if ( !( ninperm ) ) {
biffAddf( NRRD, "%s: couldn't calloc( ) temp nrrd array", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, ninperm, airFree, airMopAlways );
maxdim = mindim = nin[0]->dim;
for ( ni=0; ni<ninNum; ni++ ) {
mindim = AIR_MIN( mindim, nin[ni]->dim );
maxdim = AIR_MAX( maxdim, nin[ni]->dim );
}
diffdim = maxdim - mindim;
if ( diffdim > 1 ) {
biffAddf( NRRD, "%s: will only reshape up one dimension ( not %d )",
me, diffdim );
airMopError( mop ); return 1;
}
if ( axis > maxdim ) {
biffAddf( NRRD, "%s: can't join along axis %d with highest input dim = %d",
me, axis, maxdim );
airMopError( mop ); return 1;
}
/* figure out dimension of output ( outdim ) */
if ( diffdim ) {
/* case A: ( example ) 2D slices and 3D slabs are being joined
together to make a bigger 3D volume */
outdim = maxdim;
} else {
/* diffdim == 0 */
if ( axis == maxdim ) {
/* case B: this is like the old "stitch": a bunch of equal-sized
slices of dimension N are being stacked together to make an
N+1 dimensional volume, which is essentially just the result of
concatenating the memory of individual inputs */
outdim = maxdim + 1;
} else {
/* case C: axis < maxdim; maxdim == mindim */
/* case C1 ( !incrDim ): a bunch of N-D slabs are being joined
together to make a bigger N-D volume. The axis along which
they are being joined could be any of existing axes ( from 0
to maxdim-1 ) */
/* case C2 ( incrDim ): this is also a "stitch", but the new axis
created by the stitching is inserted into the existing
axes. ( ex: stitch 3 PGMs ( R, G, B ) together into a PPM ( with
color on axis zero ) */
outdim = maxdim + !!incrDim;
}
}
if ( outdim > NRRD_DIM_MAX ) {
biffAddf( NRRD, "%s: output dimension ( %d ) exceeds NRRD_DIM_MAX ( %d )",
me, outdim, NRRD_DIM_MAX );
airMopError( mop ); return 1;
}
/* do tacit reshaping, and possibly permuting, as needed */
for ( ai=0; ai<outdim; ai++ ) {
permute[ai] = ( ai < axis
? ai
: ( ai < outdim-1
? ai + 1
: axis ) );
/* fprintf( stderr, "!%s: 1st permute[%d] = %d\n", me, ai, permute[ai] ); */
}
for ( ni=0; ni<ninNum; ni++ ) {
ninperm[ni] = nrrdNew( );
diffdim = outdim - nin[ni]->dim;
/* fprintf( stderr, "!%s: ni = %d ---> diffdim = %d\n", me, ni, diffdim ); */
if ( diffdim ) {
/* we do a tacit reshaping, which actually includes
a tacit permuting, so we don't have to call permute
on the parts that don't actually need it */
/* NB: we register nrrdNix, not nrrdNuke */
/* fprintf( stderr, "!%s: %d: tacit reshape/permute\n", me, ni ); */
airMopAdd( mop, ninperm[ni], ( airMopper )nrrdNix, airMopAlways );
nrrdAxisInfoGet_nva( nin[ni], nrrdAxisInfoSize, size );
for ( ai=nin[ni]->dim-1; ai>=mindim+1; ai-- ) {
size[ai] = size[ai-1];
}
size[mindim] = 1;
/* this may be done needlessly often */
for ( ai=0; ai<=nin[ni]->dim; ai++ ) {
if ( ai < mindim ) {
axmap[ai] = ai;
} else if ( ai > mindim ) {
axmap[ai] = ai-1;
} else {
axmap[ai] = -1;
}
}
/* we don't have to actually call nrrdReshape( ): we just nrrdWrap( )
the input data with the reshaped size array */
if ( nrrdWrap_nva( ninperm[ni], nin[ni]->data, nin[ni]->type,
nin[ni]->dim+1, size ) ) {
biffAddf( NRRD, "%s: trouble creating interm. version of nrrd %d",
me, ni );
airMopError( mop ); return 1;
}
nrrdAxisInfoCopy( ninperm[ni], nin[ni], axmap,
( NRRD_AXIS_INFO_SIZE_BIT
/* HEY: this is being nixed because I can't think
of a sane way of keeping it consistent */
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT ) );
} else {
/* on this part, we permute ( no need for a reshape ) */
airMopAdd( mop, ninperm[ni], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesPermute( ninperm[ni], nin[ni], permute ) ) {
biffAddf( NRRD, "%s: trouble permuting input part %d", me, ni );
airMopError( mop ); return 1;
}
}
}
/* make sure all parts are compatible in type and shape,
determine length of final output along axis ( outlen ) */
outlen = 0;
for ( ni=0; ni<ninNum; ni++ ) {
if ( ninperm[ni]->type != ninperm[0]->type ) {
biffAddf( NRRD, "%s: type ( %s ) of part %d unlike first's ( %s )",
me, airEnumStr( nrrdType, ninperm[ni]->type ),
ni, airEnumStr( nrrdType, ninperm[0]->type ) );
airMopError( mop ); return 1;
}
if ( nrrdTypeBlock == ninperm[0]->type ) {
if ( ninperm[ni]->blockSize != ninperm[0]->blockSize ) {
biffAddf( NRRD, "%s: blockSize ( %s ) of part %d != first's ( %s )", me,
airSprintSize_t( stmp[0], ninperm[ni]->blockSize ), ni,
airSprintSize_t( stmp[1], ninperm[0]->blockSize ) );
airMopError( mop ); return 1;
}
}
if ( !nrrdElementSize( ninperm[ni] ) ) {
biffAddf( NRRD, "%s: got wacky elements size ( %s ) for part %d", me,
airSprintSize_t( stmp[0], nrrdElementSize( ninperm[ni] ) ), ni );
airMopError( mop ); return 1;
}
/* fprintf( stderr, "!%s: part %03d shape: ", me, ni ); */
for ( ai=0; ai<outdim-1; ai++ ) {
/* fprintf( stderr, "%03u ", ( unsigned int )ninperm[ni]->axis[ai].size );*/
if ( ninperm[ni]->axis[ai].size != ninperm[0]->axis[ai].size ) {
biffAddf( NRRD, "%s: axis %d size ( %s ) of part %d != first's ( %s )", me,
ai, airSprintSize_t( stmp[0], ninperm[ni]->axis[ai].size ),
ni, airSprintSize_t( stmp[1], ninperm[0]->axis[ai].size ) );
airMopError( mop ); return 1;
}
}
/*
fprintf( stderr, "%03u\n", ( unsigned int )ninperm[ni]->axis[outdim-1].size );
*/
outlen += ninperm[ni]->axis[outdim-1].size;
}
/* fprintf( stderr, "!%s: outlen = %u\n", me, ( unsigned int )outlen ); */
/* allocate temporary nrrd and concat input into it */
outnum = 1;
if ( outdim > 1 ) {
for ( ai=0; ai<outdim-1; ai++ ) {
size[ai] = ninperm[0]->axis[ai].size;
outnum *= size[ai];
}
}
size[outdim-1] = outlen;
outnum *= size[outdim-1];
if ( nrrdMaybeAlloc_nva( ntmpperm = nrrdNew( ), ninperm[0]->type,
outdim, size ) ) {
biffAddf( NRRD, "%s: trouble allocating permutation nrrd", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, ntmpperm, ( airMopper )nrrdNuke, airMopAlways );
dataPerm = AIR_CAST( char *, ntmpperm->data );
for ( ni=0; ni<ninNum; ni++ ) {
/* here is where the actual joining happens */
chunksize = nrrdElementNumber( ninperm[ni] )*nrrdElementSize( ninperm[ni] );
memcpy( dataPerm, ninperm[ni]->data, chunksize );
dataPerm += chunksize;
}
/* copy other axis-specific fields from nin[0] to ntmpperm */
for ( ai=0; ai<outdim-1; ai++ ) {
axmap[ai] = ai;
}
axmap[outdim-1] = -1;
nrrdAxisInfoCopy( ntmpperm, ninperm[0], axmap,
( NRRD_AXIS_INFO_NONE
/* HEY: this is being nixed because I can't think
of a sane way of keeping it consistent */
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT ) );
ntmpperm->axis[outdim-1].size = outlen;
/* do the permutation required to get output in right order */
if ( nrrdInvertPerm( ipermute, permute, outdim )
|| nrrdAxesPermute( nout, ntmpperm, ipermute ) ) {
biffAddf( NRRD, "%s: error permuting temporary nrrd", me );
airMopError( mop ); return 1;
}
/* basic info is either already set or invalidated by joining */
/* HEY: set content on output! */
airMopOkay( mop );
return 0;
}
/*
******** nrrdAxesSplit
**
** like reshape, but only for splitting one axis into a fast and slow part.
*/
int
818 nrrdAxesSplit( Nrrd *nout, const Nrrd *nin,
unsigned int saxi, size_t sizeFast, size_t sizeSlow ) {
static const char me[]="nrrdAxesSplit", func[]="axsplit";
unsigned int ai;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( saxi <= nin->dim-1 ) ) {
biffAddf( NRRD, "%s: given axis ( %d ) outside valid range [0, %d]",
me, saxi, nin->dim-1 );
return 1;
}
if ( NRRD_DIM_MAX == nin->dim ) {
biffAddf( NRRD, "%s: given nrrd already at NRRD_DIM_MAX ( %d )",
me, NRRD_DIM_MAX );
return 1;
}
if ( !( sizeFast*sizeSlow == nin->axis[saxi].size ) ) {
char stmp[4][AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: # samples along axis %d ( %s ) != "
"product of fast and slow sizes ( %s * %s = %s )", me, saxi,
airSprintSize_t( stmp[0], nin->axis[saxi].size ),
airSprintSize_t( stmp[1], sizeFast ),
airSprintSize_t( stmp[2], sizeSlow ),
airSprintSize_t( stmp[3], sizeFast*sizeSlow ) );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
nout->dim = 1 + nin->dim;
for ( ai=nin->dim-1; ai>=saxi+1; ai-- ) {
_nrrdAxisInfoCopy( &( nout->axis[ai+1] ), &( nin->axis[ai] ),
NRRD_AXIS_INFO_NONE );
}
/* the ONLY thing we can say about the new axes are their sizes */
_nrrdAxisInfoInit( &( nout->axis[saxi] ) );
_nrrdAxisInfoInit( &( nout->axis[saxi+1] ) );
nout->axis[saxi].size = sizeFast;
nout->axis[saxi+1].size = sizeSlow;
if ( nrrdContentSet_va( nout, func, nin, "%d, %d, %d",
saxi, sizeFast, sizeSlow ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* all basic information already copied by nrrdCopy */
return 0;
}
/*
******** nrrdAxesDelete
**
** like reshape, but preserves axis information on old axes, and
** this is only for removing a "stub" axis with length 1.
*/
int
882 nrrdAxesDelete( Nrrd *nout, const Nrrd *nin, unsigned int daxi ) {
static const char me[]="nrrdAxesDelete", func[]="axdelete";
unsigned int ai;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( daxi < nin->dim ) ) {
biffAddf( NRRD, "%s: given axis ( %d ) outside valid range [0, %d]",
me, daxi, nin->dim-1 );
return 1;
}
if ( 1 == nin->dim ) {
biffAddf( NRRD, "%s: given nrrd already at lowest dimension ( 1 )", me );
return 1;
}
if ( 1 != nin->axis[daxi].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: size along axis %d is %s, not 1", me, daxi,
airSprintSize_t( stmp, nin->axis[daxi].size ) );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
for ( ai=daxi; ai<nin->dim-1; ai++ ) {
_nrrdAxisInfoCopy( &( nout->axis[ai] ), &( nin->axis[ai+1] ),
NRRD_AXIS_INFO_NONE );
}
nout->dim = nin->dim - 1;
if ( nrrdContentSet_va( nout, func, nin, "%d", daxi ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* all basic information already copied by nrrdCopy */
return 0;
}
/*
******** nrrdAxesMerge
**
** like reshape, but preserves axis information on old axes
** merges axis ax and ax+1 into one
*/
int
934 nrrdAxesMerge( Nrrd *nout, const Nrrd *nin, unsigned int maxi ) {
static const char me[]="nrrdAxesMerge", func[]="axmerge";
unsigned int ai;
size_t sizeFast, sizeSlow;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( maxi < nin->dim-1 ) ) {
biffAddf( NRRD, "%s: given axis ( %d ) outside valid range [0, %d]",
me, maxi, nin->dim-2 );
return 1;
}
if ( 1 == nin->dim ) {
biffAddf( NRRD, "%s: given nrrd already at lowest dimension ( 1 )", me );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
sizeFast = nin->axis[maxi].size;
sizeSlow = nin->axis[maxi+1].size;
nout->dim = nin->dim - 1;
for ( ai=maxi+1; ai<nout->dim; ai++ ) {
_nrrdAxisInfoCopy( &( nout->axis[ai] ), &( nin->axis[ai+1] ),
NRRD_AXIS_INFO_NONE );
}
/* the ONLY thing we can say about the new axis is its size */
_nrrdAxisInfoInit( &( nout->axis[maxi] ) );
nout->axis[maxi].size = sizeFast*sizeSlow;
if ( nrrdContentSet_va( nout, func, nin, "%d", maxi ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* all basic information already copied by nrrdCopy */
return 0;
}
/*
******** nrrdReshape_nva( )
**
*/
int
984 nrrdReshape_nva( Nrrd *nout, const Nrrd *nin,
unsigned int dim, const size_t *size ) {
static const char me[]="nrrdReshape_nva", func[]="reshape";
char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL];
size_t numOut;
unsigned int ai;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nout && nin && size ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_CL( 1, dim, NRRD_DIM_MAX ) ) ) {
biffAddf( NRRD, "%s: given dimension ( %d ) outside valid range [1, %d]",
me, dim, NRRD_DIM_MAX );
return 1;
}
if ( _nrrdSizeCheck( size, dim, AIR_TRUE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
numOut = 1;
for ( ai=0; ai<dim; ai++ ) {
numOut *= size[ai];
}
if ( numOut != nrrdElementNumber( nin ) ) {
biffAddf( NRRD, "%s: new sizes product ( %s ) != # elements ( %s )", me,
airSprintSize_t( stmp[0], numOut ),
airSprintSize_t( stmp[1], nrrdElementNumber( nin ) ) );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
nout->dim = dim;
for ( ai=0; ai<dim; ai++ ) {
/* the ONLY thing we can say about the axes is the size */
_nrrdAxisInfoInit( &( nout->axis[ai] ) );
nout->axis[ai].size = size[ai];
}
strcpy( buff1, "" );
for ( ai=0; ai<dim; ai++ ) {
sprintf( buff2, "%s%s", ( ai ? "x" : "" ),
airSprintSize_t( stmp[0], size[ai] ) );
strcat( buff1, buff2 );
}
/* basic info copied by _nrrdCopy, but probably more than we
want- perhaps space dimension and origin should be nixed? */
if ( nrrdContentSet_va( nout, func, nin, "%s", buff1 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdReshape_va( )
**
** var-args version of nrrdReshape_nva( )
*/
int
1053 nrrdReshape_va( Nrrd *nout, const Nrrd *nin, unsigned int dim, ... ) {
static const char me[]="nrrdReshape_va";
unsigned int ai;
size_t size[NRRD_DIM_MAX];
va_list ap;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_CL( 1, dim, NRRD_DIM_MAX ) ) ) {
biffAddf( NRRD, "%s: given dimension ( %d ) outside valid range [1, %d]",
me, dim, NRRD_DIM_MAX );
return 1;
}
va_start( ap, dim );
for ( ai=0; ai<dim; ai++ ) {
size[ai] = va_arg( ap, size_t );
}
va_end( ap );
/* basic info copied ( indirectly ) by nrrdReshape_nva( ) */
if ( nrrdReshape_nva( nout, nin, dim, size ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdBlock( )
**
** collapse the first axis ( axis 0 ) of the nrrd into a block, making
** an output nrrd of type nrrdTypeBlock. The input type can be block.
** All information for other axes is shifted down one axis.
*/
int
1090 nrrdBlock( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="nrrdBlock", func[]="block";
unsigned int ai;
size_t numEl, size[NRRD_DIM_MAX];
int map[NRRD_DIM_MAX];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: due to laziness, nout==nin disallowed", me );
return 1;
}
if ( 1 == nin->dim ) {
biffAddf( NRRD, "%s: can't blockify 1-D nrrd", me );
return 1;
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
numEl = nin->axis[0].size;;
nout->blockSize = numEl*nrrdElementSize( nin );
/*
fprintf( stderr, "!%s: nout->blockSize = %d * %d = %d\n", me,
numEl, nrrdElementSize( nin ), nout->blockSize );
*/
for ( ai=0; ai<nin->dim-1; ai++ ) {
map[ai] = ai+1;
size[ai] = nin->axis[map[ai]].size;
}
/* nout->blockSize set above */
if ( nrrdMaybeAlloc_nva( nout, nrrdTypeBlock, nin->dim-1, size ) ) {
biffAddf( NRRD, "%s: failed to allocate output", me );
return 1;
}
memcpy( nout->data, nin->data, nrrdElementNumber( nin )*nrrdElementSize( nin ) );
if ( nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s: failed to copy axes", me );
return 1;
}
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdUnblock( )
**
** takes a nrrdTypeBlock nrrd and breaks the blocks into elements of
** type "type", and shifts other axis information up by one axis
*/
int
1162 nrrdUnblock( Nrrd *nout, const Nrrd *nin, int type ) {
static const char me[]="nrrdUnblock", func[]="unblock";
unsigned int dim;
size_t size[NRRD_DIM_MAX], outElSz;
int map[NRRD_DIM_MAX];
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: due to laziness, nout==nin disallowed", me );
return 1;
}
if ( nrrdTypeBlock != nin->type ) {
biffAddf( NRRD, "%s: need input nrrd type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( NRRD_DIM_MAX == nin->dim ) {
biffAddf( NRRD, "%s: input nrrd already at dimension limit ( %d )",
me, NRRD_DIM_MAX );
return 1;
}
if ( airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: invalid requested type %d", me, type );
return 1;
}
if ( nrrdTypeBlock == type && ( !( 0 < nout->blockSize ) ) ) {
biffAddf( NRRD, "%s: for %s type, need nout->blockSize set", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
/* this shouldn't actually be necessary .. */
if ( !( nrrdElementSize( nin ) ) ) {
biffAddf( NRRD, "%s: nin or nout reports zero element size!", me );
return 1;
}
nout->type = type;
outElSz = nrrdElementSize( nout );
if ( nin->blockSize % outElSz ) {
biffAddf( NRRD, "%s: input blockSize ( %s ) not multiple of output "
"element size ( %s )", me,
airSprintSize_t( stmp[0], nin->blockSize ),
airSprintSize_t( stmp[1], outElSz ) );
return 1;
}
for ( dim=0; dim<=nin->dim; dim++ ) {
map[dim] = !dim ? -1 : ( int )dim-1;
size[dim] = !dim ? nin->blockSize / outElSz : nin->axis[map[dim]].size;
}
/* if nout->blockSize is needed, we've checked that its set */
if ( nrrdMaybeAlloc_nva( nout, type, nin->dim+1, size ) ) {
biffAddf( NRRD, "%s: failed to allocate output", me );
return 1;
}
memcpy( nout->data, nin->data, nrrdElementNumber( nin )*nrrdElementSize( nin ) );
if ( nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s: failed to copy axes", me );
return 1;
}
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/* for nrrdTile ..
will require that # slices be <= number of images: won't crop for you,
but will happy pad with black. This will be handled in another
function. Probably unu tile.
*/
/*
******** nrrdTile2D( )
**
** Splits axis axSplit into two pieces of size sizeFast and sizeSlow.
** The data from the fast partition is juxtaposed following ax0, the
** slow after ax1. nrrdAxesMerge is then called to join ax0 and ax1
** with their respective newly permuted data. There should be one
** fewer dimensions in the output nrrd than in the input nrrd.
*/
int
1263 nrrdTile2D( Nrrd *nout, const Nrrd *nin, unsigned int ax0, unsigned int ax1,
unsigned int axSplit, size_t sizeFast, size_t sizeSlow ) {
static const char me[]="nrrdTile2D";
int E, /* error flag */
insAxis[2*NRRD_DIM_MAX], /* array for inserting the two axes resulting
from the initial split amongst the other
axes: inserted axes go in odd slots,
other axes go in even slots */
mapIdx, /* index for filling map[] */
merge[2], /* two axes to be merged post-permute */
mergeIdx; /* index for filling merge[] */
unsigned int ii,
map[NRRD_DIM_MAX]; /* axis map for axis permute */
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
/* at least for now, axSplit, ax0, and ax1 need to be distinct */
if ( !( axSplit != ax0
&& axSplit != ax1
&& ax0 != ax1 ) ) {
biffAddf( NRRD, "%s: axSplit, ax0, ax1 ( %d, %d, %d ) must be distinct",
me, axSplit, ax0, ax1 );
return 1;
}
if ( !( ax0 < nin->dim
&& ax1 < nin->dim
&& axSplit < nin->dim ) ) {
biffAddf( NRRD, "%s: axSplit, ax0, ax1 ( %d, %d, %d ) must be in range [0, %d]",
me, axSplit, ax0, ax1, nin->dim-1 );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
/* increment ax0 and ax1 if they're above axSplit, since the
initial axis split will bump up the corresponding axes */
ax0 += ( axSplit < ax0 );
ax1 += ( axSplit < ax1 );
/* initialize insAxis to all invalid ( blank ) values */
for ( ii=0; ii<2*( nout->dim+1 ); ii++ ) {
insAxis[ii] = -1;
}
/* run through post-split axes, inserting axSplit and axSplit+1
into the slots after ax0 and ax1 respectively, otherwise
set the identity map */
for ( ii=0; ii<( nout->dim+1 ); ii++ ) {
if ( axSplit == ii ) {
insAxis[2*ax0 + 1] = axSplit;
} else if ( axSplit+1 == ii ) {
insAxis[2*ax1 + 1] = axSplit+1;
} else {
insAxis[2*ii + 0] = ii;
}
}
/* settle the values from insAxis[] into map[] by removing the -1's */
mergeIdx = mapIdx = 0;
for ( ii=0; ii<2*( nout->dim+1 ); ii++ ) {
if ( insAxis[ii] != -1 ) {
if ( 1 == ii % 2 ) {
/* its an odd entry in insAxis[], so the previous axis is to be
merged. Using mapIdx-1 is legit because we disallow
axSplit == ax{0, 1} */
merge[mergeIdx++] = mapIdx-1;
}
map[mapIdx++] = insAxis[ii];
}
}
E = AIR_FALSE;
if ( !E ) E |= nrrdAxesSplit( nout, nout, axSplit, sizeFast, sizeSlow );
if ( !E ) E |= nrrdAxesPermute( nout, nout, map );
if ( !E ) E |= nrrdAxesMerge( nout, nout, merge[1] );
if ( !E ) E |= nrrdAxesMerge( nout, nout, merge[0] );
if ( E ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
/* HEY: set content */
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdUntile2D( )
**
** This will split ax0 into nin->axis[ax0].size/sizeFast and sizeFast
** sizes. ax1 will then be split into nin->axis[ax1].size/sizeSlow
** and sizeSlow sizes. The axes corresponding to sizeFast and
** sizeSlow will be permuted and merged such that
** nout->axis[axMerge].size == sizeFast*sizeSlow.
**
** The thing to be careful of is that axMerge identifies an axis
** in the array set *after* the two axis splits, not before. This
** is in contrast to the axSplit ( and ax0 and ax1 ) argument of nrrdTile2D
** which identifies axes in the original nrrd.
*/
1382 int nrrdUntile2D( Nrrd *nout, const Nrrd *nin,
unsigned int ax0, unsigned int ax1,
unsigned int axMerge, size_t sizeFast, size_t sizeSlow ) {
static const char me[]="nrrdUntile2D";
int E;
unsigned int ii, mapIdx, map[NRRD_DIM_MAX];
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( ax0 == ax1 ) {
biffAddf( NRRD, "%s: ax0 ( %d ) and ax1 ( %d ) must be distinct",
me, ax0, ax1 );
return 1;
}
if ( !( ax0 < nin->dim && ax1 < nin->dim ) ) {
biffAddf( NRRD, "%s: ax0, ax1 ( %d, %d ) must be in range [0, %d]",
me, ax0, ax1, nin->dim-1 );
return 1;
}
if ( !( axMerge <= nin->dim ) ) {
biffAddf( NRRD, "%s: axMerge ( %d ) must be in range [0, %d]",
me, axMerge, nin->dim );
return 1;
}
if ( nin->axis[ax0].size != sizeFast*( nin->axis[ax0].size/sizeFast ) ) {
biffAddf( NRRD, "%s: sizeFast ( %s ) doesn't divide into axis %d size ( %s )",
me, airSprintSize_t( stmp[0], sizeFast ),
ax0, airSprintSize_t( stmp[1], nin->axis[ax0].size ) );
return 1;
}
if ( nin->axis[ax1].size != sizeSlow*( nin->axis[ax1].size/sizeSlow ) ) {
biffAddf( NRRD, "%s: sizeSlow ( %s ) doesn't divide into axis %d size ( %s )",
me, airSprintSize_t( stmp[0], sizeSlow ),
ax1, airSprintSize_t( stmp[1], nin->axis[ax1].size ) );
return 1;
}
if ( nout != nin ) {
if ( _nrrdCopy( nout, nin, ( NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
/* Split the larger ( slower ) axis first. */
E = AIR_FALSE;
if ( ax0 < ax1 ) {
if ( !E ) E |= nrrdAxesSplit( nout, nout, ax1,
nin->axis[ax1].size/sizeSlow, sizeSlow );
if ( !E ) E |= nrrdAxesSplit( nout, nout, ax0,
nin->axis[ax0].size/sizeFast, sizeFast );
/* Increment the larger value as it will get shifted by the lower
split. */
ax1++;
} else {
if ( !E ) E |= nrrdAxesSplit( nout, nout, ax0,
nin->axis[ax0].size/sizeFast, sizeFast );
if ( !E ) E |= nrrdAxesSplit( nout, nout, ax1,
nin->axis[ax1].size/sizeSlow, sizeSlow );
ax0++;
}
if ( E ) {
biffAddf( NRRD, "%s: trouble with initial splitting", me );
return 1;
}
/* Determine the axis permutation map */
mapIdx = 0;
for ( ii=0; ii<nout->dim; ii++ ) {
if ( mapIdx == axMerge ) {
/* Insert the slow parts of the axes that have been split */
map[mapIdx++] = ax0+1;
map[mapIdx++] = ax1+1;
}
if ( ii == ax0+1 || ii == ax1+1 ) {
/* These are handled by the logic above */
} else {
/* Otherwise use the identity map */
map[mapIdx++] = ii;
}
}
/*
fprintf( stderr, "%s: map =", me );
for ( ii=0; ii<nout->dim; ii++ ) {
fprintf( stderr, " %d", map[ii] );
}
fprintf( stderr, "; axMerge = %d\n", axMerge );
*/
E = AIR_FALSE;
if ( !E ) E |= nrrdAxesPermute( nout, nout, map );
if ( !E ) E |= nrrdAxesMerge( nout, nout, axMerge );
if ( E ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
#if 0
int
1504 nrrdShift( Nrrd *nout, const Nrrd *nin, const ptrdiff_t *offset,
int boundary, double padValue ) {
static const char me[]="nrrdShift", func[] = "shift";
if ( !( nout && nin && offset ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
return 0;
}
#endif
/* ---- END non-NrrdIO */
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
** This is a largely a re-write of the functionality in
** nrrdSpatialResample( ), but with some improvements. The big API
** change is that everything happens in a nrrdResampleContext, and no
** fields in this need to be directly set ( except for rsmc->verbose ).
**
** The big behavior/API change is that the range along the axis that
** is resampled is now defined in terms of index space, instead of
** axis-aligned scaled index space ( what used to be called "world
** space", prior to general orientation ). This means that if you
** want the whole range resampled, you use [-0.5, size-0.5] for cell-
** centered, and [0, size-1] for node-centered. One function helpful
** dealing with this is nrrdResampleRangeFullSet( ).
**
** Other improvements:
** -- ability to quickly resample a different nrrd with the same
** sizes and kernels as with a previous ( all useful state and
** allocations of intermediate resampling results is preserved
** in the nrrdResampleContext ). To resample a second nrrd,
** you'd only need:
** nrrdResampleInputSet( rsmc, nin2 );
** nrrdResampleExecute( rsmc, nout2 );
** -- correct handling general orientation ( space directions and
** space origin ). This was impossible in the old resampler
** because of how its logic was hard-wired to the axis-aligned
** world space defined by the per-axis min and max.
** -- correct handling of "cheap" downsampling, with the use of
** the new nrrdKernelCheap
** -- smaller memory footprint ( smarter about freeing intermediate
** resampling results )
*/
enum {
flagUnknown, /* 0 */
flagDefaultCenter, /* 1 */
flagInput, /* 2 */
flagOverrideCenters, /* 3 */
flagInputDimension, /* 4 */
flagInputCenters, /* 5 */
flagInputSizes, /* 6 */
flagKernels, /* 7 */
flagSamples, /* 8 */
flagRanges, /* 9 */
flagBoundary, /* 10 */
flagLineAllocate, /* 11 */
flagLineFill, /* 12 */
flagVectorAllocate, /* 13 */
flagPermutation, /* 14 */
flagVectorFill, /* 15 */
flagClamp, /* 16 */
flagRound, /* 17 */
flagTypeOut, /* 18 */
flagPadValue, /* 19 */
flagRenormalize, /* 20 */
flagNonExistent, /* 21 */
flagLast
};
#define FLAG_MAX 21
void
87 nrrdResampleContextInit( NrrdResampleContext *rsmc ) {
unsigned int axIdx, axJdx, kpIdx, flagIdx;
NrrdResampleAxis *axis;
if ( rsmc ) {
rsmc->nin = NULL;
rsmc->boundary = nrrdDefaultResampleBoundary;
rsmc->typeOut = nrrdDefaultResampleType;
rsmc->renormalize = nrrdDefaultResampleRenormalize;
rsmc->roundlast = nrrdDefaultResampleRound;
rsmc->clamp = nrrdDefaultResampleClamp;
rsmc->defaultCenter = nrrdDefaultCenter;
rsmc->nonExistent = nrrdDefaultResampleNonExistent;
rsmc->padValue = nrrdDefaultResamplePadValue;
rsmc->dim = 0;
rsmc->passNum = AIR_CAST( unsigned int, -1 ); /* 4294967295 */
rsmc->topRax = AIR_CAST( unsigned int, -1 );
rsmc->botRax = AIR_CAST( unsigned int, -1 );
for ( axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++ ) {
rsmc->permute[axIdx] = AIR_CAST( unsigned int, -1 );
rsmc->passAxis[axIdx] = AIR_CAST( unsigned int, -1 );
}
for ( axIdx=0; axIdx<NRRD_DIM_MAX+1; axIdx++ ) {
axis = rsmc->axis + axIdx;
axis->kernel = NULL;
axis->kparm[0] = nrrdDefaultKernelParm0;
for ( kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++ ) {
axis->kparm[kpIdx] = AIR_NAN;
}
axis->min = axis->max = AIR_NAN;
axis->samples = AIR_CAST( unsigned int, -1 );
axis->overrideCenter = nrrdCenterUnknown;
axis->center = nrrdCenterUnknown;
axis->sizeIn = AIR_CAST( unsigned int, -1 );
axis->axIdx = axIdx; /* never changes */
axis->passIdx = AIR_CAST( unsigned int, -1 );
for ( axJdx=0; axJdx<NRRD_DIM_MAX; axJdx++ ) {
axis->sizePerm[axJdx] = AIR_CAST( size_t, -1 );
axis->axisPerm[axJdx] = AIR_CAST( unsigned int, -1 );
}
axis->ratio = AIR_NAN;
axis->nrsmp = NULL; /* these are nrrdNew( )'d as needed */
axis->nline = nrrdNew( );
axis->nindex = nrrdNew( );
axis->nweight = nrrdNew( );
}
/* initialize flags to all true */
for ( flagIdx=0; flagIdx<=FLAG_MAX; flagIdx++ ) {
rsmc->flag[flagIdx] = AIR_TRUE;
}
rsmc->time = 0.0;
}
return;
}
NrrdResampleContext *
143 nrrdResampleContextNew( ) {
NrrdResampleContext *rsmc;
rsmc = AIR_CALLOC( 1, NrrdResampleContext );
if ( rsmc ) {
rsmc->flag = AIR_CALLOC( 1+FLAG_MAX, int );
nrrdResampleContextInit( rsmc );
}
return rsmc;
}
NrrdResampleContext *
155 nrrdResampleContextNix( NrrdResampleContext *rsmc ) {
unsigned int axIdx;
if ( rsmc ) {
for ( axIdx=0; axIdx<NRRD_DIM_MAX+1; axIdx++ ) {
/* nrsmp should have been cleaned up by _nrrdResampleOutputUpdate( ) */
nrrdNuke( rsmc->axis[axIdx].nline );
nrrdNuke( rsmc->axis[axIdx].nindex );
nrrdNuke( rsmc->axis[axIdx].nweight );
}
airFree( rsmc->flag );
airFree( rsmc );
}
return NULL;
}
int
172 nrrdResampleDefaultCenterSet( NrrdResampleContext *rsmc,
int center ) {
static const char me[]="nrrdResampleDefaultCenterSet";
if ( !( rsmc ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( nrrdCenterNode == center
|| nrrdCenterCell == center ) ) {
biffAddf( NRRD, "%s: got invalid center ( %d )", me, center );
return 1;
}
if ( center != rsmc->defaultCenter ) {
rsmc->defaultCenter = center;
rsmc->flag[flagDefaultCenter] = AIR_TRUE;
}
return 0;
}
int
195 nrrdResampleNonExistentSet( NrrdResampleContext *rsmc,
int nonExist ) {
static const char me[]="nrrdResampleNonExistentSet";
if ( !( rsmc ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdResampleNonExistent, nonExist ) ) {
biffAddf( NRRD, "%s: didn't get valid non-existent behavior ( %d )",
me, nonExist );
return 1;
}
if ( nonExist != rsmc->nonExistent ) {
rsmc->nonExistent = nonExist;
rsmc->flag[flagNonExistent] = AIR_TRUE;
}
return 0;
}
#define NRRD_RESAMPLE_INPUT_SET_BODY \
unsigned int axIdx, kpIdx; \
\
if ( !( rsmc && nin ) ) { \
biffAddf( NRRD, "%s: got NULL pointer", me ); \
return 1; \
} \
if ( nrrdCheck( nin ) ) { \
biffAddf( NRRD, "%s: problems with given nrrd", me ); \
return 1; \
} \
if ( nrrdTypeBlock == nin->type ) { \
biffAddf( NRRD, "%s: can't resample from type %s", me, \
airEnumStr( nrrdType, nrrdTypeBlock ) ); \
return 1; \
} \
\
rsmc->nin = nin; \
rsmc->flag[flagInput] = AIR_TRUE; \
\
/* per-axis information should be invalidated at this point, because \
if we defer the invalidation to later ...Update( ) calls, it will \
clobber the effects of intervening calls to the likes of \
...KernelSet( ), ...SampleSet( ), and so on */ \
if ( rsmc->dim != nin->dim ) { \
for ( axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++ ) { \
rsmc->axis[axIdx].center = nrrdCenterUnknown; \
rsmc->axis[axIdx].sizeIn = 0; \
rsmc->axis[axIdx].kernel = NULL; \
rsmc->axis[axIdx].kparm[0] = nrrdDefaultKernelParm0; \
for ( kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++ ) { \
rsmc->axis[axIdx].kparm[kpIdx] = AIR_NAN; \
} \
rsmc->axis[axIdx].samples = 0; \
rsmc->axis[axIdx].min = AIR_NAN; \
rsmc->axis[axIdx].max = AIR_NAN; \
} \
} \
\
return 0
int
nrrdResampleNrrdSet( NrrdResampleContext *rsmc, const Nrrd *nin ) {
static const char me[]="nrrdResampleNrrdSet";
NRRD_RESAMPLE_INPUT_SET_BODY;
}
int
nrrdResampleInputSet( NrrdResampleContext *rsmc, const Nrrd *nin ) {
static const char me[]="nrrdResampleInputSet";
NRRD_RESAMPLE_INPUT_SET_BODY;
}
#define PER_AXIS_ERROR_CHECK \
if ( !rsmc ) { \
biffAddf( NRRD, "%s: got NULL pointer", me ); \
return 1; \
} \
if ( !rsmc->nin ) { \
biffAddf( NRRD, "%s: haven't set input nrrd yet", me ); \
return 1; \
} \
if ( !( axIdx < rsmc->nin->dim ) ) { \
biffAddf( NRRD, "%s: axis %u >= nin->dim %u", \
me, axIdx, rsmc->nin->dim ); \
return 1; \
}
int
nrrdResampleKernelSet( NrrdResampleContext *rsmc, unsigned int axIdx,
const NrrdKernel *kernel,
double kparm[NRRD_KERNEL_PARMS_NUM] ) {
static const char me[]="nrrdResampleKernelSet";
unsigned int kpIdx;
PER_AXIS_ERROR_CHECK;
rsmc->axis[axIdx].kernel = kernel;
if ( kernel ) {
for ( kpIdx=0; kpIdx<kernel->numParm; kpIdx++ ) {
rsmc->axis[axIdx].kparm[kpIdx] = kparm[kpIdx];
}
if ( rsmc->verbose ) {
char kstr[AIR_STRLEN_LARGE];
NrrdKernelSpec ksp;
nrrdKernelSpecSet( &ksp, rsmc->axis[axIdx].kernel,
rsmc->axis[axIdx].kparm );
nrrdKernelSpecSprint( kstr, &ksp );
fprintf( stderr, "%s: axis %u kernel %s\n", me, axIdx, kstr );
}
}
rsmc->flag[flagKernels] = AIR_TRUE;
return 0;
}
int
nrrdResampleSamplesSet( NrrdResampleContext *rsmc,
unsigned int axIdx,
size_t samples ) {
static const char me[]="nrrdResampleSamplesSet";
PER_AXIS_ERROR_CHECK;
if ( rsmc->axis[axIdx].samples != samples ) {
if ( rsmc->verbose ) {
fprintf( stderr, "%s: axis %u samples %u --> %u\n", me, axIdx,
AIR_CAST( unsigned int, rsmc->axis[axIdx].samples ),
AIR_CAST( unsigned int, samples ) );
}
rsmc->axis[axIdx].samples = samples;
rsmc->flag[flagSamples] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleRangeSet( NrrdResampleContext *rsmc,
unsigned int axIdx,
double min, double max ) {
static const char me[]="nrrdResampleRangeSet";
PER_AXIS_ERROR_CHECK;
if ( !( AIR_EXISTS( min ) && AIR_EXISTS( max ) && min != max ) ) {
biffAddf( NRRD, "%s: need min != max and both to exist", me );
return 1;
}
if ( !( rsmc->axis[axIdx].min == min
&& rsmc->axis[axIdx].max == max ) ) {
rsmc->axis[axIdx].min = min;
rsmc->axis[axIdx].max = max;
rsmc->flag[flagRanges] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleOverrideCenterSet( NrrdResampleContext *rsmc,
unsigned int axIdx,
int center ) {
static const char me[]="nrrdResampleOverrideCenterSet";
PER_AXIS_ERROR_CHECK;
if ( center ) {
/* we do allow passing nrrdCenterUnknown, to turn off override */
if ( airEnumValCheck( nrrdCenter, center ) ) {
biffAddf( NRRD, "%s: didn't get valid centering ( %d )", me, center );
return 1;
}
}
if ( center != rsmc->axis[axIdx].overrideCenter ) {
rsmc->axis[axIdx].overrideCenter = center;
rsmc->flag[flagOverrideCenters] = AIR_TRUE;
}
return 0;
}
void
_nrrdResampleMinMaxFull( double *minP, double *maxP,
int center, size_t size ) {
if ( nrrdCenterCell == center ) {
*minP = -0.5;
*maxP = size - 0.5;
} else {
*minP = 0.0;
*maxP = size - 1.0;
}
}
int
nrrdResampleRangeFullSet( NrrdResampleContext *rsmc,
unsigned int axIdx ) {
static const char me[]="nrrdResampleRangeFullSet";
double min, max;
int center;
PER_AXIS_ERROR_CHECK;
/* HEY trick is to figure out the axis's centering, and to
make sure its the same code as used elsewhere */
center = ( rsmc->axis[axIdx].overrideCenter
? rsmc->axis[axIdx].overrideCenter
: ( rsmc->nin->axis[axIdx].center
? rsmc->nin->axis[axIdx].center
: rsmc->defaultCenter ) );
_nrrdResampleMinMaxFull( &min, &max, center, rsmc->nin->axis[axIdx].size );
if ( !( rsmc->axis[axIdx].min == min
&& rsmc->axis[axIdx].max == max ) ) {
rsmc->axis[axIdx].min = min;
rsmc->axis[axIdx].max = max;
rsmc->flag[flagRanges] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleBoundarySet( NrrdResampleContext *rsmc,
int boundary ) {
static const char me[]="nrrdResampleBoundarySet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( nrrdBoundary, boundary ) ) {
biffAddf( NRRD, "%s: invalid boundary %d", me, boundary );
return 1;
}
if ( rsmc->boundary != boundary ) {
rsmc->boundary = boundary;
rsmc->flag[flagBoundary] = AIR_TRUE;
}
return 0;
}
int
nrrdResamplePadValueSet( NrrdResampleContext *rsmc,
double padValue ) {
static const char me[]="nrrdResamplePadValueSet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( rsmc->padValue != padValue ) {
rsmc->padValue = padValue;
rsmc->flag[flagPadValue] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleBoundarySpecSet( NrrdResampleContext *rsmc,
const NrrdBoundarySpec *bspec ) {
static const char me[]="nrrdResampleBoundarySpecSet";
if ( !( rsmc && bspec ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( rsmc->boundary != bspec->boundary ) {
rsmc->boundary = bspec->boundary;
rsmc->flag[flagBoundary] = AIR_TRUE;
}
if ( rsmc->padValue != bspec->padValue ) {
rsmc->padValue = bspec->padValue;
rsmc->flag[flagPadValue] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleRenormalizeSet( NrrdResampleContext *rsmc,
int renormalize ) {
static const char me[]="nrrdResampleRenormalizeSet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( rsmc->renormalize != renormalize ) {
rsmc->renormalize = renormalize;
rsmc->flag[flagRenormalize] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleTypeOutSet( NrrdResampleContext *rsmc,
int type ) {
static const char me[]="nrrdResampleTypeOutSet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdTypeDefault != type && airEnumValCheck( nrrdType, type ) ) {
biffAddf( NRRD, "%s: invalid type %d", me, type );
return 1;
}
if ( nrrdTypeBlock == type ) {
biffAddf( NRRD, "%s: can't output %s type", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( rsmc->typeOut != type ) {
rsmc->typeOut = type;
rsmc->flag[flagTypeOut] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleRoundSet( NrrdResampleContext *rsmc,
int roundlast ) {
static const char me[]="nrrdResampleRoundSet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( rsmc->roundlast != roundlast ) {
rsmc->roundlast = roundlast;
rsmc->flag[flagRound] = AIR_TRUE;
}
return 0;
}
int
nrrdResampleClampSet( NrrdResampleContext *rsmc,
int clamp ) {
static const char me[]="nrrdResampleClampSet";
if ( !rsmc ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( rsmc->clamp != clamp ) {
rsmc->clamp = clamp;
rsmc->flag[flagClamp] = AIR_TRUE;
}
return 0;
}
int
_nrrdResampleInputDimensionUpdate( NrrdResampleContext *rsmc ) {
if ( rsmc->flag[flagInput] ) {
if ( rsmc->dim != rsmc->nin->dim ) {
rsmc->dim = rsmc->nin->dim;
rsmc->flag[flagInputDimension] = AIR_TRUE;
}
}
return 0;
}
int
_nrrdResampleInputCentersUpdate( NrrdResampleContext *rsmc ) {
unsigned int axIdx;
int center;
if ( rsmc->flag[flagOverrideCenters]
|| rsmc->flag[flagDefaultCenter]
|| rsmc->flag[flagInputDimension]
|| rsmc->flag[flagInput] ) {
for ( axIdx=0; axIdx<NRRD_DIM_MAX; axIdx++ ) {
center = ( rsmc->axis[axIdx].overrideCenter
? rsmc->axis[axIdx].overrideCenter
: ( rsmc->nin->axis[axIdx].center
? rsmc->nin->axis[axIdx].center
: rsmc->defaultCenter ) );
if ( rsmc->axis[axIdx].center != center ) {
rsmc->axis[axIdx].center = center;
rsmc->flag[flagInputCenters] = AIR_TRUE;
}
}
rsmc->flag[flagOverrideCenters] = AIR_FALSE;
rsmc->flag[flagDefaultCenter] = AIR_FALSE;
}
return 0;
}
int
_nrrdResampleInputSizesUpdate( NrrdResampleContext *rsmc ) {
unsigned int axIdx;
if ( rsmc->flag[flagInputDimension]
|| rsmc->flag[flagInput] ) {
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( rsmc->axis[axIdx].sizeIn != rsmc->nin->axis[axIdx].size ) {
rsmc->axis[axIdx].sizeIn = rsmc->nin->axis[axIdx].size;
rsmc->flag[flagInputSizes] = AIR_TRUE;
}
}
rsmc->flag[flagInputDimension] = AIR_FALSE;
}
return 0;
}
int
_nrrdResampleLineAllocateUpdate( NrrdResampleContext *rsmc ) {
static const char me[]="_nrrdResampleLineAllocateUpdate";
unsigned int axIdx;
NrrdResampleAxis *axis;
if ( rsmc->flag[flagInputSizes]
|| rsmc->flag[flagKernels] ) {
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
axis = rsmc->axis + axIdx;
if ( !axis->kernel ) {
nrrdEmpty( axis->nline );
} else {
if ( nrrdMaybeAlloc_va( axis->nline, nrrdResample_nt, 1,
AIR_CAST( size_t, 1 + axis->sizeIn ) ) ) {
biffAddf( NRRD, "%s: couldn't allocate scanline buffer", me );
return 1;
}
}
}
rsmc->flag[flagLineAllocate] = AIR_TRUE;
}
return 0;
}
int
_nrrdResampleVectorAllocateUpdate( NrrdResampleContext *rsmc ) {
static const char me[]="_nrrdResampleVectorAllocateUpdate";
unsigned int axIdx, kpIdx, dotLen, minSamples;
nrrdResample_t spacingOut, support;
NrrdResampleAxis *axis;
if ( rsmc->flag[flagKernels]
|| rsmc->flag[flagSamples]
|| rsmc->flag[flagRanges] ) {
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
axis = rsmc->axis + axIdx;
if ( !axis->kernel ) {
/* no resampling on this axis */
continue;
}
/* check user-set parameters */
if ( !( AIR_EXISTS( axis->min ) && AIR_EXISTS( axis->max ) ) ) {
biffAddf( NRRD, "%s: don't have min, max set on axis %u", me, axIdx );
return 1;
}
for ( kpIdx=0; kpIdx<axis->kernel->numParm; kpIdx++ ) {
if ( !AIR_EXISTS( axis->kparm[kpIdx] ) ) {
biffAddf( NRRD, "%s: didn't set kernel parm %u on axis %u",
me, kpIdx, axIdx );
return 1;
}
}
minSamples = ( nrrdCenterCell == axis->center ? 1 : 2 );
if ( !( axis->samples >= minSamples ) ) {
biffAddf( NRRD, "%s: need at least %u output samples ( not %u ) for "
"%s-centered sampling along axis %u", me, minSamples,
AIR_CAST( unsigned int, axis->samples ),
airEnumStr( nrrdCenter, axis->center ), axIdx );
return 1;
}
/* compute support ( spacingIn == 1.0 by definition ) */
spacingOut = AIR_CAST( nrrdResample_t, ( ( axis->max - axis->min )
/ ( nrrdCenterCell == axis->center
? axis->samples
: axis->samples - 1 ) ) );
axis->ratio = 1.0/spacingOut;
support = AIR_CAST( nrrdResample_t, axis->kernel->support( axis->kparm ) );
if ( axis->ratio > 1 ) {
/* if upsampling, we need only as many samples as needed for
interpolation with the given kernel */
dotLen = ( int )( 2*ceil( support ) );
} else {
/* if downsampling, we need to use all the samples covered by
the stretched out version of the kernel */
dotLen = ( int )( 2*ceil( support/axis->ratio ) );
}
/* some kernels can report zero support when they're basically
delta functions */
dotLen = AIR_MAX( 2, dotLen );
if ( nrrdMaybeAlloc_va( axis->nweight, nrrdResample_nt, 2,
AIR_CAST( size_t, dotLen ),
AIR_CAST( size_t, axis->samples ) )
|| nrrdMaybeAlloc_va( axis->nindex, nrrdTypeInt, 2,
AIR_CAST( size_t, dotLen ),
AIR_CAST( size_t, axis->samples ) ) ) {
biffAddf( NRRD, "%s: trouble allocating index and weighting vectors",
me );
return 1;
}
}
rsmc->flag[flagRanges] = AIR_FALSE;
rsmc->flag[flagVectorAllocate] = AIR_TRUE;
}
return 0;
}
int
_nrrdResampleLineFillUpdate( NrrdResampleContext *rsmc ) {
unsigned int axIdx;
NrrdResampleAxis *axis;
nrrdResample_t *line;
if ( rsmc->flag[flagPadValue]
|| rsmc->flag[flagLineAllocate] ) {
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
axis = rsmc->axis + axIdx;
if ( axis->kernel ) {
line = ( nrrdResample_t* )( axis->nline->data );
line[axis->sizeIn] = AIR_CAST( nrrdResample_t, rsmc->padValue );
}
}
rsmc->flag[flagPadValue] = AIR_FALSE;
rsmc->flag[flagLineAllocate] = AIR_FALSE;
rsmc->flag[flagLineFill] = AIR_TRUE;
}
return 0;
}
int
_nrrdResampleVectorFillUpdate( NrrdResampleContext *rsmc ) {
static const char me[]="_nrrdResampleVectorFillUpdate";
unsigned int axIdx, dotIdx, dotLen, halfLen, smpIdx, kpIdx;
int *indexData, tmp, base, rawIdx;
nrrdResample_t *weightData, idx, integral;
NrrdResampleAxis *axis;
double kparm[NRRD_KERNEL_PARMS_NUM];
if ( rsmc->flag[flagRenormalize]
|| rsmc->flag[flagBoundary]
|| rsmc->flag[flagInputCenters]
|| rsmc->flag[flagInputSizes]
|| rsmc->flag[flagVectorAllocate] ) {
if ( rsmc->verbose ) {
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( rsmc->axis[axIdx].kernel ) {
fprintf( stderr, "%s: axis %u: %s-centering\n", me, axIdx,
airEnumStr( nrrdCenter, rsmc->axis[axIdx].center ) );
}
}
}
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
axis = rsmc->axis + axIdx;
if ( !axis->kernel ) {
/* no resampling on this axis */
continue;
}
/* calculate sample locations and do first pass on indices */
indexData = ( int * )axis->nindex->data;
weightData = ( nrrdResample_t * )axis->nweight->data;
dotLen = AIR_CAST( unsigned int, axis->nweight->axis[0].size );
halfLen = dotLen/2;
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
idx = AIR_CAST( nrrdResample_t,
( nrrdCenterCell == axis->center
? AIR_AFFINE( -0.5, smpIdx, axis->samples-0.5,
axis->min, axis->max )
: AIR_AFFINE( 0.0, smpIdx, axis->samples-1.0,
axis->min, axis->max ) ) );
base = ( int )floor( idx ) - halfLen + 1;
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
tmp = indexData[dotIdx + dotLen*smpIdx] = base + dotIdx;
weightData[dotIdx + dotLen*smpIdx] = idx - tmp;
}
if ( rsmc->verbose ) {
if ( !smpIdx ) {
fprintf( stderr, "%s: smpIdx=%u -> idx=%g -> base=%d\n", me,
smpIdx, idx, base );
fprintf( stderr, "%s: sample locations:\n", me );
}
fprintf( stderr, "%s: %d ( sample locations )\n ", me, smpIdx );
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
fprintf( stderr, "%d/%g ",
indexData[dotIdx + dotLen*smpIdx],
weightData[dotIdx + dotLen*smpIdx] );
}
fprintf( stderr, "\n" );
}
}
/* figure out what to do with the out-of-range indices */
for ( dotIdx=0; dotIdx<dotLen*axis->samples; dotIdx++ ) {
rawIdx = indexData[dotIdx];
if ( !AIR_IN_CL( 0, rawIdx, AIR_CAST( int, axis->sizeIn )-1 ) ) {
switch( rsmc->boundary ) {
case nrrdBoundaryPad:
case nrrdBoundaryWeight: /* this will be further handled later */
rawIdx = AIR_CAST( int, axis->sizeIn );
break;
case nrrdBoundaryBleed:
rawIdx = AIR_CLAMP( 0, rawIdx, AIR_CAST( int, axis->sizeIn )-1 );
break;
case nrrdBoundaryWrap:
rawIdx = AIR_MOD( rawIdx, AIR_CAST( int, axis->sizeIn ) );
break;
case nrrdBoundaryMirror:
rawIdx = _nrrdMirror_32( AIR_CAST( unsigned int, axis->sizeIn ), rawIdx );
break;
default:
biffAddf( NRRD, "%s: boundary behavior %d unknown/unimplemented",
me, rsmc->boundary );
return 0;
}
indexData[dotIdx] = rawIdx;
}
}
/* Wow - there is a big rift here between old conventions for how
NrrdKernels were defined, versus the newer practice of creating
parameter-free kernels. The "sneaky trick" code below for changing
parm[0] only works if the kernel actually looks at parm[0]! So at
least for the parameter-free kernels ( and maybe other kernels, but
HEY there's no principled way of knowing! ) we have to do what we
probably should have been done all along: simulating the kernel
scaling by pre-processing the evaluation locations and
post-processing the kernel weights */
if ( 0 == axis->kernel->numParm ) {
size_t nn, ii;
double ratio;
nn = dotLen*axis->samples;
ratio = axis->ratio;
if ( ratio < 1 ) {
for ( ii=0; ii<nn; ii++ ) {
weightData[ii] *= ratio;
}
}
axis->kernel->EVALN( weightData, weightData, nn, axis->kparm );
if ( ratio < 1 ) {
for ( ii=0; ii<nn; ii++ ) {
weightData[ii] *= ratio;
}
}
} else {
/* run the sample locations through the chosen kernel. We play a
sneaky trick on the kernel parameter 0 in case of downsampling
to create the blurring of the old index space */
kparm[0] = ( axis->ratio < 1
? axis->kparm[0] / axis->ratio
: axis->kparm[0] );
for ( kpIdx=1; kpIdx<NRRD_KERNEL_PARMS_NUM; kpIdx++ ) {
kparm[kpIdx] = axis->kparm[kpIdx];
}
axis->kernel->EVALN( weightData, weightData,
dotLen*axis->samples, kparm );
/* special handling of "cheap" kernel */
if ( nrrdKernelCheap == axis->kernel ) {
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
nrrdResample_t dist, minDist;
int minIdx, minSet;
minIdx = indexData[0 + dotLen*smpIdx];
minDist = weightData[0 + dotLen*smpIdx];
/* find sample closest to origin */
for ( dotIdx=1; dotIdx<dotLen; dotIdx++ ) {
dist = weightData[dotIdx + dotLen*smpIdx];
if ( dist < minDist ) {
minDist = dist;
minIdx = indexData[dotIdx + dotLen*smpIdx];
}
}
/* set kernel weights to select sample closest to origin */
minSet = AIR_FALSE;
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
if ( minIdx == indexData[dotIdx + dotLen*smpIdx] && !minSet ) {
weightData[dotIdx + dotLen*smpIdx] = 1.0;
minSet = AIR_TRUE;
} else {
weightData[dotIdx + dotLen*smpIdx] = 0.0;
}
}
}
}
}
if ( rsmc->verbose ) {
fprintf( stderr, "%s: axis %u sample weights:\n", me, axIdx );
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
fprintf( stderr, "%s: %d ( sample weights )\n ", me, smpIdx );
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
fprintf( stderr, "%d/%g ", indexData[dotIdx + dotLen*smpIdx],
weightData[dotIdx + dotLen*smpIdx] );
}
fprintf( stderr, "\n" );
}
}
/* final fixes on weighting values */
integral = AIR_CAST( nrrdResample_t, axis->kernel->integral( axis->kparm ) );
if ( nrrdBoundaryWeight == rsmc->boundary ) {
if ( integral ) {
/* above, we set to axis->sizeIn all the indices that were out of
range. We now use that to determine the sum of the weights
for the indices that were in-range */
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
nrrdResample_t wght = 0;
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
if ( AIR_CAST( int, axis->sizeIn )
!= indexData[dotIdx + dotLen*smpIdx] ) {
wght += weightData[dotIdx + dotLen*smpIdx];
}
}
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
if ( AIR_CAST( int, axis->sizeIn )
!= indexData[dotIdx + dotLen*smpIdx] ) {
weightData[dotIdx + dotLen*smpIdx] *= integral/wght;
} else {
weightData[dotIdx + dotLen*smpIdx] = 0;
}
}
}
}
} else {
/* try to remove ripple/grating on downsampling, and errors in
weighting on upsampling when using kernels that are not
first-order accurate */
if ( rsmc->renormalize && integral ) {
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
nrrdResample_t wght = 0;
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
wght += weightData[dotIdx + dotLen*smpIdx];
}
if ( wght ) {
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
/* this used to normalize the weights so that they summed
to integral ( "*= integral/wght" ), which meant that if
you use a very truncated Gaussian, then your over-all
image brightness goes down. This seems very contrary
to the whole point of renormalization. */
weightData[dotIdx + dotLen*smpIdx] *=
AIR_CAST( nrrdResample_t, 1.0/wght );
}
}
}
}
}
if ( rsmc->verbose ) {
fprintf( stderr, "%s: axis %u post-correction sample weights:\n",
me, axIdx );
for ( smpIdx=0; smpIdx<axis->samples; smpIdx++ ) {
fprintf( stderr, "%s: %d ( sample weights )\n ", me, smpIdx );
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
fprintf( stderr, "%d/%g ", indexData[dotIdx + dotLen*smpIdx],
weightData[dotIdx + dotLen*smpIdx] );
}
fprintf( stderr, "\n" );
}
}
}
rsmc->flag[flagRenormalize] = AIR_FALSE;
rsmc->flag[flagBoundary] = AIR_FALSE;
rsmc->flag[flagInputCenters] = AIR_FALSE;
rsmc->flag[flagVectorAllocate] = AIR_FALSE;
rsmc->flag[flagVectorFill] = AIR_TRUE;
}
return 0;
}
int
_nrrdResamplePermutationUpdate( NrrdResampleContext *rsmc ) {
static const char me[]="_nrrdResamplePermutationUpdate";
unsigned int axIdx, passIdx, currTop, lastTop, fromTop, toTop;
int bi;
if ( rsmc->flag[flagInputSizes]
|| rsmc->flag[flagKernels]
|| rsmc->flag[flagSamples] ) {
rsmc->topRax = rsmc->botRax = AIR_CAST( unsigned int, -1 );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( rsmc->axis[axIdx].kernel ) {
if ( AIR_CAST( unsigned int, -1 ) == rsmc->topRax ) {
rsmc->topRax = axIdx;
}
rsmc->botRax = axIdx;
}
}
if ( rsmc->verbose ) {
fprintf( stderr, "%s: topRax = %u ( %d ); botRax = %u ( %d )\n", me,
rsmc->topRax, AIR_CAST( int, rsmc->topRax ),
rsmc->botRax, AIR_CAST( int, rsmc->botRax ) );
}
/* figure out total number of passes needed, and construct the
permute[] array. permute[i] = j means that the axis in position
i of the old array will be in position j of the new one
( permute[] answers "where do I put this", not "what got put here" ).
*/
rsmc->passNum = 0;
bi = 0;
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( rsmc->axis[axIdx].kernel ) {
do {
bi = AIR_MOD( bi+1, AIR_CAST( int, rsmc->dim ) );
} while ( !rsmc->axis[bi].kernel );
rsmc->permute[bi] = axIdx;
rsmc->passNum += 1;
} else {
rsmc->permute[axIdx] = axIdx;
bi += bi == AIR_CAST( int, axIdx );
}
}
rsmc->permute[rsmc->dim] = rsmc->dim; /* HEY: what is this for? */
if ( rsmc->passNum ) {
toTop = AIR_CAST( unsigned int, -1 );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
/* this will always "break" somewhere */
if ( rsmc->topRax == rsmc->permute[axIdx] ) {
toTop = axIdx;
break;
}
}
fromTop = rsmc->permute[rsmc->topRax];
if ( rsmc->verbose ) {
fprintf( stderr, "%s: passNum = %u; permute =\n ",
me, rsmc->passNum );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
fprintf( stderr, "%u ", rsmc->permute[axIdx] );
}
fprintf( stderr, "\n" );
fprintf( stderr, "%s: toTop = %u; fromTop = %u\n", me, toTop, fromTop );
}
/* create array of how the axes will be arranged in each pass ( "ax" ),
and create array of how big each axes is in each pass ( "sz" ).
The input to pass i will have axis layout described in ax[i] and
axis sizes described in sz[i] */
passIdx = 0;
currTop = rsmc->topRax;
rsmc->passAxis[passIdx] = currTop;
rsmc->axis[currTop].passIdx = passIdx;
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
rsmc->axis[currTop].axisPerm[axIdx] = axIdx;
rsmc->axis[currTop].sizePerm[axIdx] = rsmc->axis[axIdx].sizeIn;
}
for ( passIdx=1; passIdx<rsmc->passNum+1; passIdx++ ) {
lastTop = currTop;
currTop = ( passIdx<rsmc->passNum
? rsmc->axis[currTop].axisPerm[toTop]
: NRRD_DIM_MAX );
rsmc->passAxis[passIdx] = currTop;
rsmc->axis[currTop].passIdx = passIdx;
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
rsmc->axis[currTop].axisPerm[rsmc->permute[axIdx]]
= rsmc->axis[lastTop].axisPerm[axIdx];
rsmc->axis[currTop].sizePerm[rsmc->permute[axIdx]]
= rsmc->axis[lastTop].sizePerm[axIdx];
/* modify the one size corresponding to the resampled axis */
rsmc->axis[currTop].sizePerm[fromTop] = rsmc->axis[lastTop].samples;
}
}
if ( rsmc->verbose ) {
NrrdResampleAxis *axis;
fprintf( stderr, "%s: axis and size permutations:\n", me );
for ( passIdx=0; passIdx<rsmc->passNum+1; passIdx++ ) {
axis = rsmc->axis + rsmc->passAxis[passIdx];
fprintf( stderr, "----- pass[%u=?=%u] @ %u %s:\n", passIdx,
axis->passIdx, rsmc->passAxis[passIdx],
( passIdx<rsmc->passNum ? "" : "( output of final pass )" ) );
if ( !passIdx ) {
fprintf( stderr, "resampling: " );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
fprintf( stderr, "%s ", rsmc->axis[axIdx].kernel ? " XX" : " " );
}
fprintf( stderr, "\n" );
}
fprintf( stderr, " axes: " );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
fprintf( stderr, "%3u ", axis->axisPerm[axIdx] );
}
fprintf( stderr, "\n" );
fprintf( stderr, " sizes: " );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
fprintf( stderr, "%3u ",
AIR_CAST( unsigned int, axis->sizePerm[axIdx] ) );
}
fprintf( stderr, "\n" );
}
fprintf( stderr, "\n" );
}
}
rsmc->flag[flagInputSizes] = AIR_FALSE;
rsmc->flag[flagKernels] = AIR_FALSE;
rsmc->flag[flagSamples] = AIR_FALSE;
rsmc->flag[flagPermutation] = AIR_TRUE;
}
return 0;
}
/* Copy input to output, but with the optional clamping and rounding */
int
_nrrdResampleTrivial( NrrdResampleContext *rsmc, Nrrd *nout,
int typeOut, int doRound,
nrrdResample_t ( *lup )( const void *, size_t ),
nrrdResample_t ( *clamp )( nrrdResample_t ),
nrrdResample_t ( *ins )( void *, size_t, nrrdResample_t ) ) {
static const char me[]="_nrrdResampleTrivial";
size_t size[NRRD_DIM_MAX], valNum, valIdx;
nrrdResample_t val;
const void *dataIn;
void *dataOut;
nrrdAxisInfoGet_nva( rsmc->nin, nrrdAxisInfoSize, size );
if ( nrrdMaybeAlloc_nva( nout, typeOut, rsmc->nin->dim, size ) ) {
biffAddf( NRRD, "%s: couldn't allocate output", me );
return 1;
}
valNum = nrrdElementNumber( rsmc->nin );
dataIn = rsmc->nin->data;
dataOut = nout->data;
for ( valIdx=0; valIdx<valNum; valIdx++ ) {
val = lup( dataIn, valIdx );
if ( doRound ) {
val = AIR_CAST( nrrdResample_t, AIR_ROUNDUP( val ) );
}
if ( rsmc->clamp ) {
val = clamp( val );
}
ins( dataOut, valIdx, val );
}
return 0;
}
int
_nrrdResampleCore( NrrdResampleContext *rsmc, Nrrd *nout,
int typeOut, int doRound,
nrrdResample_t ( *lup )( const void *, size_t ),
nrrdResample_t ( *clamp )( nrrdResample_t ),
nrrdResample_t ( *ins )( void *, size_t, nrrdResample_t ) ) {
static const char me[]="_nrrdResampleCore";
unsigned int axIdx, passIdx;
size_t strideIn, strideOut, lineNum, lineIdx,
coordIn[NRRD_DIM_MAX], coordOut[NRRD_DIM_MAX];
nrrdResample_t *line, *weight, *rsmpIn, *rsmpOut;
int *indx;
const void *dataIn;
void *dataOut;
NrrdResampleAxis *axisIn, *axisOut;
airArray *mop;
/* NOTE: there was an odd memory leak here with normal operation ( no
errors ), because the final airMopOkay( ) was missing, but quick
attempts at resolving it pre-Teem-1.9 release were not successful
( surprisingly, commenting out the airMopSub's led to a segfault ).
So, the airMopAdd which is supposed to manage the per-axis
resampling result is commented out, and there are no leaks and
no segfaults with normal operation, which is good enough for now */
/* compute strideIn; this is constant across passes because all
passes resample topRax, and axes with lower indices have
constant length. */
strideIn = 1;
for ( axIdx=0; axIdx<rsmc->topRax; axIdx++ ) {
strideIn *= rsmc->axis[axIdx].sizeIn;
}
mop = airMopNew( );
for ( passIdx=0; passIdx<rsmc->passNum; passIdx++ ) {
if ( rsmc->verbose ) {
fprintf( stderr, "%s: -------------- pass %u/%u \n",
me, passIdx, rsmc->passNum );
}
/* calculate pass-specific size, stride, and number info */
axisIn = rsmc->axis + rsmc->passAxis[passIdx];
axisOut = rsmc->axis + rsmc->passAxis[passIdx+1];
lineNum = strideOut = 1;
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( axIdx < rsmc->botRax ) {
strideOut *= axisOut->sizePerm[axIdx];
}
if ( axIdx != rsmc->topRax ) {
lineNum *= axisIn->sizePerm[axIdx];
}
}
if ( rsmc->verbose ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
fprintf( stderr, "%s( %u ): lineNum = %s\n", me, passIdx,
airSprintSize_t( stmp1, lineNum ) );
fprintf( stderr, "%s( %u ): strideIn = %s, strideOut = %s\n", me, passIdx,
airSprintSize_t( stmp1, strideIn ),
airSprintSize_t( stmp2, strideOut ) );
}
/* allocate output for this pass */
if ( passIdx < rsmc->passNum-1 ) {
axisOut->nrsmp = nrrdNew( );
/* see NOTE above!
airMopAdd( mop, axisOut->nrsmp, ( airMopper )nrrdNuke, airMopAlways ); */
if ( nrrdMaybeAlloc_nva( axisOut->nrsmp, nrrdResample_nt, rsmc->dim,
axisOut->sizePerm ) ) {
biffAddf( NRRD, "%s: trouble allocating output of pass %u", me,
passIdx );
airMopError( mop ); return 1;
}
if ( rsmc->verbose ) {
fprintf( stderr, "%s: allocated pass %u/%u output nrrd @ %p/%p "
"( on axis %u )\n", me, passIdx, axisIn->passIdx,
AIR_CAST( void*, axisOut->nrsmp ),
AIR_CAST( void*, axisOut->nrsmp->data ), axisOut->axIdx );
}
} else {
if ( nrrdMaybeAlloc_nva( nout, typeOut, rsmc->dim, axisOut->sizePerm ) ) {
biffAddf( NRRD, "%s: trouble allocating final output", me );
airMopError( mop ); return 1;
}
if ( rsmc->verbose ) {
fprintf( stderr, "%s: allocated final pass %u output nrrd @ %p/%p\n",
me, passIdx,
AIR_CAST( void*, nout ),
AIR_CAST( void*, nout->data ) );
}
}
/* set up data pointers */
if ( 0 == passIdx ) {
rsmpIn = NULL;
dataIn = rsmc->nin->data;
} else {
rsmpIn = ( nrrdResample_t * )( axisIn->nrsmp->data );
dataIn = NULL;
}
if ( passIdx < rsmc->passNum-1 ) {
rsmpOut = ( nrrdResample_t * )( axisOut->nrsmp->data );
dataOut = NULL;
} else {
rsmpOut = NULL;
dataOut = nout->data;
}
line = ( nrrdResample_t * )( axisIn->nline->data );
indx = ( int * )( axisIn->nindex->data );
weight = ( nrrdResample_t * )( axisIn->nweight->data );
if ( rsmc->verbose ) {
fprintf( stderr, "%s: {rsmp, data}In = %p/%p; {rsmp, data}Out = %p/%p\n",
me, rsmpIn, dataIn, rsmpOut, dataOut );
fprintf( stderr, "%s: line = %p; indx = %p; weight = %p\n",
me, line, indx, weight );
}
/* the skinny */
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
coordIn[axIdx] = 0;
coordOut[axIdx] = 0;
}
for ( lineIdx=0; lineIdx<lineNum; lineIdx++ ) {
size_t smpIdx, dotIdx, dotLen, indexIn, indexOut;
/* calculate the ( linear ) indices of the beginnings of
the input and output scanlines */
NRRD_INDEX_GEN( indexIn, coordIn, axisIn->sizePerm, rsmc->dim );
NRRD_INDEX_GEN( indexOut, coordOut, axisOut->sizePerm, rsmc->dim );
/* read input scanline into scanline buffer */
if ( 0 == passIdx ) {
for ( smpIdx=0; smpIdx<axisIn->sizeIn; smpIdx++ ) {
line[smpIdx] = lup( dataIn, smpIdx*strideIn + indexIn );
}
} else {
for ( smpIdx=0; smpIdx<axisIn->sizeIn; smpIdx++ ) {
line[smpIdx] = rsmpIn[smpIdx*strideIn + indexIn];
}
}
/* do the bloody convolution and save the output value */
dotLen = axisIn->nweight->axis[0].size;
for ( smpIdx=0; smpIdx<axisIn->samples; smpIdx++ ) {
double val;
val = 0.0;
if ( nrrdResampleNonExistentNoop != rsmc->nonExistent ) {
double wsum;
wsum = 0.0;
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
double tmpV, tmpW;
tmpV = line[indx[dotIdx + dotLen*smpIdx]];
if ( AIR_EXISTS( tmpV ) ) {
tmpW = weight[dotIdx + dotLen*smpIdx];
val += tmpV*tmpW;
wsum += tmpW;
}
}
if ( wsum ) {
if ( nrrdResampleNonExistentRenormalize == rsmc->nonExistent ) {
val /= wsum;
}
/* else nrrdResampleNonExistentWeight: leave as is */
} else {
val = AIR_NAN;
}
} else {
/* nrrdResampleNonExistentNoop: do convolution sum
w/out worries about value existance */
for ( dotIdx=0; dotIdx<dotLen; dotIdx++ ) {
val += ( line[indx[dotIdx + dotLen*smpIdx]]
* weight[dotIdx + dotLen*smpIdx] );
}
}
if ( passIdx < rsmc->passNum-1 ) {
rsmpOut[smpIdx*strideOut + indexOut] = val;
} else {
if ( doRound ) {
val = AIR_CAST( nrrdResample_t, AIR_ROUNDUP( val ) );
}
if ( rsmc->clamp ) {
val = clamp( val );
}
ins( dataOut, smpIdx*strideOut + indexOut, val );
}
}
/* as long as there's another line to be processed, increment the
coordinates for the scanline starts. We don't use the usual
NRRD_COORD macros because we're subject to the unusual constraint
that coordIn[topRax] and coordOut[permute[topRax]] must stay == 0 */
if ( lineIdx < lineNum-1 ) {
axIdx = rsmc->topRax ? 0 : 1;
coordIn[axIdx]++;
coordOut[rsmc->permute[axIdx]]++;
while ( coordIn[axIdx] == axisIn->sizePerm[axIdx] ) {
coordIn[axIdx] = coordOut[rsmc->permute[axIdx]] = 0;
axIdx++;
axIdx += axIdx == rsmc->topRax;
coordIn[axIdx]++;
coordOut[rsmc->permute[axIdx]]++;
}
}
}
/* ( maybe ) free input to this pass, now that we're done with it */
if ( axisIn->nrsmp ) {
if ( rsmc->verbose ) {
fprintf( stderr, "%s: nrrdNuke( %p ) pass %u input ( on axis %u )\n",
me, AIR_CAST( void*, axisIn->nrsmp ), axisIn->passIdx,
axisIn->axIdx );
}
axisIn->nrsmp = nrrdNuke( axisIn->nrsmp );
/* airMopSub( mop, axisIn->nrsmp, ( airMopper )nrrdNuke ); */
}
} /* for passIdx */
airMopOkay( mop );
return 0;
}
int
_nrrdResampleOutputUpdate( NrrdResampleContext *rsmc, Nrrd *nout,
const char *func ) {
static const char me[]="_nrrdResampleOutputUpdate";
#if NRRD_RESAMPLE_FLOAT
float ( *lup )( const void *, size_t ),
( *clamp )( float ), ( *ins )( void *, size_t, float );
#else
double ( *lup )( const void *, size_t ),
( *clamp )( double ), ( *ins )( void *, size_t, double );
#endif
unsigned int axIdx;
int typeOut, doRound;
if ( rsmc->flag[flagClamp]
|| rsmc->flag[flagNonExistent]
|| rsmc->flag[flagRound]
|| rsmc->flag[flagTypeOut]
|| rsmc->flag[flagLineFill]
|| rsmc->flag[flagVectorFill]
|| rsmc->flag[flagPermutation]
|| rsmc->flag[flagInput] ) {
typeOut = ( nrrdTypeDefault == rsmc->typeOut
? rsmc->nin->type
: rsmc->typeOut );
doRound = rsmc->roundlast && nrrdTypeIsIntegral[typeOut];
if ( doRound && ( nrrdTypeInt == typeOut
|| nrrdTypeUInt == typeOut
|| nrrdTypeLLong == typeOut
|| nrrdTypeULLong == typeOut ) ) {
fprintf( stderr, "%s: WARNING: possible erroneous output with "
"rounding of %s output type due to int-based implementation "
"of rounding\n", me, airEnumStr( nrrdType, typeOut ) );
}
#if NRRD_RESAMPLE_FLOAT
lup = nrrdFLookup[rsmc->nin->type];
clamp = nrrdFClamp[typeOut];
ins = nrrdFInsert[typeOut];
#else
lup = nrrdDLookup[rsmc->nin->type];
clamp = nrrdDClamp[typeOut];
ins = nrrdDInsert[typeOut];
#endif
if ( 0 == rsmc->passNum ) {
if ( _nrrdResampleTrivial( rsmc, nout, typeOut, doRound,
lup, clamp, ins ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
} else {
if ( _nrrdResampleCore( rsmc, nout, typeOut, doRound,
lup, clamp, ins ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
}
/* HEY: need to create textual representation of resampling parameters */
if ( nrrdContentSet_va( nout, func, rsmc->nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* start work of updating space origin */
nrrdSpaceVecCopy( nout->spaceOrigin, rsmc->nin->spaceOrigin );
for ( axIdx=0; axIdx<rsmc->dim; axIdx++ ) {
if ( rsmc->axis[axIdx].kernel ) {
/* this axis was resampled */
double minIdxFull, maxIdxFull,
zeroPos; /* actually its in continuous index space ... */
_nrrdAxisInfoCopy( nout->axis + axIdx, rsmc->nin->axis + axIdx,
( NRRD_AXIS_INFO_SIZE_BIT
| NRRD_AXIS_INFO_SPACING_BIT
| NRRD_AXIS_INFO_THICKNESS_BIT
| NRRD_AXIS_INFO_MIN_BIT
| NRRD_AXIS_INFO_MAX_BIT
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT
| NRRD_AXIS_INFO_CENTER_BIT
| NRRD_AXIS_INFO_KIND_BIT ) );
/* now set all the per-axis fields we just abstained from copying */
/* size was already set */
nout->axis[axIdx].spacing = ( rsmc->nin->axis[axIdx].spacing
/ rsmc->axis[axIdx].ratio );
/* for now, we don't attempt to modify thickness */
nout->axis[axIdx].thickness = AIR_NAN;
/* We had to assume a specific centering when doing resampling */
nout->axis[axIdx].center = rsmc->axis[axIdx].center;
_nrrdResampleMinMaxFull( &minIdxFull, &maxIdxFull,
rsmc->axis[axIdx].center,
rsmc->nin->axis[axIdx].size );
nout->axis[axIdx].min = AIR_AFFINE( minIdxFull,
rsmc->axis[axIdx].min,
maxIdxFull,
rsmc->nin->axis[axIdx].min,
rsmc->nin->axis[axIdx].max );
nout->axis[axIdx].max = AIR_AFFINE( minIdxFull,
rsmc->axis[axIdx].max,
maxIdxFull,
rsmc->nin->axis[axIdx].min,
rsmc->nin->axis[axIdx].max );
nrrdSpaceVecScale( nout->axis[axIdx].spaceDirection,
1.0/rsmc->axis[axIdx].ratio,
rsmc->nin->axis[axIdx].spaceDirection );
nout->axis[axIdx].kind = _nrrdKindAltered( rsmc->nin->axis[axIdx].kind,
AIR_TRUE );
/* space origin may have translated along this axis;
only do this if the axis was already spatial */
if ( AIR_EXISTS( rsmc->nin->axis[axIdx].spaceDirection[0] ) ) {
zeroPos = NRRD_POS( nout->axis[axIdx].center,
rsmc->axis[axIdx].min,
rsmc->axis[axIdx].max,
rsmc->axis[axIdx].samples,
0 );
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, nout->spaceOrigin,
zeroPos,
rsmc->nin->axis[axIdx].spaceDirection );
}
} else {
/* no resampling; this axis totally unchanged */
_nrrdAxisInfoCopy( nout->axis + axIdx, rsmc->nin->axis + axIdx,
NRRD_AXIS_INFO_NONE );
/* also: the space origin has not translated along this axis */
}
}
if ( nrrdBasicInfoCopy( nout, rsmc->nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_SPACEORIGIN_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
rsmc->flag[flagClamp] = AIR_FALSE;
rsmc->flag[flagNonExistent] = AIR_FALSE;
rsmc->flag[flagRound] = AIR_FALSE;
rsmc->flag[flagTypeOut] = AIR_FALSE;
rsmc->flag[flagLineFill] = AIR_FALSE;
rsmc->flag[flagVectorFill] = AIR_FALSE;
rsmc->flag[flagPermutation] = AIR_FALSE;
rsmc->flag[flagInput] = AIR_FALSE;
}
return 0;
}
int
nrrdResampleExecute( NrrdResampleContext *rsmc, Nrrd *nout ) {
static const char me[]="nrrdResampleExecute", func[]="resample";
double time0;
if ( !( rsmc && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
/* any other error checking? Do we need a _nrrdResampleContextCheck( ) ? */
if ( nrrdBoundaryPad == rsmc->boundary && !AIR_EXISTS( rsmc->padValue ) ) {
biffAddf( NRRD, "%s: asked for boundary padding, "
"but no pad value set", me );
return 1;
}
time0 = airTime( );
if ( _nrrdResampleInputDimensionUpdate( rsmc )
|| _nrrdResampleInputCentersUpdate( rsmc )
|| _nrrdResampleInputSizesUpdate( rsmc )
|| _nrrdResampleLineAllocateUpdate( rsmc )
|| _nrrdResampleVectorAllocateUpdate( rsmc )
|| _nrrdResampleLineFillUpdate( rsmc )
|| _nrrdResampleVectorFillUpdate( rsmc )
|| _nrrdResamplePermutationUpdate( rsmc )
|| _nrrdResampleOutputUpdate( rsmc, nout, func ) ) {
biffAddf( NRRD, "%s: trouble resampling", me );
return 1;
}
rsmc->time = airTime( ) - time0;
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
( this was written before airMopSub ... )
learned: if you start using airMop stuff, and you register a free, but
then you free the memory yourself, YOU HAVE GOT TO register a NULL in
place of the original free. The next malloc may end up at the same
address as what you just freed, and if you want this memory to NOT be
mopped up, then you'll be confused with the original registered free
goes into effect and mops it up for you, even though YOU NEVER
REGISTERED a free for the second malloc. If you want simple stupid
tools, you have to treat them accordingly ( be extremely careful with
fire ).
learned: well, duh. The reason to use:
for ( I=0; I<numOut; I++ ) {
instead of
for ( I=0; I<=numOut-1; I++ ) {
is that if numOut is of an unsigned type and has value 0, then these
two will have very different results!
*/
int
53 nrrdSimpleResample( Nrrd *nout, const Nrrd *nin,
const NrrdKernel *kernel, const double *parm,
const size_t *samples, const double *scalings ) {
static const char me[]="nrrdSimpleResample";
NrrdResampleInfo *info;
int p, np, center;
unsigned ai;
if ( !( nout && nin && kernel && ( samples || scalings ) ) ) {
biffAddf( NRRD, "%s: not NULL pointer", me );
return 1;
}
if ( !( info = nrrdResampleInfoNew( ) ) ) {
biffAddf( NRRD, "%s: can't allocate resample info struct", me );
return 1;
}
np = kernel->numParm;
for ( ai=0; ai<nin->dim; ai++ ) {
double axmin, axmax;
info->kernel[ai] = kernel;
if ( samples ) {
info->samples[ai] = samples[ai];
} else {
center = _nrrdCenter( nin->axis[ai].center );
if ( nrrdCenterCell == center ) {
info->samples[ai] = ( size_t )( nin->axis[ai].size*scalings[ai] );
} else {
info->samples[ai] = ( size_t )( ( nin->axis[ai].size - 1 )
*scalings[ai] ) + 1;
}
}
for ( p=0; p<np; p++ )
info->parm[ai][p] = parm[p];
/* set the min/max for this axis if not already set to something */
if ( !( AIR_EXISTS( nin->axis[ai].min ) && AIR_EXISTS( nin->axis[ai].max ) ) ) {
/* HEY: started as copy/paste of body of nrrdAxisInfoMinMaxSet,
because we wanted to enable const-correctness, and this
function had previously been setting per-axis min/max in
*input* when it hasn't been already set */
double spacing;
center = _nrrdCenter2( nin->axis[ai].center, nrrdDefaultCenter );
spacing = nin->axis[ai].spacing;
if ( !AIR_EXISTS( spacing ) )
spacing = nrrdDefaultSpacing;
if ( nrrdCenterCell == center ) {
axmin = 0;
axmax = spacing*nin->axis[ai].size;
} else {
axmin = 0;
axmax = spacing*( nin->axis[ai].size - 1 );
}
} else {
axmin = nin->axis[ai].min;
axmax = nin->axis[ai].max;
}
info->min[ai] = axmin;
info->max[ai] = axmax;
}
/* we go with the defaults ( enstated by _nrrdResampleInfoInit( ) )
for all the remaining fields */
if ( nrrdSpatialResample( nout, nin, info ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
info = nrrdResampleInfoNix( info );
return 0;
}
/*
** _nrrdResampleCheckInfo( )
**
** checks validity of given NrrdResampleInfo *info:
** - all required parameters exist
** - both min[d] and max[d] for all axes d
*/
int
132 _nrrdResampleCheckInfo( const Nrrd *nin, const NrrdResampleInfo *info ) {
static const char me[] = "_nrrdResampleCheckInfo";
const NrrdKernel *k;
int center, p, np;
unsigned int ai, minsmp;
char stmp[2][AIR_STRLEN_SMALL];
if ( nrrdTypeBlock == nin->type || nrrdTypeBlock == info->type ) {
biffAddf( NRRD, "%s: can't resample to or from type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nrrdBoundaryUnknown == info->boundary ) {
biffAddf( NRRD, "%s: didn't set boundary behavior\n", me );
return 1;
}
if ( nrrdBoundaryPad == info->boundary && !AIR_EXISTS( info->padValue ) ) {
biffAddf( NRRD,
"%s: asked for boundary padding, but no pad value set\n", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
k = info->kernel[ai];
/* we only care about the axes being resampled */
if ( !k )
continue;
if ( !( info->samples[ai] > 0 ) ) {
biffAddf( NRRD, "%s: axis %d # samples ( %s ) invalid", me, ai,
airSprintSize_t( stmp[0], info->samples[ai] ) );
return 1;
}
if ( !( AIR_EXISTS( nin->axis[ai].min ) && AIR_EXISTS( nin->axis[ai].max ) ) ) {
biffAddf( NRRD, "%s: input nrrd's axis %d min, max have not "
"both been set", me, ai );
return 1;
}
if ( !( AIR_EXISTS( info->min[ai] ) && AIR_EXISTS( info->max[ai] ) ) ) {
biffAddf( NRRD, "%s: info's axis %d min, max not both set", me, ai );
return 1;
}
np = k->numParm;
for ( p=0; p<np; p++ ) {
if ( !AIR_EXISTS( info->parm[ai][p] ) ) {
biffAddf( NRRD, "%s: didn't set parameter %d ( of %d ) for axis %d\n",
me, p, np, ai );
return 1;
}
}
center = _nrrdCenter( nin->axis[ai].center );
minsmp = nrrdCenterCell == center ? 1 : 2;
if ( !( nin->axis[ai].size >= minsmp && info->samples[ai] >= minsmp ) ) {
biffAddf( NRRD, "%s: axis %d # input samples ( %s ) or output samples ( %s ) "
" invalid for %s centering", me, ai,
airSprintSize_t( stmp[0], nin->axis[ai].size ),
airSprintSize_t( stmp[1], info->samples[ai] ),
airEnumStr( nrrdCenter, center ) );
return 1;
}
}
return 0;
}
/*
** _nrrdResampleComputePermute( )
**
** figures out information related to how the axes in a nrrd are
** permuted during resampling: permute, topRax, botRax, passes, ax[][], sz[][]
*/
void
201 _nrrdResampleComputePermute( unsigned int permute[],
unsigned int ax[NRRD_DIM_MAX][NRRD_DIM_MAX],
size_t sz[NRRD_DIM_MAX][NRRD_DIM_MAX],
int *topRax,
int *botRax,
unsigned int *passes,
const Nrrd *nin,
const NrrdResampleInfo *info ) {
/* char me[]="_nrrdResampleComputePermute"; */
unsigned int bi, ai, pi;
/* what are the first ( top ) and last ( bottom ) axes being resampled? */
*topRax = *botRax = -1;
for ( ai=0; ai<nin->dim; ai++ ) {
if ( info->kernel[ai] ) {
if ( *topRax < 0 ) {
*topRax = ai;
}
*botRax = ai;
}
}
/* figure out total number of passes needed, and construct the
permute[] array. permute[i] = j means that the axis in position
i of the old array will be in position j of the new one
( permute[] answers "where do I put this", not "what do I put here" ).
*/
*passes = bi = 0;
for ( ai=0; ai<nin->dim; ai++ ) {
if ( info->kernel[ai] ) {
do {
bi = AIR_MOD( ( int )bi+1, ( int )nin->dim ); /* HEY scrutinize casts */
} while ( !info->kernel[bi] );
permute[bi] = ai;
*passes += 1;
} else {
permute[ai] = ai;
bi += bi == ai;
}
}
permute[nin->dim] = nin->dim;
if ( !*passes ) {
/* none of the kernels was non-NULL */
return;
}
/*
fprintf( stderr, "%s: permute:\n", me );
for ( d=0; d<nin->dim; d++ ) {
fprintf( stderr, " permute[%d] = %d\n", d, permute[ai] );
}
*/
/* create array of how the axes will be arranged in each pass ( "ax" ),
and create array of how big each axes is in each pass ( "sz" ).
The input to pass i will have axis layout described in ax[i] and
axis sizes described in sz[i] */
for ( ai=0; ai<nin->dim; ai++ ) {
ax[0][ai] = ai;
sz[0][ai] = nin->axis[ai].size;
}
for ( pi=0; pi<*passes; pi++ ) {
for ( ai=0; ai<nin->dim; ai++ ) {
ax[pi+1][permute[ai]] = ax[pi][ai];
if ( ai == ( unsigned int )*topRax ) { /* HEY scrutinize casts */
/* this is the axis which is getting resampled,
so the number of samples is potentially changing */
sz[pi+1][permute[ai]] = ( info->kernel[ax[pi][ai]]
? info->samples[ax[pi][ai]]
: sz[pi][ai] );
} else {
/* this axis is just a shuffled version of the
previous axis; no resampling this pass.
Note: this case also includes axes which aren't
getting resampled whatsoever */
sz[pi+1][permute[ai]] = sz[pi][ai];
}
}
}
return;
}
/*
** _nrrdResampleMakeWeightIndex( )
**
** _allocate_ and fill the arrays of indices and weights that are
** needed to process all the scanlines along a given axis; also
** be so kind as to set the sampling ratio ( <1: downsampling,
** new sample spacing larger, >1: upsampling, new sample spacing smaller )
**
** returns "dotLen", the number of input samples which are required
** for resampling this axis, or 0 if there was an error. Uses biff.
*/
int
296 _nrrdResampleMakeWeightIndex( nrrdResample_t **weightP,
int **indexP, double *ratioP,
const Nrrd *nin, const NrrdResampleInfo *info,
unsigned int ai ) {
static const char me[]="_nrrdResampleMakeWeightIndex";
int sizeIn, sizeOut, center, dotLen, halfLen, *indx, base, idx;
nrrdResample_t minIn, maxIn, minOut, maxOut, spcIn, spcOut,
ratio, support, integral, pos, idxD, wght;
nrrdResample_t *weight;
double parm[NRRD_KERNEL_PARMS_NUM];
int e, i;
if ( !( info->kernel[ai] ) ) {
biffAddf( NRRD, "%s: don't see a kernel for dimension %d", me, ai );
*weightP = NULL; *indexP = NULL; return 0;
}
center = _nrrdCenter( nin->axis[ai].center );
sizeIn = AIR_CAST( int, nin->axis[ai].size );
sizeOut = AIR_CAST( int, info->samples[ai] );
minIn = AIR_CAST( nrrdResample_t, nin->axis[ai].min );
maxIn = AIR_CAST( nrrdResample_t, nin->axis[ai].max );
minOut = AIR_CAST( nrrdResample_t, info->min[ai] );
maxOut = AIR_CAST( nrrdResample_t, info->max[ai] );
spcIn = NRRD_SPACING( center, minIn, maxIn, sizeIn );
spcOut = NRRD_SPACING( center, minOut, maxOut, sizeOut );
*ratioP = ratio = spcIn/spcOut;
support = AIR_CAST( nrrdResample_t,
info->kernel[ai]->support( info->parm[ai] ) );
integral = AIR_CAST( nrrdResample_t,
info->kernel[ai]->integral( info->parm[ai] ) );
/*
fprintf( stderr,
"!%s( %d ): size{In, Out} = %d, %d, support = %f; ratio = %f\n",
me, d, sizeIn, sizeOut, support, ratio );
*/
if ( ratio > 1 ) {
/* if upsampling, we need only as many samples as needed for
interpolation with the given kernel */
dotLen = ( int )( 2*ceil( support ) );
} else {
/* if downsampling, we need to use all the samples covered by
the stretched out version of the kernel */
if ( info->cheap ) {
dotLen = ( int )( 2*ceil( support ) );
} else {
dotLen = ( int )( 2*ceil( support/ratio ) );
}
}
/*
fprintf( stderr, "!%s( %d ): dotLen = %d\n", me, d, dotLen );
*/
weight = AIR_CALLOC( sizeOut*dotLen, nrrdResample_t );
if ( !weight ) {
biffAddf( NRRD, "%s: can't allocate weight array", me );
*weightP = NULL; *indexP = NULL; return 0;
}
indx = AIR_CALLOC( sizeOut*dotLen, int );
if ( !indx ) {
biffAddf( NRRD, "%s: can't allocate index arrays", me );
*weightP = NULL; *indexP = NULL; return 0;
}
/* calculate sample locations and do first pass on indices */
halfLen = dotLen/2;
for ( i=0; i<sizeOut; i++ ) {
pos = AIR_CAST( nrrdResample_t,
NRRD_POS( center, minOut, maxOut, sizeOut, i ) );
idxD = AIR_CAST( nrrdResample_t,
NRRD_IDX( center, minIn, maxIn, sizeIn, pos ) );
base = ( int )floor( idxD ) - halfLen + 1;
for ( e=0; e<dotLen; e++ ) {
indx[e + dotLen*i] = base + e;
weight[e + dotLen*i] = idxD - indx[e + dotLen*i];
}
/* ********
if ( !i ) {
fprintf( stderr, "%s: sample locations:\n", me );
}
fprintf( stderr, "%s: %d ( sample locations )\n ", me, i );
for ( e=0; e<dotLen; e++ ) {
fprintf( stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i] );
}
fprintf( stderr, "\n" );
******** */
}
/* figure out what to do with the out-of-range indices */
for ( i=0; i<dotLen*sizeOut; i++ ) {
idx = indx[i];
if ( !AIR_IN_CL( 0, idx, sizeIn-1 ) ) {
switch( info->boundary ) {
case nrrdBoundaryPad:
case nrrdBoundaryWeight: /* this will be further handled later */
idx = sizeIn;
break;
case nrrdBoundaryBleed:
idx = AIR_CLAMP( 0, idx, sizeIn-1 );
break;
case nrrdBoundaryWrap:
idx = AIR_MOD( idx, sizeIn );
break;
case nrrdBoundaryMirror:
idx = _nrrdMirror_32( sizeIn, idx );
break;
default:
biffAddf( NRRD, "%s: boundary behavior %d unknown/unimplemented",
me, info->boundary );
*weightP = NULL; *indexP = NULL; return 0;
}
indx[i] = idx;
}
}
/* run the sample locations through the chosen kernel. We play a
sneaky trick on the kernel parameter 0 in case of downsampling
to create the blurring of the old index space, but only if !cheap */
memcpy( parm, info->parm[ai], NRRD_KERNEL_PARMS_NUM*sizeof( double ) );
if ( ratio < 1 && !( info->cheap ) ) {
parm[0] /= ratio;
}
info->kernel[ai]->EVALN( weight, weight, dotLen*sizeOut, parm );
/* ********
for ( i=0; i<sizeOut; i++ ) {
fprintf( stderr, "%s: %d ( sample weights )\n ", me, i );
for ( e=0; e<dotLen; e++ ) {
fprintf( stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i] );
}
fprintf( stderr, "\n" );
}
******** */
if ( nrrdBoundaryWeight == info->boundary ) {
if ( integral ) {
/* above, we set to sizeIn all the indices that were out of
range. We now use that to determine the sum of the weights
for the indices that were in-range */
for ( i=0; i<sizeOut; i++ ) {
wght = 0;
for ( e=0; e<dotLen; e++ ) {
if ( sizeIn != indx[e + dotLen*i] ) {
wght += weight[e + dotLen*i];
}
}
for ( e=0; e<dotLen; e++ ) {
idx = indx[e + dotLen*i];
if ( sizeIn != idx ) {
weight[e + dotLen*i] *= integral/wght;
} else {
weight[e + dotLen*i] = 0;
}
}
}
}
} else {
/* try to remove ripple/grating on downsampling */
/* if ( ratio < 1 && info->renormalize && integral ) { */
if ( info->renormalize && integral ) {
for ( i=0; i<sizeOut; i++ ) {
wght = 0;
for ( e=0; e<dotLen; e++ ) {
wght += weight[e + dotLen*i];
}
if ( wght ) {
for ( e=0; e<dotLen; e++ ) {
/* this used to normalize the weights so that they summed
to integral ( "*= integral/wght" ), which meant that if
you use a very truncated Gaussian, then your over-all
image brightness goes down. This seems very contrary
to the whole point of renormalization. */
weight[e + dotLen*i] *= AIR_CAST( nrrdResample_t, 1.0/wght );
}
}
}
}
}
/* ********
fprintf( stderr, "%s: sample weights:\n", me );
for ( i=0; i<sizeOut; i++ ) {
fprintf( stderr, "%s: %d\n ", me, i );
wght = 0;
for ( e=0; e<dotLen; e++ ) {
fprintf( stderr, "%d/%g ", indx[e + dotLen*i], weight[e + dotLen*i] );
wght += weight[e + dotLen*i];
}
fprintf( stderr, " ( sum = %g )\n", wght );
}
******** */
*weightP = weight;
*indexP = indx;
/*
fprintf( stderr, "!%s: dotLen = %d\n", me, dotLen );
*/
return dotLen;
}
/*
******** nrrdSpatialResample( )
**
** general-purpose array-resampler: resamples a nrrd of any type
** ( except block ) and any dimension along any or all of its axes, with
** any combination of up- or down-sampling along the axes, with any
** kernel ( specified by callback ), with potentially a different kernel
** for each axis. Whether or not to resample along axis d is
** controlled by the non-NULL-ity of info->kernel[ai]. Where to sample
** on the axis is controlled by info->min[ai] and info->max[ai]; these
** specify a range of "positions" aka "world space" positions, as
** determined by the per-axis min and max of the input nrrd, which must
** be set for every resampled axis.
**
** we cyclically permute those axes being resampled, and never touch
** the position ( in axis ordering ) of axes along which we are not
** resampling. This strategy is certainly not the most intelligent
** one possible, but it does mean that the axis along which we're
** currently resampling-- the one along which we'll have to look at
** multiple adjecent samples-- is that resampling axis which is
** currently most contiguous in memory. It may make sense to precede
** the resampling with an axis permutation which bubbles all the
** resampled axes to the front ( most contiguous ) end of the axis list,
** and then puts them back in place afterwards, depending on the cost
** of such axis permutation overhead.
*/
int
523 nrrdSpatialResample( Nrrd *nout, const Nrrd *nin,
const NrrdResampleInfo *info ) {
static const char me[]="nrrdSpatialResample", func[]="resample";
nrrdResample_t
*array[NRRD_DIM_MAX], /* intermediate copies of the input data
undergoing resampling; we don't need a full-
fledged nrrd for these. Only about two of
these arrays will be allocated at a time;
intermediate results will be free( )d when not
needed */
*_inVec, /* current input vector being resampled;
not necessarily contiguous in memory
( if strideIn != 1 ) */
*inVec, /* buffer for input vector; contiguous */
*_outVec; /* output vector in context of volume;
never contiguous */
double tmpF;
double ratio, /* factor by which or up or downsampled */
ratios[NRRD_DIM_MAX]; /* record of "ratio" for all resampled axes,
used to compute new spacing in output */
Nrrd *floatNin; /* if the input nrrd type is not nrrdResample_t,
then we convert it and keep it here */
unsigned int ai,
pi, /* current pass */
topLax,
permute[NRRD_DIM_MAX], /* how to permute axes of last pass to get
axes for current pass */
ax[NRRD_DIM_MAX+1][NRRD_DIM_MAX], /* axis ordering on each pass */
passes; /* # of passes needed to resample all axes */
int i, s, e,
topRax, /* the lowest index of an axis which is
resampled. If all axes are being resampled,
then this is 0. If for some reason the
"x" axis ( fastest stride ) is not being
resampled, but "y" is, then topRax is 1 */
botRax, /* index of highest axis being resampled */
typeIn, typeOut; /* types of input and output of resampling */
size_t sz[NRRD_DIM_MAX+1][NRRD_DIM_MAX];
/* how many samples along each
axis, changing on each pass */
/* all these variables have to do with the spacing of elements in
memory for the current pass of resampling, and they ( except
strideIn ) are re-set at the beginning of each pass */
nrrdResample_t
*weight; /* sample weights */
unsigned int ci[NRRD_DIM_MAX+1],
co[NRRD_DIM_MAX+1];
int
sizeIn, sizeOut, /* lengths of input and output vectors */
dotLen, /* # input samples to dot with weights to get
one output sample */
doRound, /* actually do rounding on output: we DO NOT
round when info->round but the output
type is not integral */
*indx; /* dotLen*sizeOut 2D array of input indices */
size_t
I, /* swiss-army int */
strideIn, /* the stride between samples in the input
"scanline" being resampled */
strideOut, /* stride between samples in output
"scanline" from resampling */
L, LI, LO, numLines, /* top secret */
numOut; /* # of _samples_, total, in output volume;
this is for allocating the output */
airArray *mop; /* for cleaning up */
if ( !( nout && nin && info ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdBoundaryUnknown == info->boundary ) {
biffAddf( NRRD, "%s: need to specify a boundary behavior", me );
return 1;
}
typeIn = nin->type;
typeOut = nrrdTypeDefault == info->type ? typeIn : info->type;
if ( _nrrdResampleCheckInfo( nin, info ) ) {
biffAddf( NRRD, "%s: problem with arguments", me );
return 1;
}
_nrrdResampleComputePermute( permute, ax, sz,
&topRax, &botRax, &passes,
nin, info );
topLax = topRax ? 0 : 1;
/* not sure where else to put this:
( want to put it before 0 == passes branch )
We have to assume some centering when doing resampling, and it would
be stupid to not record it in the outgoing nrrd, since the value of
nrrdDefaultCenter could always change. */
for ( ai=0; ai<nin->dim; ai++ ) {
if ( info->kernel[ai] ) {
nout->axis[ai].center = _nrrdCenter( nin->axis[ai].center );
}
}
if ( 0 == passes ) {
/* actually, no resampling was desired. Copy input to output,
but with the clamping that we normally do at the end of resampling */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, sz[0] );
if ( nrrdMaybeAlloc_nva( nout, typeOut, nin->dim, sz[0] ) ) {
biffAddf( NRRD, "%s: couldn't allocate output", me );
return 1;
}
numOut = nrrdElementNumber( nout );
for ( I=0; I<numOut; I++ ) {
tmpF = nrrdDLookup[nin->type]( nin->data, I );
tmpF = nrrdDClamp[typeOut]( tmpF );
nrrdDInsert[typeOut]( nout->data, I, tmpF );
}
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
/* HEY: need to create textual representation of resampling parameters */
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
mop = airMopNew( );
/* convert input nrrd to nrrdResample_t if necessary */
if ( nrrdResample_nrrdType != typeIn ) {
if ( nrrdConvert( floatNin = nrrdNew( ), nin, nrrdResample_nrrdType ) ) {
biffAddf( NRRD, "%s: couldn't create float copy of input", me );
airMopError( mop ); return 1;
}
array[0] = ( nrrdResample_t* )floatNin->data;
airMopAdd( mop, floatNin, ( airMopper )nrrdNuke, airMopAlways );
} else {
floatNin = NULL;
array[0] = ( nrrdResample_t* )nin->data;
}
/* compute strideIn; this is actually the same for every pass
because ( strictly speaking ) in every pass we are resampling
the same axis, and axes with lower indices are constant length */
strideIn = 1;
for ( ai=0; ai<( unsigned int )topRax; ai++ ) { /* HEY scrutinize casts */
strideIn *= nin->axis[ai].size;
}
/* printf( "%s: strideIn = " _AIR_SIZE_T_CNV "\n", me, strideIn ); */
/* go! */
for ( pi=0; pi<passes; pi++ ) {
/*
printf( "%s: --- pass %d --- \n", me, pi );
*/
numLines = strideOut = 1;
for ( ai=0; ai<nin->dim; ai++ ) {
if ( ai < ( unsigned int )botRax ) { /* HEY scrutinize cast */
strideOut *= sz[pi+1][ai];
}
if ( ai != ( unsigned int )topRax ) { /* HEY scrutinize cast */
numLines *= sz[pi][ai];
}
}
sizeIn = AIR_CAST( int, sz[pi][topRax] );
sizeOut = AIR_CAST( int, sz[pi+1][botRax] );
numOut = numLines*sizeOut;
/* for the rest of the loop body, d is the original "dimension"
for the axis being resampled */
ai = ax[pi][topRax];
/* printf( "%s( %d ): numOut = " _AIR_SIZE_T_CNV "\n", me, pi, numOut ); */
/* printf( "%s( %d ): numLines = " _AIR_SIZE_T_CNV "\n", me, pi, numLines ); */
/* printf( "%s( %d ): stride: In=%d, Out=%d\n", me, pi, */
/* ( int )strideIn, ( int )strideOut ); */
/* printf( "%s( %d ): sizeIn = %d\n", me, pi, sizeIn ); */
/* printf( "%s( %d ): sizeOut = %d\n", me, pi, sizeOut ); */
/* we can free the input to the previous pass
( if its not the given data ) */
if ( pi > 0 ) {
if ( pi == 1 ) {
if ( array[0] != nin->data ) {
airMopSub( mop, floatNin, ( airMopper )nrrdNuke );
floatNin = nrrdNuke( floatNin );
array[0] = NULL;
/*
printf( "%s: pi %d: freeing array[0]\n", me, pi );
*/
}
} else {
airMopSub( mop, array[pi-1], airFree );
array[pi-1] = ( nrrdResample_t* )airFree( array[pi-1] );
/*
printf( "%s: pi %d: freeing array[%d]\n", me, pi, pi-1 );
*/
}
}
/* allocate output volume */
array[pi+1] = ( nrrdResample_t* )calloc( numOut, sizeof( nrrdResample_t ) );
if ( !array[pi+1] ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( NRRD, "%s: couldn't create array of %s nrrdResample_t's for "
"output of pass %d", me, airSprintSize_t( stmp, numOut ), pi );
airMopError( mop ); return 1;
}
airMopAdd( mop, array[pi+1], airFree, airMopAlways );
/*
printf( "%s: allocated array[%d]\n", me, pi+1 );
*/
/* allocate contiguous input scanline buffer, we alloc one more
than needed to provide a place for the pad value. That is, in
fact, the over-riding reason to copy a scanline to a local
array: so that there is a simple consistent ( non-branchy ) way
to incorporate the pad values */
inVec = ( nrrdResample_t * )calloc( sizeIn+1, sizeof( nrrdResample_t ) );
airMopAdd( mop, inVec, airFree, airMopAlways );
inVec[sizeIn] = AIR_CAST( nrrdResample_t, info->padValue );
dotLen = _nrrdResampleMakeWeightIndex( &weight, &indx, &ratio,
nin, info, ai );
if ( !dotLen ) {
biffAddf( NRRD, "%s: trouble creating weight and index vector arrays",
me );
airMopError( mop ); return 1;
}
ratios[ai] = ratio;
airMopAdd( mop, weight, airFree, airMopAlways );
airMopAdd( mop, indx, airFree, airMopAlways );
/* the skinny: resample all the scanlines */
_inVec = array[pi];
_outVec = array[pi+1];
memset( ci, 0, ( NRRD_DIM_MAX+1 )*sizeof( int ) );
memset( co, 0, ( NRRD_DIM_MAX+1 )*sizeof( int ) );
for ( L=0; L<numLines; L++ ) {
/* calculate the index to get to input and output scanlines,
according the coordinates of the start of the scanline */
NRRD_INDEX_GEN( LI, ci, sz[pi], nin->dim );
NRRD_INDEX_GEN( LO, co, sz[pi+1], nin->dim );
_inVec = array[pi] + LI;
_outVec = array[pi+1] + LO;
/* read input scanline into contiguous array */
for ( i=0; i<sizeIn; i++ ) {
inVec[i] = _inVec[i*strideIn];
}
/* do the weighting */
for ( i=0; i<sizeOut; i++ ) {
tmpF = 0.0;
/*
fprintf( stderr, "%s: i = %d ( tmpF=0 )\n", me, ( int )i );
*/
for ( s=0; s<dotLen; s++ ) {
tmpF += inVec[indx[s + dotLen*i]]*weight[s + dotLen*i];
/*
fprintf( stderr, " tmpF += %g*%g == %g\n",
inVec[indx[s + dotLen*i]], weight[s + dotLen*i], tmpF );
*/
}
_outVec[i*strideOut] = tmpF;
/*
fprintf( stderr, "--> out[%d] = %g\n",
i*strideOut, _outVec[i*strideOut] );
*/
}
/* update the coordinates for the scanline starts. We don't
use the usual NRRD_COORD macros because we're subject to
the unusual constraint that ci[topRax] and co[permute[topRax]]
must stay exactly zero */
e = topLax;
ci[e]++;
co[permute[e]]++;
while ( L < numLines-1 && ci[e] == sz[pi][e] ) {
ci[e] = co[permute[e]] = 0;
e++;
e += e == topRax;
ci[e]++;
co[permute[e]]++;
}
}
/* pass-specific clean up */
airMopSub( mop, weight, airFree );
airMopSub( mop, indx, airFree );
airMopSub( mop, inVec, airFree );
weight = ( nrrdResample_t* )airFree( weight );
indx = ( int* )airFree( indx );
inVec = ( nrrdResample_t* )airFree( inVec );
}
/* clean up second-to-last array and scanline buffers */
if ( passes > 1 ) {
airMopSub( mop, array[passes-1], airFree );
array[passes-1] = ( nrrdResample_t* )airFree( array[passes-1] );
/*
printf( "%s: now freeing array[%d]\n", me, passes-1 );
*/
} else if ( array[passes-1] != nin->data ) {
airMopSub( mop, floatNin, ( airMopper )nrrdNuke );
floatNin = nrrdNuke( floatNin );
}
array[passes-1] = NULL;
/* create output nrrd and set axis info */
if ( nrrdMaybeAlloc_nva( nout, typeOut, nin->dim, sz[passes] ) ) {
biffAddf( NRRD, "%s: couldn't allocate final output nrrd", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopOnError );
nrrdAxisInfoCopy( nout, nin, NULL,
( NRRD_AXIS_INFO_SIZE_BIT
| NRRD_AXIS_INFO_MIN_BIT
| NRRD_AXIS_INFO_MAX_BIT
| NRRD_AXIS_INFO_SPACING_BIT
| NRRD_AXIS_INFO_SPACEDIRECTION_BIT /* see below */
| NRRD_AXIS_INFO_THICKNESS_BIT
| NRRD_AXIS_INFO_KIND_BIT ) );
for ( ai=0; ai<nin->dim; ai++ ) {
if ( info->kernel[ai] ) {
/* we do resample this axis */
nout->axis[ai].spacing = nin->axis[ai].spacing/ratios[ai];
/* no way to usefully update thickness: we could be doing blurring
but maintaining the number of samples: thickness increases, or
we could be downsampling, in which the relationship between the
sampled and the skipped regions of space becomes complicated:
no single scalar can represent it, or we could be upsampling,
in which the notion of "skip" could be rendered meaningless */
nout->axis[ai].thickness = AIR_NAN;
nout->axis[ai].min = info->min[ai];
nout->axis[ai].max = info->max[ai];
/*
HEY: this is currently a bug: all this code was written long
before there were space directions, so min/max are always
set, regardless of whethere there are incoming space directions
which then disallows output space directions on the same axes
_nrrdSpaceVecScale( nout->axis[ai].spaceDirection,
1.0/ratios[ai], nin->axis[ai].spaceDirection );
*/
nout->axis[ai].kind = _nrrdKindAltered( nin->axis[ai].kind, AIR_TRUE );
} else {
/* this axis remains untouched */
nout->axis[ai].min = nin->axis[ai].min;
nout->axis[ai].max = nin->axis[ai].max;
nout->axis[ai].spacing = nin->axis[ai].spacing;
nout->axis[ai].thickness = nin->axis[ai].thickness;
nout->axis[ai].kind = nin->axis[ai].kind;
}
}
/* HEY: need to create textual representation of resampling parameters */
if ( nrrdContentSet_va( nout, func, nin, "" ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* copy the resampling final result into the output nrrd, maybe
rounding as we go to make sure that 254.9999 is saved as 255
in uchar output, and maybe clamping as we go to insure that
integral results don't have unexpected wrap-around. */
if ( info->round ) {
if ( nrrdTypeInt == typeOut ||
nrrdTypeUInt == typeOut ||
nrrdTypeLLong == typeOut ||
nrrdTypeULLong == typeOut ) {
fprintf( stderr, "%s: WARNING: possible erroneous output with "
"rounding of %s output type due to int-based implementation "
"of rounding\n", me, airEnumStr( nrrdType, typeOut ) );
}
doRound = nrrdTypeIsIntegral[typeOut];
} else {
doRound = AIR_FALSE;
}
numOut = nrrdElementNumber( nout );
for ( I=0; I<numOut; I++ ) {
tmpF = array[passes][I];
if ( doRound ) {
tmpF = AIR_CAST( nrrdResample_t, AIR_ROUNDUP( tmpF ) );
}
if ( info->clamp ) {
tmpF = nrrdDClamp[typeOut]( tmpF );
}
nrrdDInsert[typeOut]( nout->data, I, tmpF );
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* enough already */
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2015, 2014, 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
#include <limits.h>
/* The "/ *Teem:" ( without space ) comments in here are an experiment */
const char *
nrrdBiffKey = "nrrd";
/*
******** nrrdSpaceDimension
**
** returns expected dimension of given space ( from nrrdSpace* enum ), or,
** 0 if there is no expected dimension.
**
** The expected behavior here is to return 0 for nrrdSpaceUnknown, because
** that is the right answer, not because its an error of any kind.
*/
unsigned int
44 nrrdSpaceDimension( int space ) {
static const char me[]="nrrdSpaceDimension";
unsigned int ret;
if ( !( AIR_IN_OP( nrrdSpaceUnknown, space, nrrdSpaceLast ) ) ) {
/* they gave us invalid or unknown space */
return 0;
}
switch ( space ) {
case nrrdSpaceRightUp:
case nrrdSpaceRightDown:
ret = 2;
break;
case nrrdSpaceRightAnteriorSuperior:
case nrrdSpaceLeftAnteriorSuperior:
case nrrdSpaceLeftPosteriorSuperior:
case nrrdSpaceScannerXYZ:
case nrrdSpace3DRightHanded:
case nrrdSpace3DLeftHanded:
ret = 3;
break;
case nrrdSpaceRightAnteriorSuperiorTime:
case nrrdSpaceLeftAnteriorSuperiorTime:
case nrrdSpaceLeftPosteriorSuperiorTime:
case nrrdSpaceScannerXYZTime:
case nrrdSpace3DRightHandedTime:
case nrrdSpace3DLeftHandedTime:
ret = 4;
break;
default:
fprintf( stderr, "%s: PANIC: nrrdSpace %d not implemented!\n", me, space );
ret = UINT_MAX; /* exit( 1 ); */
break;
}
return ret;
}
/*
******** nrrdSpaceSet
**
** What to use to set space, when a value from nrrdSpace enum is known,
** or, to nullify all space-related information when passed nrrdSpaceUnknown
*/
int
88 nrrdSpaceSet( Nrrd *nrrd, int space ) {
static const char me[]="nrrdSpaceSet";
unsigned axi, saxi;
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdSpaceUnknown == space ) {
nrrd->space = nrrdSpaceUnknown;
nrrd->spaceDim = 0;
for ( axi=0; axi<NRRD_DIM_MAX; axi++ ) {
nrrdSpaceVecSetNaN( nrrd->axis[axi].spaceDirection );
}
for ( saxi=0; saxi<NRRD_SPACE_DIM_MAX; saxi++ ) {
airFree( nrrd->spaceUnits[saxi] );
nrrd->spaceUnits[saxi] = NULL;
}
nrrdSpaceVecSetNaN( nrrd->spaceOrigin );
} else {
if ( airEnumValCheck( nrrdSpace, space ) ) {
biffAddf( NRRD, "%s: given space ( %d ) not valid", me, space );
return 1;
}
nrrd->space = space;
nrrd->spaceDim = nrrdSpaceDimension( space );
}
return 0;
}
/*
******** nrrdSpaceDimensionSet
**
** What to use to set space, based on spaceDim alone ( nrrd->space set to
** nrrdSpaceUnknown )
*/
int
125 nrrdSpaceDimensionSet( Nrrd *nrrd, unsigned int spaceDim ) {
static const char me[]="nrrdSpaceDimensionSet";
if ( !nrrd ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( spaceDim <= NRRD_SPACE_DIM_MAX ) ) {
biffAddf( NRRD, "%s: given spaceDim ( %u ) not valid", me, spaceDim );
return 1;
}
nrrd->space = nrrdSpaceUnknown;
nrrd->spaceDim = spaceDim;
return 0;
}
/*
******** nrrdSpaceOriginGet
**
** retrieves the spaceOrigin from given nrrd, and returns spaceDim
** Indices 0 through spaceDim-1 are set in given vector[] to coords
** of space origin, and all further indices are set to NaN. That is,
** this really does write to all NRRD_SPACE_DIM_MAX elements of vector[]
*/
unsigned int
150 nrrdSpaceOriginGet( const Nrrd *nrrd,
double vector[NRRD_SPACE_DIM_MAX] ) {
unsigned int sdi, ret;
if ( nrrd && vector ) {
for ( sdi=0; sdi<nrrd->spaceDim; sdi++ ) {
vector[sdi] = nrrd->spaceOrigin[sdi];
}
for ( sdi=nrrd->spaceDim; sdi<NRRD_SPACE_DIM_MAX; sdi++ ) {
vector[sdi] = AIR_NAN;
}
ret = nrrd->spaceDim;
} else {
ret = 0;
}
return ret;
}
/*
******** nrrdSpaceOriginSet
**
** convenience function for setting spaceOrigin.
** Note: space ( or spaceDim ) must be already set.
** The given "vector" is only read for spaceDim elements
**
** returns 1 if there were problems, 0 otherwise
*/
int
178 nrrdSpaceOriginSet( Nrrd *nrrd, const double *vector ) {
static const char me[]="nrrdSpaceOriginSet";
unsigned int sdi;
if ( !( nrrd && vector ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( 0 < nrrd->spaceDim && nrrd->spaceDim <= NRRD_SPACE_DIM_MAX ) ) {
biffAddf( NRRD, "%s: set spaceDim %d not valid", me, nrrd->spaceDim );
return 1;
}
for ( sdi=0; sdi<nrrd->spaceDim; sdi++ ) {
nrrd->spaceOrigin[sdi] = vector[sdi];
}
for ( sdi=nrrd->spaceDim; sdi<NRRD_SPACE_DIM_MAX; sdi++ ) {
nrrd->spaceOrigin[sdi] = AIR_NAN;
}
return 0;
}
/*
******** nrrdOriginCalculate
**
** makes an effort to calculate something like an "origin" ( as in
** nrrd->spaceOrigin ) from the per-axis min, max, or spacing, when
** there is no real space information. Like the spaceOrigin, this
** location is supposed to be THE CENTER of the first sample. To
** avoid making assumptions about the nrrd or the caller, a default
** sample centering ( defaultCenter ) has to be provided ( use either
** nrrdCenterNode or nrrdCenterCell ). The axes that are used
** for the origin calculation have to be given explicitly- but they
** are likely the return of nrrdDomainAxesGet
**
** The computed origin is put into the given vector ( origin ). The return
** value takes on values from the nrrdOriginStatus* enum:
**
** nrrdOriginStatusUnknown: invalid arguments ( e.g. NULL pointer, or
** axis values out of range )
**
** nrrdOriginStatusDirection: the chosen axes have spaceDirection set,
** which means caller should instead be using
** nrrdSpaceOriginGet
**
** nrrdOriginStatusNoMin: can't compute "origin" without axis->min
**
** nrrdOriginStatusNoMaxOrSpacing: can't compute origin without ( axis->min
** and ) either axis->max or axis->spacing
**
** nrrdOriginStatusOkay: all is well
*/
int
231 nrrdOriginCalculate( const Nrrd *nrrd,
unsigned int *axisIdx, unsigned int axisIdxNum,
int defaultCenter, double *origin ) {
const NrrdAxisInfo *axis[NRRD_SPACE_DIM_MAX];
int center, okay, gotSpace, gotMin, gotMaxOrSpacing;
unsigned int ai;
double min, spacing;
#define ERROR \
if ( origin ) { \
for ( ai=0; ai<axisIdxNum; ai++ ) { \
origin[ai] = AIR_NAN; \
} \
}
if ( !( nrrd
&& ( nrrdCenterCell == defaultCenter
|| nrrdCenterNode == defaultCenter )
&& origin ) ) {
ERROR;
return nrrdOriginStatusUnknown;
}
okay = AIR_TRUE;
for ( ai=0; ai<axisIdxNum; ai++ ) {
okay &= axisIdx[ai] < nrrd->dim;
}
if ( !okay ) {
ERROR;
return nrrdOriginStatusUnknown;
}
/* learn axisInfo pointers */
for ( ai=0; ai<axisIdxNum; ai++ ) {
axis[ai] = nrrd->axis + axisIdx[ai];
}
gotSpace = AIR_FALSE;
for ( ai=0; ai<axisIdxNum; ai++ ) {
gotSpace |= AIR_EXISTS( axis[ai]->spaceDirection[0] );
}
if ( nrrd->spaceDim > 0 && gotSpace ) {
ERROR;
return nrrdOriginStatusDirection;
}
gotMin = AIR_TRUE;
for ( ai=0; ai<axisIdxNum; ai++ ) {
gotMin &= AIR_EXISTS( axis[0]->min );
}
if ( !gotMin ) {
ERROR;
return nrrdOriginStatusNoMin;
}
gotMaxOrSpacing = AIR_TRUE;
for ( ai=0; ai<axisIdxNum; ai++ ) {
gotMaxOrSpacing &= ( AIR_EXISTS( axis[ai]->max )
|| AIR_EXISTS( axis[ai]->spacing ) );
}
if ( !gotMaxOrSpacing ) {
ERROR;
return nrrdOriginStatusNoMaxOrSpacing;
}
for ( ai=0; ai<axisIdxNum; ai++ ) {
size_t size;
double denom;
size = axis[ai]->size;
min = axis[ai]->min;
center = ( nrrdCenterUnknown != axis[ai]->center
? axis[ai]->center
: defaultCenter );
denom = AIR_CAST( double, nrrdCenterCell == center ? size : size-1 );
spacing = ( AIR_EXISTS( axis[ai]->spacing )
? axis[ai]->spacing
: ( axis[ai]->max - min )/denom );
origin[ai] = min + ( nrrdCenterCell == center ? spacing/2 : 0 );
309 }
return nrrdOriginStatusOkay;
}
void
nrrdSpaceVecCopy( double dst[NRRD_SPACE_DIM_MAX],
const double src[NRRD_SPACE_DIM_MAX] ) {
unsigned int ii;
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
dst[ii] = src[ii];
}
}
/*
** NOTE: since this was created until Wed Sep 21 13:34:17 EDT 2005,
** nrrdSpaceVecScaleAdd2 and nrrdSpaceVecScale would treat a
** non-existent vector coefficient as 0.0. The reason for this had
** to do with how the function is used. For example, in nrrdCrop
**
** _nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
** for ( ai=0; ai<nin->dim; ai++ ) {
** _nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
** 1.0, nout->spaceOrigin,
** min[ai], nin->axis[ai].spaceDirection );
** }
**
** but the problem with this is that non-spatial axes end up clobbering
** the existance of otherwise existing spaceOrigin and spaceDirections.
** It was decided, however, that this logic should be outside the
339 ** arithmetic functions below, not inside. NOTE: the old functionality
** is stuck in ITK 2.2, via NrrdIO.
*/
void
nrrdSpaceVecScaleAdd2( double sum[NRRD_SPACE_DIM_MAX],
double sclA, const double vecA[NRRD_SPACE_DIM_MAX],
double sclB, const double vecB[NRRD_SPACE_DIM_MAX] ) {
unsigned int ii;
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
350 sum[ii] = sclA*vecA[ii] + sclB*vecB[ii];
}
}
void
nrrdSpaceVecScale( double out[NRRD_SPACE_DIM_MAX],
double scl, const double vec[NRRD_SPACE_DIM_MAX] ) {
unsigned int ii;
for ( ii=0; ii<NRRD_SPACE_DIM_MAX; ii++ ) {
360 out[ii] = scl*vec[ii];
}
}
double
nrrdSpaceVecNorm( unsigned int sdim, const double vec[NRRD_SPACE_DIM_MAX] ) {
unsigned int di;
double nn;
nn = 0;
for ( di=0; di<sdim; di++ ) {
nn += vec[di]*vec[di];
372 }
return sqrt( nn );
}
void
nrrdSpaceVecSetNaN( double vec[NRRD_SPACE_DIM_MAX] ) {
unsigned int di;
for ( di=0; di<NRRD_SPACE_DIM_MAX; di++ ) {
vec[di] = AIR_NAN;
}
return;
384 }
/* ---- BEGIN non-NrrdIO */
void
nrrdSpaceVecSetZero( double vec[NRRD_SPACE_DIM_MAX] ) {
int di;
for ( di=0; di<NRRD_SPACE_DIM_MAX; di++ ) {
vec[di] = 0;
}
return;
}
/* ---- END non-NrrdIO */
/*
** _nrrdContentGet
401 **
** ALLOCATES a string for the content of a given nrrd
** panics if allocation failed
*/
char *
_nrrdContentGet( const Nrrd *nin ) {
static const char me[]="_nrrdContentGet";
char *ret;
ret = ( ( nin && nin->content )
? airStrdup( nin->content )
: airStrdup( nrrdStateUnknownContent ) );
if ( !ret ) {
fprintf( stderr, "%s: PANIC: content strdup failed!\n", me );
return NULL;
416 }
return ret;
}
int
_nrrdContentSet_nva( Nrrd *nout, const char *func,
char *content, const char *format, va_list arg ) {
static const char me[]="_nrrdContentSet_nva";
char *buff;
if ( nrrdStateDisableContent ) {
/* we kill content always */
nout->content = ( char * )airFree( nout->content );
return 0;
}
buff = ( char * )malloc( 128*AIR_STRLEN_HUGE );
if ( !buff ) {
biffAddf( NRRD, "%s: couln't alloc buffer!", me );
return 1;
}
nout->content = ( char * )airFree( nout->content );
/* we are currently praying that this won't overflow the "buff" array */
/* HEY: replace with vsnprintf or whatever when its available */
vsprintf( buff, format, arg );
nout->content = ( char * )calloc( strlen( "( , )" )
+ airStrlen( func )
+ 1 /* '( ' */
+ airStrlen( content )
+ 1 /* ', ' */
+ airStrlen( buff )
+ 1 /* ' )' */
+ 1, sizeof( char ) ); /* '\0' */
if ( !nout->content ) {
biffAddf( NRRD, "%s: couln't alloc output content!", me );
airFree( buff ); return 1;
}
sprintf( nout->content, "%s( %s%s%s )", func, content,
airStrlen( buff ) ? ", " : "", buff );
456 airFree( buff ); /* no NULL assignment, else compile warnings */
return 0;
}
int
_nrrdContentSet_va( Nrrd *nout, const char *func,
char *content, const char *format, ... ) {
static const char me[]="_nrrdContentSet_va";
va_list ap;
va_start( ap, format );
if ( _nrrdContentSet_nva( nout, func, content, format, ap ) ) {
biffAddf( NRRD, "%s:", me );
free( content ); return 1;
}
va_end( ap );
/* free( content ); */
return 0;
}
/*
******** nrrdContentSet
**
** Kind of like sprintf, but for the content string of the nrrd.
**
** Whether or not we write a new content for an old nrrd ( "nin" ) with
** NULL content is decided here, according to
** nrrdStateAlwaysSetContent.
485 **
** Does the string allocation and some attempts at error detection.
** Does allow nout==nin, which requires some care.
*/
int
nrrdContentSet_va( Nrrd *nout, const char *func,
const Nrrd *nin, const char *format, ... ) {
static const char me[]="nrrdContentSet_va";
va_list ap;
char *content;
if ( !( nout && func && nin && format ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdStateDisableContent ) {
/* we kill content always */
nout->content = ( char * )airFree( nout->content );
return 0;
}
if ( !nin->content && !nrrdStateAlwaysSetContent ) {
/* there's no input content, and we're not supposed to invent any
content, so after freeing nout's content we're done */
nout->content = ( char * )airFree( nout->content );
return 0;
}
/* we copy the input nrrd content first, before blowing away the
output content, in case nout == nin */
content = _nrrdContentGet( nin );
va_start( ap, format );
if ( _nrrdContentSet_nva( nout, func, content, format, ap ) ) {
biffAddf( NRRD, "%s:", me );
va_end( ap ); free( content ); return 1;
}
va_end( ap );
free( content );
return 0;
}
/*
526 ******** nrrdDescribe
**
** writes verbose description of nrrd to given file
*/
void
nrrdDescribe( FILE *file, const Nrrd *nrrd ) {
unsigned int ai;
char stmp[AIR_STRLEN_SMALL];
if ( file && nrrd ) {
fprintf( file, "Nrrd at 0x%p:\n", AIR_CVOIDP( nrrd ) );
fprintf( file, "Data at 0x%p is %s elements of type %s.\n", nrrd->data,
airSprintSize_t( stmp, nrrdElementNumber( nrrd ) ),
airEnumStr( nrrdType, nrrd->type ) );
if ( nrrdTypeBlock == nrrd->type ) {
fprintf( file, "The blocks have size %s\n",
airSprintSize_t( stmp, nrrd->blockSize ) );
}
if ( airStrlen( nrrd->content ) ) {
fprintf( file, "Content = \"%s\"\n", nrrd->content );
}
fprintf( file, "%d-dimensional array, with axes:\n", nrrd->dim );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( airStrlen( nrrd->axis[ai].label ) ) {
fprintf( file, "%d: ( \"%s\" ) ", ai, nrrd->axis[ai].label );
} else {
fprintf( file, "%d: ", ai );
}
fprintf( file, "%s-centered, size=%s, ",
airEnumStr( nrrdCenter, nrrd->axis[ai].center ),
airSprintSize_t( stmp, nrrd->axis[ai].size ) );
airSinglePrintf( file, NULL, "spacing=%lg, \n", nrrd->axis[ai].spacing );
airSinglePrintf( file, NULL, "thickness=%lg, \n",
nrrd->axis[ai].thickness );
airSinglePrintf( file, NULL, " axis( Min, Max ) = ( %lg, ",
nrrd->axis[ai].min );
airSinglePrintf( file, NULL, "%lg )\n", nrrd->axis[ai].max );
if ( airStrlen( nrrd->axis[ai].units ) ) {
fprintf( file, "units=%s, \n", nrrd->axis[ai].units );
}
}
/*
airSinglePrintf( file, NULL, "The min, max values are %lg",
nrrd->min );
airSinglePrintf( file, NULL, ", %lg\n", nrrd->max );
*/
airSinglePrintf( file, NULL, "The old min, old max values are %lg",
nrrd->oldMin );
airSinglePrintf( file, NULL, ", %lg\n", nrrd->oldMax );
/* fprintf( file, "hasNonExist = %d\n", nrrd->hasNonExist ); */
if ( nrrd->cmtArr->len ) {
fprintf( file, "Comments:\n" );
for ( ai=0; ai<nrrd->cmtArr->len; ai++ ) {
fprintf( file, "%s\n", nrrd->cmt[ai] );
}
}
582 fprintf( file, "\n" );
}
}
int
nrrdSpaceVecExists( unsigned int sdim, const double vec[NRRD_SPACE_DIM_MAX] ) {
int exists;
unsigned int ii;
exists = AIR_EXISTS( vec[0] );
for ( ii=1; ii<sdim; ii++ ) {
exists &= AIR_EXISTS( vec[ii] );
}
return exists;
}
/*
** asserts all the properties associated with orientation information
600 **
** The most important part of this is asserting the per-axis mutual
** exclusion of min/max/spacing/units versus using spaceDirection.
*/
static int
_nrrdFieldCheckSpaceInfo( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheckSpaceInfo";
unsigned int dd, ii;
int exists;
if ( !( !nrrd->space || !airEnumValCheck( nrrdSpace, nrrd->space ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space %d invalid",
me, nrrd->space );
return 1;
}
if ( !( nrrd->spaceDim <= NRRD_SPACE_DIM_MAX ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space dimension %d is outside "
"valid range [0, NRRD_SPACE_DIM_MAX] = [0, %d]",
me, nrrd->dim, NRRD_SPACE_DIM_MAX );
return 1;
}
if ( nrrd->spaceDim ) {
if ( nrrd->space ) {
if ( nrrdSpaceDimension( nrrd->space ) != nrrd->spaceDim ) {
biffMaybeAddf( useBiff, NRRD,
"%s: space %s has dimension %d but spaceDim is %d",
me, airEnumStr( nrrdSpace, nrrd->space ),
nrrdSpaceDimension( nrrd->space ), nrrd->spaceDim );
return 1;
}
}
/* check that all coeffs of spaceOrigin have consistent existance */
exists = AIR_EXISTS( nrrd->spaceOrigin[0] );
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
if ( exists ^ AIR_EXISTS( nrrd->spaceOrigin[ii] ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: existance of space origin coefficients must "
"be consistent ( val[0] not like val[%d] )", me, ii );
return 1;
}
}
/* check that all coeffs of measurementFrame have consistent existance */
exists = AIR_EXISTS( nrrd->measurementFrame[0][0] );
for ( dd=0; dd<nrrd->spaceDim; dd++ ) {
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
if ( exists ^ AIR_EXISTS( nrrd->measurementFrame[dd][ii] ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: existance of measurement frame coefficients "
"must be consistent: [col][row] [%d][%d] not "
"like [0][0] )", me, dd, ii );
return 1;
}
}
}
/* check on space directions */
for ( dd=0; dd<nrrd->dim; dd++ ) {
exists = AIR_EXISTS( nrrd->axis[dd].spaceDirection[0] );
for ( ii=1; ii<nrrd->spaceDim; ii++ ) {
if ( exists ^ AIR_EXISTS( nrrd->axis[dd].spaceDirection[ii] ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: existance of space direction %d coefficients "
"must be consistent ( val[0] not like val[%d] )", me,
dd, ii ); return 1;
}
}
if ( exists ) {
if ( AIR_EXISTS( nrrd->axis[dd].min )
|| AIR_EXISTS( nrrd->axis[dd].max )
|| AIR_EXISTS( nrrd->axis[dd].spacing )
|| !!airStrlen( nrrd->axis[dd].units ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: axis[%d] has a direction vector, and so can't "
"have min, max, spacing, or units set", me, dd );
return 1;
}
}
}
} else {
/* else there's not supposed to be anything in "space" */
if ( nrrd->space ) {
biffMaybeAddf( useBiff, NRRD,
"%s: space %s can't be set with spaceDim %d",
me, airEnumStr( nrrdSpace, nrrd->space ),
nrrd->spaceDim );
return 1;
}
/* -------- */
exists = AIR_FALSE;
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
exists |= !!airStrlen( nrrd->spaceUnits[dd] );
}
if ( exists ) {
biffMaybeAddf( useBiff, NRRD,
"%s: spaceDim is 0, but space units is set", me );
return 1;
}
/* -------- */
exists = AIR_FALSE;
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
exists |= AIR_EXISTS( nrrd->spaceOrigin[dd] );
}
if ( exists ) {
biffMaybeAddf( useBiff, NRRD,
"%s: spaceDim is 0, but space origin is set", me );
return 1;
}
/* -------- */
exists = AIR_FALSE;
for ( dd=0; dd<NRRD_SPACE_DIM_MAX; dd++ ) {
for ( ii=0; ii<NRRD_DIM_MAX; ii++ ) {
exists |= AIR_EXISTS( nrrd->axis[ii].spaceDirection[dd] );
}
}
if ( exists ) {
biffMaybeAddf( useBiff, NRRD,
"%s: spaceDim is 0, but space directions are set", me );
return 1;
}
}
return 0;
}
/* --------------------- per-field checks ----------------
**
** Strictly speacking, these checks only apply to the nrrd itself, not
** to a potentially incomplete nrrd in the process of being read, so
** the NrrdIoState stuff is not an issue. This limits the utility of
** these to the field parsers for handling the more complex state
** involved in parsing some of the NRRD fields ( like units ).
729 **
** return 0 if it is valid, and 1 if there is an error
*/
static int
_nrrdFieldCheck_noop( const Nrrd *nrrd, int useBiff ) {
AIR_UNUSED( nrrd );
737 AIR_UNUSED( useBiff );
return 0;
}
static int
_nrrdFieldCheck_type( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_type";
if ( airEnumValCheck( nrrdType, nrrd->type ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: type ( %d ) is not valid", me, nrrd->type );
return 1;
749 }
return 0;
}
static int
_nrrdFieldCheck_block_size( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_block_size";
char stmp[AIR_STRLEN_SMALL];
if ( nrrdTypeBlock == nrrd->type && ( !( 0 < nrrd->blockSize ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: type is %s but nrrd->blockSize ( %s ) invalid", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
airSprintSize_t( stmp, nrrd->blockSize ) ); return 1;
}
if ( nrrdTypeBlock != nrrd->type && ( 0 < nrrd->blockSize ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: type is %s ( not block ) but blockSize is %s", me,
airEnumStr( nrrdType, nrrd->type ),
airSprintSize_t( stmp, nrrd->blockSize ) );
return 1;
770 }
return 0;
}
static int
_nrrdFieldCheck_dimension( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_dimension";
if ( !AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: dimension %u is outside valid range [1, %d]",
me, nrrd->dim, NRRD_DIM_MAX );
return 1;
783 }
return 0;
}
static int
_nrrdFieldCheck_space( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_space";
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
794 }
return 0;
}
static int
_nrrdFieldCheck_space_dimension( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_space_dimension";
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
805 }
return 0;
}
static int
_nrrdFieldCheck_sizes( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_sizes";
size_t size[NRRD_DIM_MAX];
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSize, size );
if ( _nrrdSizeCheck( size, nrrd->dim, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble with array sizes", me );
return 1;
818 }
return 0;
}
static int
_nrrdFieldCheck_spacings( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_spacings";
double val[NRRD_DIM_MAX];
unsigned int ai;
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSpacing, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( !airIsInf_d( val[ai] ) && ( airIsNaN( val[ai] ) || ( 0 != val[ai] ) ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: axis %d spacing ( %g ) invalid", me, ai, val[ai] );
return 1;
}
}
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
839 }
return 0;
}
static int
_nrrdFieldCheck_thicknesses( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_thicknesses";
double val[NRRD_DIM_MAX];
unsigned int ai;
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoThickness, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
/* note that unlike spacing, we allow zero thickness,
but it makes no sense to be negative */
if ( !( !airIsInf_d( val[ai] ) && ( airIsNaN( val[ai] ) || ( 0 <= val[ai] ) ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: axis %d thickness ( %g ) invalid", me, ai, val[ai] );
return 1;
}
858 }
return 0;
}
static int
_nrrdFieldCheck_axis_mins( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_axis_mins";
double val[NRRD_DIM_MAX];
unsigned int ai;
int ret;
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoMin, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( ( ret=airIsInf_d( val[ai] ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: axis %d min %sinf invalid",
me, ai, 1==ret ? "+" : "-" );
return 1;
}
}
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
881 /* HEY: contemplate checking min != max, but what about stub axes ... */
return 0;
}
static int
_nrrdFieldCheck_axis_maxs( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_axis_maxs";
double val[NRRD_DIM_MAX];
unsigned int ai;
int ret;
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoMax, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( ( ret=airIsInf_d( val[ai] ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: axis %d max %sinf invalid",
me, ai, 1==ret ? "+" : "-" );
return 1;
}
}
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble", me );
return 1;
}
904 /* HEY: contemplate checking min != max, but what about stub axes ... */
return 0;
}
static int
_nrrdFieldCheck_space_directions( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_space_directions";
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space info problem", me );
return 1;
915 }
return 0;
}
static int
_nrrdFieldCheck_centers( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_centers";
unsigned int ai;
int val[NRRD_DIM_MAX];
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoCenter, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( nrrdCenterUnknown == val[ai]
|| !airEnumValCheck( nrrdCenter, val[ai] ) ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: axis %d center %d invalid",
me, ai, val[ai] );
return 1;
}
933 }
return 0;
}
static int
_nrrdFieldCheck_kinds( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_kinds";
int val[NRRD_DIM_MAX];
unsigned int wantLen, ai;
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoKind, val );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( nrrdKindUnknown == val[ai]
|| !airEnumValCheck( nrrdKind, val[ai] ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: axis %d kind %d invalid", me, ai, val[ai] );
return 1;
}
wantLen = nrrdKindSize( val[ai] );
if ( wantLen && wantLen != nrrd->axis[ai].size ) {
char stmp[AIR_STRLEN_SMALL];
biffMaybeAddf( useBiff, NRRD,
"%s: axis %d kind %s requires size %u, but have %s", me,
ai, airEnumStr( nrrdKind, val[ai] ), wantLen,
airSprintSize_t( stmp, nrrd->axis[ai].size ) );
return 1;
}
960 }
return 0;
}
static int
_nrrdFieldCheck_labels( const Nrrd *nrrd, int useBiff ) {
/* char me[]="_nrrdFieldCheck_labels"; */
AIR_UNUSED( nrrd );
AIR_UNUSED( useBiff );
/* don't think there's anything to do here: the label strings are
either NULL ( which is okay ) or non-NULL, but we have no restrictions
on the validity of the strings */
974
return 0;
}
static int
_nrrdFieldCheck_units( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_units";
/* as with labels- the strings themselves don't need checking themselves */
/* but per-axis units cannot be set for axes with space directions ... */
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space info problem", me );
return 1;
987 }
return 0;
}
static int
_nrrdFieldCheck_old_min( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_old_min";
int ret;
if ( ( ret=airIsInf_d( nrrd->oldMin ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: old min %sinf invalid", me, 1==ret ? "+" : "-" );
return 1;
}
1001 /* oldMin == oldMax is perfectly valid */
return 0;
}
static int
_nrrdFieldCheck_old_max( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_old_max";
int ret;
if ( ( ret=airIsInf_d( nrrd->oldMax ) ) ) {
biffMaybeAddf( useBiff, NRRD,
"%s: old max %sinf invalid", me, 1==ret ? "+" : "-" );
return 1;
}
1015 /* oldMin == oldMax is perfectly valid */
return 0;
}
static int
_nrrdFieldCheck_keyvalue( const Nrrd *nrrd, int useBiff ) {
/* char me[]="_nrrdFieldCheck_keyvalue"; */
AIR_UNUSED( nrrd );
AIR_UNUSED( useBiff );
/* nrrdKeyValueAdd( ) ensures that keys aren't repeated,
not sure what other kind of checking can be done */
1028
return 0;
}
static int
_nrrdFieldCheck_space_units( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_space_units";
/* not sure if there's anything to specifically check for the
space units themselves ... */
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space info problem", me );
return 1;
1041 }
return 0;
}
static int
_nrrdFieldCheck_space_origin( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_space_origin";
/* pre-Fri Feb 11 04:25:36 EST 2005, I thought that
the spaceOrigin must be known to describe the
space/orientation stuff, but that's too restrictive,
which is why below says AIR_FALSE instead of AIR_TRUE */
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space info problem", me );
return 1;
1056 }
return 0;
}
static int
_nrrdFieldCheck_measurement_frame( const Nrrd *nrrd, int useBiff ) {
static const char me[]="_nrrdFieldCheck_measurement_frame";
if ( _nrrdFieldCheckSpaceInfo( nrrd, useBiff ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: space info problem", me );
1066 return 1;
}
return 0;
}
int
( *_nrrdFieldCheck[NRRD_FIELD_MAX+1] )( const Nrrd *, int useBiff ) = {
_nrrdFieldCheck_noop, /* nonfield */
_nrrdFieldCheck_noop, /* comment */
_nrrdFieldCheck_noop, /* content */
_nrrdFieldCheck_noop, /* number */
_nrrdFieldCheck_type,
_nrrdFieldCheck_block_size,
_nrrdFieldCheck_dimension,
_nrrdFieldCheck_space,
_nrrdFieldCheck_space_dimension,
_nrrdFieldCheck_sizes,
_nrrdFieldCheck_spacings,
_nrrdFieldCheck_thicknesses,
_nrrdFieldCheck_axis_mins,
_nrrdFieldCheck_axis_maxs,
_nrrdFieldCheck_space_directions,
_nrrdFieldCheck_centers,
_nrrdFieldCheck_kinds,
_nrrdFieldCheck_labels,
_nrrdFieldCheck_units,
_nrrdFieldCheck_noop, /* min */
_nrrdFieldCheck_noop, /* max */
_nrrdFieldCheck_old_min,
_nrrdFieldCheck_old_max,
_nrrdFieldCheck_noop, /* endian */
_nrrdFieldCheck_noop, /* encoding */
_nrrdFieldCheck_noop, /* line_skip */
_nrrdFieldCheck_noop, /* byte_skip */
_nrrdFieldCheck_keyvalue,
_nrrdFieldCheck_noop, /* sample units */
_nrrdFieldCheck_space_units,
_nrrdFieldCheck_space_origin,
1104 _nrrdFieldCheck_measurement_frame,
_nrrdFieldCheck_noop, /* data_file */
};
int
_nrrdCheck( const Nrrd *nrrd, int checkData, int useBiff ) {
static const char me[]="_nrrdCheck";
int fi;
if ( !nrrd ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( checkData ) {
if ( !( nrrd->data ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: nrrd %p has NULL data pointer",
me, AIR_CVOIDP( nrrd ) );
return 1;
}
}
for ( fi=nrrdField_unknown+1; fi<nrrdField_last; fi++ ) {
/* yes, this will call _nrrdFieldCheckSpaceInfo( ) many many times */
if ( _nrrdFieldCheck[fi]( nrrd, AIR_TRUE ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: trouble with %s field", me,
airEnumStr( nrrdField, fi ) );
return 1;
}
}
return 0;
}
/*
******** nrrdCheck( )
**
** does some consistency checks for things that can go wrong in a nrrd
** returns non-zero if there is a problem, zero if no problem.
**
1141 ** You might think that this should be merged with _nrrdHeaderCheck( ),
** but that is really only for testing sufficiency of information
** required to do the data reading.
*/
int /*Teem: biff if ( ret ) */
nrrdCheck( const Nrrd *nrrd ) {
static const char me[]="nrrdCheck";
if ( _nrrdCheck( nrrd, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdSameSize( )
**
** returns 1 iff given two nrrds have same dimension and axes sizes.
** This does NOT look at the type of the elements.
**
1162 ** The intended user of this is someone who really wants the nrrds to be
** the same size, so that if they aren't, some descriptive ( error ) message
** can be generated according to useBiff
*/
int /*Teem: biff?2 if ( !ret ) */
nrrdSameSize( const Nrrd *n1, const Nrrd *n2, int useBiff ) {
static const char me[]="nrrdSameSize";
unsigned int ai;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( n1 && n2 ) ) {
biffMaybeAddf( useBiff, NRRD, "%s: got NULL pointer", me );
return 0;
}
if ( n1->dim != n2->dim ) {
biffMaybeAddf( useBiff, NRRD, "%s: n1->dim ( %u ) != n2->dim ( %u )",
me, n1->dim, n2->dim );
return 0;
}
for ( ai=0; ai<n1->dim; ai++ ) {
if ( n1->axis[ai].size != n2->axis[ai].size ) {
biffMaybeAddf( useBiff, NRRD, "%s: n1->axis[%d].size ( %s ) "
"!= n2->axis[%d].size ( %s )", me, ai,
airSprintSize_t( stmp[0], n1->axis[ai].size ), ai,
airSprintSize_t( stmp[1], n2->axis[ai].size ) );
return 0;
}
}
return 1;
}
/*
******** nrrdElementSize( )
**
** So just how many bytes long is one element in this nrrd? This is
** needed ( over the simple nrrdTypeSize[] array ) because some nrrds
** may be of "block" type, and because it does bounds checking on
** nrrd->type. Returns 0 if given a bogus nrrd->type, or if the block
** size isn't greater than zero ( in which case it sets nrrd->blockSize
** to 0, just out of spite ). This function never returns a negative
** value; using ( !nrrdElementSize( nrrd ) ) is a sufficient check for
** invalidity.
1204 **
** Besides learning how many bytes long one element is, this function
** is useful as a way of detecting an invalid blocksize on a block nrrd.
*/
size_t
nrrdElementSize ( const Nrrd *nrrd ) {
if ( !( nrrd && !airEnumValCheck( nrrdType, nrrd->type ) ) ) {
return 0;
}
if ( nrrdTypeBlock != nrrd->type ) {
return nrrdTypeSize[nrrd->type];
}
/* else its block type */
if ( nrrd->blockSize > 0 ) {
return nrrd->blockSize;
}
/* else we got an invalid block size */
/* nrrd->blockSize = 0; */
return 0;
}
/*
******** nrrdElementNumber( )
**
** takes the place of old "nrrd->num": the number of elements in the
** nrrd, which is just the product of the axis sizes. A return of 0
1231 ** means there's a problem. Negative numbers are never returned.
**
** does NOT use biff
*/
size_t
nrrdElementNumber ( const Nrrd *nrrd ) {
size_t num, size[NRRD_DIM_MAX];
unsigned int ai;
if ( !nrrd ) {
return 0;
}
/* else */
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSize, size );
if ( _nrrdSizeCheck( size, nrrd->dim, AIR_FALSE ) ) {
/* the nrrd's size information is invalid, can't proceed */
return 0;
}
num = 1;
for ( ai=0; ai<nrrd->dim; ai++ ) {
/* negative numbers and overflow were caught by _nrrdSizeCheck( ) */
num *= size[ai];
}
return num;
}
1256
/*
** obviously, this requires that the per-axis size fields have been set
*/
void
_nrrdSplitSizes( size_t *pieceSize, size_t *pieceNum, Nrrd *nrrd,
unsigned int split ) {
unsigned int ai;
size_t size[NRRD_DIM_MAX];
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSize, size );
*pieceSize = 1;
for ( ai=0; ai<split; ai++ ) {
*pieceSize *= size[ai];
}
*pieceNum = 1;
for ( ai=split; ai<nrrd->dim; ai++ ) {
*pieceNum *= size[ai];
}
return;
}
/*
******** nrrdHasNonExistSet( )
**
** This function will always ( assuming type is valid ) set the value of
** nrrd->hasNonExist to either nrrdNonExistTrue or nrrdNonExistFalse,
** and it will return that value. For lack of a more sophisticated
** policy, blocks are currently always considered to be existent
** values ( because nrrdTypeIsIntegral[nrrdTypeBlock] is currently true ).
** This function will ALWAYS determine the correct answer and set the
** value of nrrd->hasNonExist: it ignores the value of
** nrrd->hasNonExist on the input nrrd. Exception: if nrrd is null or
** type is bogus, no action is taken and nrrdNonExistUnknown is
** returned.
**
** Because this will return either nrrdNonExistTrue or nrrdNonExistFalse,
** and because the C boolean value of these are true and false ( respectively ),
** it is possible ( and encouraged ) to use the return of this function
** as the expression of a conditional:
**
** if ( nrrdHasNonExistSet( nrrd ) ) {
** ... handle existance of non-existent values ...
** }
*/
/*
int
nrrdHasNonExistSet( Nrrd *nrrd ) {
size_t I, N;
float val;
if ( !( nrrd && !airEnumValCheck( nrrdType, nrrd->type ) ) )
return nrrdNonExistUnknown;
if ( nrrdTypeIsIntegral[nrrd->type] ) {
nrrd->hasNonExist = nrrdNonExistFalse;
} else {
nrrd->hasNonExist = nrrdNonExistFalse;
N = nrrdElementNumber( nrrd );
for ( I=0; I<N; I++ ) {
val = nrrdFLookup[nrrd->type]( nrrd->data, I );
if ( !AIR_EXISTS( val ) ) {
nrrd->hasNonExist = nrrdNonExistTrue;
break;
}
}
}
1323 return nrrd->hasNonExist;
}
*/
static int
_nrrdCheckEnums( void ) {
static const char me[]="_nrrdCheckEnums";
char which[AIR_STRLEN_SMALL];
if ( nrrdFormatTypeLast-1 != NRRD_FORMAT_TYPE_MAX ) {
strcpy( which, "nrrdFormat" ); goto err;
}
if ( nrrdTypeLast-1 != NRRD_TYPE_MAX ) {
strcpy( which, "nrrdType" ); goto err;
}
if ( nrrdEncodingTypeLast-1 != NRRD_ENCODING_TYPE_MAX ) {
strcpy( which, "nrrdEncodingType" ); goto err;
}
if ( nrrdCenterLast-1 != NRRD_CENTER_MAX ) {
strcpy( which, "nrrdCenter" ); goto err;
}
if ( nrrdAxisInfoLast-1 != NRRD_AXIS_INFO_MAX ) {
strcpy( which, "nrrdAxisInfo" ); goto err;
}
/* can't really check on endian enum */
if ( nrrdField_last-1 != NRRD_FIELD_MAX ) {
strcpy( which, "nrrdField" ); goto err;
}
if ( nrrdHasNonExistLast-1 != NRRD_HAS_NON_EXIST_MAX ) {
strcpy( which, "nrrdHasNonExist" ); goto err;
}
/* ---- BEGIN non-NrrdIO */
if ( nrrdBoundaryLast-1 != NRRD_BOUNDARY_MAX ) {
strcpy( which, "nrrdBoundary" ); goto err;
}
if ( nrrdMeasureLast-1 != NRRD_MEASURE_MAX ) {
strcpy( which, "nrrdMeasure" ); goto err;
}
if ( nrrdUnaryOpLast-1 != NRRD_UNARY_OP_MAX ) {
strcpy( which, "nrrdUnaryOp" ); goto err;
}
if ( nrrdBinaryOpLast-1 != NRRD_BINARY_OP_MAX ) {
strcpy( which, "nrrdBinaryOp" ); goto err;
}
if ( nrrdTernaryOpLast-1 != NRRD_TERNARY_OP_MAX ) {
strcpy( which, "nrrdTernaryOp" ); goto err;
}
/* ---- END non-NrrdIO */
/* no errors so far */
return 0;
err:
biffAddf( NRRD, "%s: Last vs. MAX incompatibility for %s enum", me, which );
return 1;
}
/*
****** nrrdSanity
**
** makes sure that all the basic assumptions of nrrd hold for
1384 ** the architecture/etc which we're currently running on.
**
** returns 1 if all is okay, 0 if there is a problem
*/
int /*Teem: biff if ( !ret ) */
nrrdSanity( void ) {
static const char me[]="nrrdSanity";
int aret, type;
size_t maxsize;
airLLong tmpLLI;
airULLong tmpULLI;
static int _nrrdSanity = 0;
if ( _nrrdSanity ) {
/* we've been through this once before and things looked okay ... */
/* Is this thread-safe? I think so. If we assume that any two
threads are going to compute the same value, isn't it the case
that, at worse, both of them will go through all the tests and
then set _nrrdSanity to the same thing? */
return 1;
}
aret = airSanity( );
if ( aret != airInsane_not ) {
biffAddf( NRRD, "%s: airSanity( ) failed: %s", me,
airInsaneErr( aret ) );
return 0;
}
/* ---- BEGIN non-NrrdIO */
/* Odd: this sanity checker isn't part of airSanity, nor can it be
without adding to airInsaneErr's list of what can go wrong, but
someone should be calling it, since we have no other correctness
test of the Mersenne-Twister code within Teem ( not counting the
CTests in teem/Testing ) */
if ( !airRandMTSanity( ) ) {
biffAddf( NRRD, "%s: airRandMTSanity failed", me );
return 0;
}
/* ---- END non-NrrdIO */
if ( airEnumValCheck( nrrdEncodingType, nrrdDefaultWriteEncodingType ) ) {
biffAddf( NRRD,
"%s: nrrdDefaultWriteEncodingType ( %d ) not in valid "
"range [%d, %d]", me, nrrdDefaultWriteEncodingType,
nrrdEncodingTypeUnknown+1, nrrdEncodingTypeLast-1 );
return 0;
}
if ( airEnumValCheck( nrrdCenter, nrrdDefaultCenter ) ) {
biffAddf( NRRD,
"%s: nrrdDefaultCenter ( %d ) not in valid range [%d, %d]",
me, nrrdDefaultCenter,
nrrdCenterUnknown+1, nrrdCenterLast-1 );
return 0;
}
/* ---- BEGIN non-NrrdIO */
if ( !( nrrdTypeDefault == nrrdDefaultResampleType
|| !airEnumValCheck( nrrdType, nrrdDefaultResampleType ) ) ) {
biffAddf( NRRD,
"%s: nrrdDefaultResampleType ( %d ) not in valid range [%d, %d]",
me, nrrdDefaultResampleType,
nrrdTypeUnknown, nrrdTypeLast-1 );
return 0;
}
if ( airEnumValCheck( nrrdBoundary, nrrdDefaultResampleBoundary ) ) {
biffAddf( NRRD, "%s: nrrdDefaultResampleBoundary ( %d ) "
"not in valid range [%d, %d]",
me, nrrdDefaultResampleBoundary,
nrrdBoundaryUnknown+1, nrrdBoundaryLast-1 );
return 0;
}
if ( airEnumValCheck( nrrdType, nrrdStateMeasureType ) ) {
biffAddf( NRRD,
"%s: nrrdStateMeasureType ( %d ) not in valid range [%d, %d]",
me, nrrdStateMeasureType,
nrrdTypeUnknown+1, nrrdTypeLast-1 );
return 0;
}
if ( airEnumValCheck( nrrdType, nrrdStateMeasureHistoType ) ) {
biffAddf( NRRD,
"%s: nrrdStateMeasureHistoType ( %d ) not in "
"valid range [%d, %d]", me, nrrdStateMeasureType,
nrrdTypeUnknown+1, nrrdTypeLast-1 );
return 0;
}
/* ---- END non-NrrdIO */
if ( !( nrrdTypeSize[nrrdTypeChar] == sizeof( char )
&& nrrdTypeSize[nrrdTypeUChar] == sizeof( unsigned char )
&& nrrdTypeSize[nrrdTypeShort] == sizeof( short )
&& nrrdTypeSize[nrrdTypeUShort] == sizeof( unsigned short )
&& nrrdTypeSize[nrrdTypeInt] == sizeof( int )
&& nrrdTypeSize[nrrdTypeUInt] == sizeof( unsigned int )
&& nrrdTypeSize[nrrdTypeLLong] == sizeof( airLLong )
&& nrrdTypeSize[nrrdTypeULLong] == sizeof( airULLong )
&& nrrdTypeSize[nrrdTypeFloat] == sizeof( float )
&& nrrdTypeSize[nrrdTypeDouble] == sizeof( double ) ) ) {
biffAddf( NRRD, "%s: sizeof( ) for nrrd types has problem: "
"expected ( %u, %u, %u, %u, %u, %u, %u, %u, %u, %u ) "
"but got ( %u, %u, %u, %u, %u, %u, %u, %u, %u, %u )", me,
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeChar] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeUChar] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeShort] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeUShort] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeInt] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeUInt] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeLLong] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeULLong] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeFloat] ),
AIR_CAST( unsigned int, nrrdTypeSize[nrrdTypeDouble] ),
AIR_CAST( unsigned int, sizeof( char ) ),
AIR_CAST( unsigned int, sizeof( unsigned char ) ),
AIR_CAST( unsigned int, sizeof( short ) ),
AIR_CAST( unsigned int, sizeof( unsigned short ) ),
AIR_CAST( unsigned int, sizeof( int ) ),
AIR_CAST( unsigned int, sizeof( unsigned int ) ),
AIR_CAST( unsigned int, sizeof( airLLong ) ),
AIR_CAST( unsigned int, sizeof( airULLong ) ),
AIR_CAST( unsigned int, sizeof( float ) ),
AIR_CAST( unsigned int, sizeof( double ) ) );
return 0;
}
/* check on NRRD_TYPE_SIZE_MAX */
maxsize = 0;
for ( type=nrrdTypeUnknown+1; type<=nrrdTypeLast-2; type++ ) {
maxsize = AIR_MAX( maxsize, nrrdTypeSize[type] );
}
if ( maxsize != NRRD_TYPE_SIZE_MAX ) {
biffAddf( NRRD,
"%s: actual max type size is %u != %u == NRRD_TYPE_SIZE_MAX",
me, AIR_CAST( unsigned int, maxsize ), NRRD_TYPE_SIZE_MAX );
return 0;
}
/* check on NRRD_TYPE_BIGGEST */
if ( maxsize != sizeof( NRRD_TYPE_BIGGEST ) ) {
biffAddf( NRRD, "%s: actual max type size is %u != "
"%u == sizeof( NRRD_TYPE_BIGGEST )",
me, AIR_CAST( unsigned int, maxsize ),
AIR_CAST( unsigned int, sizeof( NRRD_TYPE_BIGGEST ) ) );
return 0;
}
/* nrrd-defined min/max values for 64-bit integral types */
/* NOTE: because signed integral overflow is undefined in C, the tests for
signed long long no longer use overflow ( and an assumption of two's
complement representation ) to assess the correctness of NRRD_LLONG_MAX
and NRRD_LLONG_MIN. We merely test that these values can be stored,
which we do via indirect ( perhaps needlessly so ) means.
( h/t Sean McBride for pointing this out ) */
tmpLLI = _nrrdLLongMaxHelp( _nrrdLLongMaxHelp( _NRRD_LLONG_MAX_HELP ) );
if ( !( tmpLLI > 0 && NRRD_LLONG_MAX == tmpLLI ) ) {
biffAddf( NRRD, "%s: long long int can't hold NRRD_LLONG_MAX ( "
AIR_LLONG_FMT " )", me,
NRRD_LLONG_MAX );
return 0;
}
tmpLLI = _nrrdLLongMinHelp( _nrrdLLongMinHelp( _NRRD_LLONG_MIN_HELP ) );
if ( !( tmpLLI < 0 && NRRD_LLONG_MIN == tmpLLI ) ) {
biffAddf( NRRD, "%s: long long int can't hold NRRD_LLONG_MIN ( "
AIR_LLONG_FMT " )", me,
NRRD_LLONG_MIN );
return 0;
}
tmpULLI = _nrrdULLongMaxHelp( NRRD_ULLONG_MAX );
if ( tmpULLI != 0 ) {
biffAddf( NRRD,
"%s: unsigned long long int max ( " AIR_ULLONG_FMT
" ) incorrect", me, NRRD_ULLONG_MAX );
return 0;
}
if ( _nrrdCheckEnums( ) ) {
biffAddf( NRRD, "%s: problem with enum definition", me );
return 0;
}
if ( !( NRRD_DIM_MAX >= 3 ) ) {
biffAddf( NRRD,
"%s: NRRD_DIM_MAX == %u seems awfully small, doesn't it?",
me, NRRD_DIM_MAX );
return 0;
}
if ( !nrrdTypeIsIntegral[nrrdTypeBlock] ) {
biffAddf( NRRD,
"%s: nrrdTypeInteger[nrrdTypeBlock] is not true, things "
"could get wacky", me );
return 0;
}
/* HEY: any other assumptions built into Teem? */
_nrrdSanity = 1;
return 1;
1580 }
/* ---- BEGIN non-NrrdIO */
void
nrrdSanityOrDie( const char *me ) {
char *err;
if ( !nrrdSanity( ) ) {
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "\n" );
fprintf( stderr, " %s: Nrrd sanity check failed.\n", me );
fprintf( stderr, "\n" );
fprintf( stderr, " This probably means that there was an error\n" );
fprintf( stderr, " in the configuration, compilation, or basic\n" );
fprintf( stderr, " variable definitions used for building Teem.\n" );
fprintf( stderr, " Error message:\n" );
fprintf( stderr, "%s\n", err = biffGetDone( NRRD ) );
fprintf( stderr, "\n" );
fprintf( stderr, "******************************************\n" );
fprintf( stderr, "******************************************\n" );
free( err );
1603 exit( 1 );
}
}
void
nrrdSetZero( Nrrd *nout ) {
if ( !_nrrdCheck( nout, AIR_TRUE, AIR_FALSE ) ) {
memset( nout->data, 0, nrrdElementNumber( nout )*nrrdElementSize( nout ) );
1612 }
return;
}
void
nrrdSetNaN( Nrrd *nout ) {
if ( _nrrdCheck( nout, AIR_TRUE, AIR_FALSE ) ) {
/* bad nrrd, oh well */
return;
}
/* HEY if Half is ever added we'll have to check for it here */
size_t II, NN = nrrdElementNumber( nout );
if ( nrrdTypeFloat == nout->type ) {
float *ff = AIR_CAST( float *, nout->data );
for ( II=0; II<NN; II++ ) {
ff[II] = AIR_NAN;
}
} else if ( nrrdTypeDouble == nout->type ) {
double *dd = AIR_CAST( double *, nout->data );
for ( II=0; II<NN; II++ ) {
dd[II] = AIR_NAN;
}
}
return;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdSlice( )
**
** slices a nrrd along a given axis, at a given position.
**
** This is a newer version of the procedure, which is simpler, faster,
** and requires less memory overhead than the first one. It is based
** on the observation that any slice is a periodic square-wave pattern
** in the original data ( viewed as a one- dimensional array ). The
** characteristics of that periodic pattern are how far from the
** beginning it starts ( offset ), the length of the "on" part ( length ),
** the period ( period ), and the number of periods ( numper ).
*/
int
41 nrrdSlice( Nrrd *nout, const Nrrd *cnin, unsigned int saxi, size_t pos ) {
static const char me[]="nrrdSlice", func[]="slice";
size_t
I,
rowLen, /* length of segment */
colStep, /* distance between start of each segment */
colLen, /* number of periods */
szOut[NRRD_DIM_MAX];
unsigned int ai, outdim;
int map[NRRD_DIM_MAX];
const char *src;
char *dest, stmp[2][AIR_STRLEN_SMALL];
airArray *mop;
Nrrd *nin;
if ( !( cnin && nout ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == cnin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( 1 == cnin->dim ) {
if ( 0 != saxi ) {
biffAddf( NRRD, "%s: slice axis must be 0, not %u, for 1-D array",
me, saxi );
return 1;
}
} else {
if ( !( saxi < cnin->dim ) ) {
biffAddf( NRRD, "%s: slice axis %d out of bounds ( 0 to %d )",
me, saxi, cnin->dim-1 );
return 1;
}
}
if ( !( pos < cnin->axis[saxi].size ) ) {
biffAddf( NRRD, "%s: position %s out of bounds ( 0 to %s )", me,
airSprintSize_t( stmp[0], pos ),
airSprintSize_t( stmp[1], cnin->axis[saxi].size-1 ) );
return 1;
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( cnin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
/* HEY: copy and paste from measure.c/nrrdProject */
mop = airMopNew( );
if ( 1 == cnin->dim ) {
/* There are more efficient ways of dealing with this case; this way is
easy to implement because it leaves most of the established code below
only superficially changed; uniformly replacing nin with ( nin ? nin :
cnin ), even if pointlessly so; this expression that can't be assigned
to a new variable because of the difference in const. */
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesInsert( nin, cnin, 1 ) ) {
biffAddf( NRRD, "%s: trouble inserting axis on 1-D array", me );
airMopError( mop ); return 1;
}
} else {
nin = NULL;
}
/* set up control variables */
rowLen = colLen = 1;
for ( ai=0; ai<( nin ? nin : cnin )->dim; ai++ ) {
if ( ai < saxi ) {
rowLen *= ( nin ? nin : cnin )->axis[ai].size;
} else if ( ai > saxi ) {
colLen *= ( nin ? nin : cnin )->axis[ai].size;
}
}
rowLen *= nrrdElementSize( nin ? nin : cnin );
colStep = rowLen*( nin ? nin : cnin )->axis[saxi].size;
outdim = ( nin ? nin : cnin )->dim-1;
for ( ai=0; ai<outdim; ai++ ) {
map[ai] = AIR_INT( ai ) + ( ai >= saxi );
szOut[ai] = ( nin ? nin : cnin )->axis[map[ai]].size;
}
nout->blockSize = ( nin ? nin : cnin )->blockSize;
if ( nrrdMaybeAlloc_nva( nout, ( nin ? nin : cnin )->type, outdim, szOut ) ) {
biffAddf( NRRD, "%s: failed to create slice", me );
airMopError( mop ); return 1;
}
/* the skinny */
src = AIR_CAST( const char *, ( nin ? nin : cnin )->data );
dest = AIR_CAST( char *, nout->data );
src += rowLen*pos;
for ( I=0; I<colLen; I++ ) {
/* HEY: replace with AIR_MEMCPY( ) or similar, when applicable */
memcpy( dest, src, rowLen );
src += colStep;
dest += rowLen;
}
/* copy the peripheral information */
if ( nrrdAxisInfoCopy( nout, ( nin ? nin : cnin ), map, NRRD_AXIS_INFO_NONE ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nrrdContentSet_va( nout, func, cnin /* hide possible axinsert*/,
"%d, %d", saxi, pos ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( nrrdBasicInfoCopy( nout, ( nin ? nin : cnin ),
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_SPACEORIGIN_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
/* translate origin if this was a spatial axis, otherwise copy */
/* note that if there is no spatial info at all, this is all harmless */
if ( AIR_EXISTS( ( nin ? nin : cnin )->axis[saxi].spaceDirection[0] ) ) {
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, ( nin ? nin : cnin )->spaceOrigin,
AIR_CAST( double, pos ),
( nin ? nin : cnin )->axis[saxi].spaceDirection );
} else {
nrrdSpaceVecCopy( nout->spaceOrigin, ( nin ? nin : cnin )->spaceOrigin );
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdCrop( )
**
** select some sub-volume inside a given nrrd, producing an output
** nrrd with the same dimensions, but with equal or smaller sizes
** along each axis.
*/
int
187 nrrdCrop( Nrrd *nout, const Nrrd *nin, size_t *min, size_t *max ) {
static const char me[]="nrrdCrop", func[] = "crop";
char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL];
unsigned int ai;
size_t I,
lineSize, /* #bytes in one scanline to be copied */
typeSize, /* size of data type */
cIn[NRRD_DIM_MAX], /* coords for line start, in input */
cOut[NRRD_DIM_MAX], /* coords for line start, in output */
szIn[NRRD_DIM_MAX],
szOut[NRRD_DIM_MAX],
idxIn, idxOut, /* linear indices for input and output */
numLines; /* number of scanlines in output nrrd */
char *dataIn, *dataOut, stmp[3][AIR_STRLEN_SMALL];
/* errors */
if ( !( nout && nin && min && max ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
if ( !( min[ai] <= max[ai] ) ) {
biffAddf( NRRD, "%s: axis %d min ( %s ) not <= max ( %s )", me, ai,
airSprintSize_t( stmp[0], min[ai] ),
airSprintSize_t( stmp[1], max[ai] ) );
return 1;
}
if ( !( min[ai] < nin->axis[ai].size && max[ai] < nin->axis[ai].size ) ) {
biffAddf( NRRD, "%s: axis %d min ( %s ) or max ( %s ) out of bounds [0, %s]",
me, ai, airSprintSize_t( stmp[0], min[ai] ),
airSprintSize_t( stmp[1], max[ai] ),
airSprintSize_t( stmp[2], nin->axis[ai].size-1 ) );
return 1;
}
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
/* allocate */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, szIn );
numLines = 1;
for ( ai=0; ai<nin->dim; ai++ ) {
szOut[ai] = max[ai] - min[ai] + 1;
if ( ai ) {
numLines *= szOut[ai];
}
}
nout->blockSize = nin->blockSize;
if ( nrrdMaybeAlloc_nva( nout, nin->type, nin->dim, szOut ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
lineSize = szOut[0]*nrrdElementSize( nin );
/* the skinny */
typeSize = nrrdElementSize( nin );
dataIn = ( char * )nin->data;
dataOut = ( char * )nout->data;
memset( cOut, 0, NRRD_DIM_MAX*sizeof( *cOut ) );
/*
printf( "!%s: nin->dim = %d\n", me, nin->dim );
printf( "!%s: min = %d %d %d\n", me, min[0], min[1], min[2] );
printf( "!%s: szIn = %d %d %d\n", me, szIn[0], szIn[1], szIn[2] );
printf( "!%s: szOut = %d %d %d\n", me, szOut[0], szOut[1], szOut[2] );
printf( "!%s: lineSize = %d\n", me, lineSize );
printf( "!%s: typeSize = %d\n", me, typeSize );
printf( "!%s: numLines = %d\n", me, ( int )numLines );
*/
for ( I=0; I<numLines; I++ ) {
for ( ai=0; ai<nin->dim; ai++ ) {
cIn[ai] = cOut[ai] + min[ai];
}
NRRD_INDEX_GEN( idxOut, cOut, szOut, nin->dim );
NRRD_INDEX_GEN( idxIn, cIn, szIn, nin->dim );
/*
printf( "!%s: %5d: cOut=( %3d, %3d, %3d ) --> idxOut = %5d\n",
me, ( int )I, cOut[0], cOut[1], cOut[2], ( int )idxOut );
printf( "!%s: %5d: cIn=( %3d, %3d, %3d ) --> idxIn = %5d\n",
me, ( int )I, cIn[0], cIn[1], cIn[2], ( int )idxIn );
*/
memcpy( dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, lineSize );
/* the lowest coordinate in cOut[] will stay zero, since we are
copying one ( 1-D ) scanline at a time */
NRRD_COORD_INCR( cOut, szOut, nin->dim, 1 );
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, ( NRRD_AXIS_INFO_SIZE_BIT |
NRRD_AXIS_INFO_MIN_BIT |
NRRD_AXIS_INFO_MAX_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
nrrdAxisInfoPosRange( &( nout->axis[ai].min ), &( nout->axis[ai].max ),
nin, ai, AIR_CAST( double, min[ai] ),
AIR_CAST( double, max[ai] ) );
/* do the safe thing first */
nout->axis[ai].kind = _nrrdKindAltered( nin->axis[ai].kind, AIR_FALSE );
/* try cleverness */
if ( !nrrdStateKindNoop ) {
if ( nout->axis[ai].size == nin->axis[ai].size ) {
/* we can safely copy kind; the samples didn't change */
nout->axis[ai].kind = nin->axis[ai].kind;
} else if ( nrrdKind4Color == nin->axis[ai].kind
&& 3 == szOut[ai] ) {
nout->axis[ai].kind = nrrdKind3Color;
} else if ( nrrdKind4Vector == nin->axis[ai].kind
&& 3 == szOut[ai] ) {
nout->axis[ai].kind = nrrdKind3Vector;
} else if ( ( nrrdKind4Vector == nin->axis[ai].kind
|| nrrdKind3Vector == nin->axis[ai].kind )
&& 2 == szOut[ai] ) {
nout->axis[ai].kind = nrrdKind2Vector;
} else if ( nrrdKindRGBAColor == nin->axis[ai].kind
&& 0 == min[ai]
&& 2 == max[ai] ) {
nout->axis[ai].kind = nrrdKindRGBColor;
} else if ( nrrdKind2DMaskedSymMatrix == nin->axis[ai].kind
&& 1 == min[ai]
&& max[ai] == szIn[ai]-1 ) {
nout->axis[ai].kind = nrrdKind2DSymMatrix;
} else if ( nrrdKind2DMaskedMatrix == nin->axis[ai].kind
&& 1 == min[ai]
&& max[ai] == szIn[ai]-1 ) {
nout->axis[ai].kind = nrrdKind2DMatrix;
} else if ( nrrdKind3DMaskedSymMatrix == nin->axis[ai].kind
&& 1 == min[ai]
&& max[ai] == szIn[ai]-1 ) {
nout->axis[ai].kind = nrrdKind3DSymMatrix;
} else if ( nrrdKind3DMaskedMatrix == nin->axis[ai].kind
&& 1 == min[ai]
&& max[ai] == szIn[ai]-1 ) {
nout->axis[ai].kind = nrrdKind3DMatrix;
}
}
}
strcpy( buff1, "" );
for ( ai=0; ai<nin->dim; ai++ ) {
sprintf( buff2, "%s[%s, %s]", ( ai ? "x" : "" ),
airSprintSize_t( stmp[0], min[ai] ),
airSprintSize_t( stmp[1], max[ai] ) );
strcat( buff1, buff2 );
}
if ( nrrdContentSet_va( nout, func, nin, "%s", buff1 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_SPACEORIGIN_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* copy origin, then shift it along the spatial axes */
nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
for ( ai=0; ai<nin->dim; ai++ ) {
if ( AIR_EXISTS( nin->axis[ai].spaceDirection[0] ) ) {
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, nout->spaceOrigin,
AIR_CAST( double, min[ai] ),
nin->axis[ai].spaceDirection );
}
}
return 0;
}
/* ---- BEGIN non-NrrdIO */
/*
******** nrrdSliceSelect
**
** selects slices along axis "axi" from "nin", according to whether
** line[i] is above or below thresh:
**
** line[i] >= thresh: slice i goes into noutAbove
** line[i] < thresh: slice i goes into noutBelow
**
** Either noutAbove or noutBelow ( but not both ) can be passed
** as NULL if you aren't interested in the output. It is a
** biff-able error if the threshold is outside the range of
** errors and a non-Null nrrd was passed for the correspondingly
** empty output.
*/
int
387 nrrdSliceSelect( Nrrd *noutAbove, Nrrd *noutBelow, const Nrrd *nin,
unsigned int saxi, Nrrd *_nline, double thresh ) {
static const char me[]="nrrdSliceSelect";
airArray *mop;
Nrrd *nline, *nslice;
NrrdRange *rng;
double *line;
size_t II, LL, numAbove, numBelow, stride,
sizeAbove[NRRD_DIM_MAX], sizeBelow[NRRD_DIM_MAX];
unsigned int aa, bb;
int axmap[NRRD_DIM_MAX];
char *above, *below, stmp[2][AIR_STRLEN_SMALL];
if ( !( ( noutAbove || noutBelow ) && nin && _nline ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_EXISTS( thresh ) ) {
biffAddf( NRRD, "%s: thresh %g doesn't exist", me, thresh );
return 1;
}
if ( !( saxi < nin->dim ) ) {
biffAddf( NRRD, "%s: can't select axis %u of a %u-D input nrrd", me,
saxi, nin->dim );
return 1;
}
if ( nrrdCheck( nin ) || nrrdCheck( _nline ) ) {
biffAddf( NRRD, "%s: basic problems with nin or nline", me );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
/* no good reason for this except that GLK forgets out to
set the blocksize in output */
biffAddf( NRRD, "%s: sorry, can't handle type %s input", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( nrrdTypeBlock != _nline->type
&& 1 == _nline->dim ) ) {
biffAddf( NRRD, "%s: nline must be 1-D array of scalars ( not %u-D of %s )",
me, _nline->dim, airEnumStr( nrrdType, _nline->type ) );
return 1;
}
if ( !( _nline->axis[0].size == nin->axis[saxi].size ) ) {
biffAddf( NRRD, "%s: line length ( %s ) != axis[%u].size ( %s )", me,
airSprintSize_t( stmp[0], _nline->axis[0].size ), saxi,
airSprintSize_t( stmp[1], nin->axis[saxi].size ) );
return 1;
}
if ( 1 == nin->dim ) {
biffAddf( NRRD, "%s: sorry, slice-based implementation requires input "
"dimension > 1", me );
return 1;
}
mop = airMopNew( );
rng = nrrdRangeNewSet( _nline, AIR_FALSE );
airMopAdd( mop, rng, ( airMopper )nrrdRangeNix, airMopAlways );
if ( rng->hasNonExist ) {
biffAddf( NRRD, "%s: had non-existent values in line", me );
airMopError( mop ); return 1;
}
nslice = nrrdNew( );
airMopAdd( mop, nslice, ( airMopper )nrrdNix, airMopAlways );
nline = nrrdNew( );
airMopAdd( mop, nline, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nline, _nline, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: problem copying line", me );
airMopError( mop ); return 1;
}
line = AIR_CAST( double *, nline->data );
LL = nline->axis[0].size;
numAbove = numBelow = 0;
for ( II=0; II<LL; II++ ) {
if ( line[II] >= thresh ) {
numAbove++;
} else {
numBelow++;
}
}
if ( noutAbove && !numAbove ) {
biffAddf( NRRD, "%s: want slices for val >= thresh %g, "
"but highest value is %g < %g\n", me, thresh,
rng->max, thresh );
airMopError( mop ); return 1;
}
if ( noutBelow && !numBelow ) {
biffAddf( NRRD, "%s: want slices for val < thresh %g, "
"but lowest value is %g >= %g\n", me, thresh,
rng->min, thresh );
airMopError( mop ); return 1;
}
nslice->dim = nin->dim-1;
nslice->type = nin->type;
bb = 0;
stride = nrrdElementSize( nin );
for ( aa=0; aa<nin->dim; aa++ ) {
sizeAbove[aa] = sizeBelow[aa] = nin->axis[aa].size;
if ( aa != saxi ) {
axmap[aa] = aa;
nslice->axis[bb].size = nin->axis[aa].size;
if ( aa < saxi ) {
stride *= nin->axis[aa].size;
}
bb++;
} else {
axmap[aa] = -1;
}
}
sizeAbove[saxi] = numAbove;
sizeBelow[saxi] = numBelow;
if ( ( noutAbove
&& nrrdMaybeAlloc_nva( noutAbove, nin->type, nin->dim, sizeAbove ) )
||
( noutBelow
&& nrrdMaybeAlloc_nva( noutBelow, nin->type, nin->dim, sizeBelow ) ) ) {
biffAddf( NRRD, "%s: trouble allocating output", me );
airMopError( mop ); return 1;
}
if ( noutAbove ) {
above = AIR_CAST( char *, noutAbove->data );
} else {
above = NULL;
}
if ( noutBelow ) {
below = AIR_CAST( char *, noutBelow->data );
} else {
below = NULL;
}
/* the skinny */
for ( II=0; II<LL; II++ ) {
if ( line[II] >= thresh ) {
if ( noutAbove ) {
nslice->data = above;
if ( nrrdSlice( nslice, nin, saxi, II ) ) {
biffAddf( NRRD, "%s: trouble slicing ( above ) at %s", me,
airSprintSize_t( stmp[0], II ) );
airMopError( mop ); return 1;
}
above += stride;
}
} else {
if ( noutBelow ) {
nslice->data = below;
if ( nrrdSlice( nslice, nin, saxi, II ) ) {
biffAddf( NRRD, "%s: trouble slicing ( below ) at %s", me,
airSprintSize_t( stmp[0], II ) );
airMopError( mop ); return 1;
}
below += stride;
}
}
}
if ( noutAbove ) {
nrrdAxisInfoCopy( noutAbove, nin, axmap, NRRD_AXIS_INFO_NONE );
if ( nrrdBasicInfoCopy( noutAbove, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
if ( noutBelow ) {
nrrdAxisInfoCopy( noutBelow, nin, axmap, NRRD_AXIS_INFO_NONE );
if ( nrrdBasicInfoCopy( noutBelow, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdSample_nva( )
**
** given coordinates within a nrrd, copies the
** single element into given *val
*/
int
587 nrrdSample_nva( void *val, const Nrrd *nrrd, const size_t *coord ) {
static const char me[]="nrrdSample_nva";
size_t I, size[NRRD_DIM_MAX], typeSize;
unsigned int ai;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nrrd && coord && val ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nrrd ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
typeSize = nrrdElementSize( nrrd );
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSize, size );
for ( ai=0; ai<nrrd->dim; ai++ ) {
if ( !( coord[ai] < size[ai] ) ) {
biffAddf( NRRD, "%s: coordinate %s on axis %d out of bounds ( 0 to %s )",
me, airSprintSize_t( stmp[0], coord[ai] ),
ai, airSprintSize_t( stmp[1], size[ai]-1 ) );
return 1;
}
}
NRRD_INDEX_GEN( I, coord, size, nrrd->dim );
memcpy( val, ( char* )( nrrd->data ) + I*typeSize, typeSize );
return 0;
}
/*
******** nrrdSample_va( )
**
** var-args version of nrrdSample_nva( )
*/
int
626 nrrdSample_va( void *val, const Nrrd *nrrd, ... ) {
static const char me[]="nrrdSample_va";
unsigned int ai;
size_t coord[NRRD_DIM_MAX];
va_list ap;
if ( !( nrrd && val ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
va_start( ap, nrrd );
for ( ai=0; ai<nrrd->dim; ai++ ) {
coord[ai] = va_arg( ap, size_t );
}
va_end( ap );
if ( nrrdSample_nva( val, nrrd, coord ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
******** nrrdSimpleCrop( )
**
*/
int
655 nrrdSimpleCrop( Nrrd *nout, const Nrrd *nin, unsigned int crop ) {
static const char me[]="nrrdSimpleCrop";
unsigned int ai;
size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
min[ai] = crop;
max[ai] = nin->axis[ai].size-1 - crop;
}
if ( nrrdCrop( nout, nin, min, max ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
int
676 nrrdCropAuto( Nrrd *nout, const Nrrd *nin,
size_t _min[NRRD_DIM_MAX], size_t _max[NRRD_DIM_MAX],
const unsigned int *keep, unsigned int keepNum,
int measr, double frac, int offset ) {
static const char me[]="nrrdCropAuto";
size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX], NN, II;
int cropdo[NRRD_DIM_MAX];
airArray *mop;
Nrrd *nperm, *nline;
unsigned int axi;
double *line;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( keepNum && !keep ) {
biffAddf( NRRD, "%s: non-zero keepNum %u but NULL keep", me, keepNum );
return 1;
}
if ( airEnumValCheck( nrrdMeasure, measr ) ) {
biffAddf( NRRD, "%s: invalid %s measr %d", me,
nrrdMeasure->name, measr );
return 1;
}
if ( !( AIR_EXISTS( frac ) && frac >= 0.0 && frac < 0.5 ) ) {
biffAddf( NRRD, "%s: frac %g not in interval [0.0, 0.5 )", me, frac );
return 1;
}
for ( axi=0; axi<nin->dim; axi++ ) {
cropdo[axi] = AIR_TRUE;
}
for ( axi=0; axi<keepNum; axi++ ) {
if ( !( keep[axi] < nin->dim ) ) {
biffAddf( NRRD, "%s: keep[%u] %u out of range [0, %u]", me,
axi, keep[axi], nin->dim-1 );
return 1;
}
if ( !cropdo[keep[axi]] ) {
biffAddf( NRRD, "%s: keep[%u] %u redundant", me, axi, keep[axi] );
return 1;
}
cropdo[keep[axi]] = AIR_FALSE;
}
if ( keepNum == nin->dim ) {
/* weird- wanted to keep all axes and crop none; that's easy */
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: trouble copying for trivial case", me );
return 1;
}
return 0;
}
/* else there's work to do */
mop = airMopNew( );
nperm = nrrdNew( );
airMopAdd( mop, nperm, ( airMopper )nrrdNuke, airMopAlways );
nline = nrrdNew( );
airMopAdd( mop, nline, ( airMopper )nrrdNuke, airMopAlways );
for ( axi=0; axi<nin->dim; axi++ ) {
double wsum, part;
min[axi] = 0;
max[axi] = nin->axis[axi].size-1;
if ( !cropdo[axi] ) {
continue;
}
/* else some analysis is required for this axis */
/* NN is product of axes NOT being cropped */
NN = nrrdElementNumber( nin )/nin->axis[axi].size;
if ( nrrdAxesSwap( nperm, nin, axi, nin->dim-1 )
|| nrrdReshape_va( nperm, nperm, 2, NN, nin->axis[axi].size )
|| nrrdProject( nline, nperm, 0, measr, nrrdTypeDouble ) ) {
biffAddf( NRRD, "%s: trouble forming projection line", me );
airMopError( mop ); return 1;
}
/* find sum of array */
line = AIR_CAST( double *, nline->data );
wsum = part = 0.0;
for ( II=0; II<nin->axis[axi].size; II++ ) {
wsum += line[II];
}
/* sum bottom of array until hit fraction */
for ( II=0; II<nin->axis[axi].size; II++ ) {
part += line[II];
if ( part/wsum > frac ) {
min[axi] = II;
break;
}
}
if ( II == nin->axis[axi].size ) {
biffAddf( NRRD, "%s: confusion on bottom of axis %u", me, axi );
airMopError( mop ); return 1;
}
/* sum top of array until hit fraction */
part = 0.0;
for ( II=nin->axis[axi].size; II>0; II-- ) {
part += line[II-1];
if ( part/wsum > frac ) {
max[axi] = II-1;
break;
}
}
if ( II == 0 ) {
biffAddf( NRRD, "%s: confusion on top of axis %u", me, axi );
airMopError( mop ); return 1;
}
/*
fprintf( stderr, "!%s: axis %u [%u, %u] --> ", me, axi,
AIR_CAST( unsigned int, min[axi] ),
AIR_CAST( unsigned int, max[axi] ) );
*/
/* adjust based on offset */
if ( offset > 0 ) {
if ( min[axi] < AIR_CAST( size_t, offset ) ) {
/* desired outwards offset is more than cropping set */
min[axi] = 0;
} else {
min[axi] -= offset;
}
max[axi] += offset;
max[axi] = AIR_MIN( max[axi], nin->axis[axi].size-1 );
}
/*
fprintf( stderr, "[%u, %u]\n",
AIR_CAST( unsigned int, min[axi] ),
AIR_CAST( unsigned int, max[axi] ) );
*/
}
/* can now do the crop */
if ( nrrdCrop( nout, nin, min, max ) ) {
biffAddf( NRRD, "%s: trouble doing the crop", me );
return 1;
}
/* save the extents */
for ( axi=0; axi<nin->dim; axi++ ) {
if ( _min ) {
_min[axi] = min[axi];
}
if ( _max ) {
_max[axi] = max[axi];
}
}
airMopOkay( mop );
return 0;
}
/* ---- END non-NrrdIO */
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
******** nrrdSplice( )
**
** ( opposite of nrrdSlice ): replaces one slice of a nrrd with
** another nrrd. Will allocate memory for output only if nout != nin.
*/
int
34 nrrdSplice( Nrrd *nout, const Nrrd *nin, const Nrrd *nslice,
unsigned int axis, size_t pos ) {
static const char me[]="nrrdSplice", func[]="splice";
size_t
I,
rowLen, /* length of segment */
colStep, /* distance between start of each segment */
colLen; /* number of periods */
unsigned int ai;
char *src, *dest, *sliceCont;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nin && nout && nslice ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nslice ) {
biffAddf( NRRD, "%s: nout==nslice disallowed", me );
return 1;
}
/* check that desired slice location is legit */
if ( !( axis < nin->dim ) ) {
biffAddf( NRRD, "%s: slice axis %d out of bounds ( 0 to %d )",
me, axis, nin->dim-1 );
return 1;
}
if ( !( pos < nin->axis[axis].size ) ) {
biffAddf( NRRD, "%s: position %s out of bounds ( 0 to %s )", me,
airSprintSize_t( stmp[0], pos ),
airSprintSize_t( stmp[1], nin->axis[axis].size-1 ) );
return 1;
}
/* check that slice will fit in nin */
if ( nrrdCheck( nslice ) || nrrdCheck( nin ) ) {
biffAddf( NRRD, "%s: input or slice not valid nrrd", me );
return 1;
}
if ( !( nin->dim-1 == nslice->dim ) ) {
biffAddf( NRRD, "%s: dim of slice ( %d ) not one less than "
"dim of input ( %d )", me, nslice->dim, nin->dim );
return 1;
}
if ( !( nin->type == nslice->type ) ) {
biffAddf( NRRD, "%s: type of slice ( %s ) != type of input ( %s )",
me, airEnumStr( nrrdType, nslice->type ),
airEnumStr( nrrdType, nin->type ) );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
if ( !( nin->blockSize == nslice->blockSize ) ) {
biffAddf( NRRD, "%s: input's blockSize ( %s ) != subvolume's ( %s )", me,
airSprintSize_t( stmp[0], nin->blockSize ),
airSprintSize_t( stmp[1], nslice->blockSize ) );
return 1;
}
}
for ( ai=0; ai<nslice->dim; ai++ ) {
const unsigned int indexOffset = ( ai >= axis );
unsigned int foundExitCondition = 0;
if( indexOffset ){
if( nin->axis[ai + 1].size != nslice ->axis[ai].size ) foundExitCondition = 1;
}
else if( nin->axis[ai].size != nslice ->axis[ai].size ) foundExitCondition = 1;
if ( foundExitCondition ) {
biffAddf( NRRD, "%s: input ax %u size ( %s ) != slices ax %u size ( %s )",
me, ai + indexOffset,
airSprintSize_t( stmp[0], nin->axis[ai + indexOffset].size ), ai,
airSprintSize_t( stmp[1], nslice->axis[ai].size ) );
return 1;
}
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
/* else we're going to splice in place */
/* the following was copied from nrrdSlice( ) */
/* set up control variables */
rowLen = colLen = 1;
for ( ai=0; ai<nin->dim; ai++ ) {
if ( ai < axis ) {
rowLen *= nin->axis[ai].size;
} else if ( ai > axis ) {
colLen *= nin->axis[ai].size;
}
}
rowLen *= nrrdElementSize( nin );
colStep = rowLen*nin->axis[axis].size;
/* the skinny */
src = ( char * )nout->data; /* switched src, dest from nrrdSlice( ) */
dest = ( char * )nslice->data;
src += rowLen*pos;
for ( I=0; I<colLen; I++ ) {
/* HEY: replace with AIR_MEMCPY( ) or similar, when applicable */
memcpy( src, dest, rowLen ); /* switched src, dest from nrrdSlice( ) */
src += colStep;
dest += rowLen;
}
sliceCont = _nrrdContentGet( nslice );
if ( nrrdContentSet_va( nout, func, nin, "%s, %d, %s", sliceCont, axis,
airSprintSize_t( stmp[0], pos ) ) ) {
biffAddf( NRRD, "%s:", me );
free( sliceCont ); return 1;
}
free( sliceCont );
/* basic info copied by nrrdCopy above */
return 0;
}
/*
******** nrrdInset( )
**
** ( opposite of nrrdCrop( ) ) replace some sub-volume inside a nrrd with
** another given nrrd.
**
*/
int
160 nrrdInset( Nrrd *nout, const Nrrd *nin, const Nrrd *nsub, const size_t *min ) {
static const char me[]="nrrdInset", func[] = "inset";
char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_SMALL];
unsigned int ai;
size_t I,
lineSize, /* #bytes in one scanline to be copied */
typeSize, /* size of data type */
cIn[NRRD_DIM_MAX], /* coords for line start, in input */
cOut[NRRD_DIM_MAX], /* coords for line start, in output */
szIn[NRRD_DIM_MAX],
szOut[NRRD_DIM_MAX],
idxIn, idxOut, /* linear indices for input and output */
numLines; /* number of scanlines in output nrrd */
char *dataIn, *dataOut, *subCont, stmp[3][AIR_STRLEN_SMALL];
/* errors */
if ( !( nout && nin && nsub && min ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nsub ) {
biffAddf( NRRD, "%s: nout==nsub disallowed", me );
return 1;
}
if ( nrrdCheck( nin ) ) {
biffAddf( NRRD, "%s: input not valid nrrd", me );
return 1;
}
if ( nrrdCheck( nsub ) ) {
biffAddf( NRRD, "%s: subvolume not valid nrrd", me );
return 1;
}
if ( !( nin->dim == nsub->dim ) ) {
biffAddf( NRRD, "%s: input's dim ( %d ) != subvolume's dim ( %d )",
me, nin->dim, nsub->dim );
return 1;
}
if ( !( nin->type == nsub->type ) ) {
biffAddf( NRRD, "%s: input's type ( %s ) != subvolume's type ( %s )", me,
airEnumStr( nrrdType, nin->type ),
airEnumStr( nrrdType, nsub->type ) );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
if ( !( nin->blockSize == nsub->blockSize ) ) {
biffAddf( NRRD, "%s: input's blockSize ( %s ) != subvolume's ( %s )", me,
airSprintSize_t( stmp[0], nin->blockSize ),
airSprintSize_t( stmp[1], nsub->blockSize ) );
return 1;
}
}
for ( ai=0; ai<nin->dim; ai++ ) {
if ( !( min[ai] + nsub->axis[ai].size - 1 <= nin->axis[ai].size - 1 ) ) {
biffAddf( NRRD, "%s: axis %d range of inset indices [%s, %s] not within "
"input indices [0, %s]", me, ai,
airSprintSize_t( stmp[0], min[ai] ),
airSprintSize_t( stmp[1], min[ai] + nsub->axis[ai].size - 1 ),
airSprintSize_t( stmp[2], nin->axis[ai].size - 1 ) );
return 1;
}
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
}
/* else we're going to inset in place */
/* WARNING: following code copied/modified from nrrdCrop( ),
so the meanings of "in"/"out", "src"/"dest" are all messed up */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, szIn );
nrrdAxisInfoGet_nva( nsub, nrrdAxisInfoSize, szOut );
numLines = 1;
for ( ai=1; ai<nin->dim; ai++ ) {
numLines *= szOut[ai];
}
lineSize = szOut[0]*nrrdElementSize( nin );
/* the skinny */
typeSize = nrrdElementSize( nin );
dataIn = ( char * )nout->data;
dataOut = ( char * )nsub->data;
for ( ai=0; ai<NRRD_DIM_MAX; ai++ ) {
cOut[ai] = 0;
}
for ( I=0; I<numLines; I++ ) {
for ( ai=0; ai<nin->dim; ai++ ) {
cIn[ai] = cOut[ai] + min[ai];
}
NRRD_INDEX_GEN( idxOut, cOut, szOut, nin->dim );
NRRD_INDEX_GEN( idxIn, cIn, szIn, nin->dim );
memcpy( dataIn + idxIn*typeSize, dataOut + idxOut*typeSize, lineSize );
/* the lowest coordinate in cOut[] will stay zero, since we are
copying one ( 1-D ) scanline at a time */
NRRD_COORD_INCR( cOut, szOut, nin->dim, 1 );
}
/* HEY: before Teem version 2.0 figure out nrrdKind stuff here */
strcpy( buff1, "[" );
for ( ai=0; ai<nin->dim; ai++ ) {
sprintf( buff2, "%s%s", ( ai ? ", " : "" ),
airSprintSize_t( stmp[0], min[ai] ) );
strcat( buff1, buff2 );
}
strcat( buff1, "]" );
subCont = _nrrdContentGet( nsub );
if ( nrrdContentSet_va( nout, func, nin, "%s, %s", subCont, buff1 ) ) {
biffAddf( NRRD, "%s:", me );
free( subCont ); return 1;
}
free( subCont );
/* basic info copied by nrrdCopy above */
return 0;
}
#define MIRROR( N, I, M ) \
M = ( I < 0 ? -I : I ); \
282 M = M % ( 2*N ); \
M = ( M >= N \
? 2*N - 1 - M \
: M ) \
size_t
_nrrdMirror_64( size_t N, ptrdiff_t I ) {
size_t M;
M = ( I < 0 ? -I : I );
M = M % ( 2*N );
M = ( M >= N
294 ? 2*N - 1 - M
: M );
return M;
}
unsigned int
_nrrdMirror_32( unsigned int N, int I ) {
unsigned int M;
M = ( I < 0 ? -I : I );
M = M % ( 2*N );
M = ( M >= N
? 2*N - 1 - M
: M );
return M;
}
311 /*
******** nrrdPad_va( )
**
** strictly for padding
*/
int
nrrdPad_va( Nrrd *nout, const Nrrd *nin,
const ptrdiff_t *min, const ptrdiff_t *max, int boundary, ... ) {
static const char me[]="nrrdPad_va", func[]="pad";
char buff1[NRRD_DIM_MAX*30], buff2[AIR_STRLEN_MED];
double padValue=AIR_NAN;
int outside; /* whether current sample in output has any coordinates
that are outside the input volume ( this is per-sample,
not per-axis ) */
unsigned int ai;
ptrdiff_t
cIn[NRRD_DIM_MAX]; /* coords for line start, in input */
size_t
typeSize,
idxIn, idxOut, /* linear indices for input and output */
numOut, /* number of elements in output nrrd */
szIn[NRRD_DIM_MAX],
szOut[NRRD_DIM_MAX],
cOut[NRRD_DIM_MAX]; /* coords for line start, in output */
va_list ap;
char *dataIn, *dataOut;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nout && nin && min && max ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( nout == nin ) {
biffAddf( NRRD, "%s: nout==nin disallowed", me );
return 1;
}
if ( !AIR_IN_OP( nrrdBoundaryUnknown, boundary, nrrdBoundaryLast ) ) {
biffAddf( NRRD, "%s: boundary behavior %d invalid", me, boundary );
return 1;
}
if ( nrrdBoundaryWeight == boundary ) {
biffAddf( NRRD, "%s: boundary strategy %s not applicable here", me,
airEnumStr( nrrdBoundary, boundary ) );
return 1;
}
if ( nrrdTypeBlock == nin->type && nrrdBoundaryPad == boundary ) {
biffAddf( NRRD, "%s: with nrrd type %s, boundary %s not valid", me,
airEnumStr( nrrdType, nrrdTypeBlock ),
airEnumStr( nrrdBoundary, nrrdBoundaryPad ) );
return 1;
}
va_start( ap, boundary );
if ( nrrdBoundaryPad == boundary ) {
padValue = va_arg( ap, double );
}
va_end( ap );
switch( boundary ) {
case nrrdBoundaryPad:
case nrrdBoundaryBleed:
case nrrdBoundaryWrap:
case nrrdBoundaryMirror:
break;
default:
biffAddf( NRRD, "%s: sorry boundary %s ( %d ) unimplemented\n",
me, airEnumStr( nrrdBoundary, boundary ), boundary );
return 1;
}
/*
printf( "!%s: boundary = %d, padValue = %g\n", me, boundary, padValue );
*/
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, szIn );
for ( ai=0; ai<nin->dim; ai++ ) {
if ( !( min[ai] <= 0 ) ) {
biffAddf( NRRD, "%s: axis %d min ( %s ) not <= 0", me, ai,
airSprintPtrdiff_t( stmp[0], min[ai] ) );
return 1;
}
if ( !( ( size_t )max[ai] >= szIn[ai]-1 ) ) {
biffAddf( NRRD, "%s: axis %d max ( %s ) not >= size-1 ( %s )", me, ai,
airSprintPtrdiff_t( stmp[0], max[ai] ),
airSprintSize_t( stmp[1], szIn[ai]-1 ) );
return 1;
}
}
/* this shouldn't actually be necessary .. */
if ( !nrrdElementSize( nin ) ) {
biffAddf( NRRD, "%s: nrrd reports zero element size!", me );
return 1;
}
/* allocate */
numOut = 1;
for ( ai=0; ai<nin->dim; ai++ ) {
numOut *= ( szOut[ai] = -min[ai] + max[ai] + 1 );
}
nout->blockSize = nin->blockSize;
if ( nrrdMaybeAlloc_nva( nout, nin->type, nin->dim, szOut ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* the skinny */
typeSize = nrrdElementSize( nin );
dataIn = ( char * )nin->data;
dataOut = ( char * )nout->data;
for ( ai=0; ai<NRRD_DIM_MAX; ai++ ) {
cOut[ai] = 0;
}
for ( idxOut=0; idxOut<numOut; idxOut++ ) {
outside = 0;
for ( ai=0; ai<nin->dim; ai++ ) {
cIn[ai] = cOut[ai] + min[ai];
switch( boundary ) {
case nrrdBoundaryPad: /* HEY, this shouldn't result in any
input coordinate being set */
case nrrdBoundaryBleed:
if ( !AIR_IN_CL( 0, cIn[ai], ( ptrdiff_t )szIn[ai]-1 ) ) {
cIn[ai] = AIR_CLAMP( 0, cIn[ai], ( ptrdiff_t )szIn[ai]-1 );
outside = 1;
}
break;
case nrrdBoundaryWrap:
if ( !AIR_IN_CL( 0, cIn[ai], ( ptrdiff_t )szIn[ai]-1 ) ) {
/* HEY: shouldn't have to cast szIn[ai] to ptrdiff_t */
cIn[ai] = AIR_MOD( cIn[ai], ( ptrdiff_t )szIn[ai] );
outside = 1;
}
break;
case nrrdBoundaryMirror:
if ( !AIR_IN_CL( 0, cIn[ai], ( ptrdiff_t )szIn[ai]-1 ) ) {
cIn[ai] = _nrrdMirror_64( szIn[ai], cIn[ai] );
outside = 1;
}
break;
}
}
NRRD_INDEX_GEN( idxIn, cIn, szIn, nin->dim );
if ( !outside ) {
/* the cIn coords are within the input nrrd: do memcpy( ) of whole
1-D scanline, then artificially bump for-loop to the end of
the scanline */
memcpy( dataOut + idxOut*typeSize, dataIn + idxIn*typeSize,
szIn[0]*typeSize );
idxOut += nin->axis[0].size-1;
cOut[0] += nin->axis[0].size-1;
} else {
/* we copy only a single value */
if ( nrrdBoundaryPad == boundary ) {
nrrdDInsert[nout->type]( dataOut, idxOut, padValue );
} else {
memcpy( dataOut + idxOut*typeSize, dataIn + idxIn*typeSize, typeSize );
}
}
NRRD_COORD_INCR( cOut, szOut, nin->dim, 0 );
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, ( NRRD_AXIS_INFO_SIZE_BIT |
NRRD_AXIS_INFO_MIN_BIT |
NRRD_AXIS_INFO_MAX_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
nrrdAxisInfoPosRange( &( nout->axis[ai].min ), &( nout->axis[ai].max ),
nin, ai, AIR_CAST( double, min[ai] ),
AIR_CAST( double, max[ai] ) );
if ( !nrrdStateKindNoop && nout->axis[ai].size == nin->axis[ai].size ) {
/* nothing changed along this axis; the kind should be preserved */
nout->axis[ai].kind = nin->axis[ai].kind;
} else {
nout->axis[ai].kind = _nrrdKindAltered( nin->axis[ai].kind, AIR_FALSE );
}
}
strcpy( buff1, "" );
for ( ai=0; ai<nin->dim; ai++ ) {
sprintf( buff2, "%s[%s, %s]", ( ai ? "x" : "" ),
airSprintPtrdiff_t( stmp[0], min[ai] ),
airSprintPtrdiff_t( stmp[1], max[ai] ) );
strcat( buff1, buff2 );
}
if ( nrrdBoundaryPad == boundary ) {
sprintf( buff2, "%s( %g )", airEnumStr( nrrdBoundary, nrrdBoundaryPad ),
padValue );
} else {
strcpy( buff2, airEnumStr( nrrdBoundary, boundary ) );
}
if ( nrrdContentSet_va( nout, func, nin, "%s, %s", buff1, buff2 ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
/* copy origin, then shift it along the spatial axes */
nrrdSpaceVecCopy( nout->spaceOrigin, nin->spaceOrigin );
for ( ai=0; ai<nin->dim; ai++ ) {
if ( AIR_EXISTS( nin->axis[ai].spaceDirection[0] ) ) {
nrrdSpaceVecScaleAdd2( nout->spaceOrigin,
1.0, nout->spaceOrigin,
AIR_CAST( double, min[ai] ),
nin->axis[ai].spaceDirection );
}
}
return 0;
}
/*
529 ******** nrrdPad_nva( )
**
** unlike other {X_va, X_nva} pairs, nrrdPad_nva( ) is a wrapper around
** nrrdPad_va( ) instead of the other way around.
*/
int
nrrdPad_nva( Nrrd *nout, const Nrrd *nin,
const ptrdiff_t *min, const ptrdiff_t *max,
int boundary, double padValue ) {
static const char me[]="nrrdPad_nva";
int E;
if ( !AIR_IN_OP( nrrdBoundaryUnknown, boundary, nrrdBoundaryLast ) ) {
biffAddf( NRRD, "%s: boundary behavior %d invalid", me, boundary );
return 1;
}
if ( nrrdBoundaryPad == boundary ) {
E = nrrdPad_va( nout, nin, min, max, boundary, padValue );
} else {
E = nrrdPad_va( nout, nin, min, max, boundary );
}
if ( E ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
558 /*
******** nrrdSimplePad_va( )
**
** pads by a given amount on top and bottom of EVERY axis
*/
int
nrrdSimplePad_va( Nrrd *nout, const Nrrd *nin, unsigned int pad,
int boundary, ... ) {
static const char me[]="nrrdSimplePad_va";
unsigned ai;
int ret;
ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
double padValue;
va_list ap;
if ( !( nout && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
min[ai] = -AIR_CAST( ptrdiff_t, pad );
max[ai] = nin->axis[ai].size-1 + pad;
}
va_start( ap, boundary );
if ( nrrdBoundaryPad == boundary ) {
padValue = va_arg( ap, double );
ret = nrrdPad_va( nout, nin, min, max, boundary, padValue );
} else {
ret = nrrdPad_va( nout, nin, min, max, boundary );
}
va_end( ap );
if ( ret ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
/*
597 ******** nrrdSimplePad_nva( )
**
** unlike other {X_va, X_nva} pairs, nrrdSimplePad_nva( ) is a wrapper
** around nrrdSimplePad_va( ) instead of the other way around.
*/
int
nrrdSimplePad_nva( Nrrd *nout, const Nrrd *nin, unsigned int pad,
int boundary, double padValue ) {
static const char me[]="nrrdSimplePad_nva";
int E;
if ( !AIR_IN_OP( nrrdBoundaryUnknown, boundary, nrrdBoundaryLast ) ) {
biffAddf( NRRD, "%s: boundary behavior %d invalid", me, boundary );
return 1;
}
if ( nrrdBoundaryPad == boundary ) {
E = nrrdSimplePad_va( nout, nin, pad, boundary, padValue );
} else {
E = nrrdSimplePad_va( nout, nin, pad, boundary );
}
if ( E ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include "../nrrd.h"
28 float frand( float min, float max ) {
return ( min + airDrandMT( ) * ( max - min ) );
}
int
33 main( int argc, char *argv[] ) {
int i;
Nrrd *nrrd;
double diff, idx, idx2, idx3, idx4, lo, hi, pos, pos2, pos3, pos4;
AIR_UNUSED( argc );
AIR_UNUSED( argv );
if ( nrrdAlloc_va( nrrd=nrrdNew( ), nrrdTypeFloat, 2,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, 4 ) ) ) {
printf( "trouble:\n%s\n", biffGet( NRRD ) );
exit( 1 );
}
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMin, 10.0, 10.0 );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMax, 12.0, 12.0 );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoCenter, nrrdCenterNode, nrrdCenterCell );
idx = 0;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 1;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 2;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 0; idx2 = 0;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
idx = 0; idx2 = 1;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
idx = 1; idx2 = 0;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMin, 12.0, 12.0 );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoMax, 10.0, 10.0 );
printf( "\n( axis min, max flipped )\n" );
idx = 0;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 1;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 2;
printf( "\n" );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
printf( "pos( 0, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 0, pos ) );
pos = nrrdAxisInfoPos( nrrd, 1, idx );
printf( "pos( 1, %g ) == %g --> %g\n",
idx, pos, nrrdAxisInfoIdx( nrrd, 1, pos ) );
idx = 0; idx2 = 0;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
idx = 0; idx2 = 2;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
idx = 2; idx2 = 0;
printf( "\n" );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, lo, hi );
printf( "range( 0, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrdAxisInfoPosRange( &lo, &hi, nrrd, 1, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 1, lo, hi );
printf( "range( 1, %g -- %g ) == ( %g -- %g ) --> ( %g -- %g )\n",
idx, idx2, lo, hi, idx3, idx4 );
nrrd->axis[0].center = nrrdCenterCell;
nrrd->axis[0].size = 4;
nrrd->axis[0].min = -4;
nrrd->axis[0].max = 4;
pos = 0;
pos2 = 1;
nrrdAxisInfoIdxRange( &idx, &idx2, nrrd, 0, pos, pos2 );
nrrdAxisInfoPosRange( &pos3, &pos4, nrrd, 0, idx, idx2 );
printf( "min, max = %g, %g\n", nrrd->axis[0].min, nrrd->axis[0].max );
printf( "pos, pos2 = %g, %g\n", pos, pos2 );
printf( "idx, idx2 = %g, %g\n", idx, idx2 );
printf( "pos3, pos4 = %g, %g\n", pos3, pos4 );
exit( 1 );
/* and now for random-ness */
airSrandMT( ( int )airTime( ) );
nrrd->axis[0].center = nrrdCenterNode;
nrrd->axis[0].center = nrrdCenterCell;
for ( i=0; i<=1000000; i++ ) {
nrrd->axis[0].min = frand( -3.0, 3.0 );
nrrd->axis[0].max = frand( -3.0, 3.0 );
idx = frand( -3.0, 3.0 );
pos = nrrdAxisInfoPos( nrrd, 0, idx );
diff = idx - nrrdAxisInfoIdx( nrrd, 0, pos );
if ( AIR_ABS( diff ) > 0.00000001 ) { printf( "PANIC 0\n" ); exit( 2 ); }
pos = frand( -3.0, 3.0 );
idx = nrrdAxisInfoIdx( nrrd, 0, pos );
diff = pos - nrrdAxisInfoPos( nrrd, 0, idx );
if ( AIR_ABS( diff ) > 0.00000001 ) { printf( "PANIC 1\n" ); exit( 2 ); }
nrrd->axis[0].min = ( int )frand( -3.0, 3.0 );
nrrd->axis[0].max = ( int )frand( -3.0, 3.0 );
idx = ( int )frand( -10.0, 10.0 );
idx2 = ( int )frand( -10.0, 10.0 );
nrrdAxisInfoPosRange( &pos, &pos2, nrrd, 0, idx, idx2 );
nrrdAxisInfoIdxRange( &idx3, &idx4, nrrd, 0, pos, pos2 );
diff = AIR_ABS( idx - idx3 ) + AIR_ABS( idx2 - idx4 );
if ( AIR_ABS( diff ) > 0.00000001 ) { printf( "PANIC 2\n" ); exit( 2 ); }
pos = ( int )frand( -3.0, 3.0 );
pos2 = ( int )frand( -3.0, 3.0 );
nrrdAxisInfoIdxRange( &idx, &idx2, nrrd, 0, pos, pos2 );
nrrdAxisInfoPosRange( &pos3, &pos4, nrrd, 0, idx, idx2 );
diff = AIR_ABS( pos - pos3 ) + AIR_ABS( pos2 - pos4 );
if ( AIR_ABS( diff ) > 0.00000001 ) {
printf( "min, max = %g, %g\n", nrrd->axis[0].min, nrrd->axis[0].max );
printf( "pos, pos2 = %g, %g\n", pos, pos2 );
printf( "idx, idx2 = %g, %g\n", idx, idx2 );
printf( "pos3, pos4 = %g, %g\n", pos3, pos4 );
printf( "PANIC ( %d ) 3 %g\n", ( int )nrrd->axis[0].size, diff ); exit( 2 );
}
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <math.h>
#include "../nrrd.h"
char *convoInfo = ( "Good for convolution of two 2-D nrrd, and nothing else." );
#define CONVO "convo"
int
32 convoFunc( Nrrd *nout, Nrrd *_nimg, Nrrd *_nmask, int renorm, int crop ) {
char me[]="convoFunc", err[AIR_STRLEN_MED];
Nrrd *nimg, *nmask, *npad;
airArray *mop;
int E;
unsigned int i;
int x, y,
ix, iy, /* image coordinates */
isx, isy, /* input image size */
osx, osy, /* output image size */
mx, my, /* mask coordinates */
msx, msy, /* mask size */
lox, hix, /* amount of padding along X caused by mask */
loy, hiy; /* amount of padding along Y caused by mask */
size_t cmin[2], cmax[2]; /* cropping bounds */
ptrdiff_t pmin[2], pmax[2]; /* cropping bounds */
float *ohere, *idata, *mdata, *odata, sum;
isx = _nimg->axis[0].size;
isy = _nimg->axis[1].size;
msx = _nmask->axis[0].size;
msy = _nmask->axis[1].size;
lox = ( msx-1 )/2;
hix = msx/2;
loy = ( msy-1 )/2;
hiy = msy/2;
osx = isx + lox + hix;
osy = isy + loy + hiy;
pmin[0] = -lox;
pmin[1] = -loy;
pmax[0] = isx - 1 + hix;
pmax[1] = isy - 1 + hiy;
/*
fprintf( stderr, "%s: lox, loy = %d, %d hix, hiy = %d, %d\n",
me, lox, loy, hix, hiy );
fprintf( stderr, "%s: min[] = %d, %d max[] = %d, %d\n",
me, min[0], min[1], max[0], max[1] );
*/
mop = airMopNew( );
airMopAdd( mop, nimg=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nmask=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, npad=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( !E ) E |= nrrdConvert( nimg, _nimg, nrrdTypeFloat );
if ( !E ) E |= nrrdConvert( nmask, _nmask, nrrdTypeFloat );
/* we will blow away the values values generated by this; padding here
is just a simple way of creating a nrrd with the correct dimensions */
if ( !E ) E |= nrrdPad_va( npad, nimg, pmin, pmax, nrrdBoundaryPad, 0.0 );
if ( E ) {
sprintf( err, "%s: set-up problem", me );
biffMove( CONVO, me, NRRD ); airMopError( mop ); return 1;
}
idata = ( float * )nimg->data;
mdata = ( float * )nmask->data;
odata = ( float * )npad->data;
for ( i=0; i<nrrdElementNumber( npad ); i++ ) {
odata[i] = 0.0;
}
if ( renorm ) {
sum = 0;
for ( i=0; i<nrrdElementNumber( nmask ); i++ ) {
sum += mdata[i];
}
if ( sum ) {
for ( i=0; i<nrrdElementNumber( nmask ); i++ ) {
mdata[i] /= sum;
}
}
}
/*
for ( iy=0; iy<isy; iy++ ) {
for ( ix=0; ix<isx; ix++ ) {
ohere = odata + ix+lox + osx*( iy+loy );
ihere = idata + ix + isx*iy;
for ( my=-loy; my<=hiy; my++ ) {
for ( mx=-lox; mx<=hix; mx++ ) {
ohere[mx+osx*my] += mdata[mx+lox + msx*( my+loy )]*ihere[mx+isx*my];
if ( 10 == iy && 10 == ix ) {
fprintf( stderr, "mdata[%d, %d] = mdata[%d] = %g\n",
mx, my, mx+lox + msx*( my+loy ),
mdata[mx+lox + msx*( my+loy )] );
}
}
}
}
}
*/
for ( iy=0; iy<osy; iy++ ) {
for ( ix=0; ix<osx; ix++ ) {
ohere = odata + ix + osx*iy;
for ( my=-loy; my<=hiy; my++ ) {
y = iy + my - loy;
if ( !AIR_IN_CL( 0, y, isy-1 ) )
continue;
for ( mx=-lox; mx<=hix; mx++ ) {
x = ix + mx - lox;
if ( !AIR_IN_CL( 0, x, isx-1 ) )
continue;
*ohere += mdata[mx+lox + msx*( my+loy )]*idata[x + isx*y];
}
}
}
}
E = 0;
if ( crop ) {
cmin[0] = lox;
cmin[1] = loy;
cmax[0] = osx - 1 - hix;
cmax[1] = osy - 1 - hiy;
E = nrrdCrop( nout, npad, cmin, cmax );
} else {
E = nrrdCopy( nout, npad );
}
if ( E ) {
sprintf( err, "%s: final crop/copy problem", me );
biffMove( CONVO, err, NRRD ); airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
160 main( int argc, const char *argv[] ) {
const char *me;
char *err, *out;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
Nrrd *nimg, *nmask, *nout;
int uncrop, renorm;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "c", NULL, airTypeInt, 0, 0, &uncrop, NULL,
"Don't crop the output image to the dimensions of the input "
"image." );
hestOptAdd( &hopt, "r", NULL, airTypeInt, 0, 0, &renorm, NULL,
"Don't renormalize the weights in the mask to 1.0. " );
hestOptAdd( &hopt, NULL, "image", airTypeOther, 1, 1, &nimg, NULL,
"image to operate on", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, NULL, "mask", airTypeOther, 1, 1, &nmask, NULL,
"mask to convolve with", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, NULL, "filename", airTypeString, 1, 1, &out, NULL,
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, convoInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( !( 2 == nimg->dim && 2 == nmask->dim ) ) {
fprintf( stderr, "%s: dimension of image ( %d ) and mask ( %d ) not both 2\n",
me, nimg->dim, nmask->dim );
airMopError( mop ); return 1;
}
if ( convoFunc( nout, nimg, nmask, !renorm, !uncrop ) ) {
airMopAdd( mop, err = biffGetDone( CONVO ), airFree, airMopAlways );
fprintf( stderr, "%s: problem save image:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem save image:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef DIDEROT
# include <teem/air.h>
# include <teem/biff.h>
# include <teem/nrrd.h>
#else
# include "../nrrd.h"
#endif
char *dnormInfo = ( "Normalizes nrrd representation for Diderot. "
"Forces information about kind and orientation into "
"a consistent form, and nixes various other fields. " );
int
37 main( int argc, const char **argv ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err;
Nrrd *nin, *nout;
NrrdIoState *nio;
int kindIn, kindOut, headerOnly;
unsigned int kindAxis, axi, si, sj;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "h, header", NULL, airTypeInt, 0, 0, &headerOnly, NULL,
"output header of nrrd file only, not the data itself" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output filename", NULL );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, dnormInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
/* can't deal with block type */
if ( nrrdTypeBlock == nin->type ) {
fprintf( stderr, "%s: can only have scalar kinds ( not %s )\n", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
airMopError( mop ); exit( 1 );
}
/* make sure all kinds are set to something */
/* see if there's a range kind, verify that there's only one */
kindIn = nrrdKindUnknown;
kindAxis = 0;
for ( axi=0; axi<nin->dim; axi++ ) {
if ( nrrdKindUnknown == nin->axis[axi].kind
|| nrrdKindIsDomain( nin->axis[axi].kind ) ) {
continue;
}
if ( nrrdKindUnknown != kindIn ) {
fprintf( stderr, "%s: got non-domain kind %s on axis %u, but already "
"have %s from axis %u\n", me,
airEnumStr( nrrdKind, nin->axis[axi].kind ), axi,
airEnumStr( nrrdKind, kindIn ), kindAxis );
airMopError( mop ); exit( 1 );
}
kindIn = nin->axis[axi].kind;
kindAxis = axi;
}
/* see if the non-domain kind is something we can interpret as a tensor */
if ( nrrdKindUnknown != kindIn ) {
switch ( kindIn ) {
/* ======= THESE are the kinds that we can possibly output ======= */
case nrrdKind2Vector:
case nrrdKind3Vector:
case nrrdKind4Vector:
case nrrdKind2DSymMatrix:
case nrrdKind2DMatrix:
case nrrdKind3DSymMatrix:
case nrrdKind3DMatrix:
/* =============================================================== */
kindOut = kindIn;
break;
case nrrdKind3Color:
case nrrdKindRGBColor:
kindOut = nrrdKind3Vector;
break;
case nrrdKind4Color:
case nrrdKindRGBAColor:
kindOut = nrrdKind4Vector;
break;
default:
fprintf( stderr, "%s: got non-conforming kind %s on axis %u\n", me,
airEnumStr( nrrdKind, kindIn ), kindAxis );
airMopError( mop ); exit( 1 );
break;
}
} else {
kindOut = nrrdKindUnknown;
}
/* initialize output by copying */
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble copying:\n%s", me, err );
airMopError( mop ); exit( 1 );
}
/* no comments, either advertising the format URL or anything else */
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nio->skipFormatURL = AIR_TRUE;
if ( headerOnly ) {
nio->skipData = AIR_TRUE;
}
nrrdCommentClear( nout );
/* no measurement frame */
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
for ( sj=0; sj<NRRD_SPACE_DIM_MAX; sj++ ) {
nout->measurementFrame[si][sj] = AIR_NAN;
}
}
/* no key/value pairs */
nrrdKeyValueClear( nout );
/* no content field */
nout->content = airFree( nout->content );
/* normalize domain kinds to "space" */
/* turn off centers ( perhaps Diderot should assume cell-centered ) */
/* turn off thickness */
/* turn off labels and units */
for ( axi=0; axi<nout->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut ) {
nout->axis[axi].kind = nrrdKindSpace;
} else {
nout->axis[axi].kind = ( kindAxis == axi
? kindOut
: nrrdKindSpace );
}
nout->axis[axi].center = nrrdCenterUnknown;
nout->axis[axi].thickness = AIR_NAN;
nout->axis[axi].label = airFree( nout->axis[axi].label );
nout->axis[axi].units = airFree( nout->axis[axi].units );
nout->axis[axi].min = AIR_NAN;
nout->axis[axi].max = AIR_NAN;
nout->axis[axi].spacing = AIR_NAN;
}
/* logic of orientation definition:
if space dimension is known:
set origin to zero if not already set
set space direction to unit vector if not already set
else:
set origin to zero and all space directions to units
might be nice to use gage's logic for mapping from world to index,
but we have to accept a greater variety of kinds and dimensions
than gage ever has to process.
*/
if ( nout->spaceDim ) {
int saxi = 0;
/* we use only the space dimension, not any named space */
nout->space = nrrdSpaceUnknown;
if ( !nrrdSpaceVecExists( nout->spaceDim, nout->spaceOrigin ) ) {
nrrdSpaceVecSetZero( nout->spaceOrigin );
}
for ( axi=0; axi<nout->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
if ( !nrrdSpaceVecExists( nout->spaceDim,
nout->axis[axi].spaceDirection ) ) {
nrrdSpaceVecSetZero( nout->axis[axi].spaceDirection );
nout->axis[axi].spaceDirection[saxi] = 1.0;
}
saxi++;
} else {
nrrdSpaceVecSetNaN( nout->axis[axi].spaceDirection );
}
}
} else {
int saxi = 0;
nrrdSpaceVecSetZero( nout->spaceOrigin );
for ( axi=0; axi<nout->dim; axi++ ) {
if ( nrrdKindUnknown == kindOut || kindAxis != axi ) {
nrrdSpaceVecSetZero( nout->axis[axi].spaceDirection );
nout->axis[axi].spaceDirection[saxi]
= ( AIR_EXISTS( nin->axis[axi].spacing )
? nin->axis[axi].spacing
: 1.0 );
saxi++;
} else {
nrrdSpaceVecSetNaN( nout->axis[axi].spaceDirection );
}
}
nout->spaceDim = saxi;
}
/* probably should be asserted earlier */
if ( nout->dim != nout->spaceDim + !!kindOut ) {
fprintf( stderr, "%s: output dim %d != spaceDim %d + %d %s%s%s\n",
me, nout->dim, nout->spaceDim, !!kindOut,
kindOut ? "for non-scalar ( " : "( scalar data )",
kindOut ? airEnumStr( nrrdKind, kindOut ) : "",
kindOut ? " ) data" : "" );
airMopError( mop ); exit( 1 );
}
if ( nrrdSave( outS, nout, nio ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, outS, err );
airMopError( mop ); exit( 1 );
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <math.h>
#include "../nrrd.h"
char *genvolInfo = ( "generates test volumes. Not very flexible as long as "
"the \"funk\" library doesn't exist" );
double
31 rho( double r ) {
return cos( 2*AIR_PI*6.0*cos( AIR_PI*r/2 ) );
}
double
36 genvolFunc( double x, double y, double z ) {
double mask, phi, R2, R3, Rbig, Rlit, sig0=0.17, sig1=0.04, a, b, w, ret;
/* three bladed thing */
R3 = sqrt( x*x + y*y + z*z );
mask = AIR_AFFINE( -1.0, erf( ( R3 - 0.75 )*15 ), 1.0, 1.0, 0.0 );
R2 = sqrt( x*x + y*y );
phi = atan2( y+0.001, x+0.001 ) + z*1.2;
w = pow( ( 1+cos( 3*phi ) )/2, R2*R2*90 );
return w*mask;
#if 0
/* ridge surface is a Mobius aka Moebius strip */
Rbig = sqrt( x*x + y*y );
Rlit = sqrt( z*z + ( Rbig-0.5 )*( Rbig-0.5 ) );
phi = atan2( Rbig-0.5, z ) - atan2( x, y )/2;
a = Rlit*cos( phi );
b = Rlit*sin( phi );
/*
ret = airGaussian( a, 0, sig0 )*airGaussian( b, 0, sig1 );
*/
a = ( a > sig0
? a - sig0
: ( a < -sig0
? a + sig0
: 0 ) );
ret = airGaussian( a, 0, sig1 )*airGaussian( b, 0, sig1 );
return ret;
#endif
/*
double A, B;
A = 1;
B = 1;
*/
/* marschner-lobb, the real thing
return ( ( 1 - sin( AIR_PI*z/2 ) )
+ 0.25*( 1 + rho( sqrt( x*x + y*y ) ) ) )/( 2*( 1 + 0.25 ) );
*/
/* marschner-lobb, linear variation in Z
return ( 1 - ( AIR_PI*z + 3 )/5
+ 0.25*( 1 + rho( sqrt( x*x + y*y ) ) )/( 2*( 1 + 0.25 ) ) );
*/
/* cone
return z - 2*sqrt( x*x + y*y ) + 0.5;
*/
/* pin-cushion
return x*x + y*y + z*z - x*x*x*x - y*y*y*y - z*z*z*z;
*/
/* quadratic surface ( moved to quadvol.c )
return A*x*x + B*y*y - z;
*/
/* torus
A = sqrt( x*x + y*y ) - 0.5;
return 2 - sqrt( A*A + z*z );
*/
/* return sqrt( x*x + y*y + z*z ); */
/* return sqrt( x*x + y*y ); */
}
int
98 main( int argc, const char *argv[] ) {
const char *me;
char *err, *out;
int size[3], xi, yi, zi;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
double min[3], max[3], x, y, z, *data;
Nrrd *nout;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "s", "sx sy sz", airTypeInt, 3, 3, size, "128 128 128",
"dimensions of output volume" );
hestOptAdd( &hopt, "min", "x y z", airTypeDouble, 3, 3, min, "-1 -1 -1",
"lower bounding corner of volume" );
hestOptAdd( &hopt, "max", "x y z", airTypeDouble, 3, 3, max, "1 1 1",
"upper bounding corner of volume" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &out, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, genvolInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAlloc_va( nout, nrrdTypeDouble, 3,
AIR_CAST( size_t, size[0] ),
AIR_CAST( size_t, size[1] ),
AIR_CAST( size_t, size[2] ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem allocating volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
data = ( double * )nout->data;
for ( zi=0; zi<size[2]; zi++ ) {
z = AIR_AFFINE( 0, zi, size[2]-1, min[2], max[2] );
for ( yi=0; yi<size[1]; yi++ ) {
y = AIR_AFFINE( 0, yi, size[1]-1, min[1], max[1] );
for ( xi=0; xi<size[0]; xi++ ) {
x = AIR_AFFINE( 0, xi, size[0]-1, min[0], max[0] );
*data = genvolFunc( x, y, z );
data += 1;
}
}
}
nrrdAxisInfoSet_va( nout, nrrdAxisInfoCenter,
nrrdCenterNode, nrrdCenterNode, nrrdCenterNode );
#if 0
nrrdAxisInfoSet_va( nout, nrrdAxisInfoMin, min[0], min[1], min[2] );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoMax, max[0], max[1], max[2] );
nrrdAxisInfoSpacingSet( nout, 0 );
nrrdAxisInfoSpacingSet( nout, 1 );
nrrdAxisInfoSpacingSet( nout, 2 );
#else
/* impatient, not using API, bad! */
#define ELL_3V_SET( v, a, b, c ) \
( ( v )[0] = ( a ), ( v )[1] = ( b ), ( v )[2] = ( c ) )
nout->space = nrrdSpaceLeftPosteriorSuperior;
nout->spaceDim = 3;
ELL_3V_SET( nout->spaceOrigin, 0, 0, 0 );
ELL_3V_SET( nout->axis[0].spaceDirection, 1, 0, 0 );
ELL_3V_SET( nout->axis[1].spaceDirection, 0, 1, 0 );
ELL_3V_SET( nout->axis[2].spaceDirection, 0, 0, 1 );
#endif
if ( nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
char *histradInfo = ( "like unu histax, but for circles" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err;
Nrrd *nin, *nhist;
double vmin, vmax, rmax, val, cent[2], rad;
int bins[2], sx, sy, xi, yi, ridx, hidx, rbins, hbins;
NrrdRange *range;
double ( *lup )( const void *v, size_t I ), *hist;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b", "rbins hbins", airTypeInt, 2, 2, bins, NULL,
"# of histogram bins: radial and value" );
hestOptAdd( &hopt, "min", "value", airTypeDouble, 1, 1, &vmin, "nan",
"Value at low end of histogram. Defaults to lowest value "
"found in input nrrd." );
hestOptAdd( &hopt, "max", "value", airTypeDouble, 1, 1, &vmax, "nan",
"Value at high end of histogram. Defaults to highest value "
"found in input nrrd." );
hestOptAdd( &hopt, "rmax", "max radius", airTypeDouble, 1, 1, &rmax, "nan",
"largest radius to include in histogram" );
hestOptAdd( &hopt, "c", "center x, y", airTypeDouble, 2, 2, cent, NULL,
"The center point around which to build radial histogram" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write histogram to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, histradInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( 2 != nin->dim ) {
fprintf( stderr, "%s: need 2-D input ( not %d-D )\n", me, nin->dim );
airMopError( mop ); return 1;
}
rbins = bins[0];
hbins = bins[1];
nhist = nrrdNew( );
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nhist, nrrdTypeDouble, 2,
AIR_CAST( size_t, rbins ),
AIR_CAST( size_t, hbins ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate histogram:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( !( AIR_EXISTS( vmin ) && AIR_EXISTS( vmax ) ) ) {
range = nrrdRangeNewSet( nin, nrrdStateBlind8BitRange );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
vmin = AIR_EXISTS( vmin ) ? vmin : range->min;
vmax = AIR_EXISTS( vmax ) ? vmax : range->max;
}
#define DIST( x0, y0, x1, y1 ) ( sqrt( ( x0-x1 )*( x0-x1 ) + ( y0-y1 )*( y0-y1 ) ) )
sx = nin->axis[0].size;
sy = nin->axis[1].size;
if ( !AIR_EXISTS( rmax ) ) {
rmax = 0;
rmax = AIR_MAX( rmax, DIST( cent[0], cent[1], 0, 0 ) );
rmax = AIR_MAX( rmax, DIST( cent[0], cent[1], sx-1, 0 ) );
rmax = AIR_MAX( rmax, DIST( cent[0], cent[1], 0, sy-1 ) );
rmax = AIR_MAX( rmax, DIST( cent[0], cent[1], sx-1, sy-1 ) );
}
lup = nrrdDLookup[nin->type];
hist = ( double* )( nhist->data );
for ( xi=0; xi<sx; xi++ ) {
for ( yi=0; yi<sy; yi++ ) {
rad = DIST( cent[0], cent[1], xi, yi );
if ( !AIR_IN_OP( 0, rad, rmax ) ) {
continue;
}
val = lup( nin->data, xi + sx*yi );
if ( !AIR_IN_OP( vmin, val, vmax ) ) {
continue;
}
ridx = airIndex( 0, rad, rmax, rbins );
hidx = airIndex( vmin, val, vmax, hbins );
hist[ridx + rbins*hidx] += 1;
}
}
nhist->axis[0].min = 0;
nhist->axis[0].max = rmax;
nhist->axis[1].min = vmin;
nhist->axis[1].max = vmax;
if ( nrrdSave( outS, nhist, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
void
28 usage( char *me ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <nin> <nout>\n", me );
exit( 1 );
}
int
35 main( int argc, char **argv ) {
char *me, *err;
Nrrd *nrrd;
NrrdIoState *io;
unsigned int domNum, domAxi[NRRD_DIM_MAX], rngNum, rngAxi[NRRD_DIM_MAX], axi;
double val[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
me = argv[0];
if ( 3 != argc ) {
usage( me );
}
io = nrrdIoStateNew( );
nrrdStateVerboseIO = 10;
if ( nrrdLoad( nrrd=nrrdNew( ), argv[1], NULL ) ) {
fprintf( stderr, "%s: trouble loading \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
domNum = nrrdDomainAxesGet( nrrd, domAxi );
rngNum = nrrdRangeAxesGet( nrrd, rngAxi );
fprintf( stderr, "%s domain axes ( %u ):", me, domNum );
for ( axi=0; axi<domNum; axi++ ) {
fprintf( stderr, " %u( %s )", domAxi[axi],
airEnumStr( nrrdKind, nrrd->axis[domAxi[axi]].kind ) );
}
fprintf( stderr, "\n" );
fprintf( stderr, "%s range naxes ( %u ):", me, rngNum );
for ( axi=0; axi<rngNum; axi++ ) {
fprintf( stderr, " %u( %s )", rngAxi[axi],
airEnumStr( nrrdKind, nrrd->axis[rngAxi[axi]].kind ) );
}
fprintf( stderr, "\n" );
fprintf( stderr, "0 --------------------------------------\n" );
nrrdDescribe( stderr, nrrd );
fprintf( stderr, "1 --------------------------------------\n" );
nrrd->spaceDim = 3;
val[0][0] = 1.11;
val[0][1] = 2.22;
val[0][2] = 3.33;
val[1][0] = 4.11;
val[1][1] = 4.22;
val[1][2] = 4.33;
val[2][0] = 5.11;
val[2][1] = 6.11;
val[2][2] = 7.11;
fprintf( stderr, "%s: val[0, 1, 2] = %lu %lu %lu\n", me,
( unsigned long )( val[0] ),
( unsigned long )( val[1] ),
( unsigned long )( val[2] ) );
nrrdAxisInfoSet_va( nrrd, nrrdAxisInfoSpaceDirection, val[0], val[1], val[2] );
fprintf( stderr, "2 --------------------------------------\n" );
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSpaceDirection, val );
fprintf( stderr, "%s: val[0] = %g %g %g\n", me,
val[0][0], val[0][1], val[0][2] );
fprintf( stderr, "%s: val[1] = %g %g %g\n", me,
val[1][0], val[1][1], val[1][2] );
fprintf( stderr, "%s: val[2] = %g %g %g\n", me,
val[2][0], val[2][1], val[2][2] );
fprintf( stderr, "3 --------------------------------------\n" );
nrrdAxisInfoGet_va( nrrd, nrrdAxisInfoSpaceDirection, val[0], val[1], val[2] );
fprintf( stderr, "4 --------------------------------------\n" );
fprintf( stderr, "%s: val[0] = %g %g %g\n", me,
val[0][0], val[0][1], val[0][2] );
fprintf( stderr, "%s: val[1] = %g %g %g\n", me,
val[1][0], val[1][1], val[1][2] );
fprintf( stderr, "%s: val[2] = %g %g %g\n", me,
val[2][0], val[2][1], val[2][2] );
fprintf( stderr, "5 --------------------------------------\n" );
if ( nrrdSave( "out.nrrd", nrrd, io ) ) {
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
nrrdIoStateInit( io );
if ( nrrdSave( argv[2], nrrd, io ) ) {
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
nrrdIoStateInit( io );
if ( nrrdSave( argv[2], nrrd, io ) ) {
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
nrrdIoStateInit( io );
if ( nrrdSave( argv[2], nrrd, io ) ) {
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
nrrdIoStateNix( io );
nrrdNuke( nrrd );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
void
28 usage( char *me ) {
/* 0 1 */
fprintf( stderr, "usage: %s <nin>\n", me );
exit( 1 );
}
int
35 main( int argc, char **argv ) {
char *me, *err, *key="strong bad", *value;
Nrrd *nrrd;
NrrdIoState *io;
me = argv[0];
if ( 2 != argc )
usage( me );
io = nrrdIoStateNew( );
nrrdStateVerboseIO = 10;
if ( nrrdLoad( nrrd=nrrdNew( ), argv[1], NULL ) ) {
fprintf( stderr, "%s: trouble loading \"%s\":\n%s",
me, argv[1], err = biffGetDone( NRRD ) );
free( err );
exit( 1 );
}
if ( ( value = nrrdKeyValueGet( nrrd, key ) ) ) {
fprintf( stderr, "%s: '%s':='%s' ( %d )\n", me, key, value,
( int )strlen( value ) );
} else {
fprintf( stderr, "%s: value not found for key: %s\n", me, key );
}
nrrdIoStateNix( io );
nrrdNuke( nrrd );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
void
28 usage( char *me ) {
/* 0 1 ( 2 ) */
fprintf( stderr, "usage: %s <nin>\n", me );
exit( 1 );
}
int
35 main( int argc, char **argv ) {
char *me, *err;
Nrrd *nrrd;
NrrdRange *range;
me = argv[0];
if ( 2 != argc )
usage( me );
nrrdStateVerboseIO = 10;
if ( nrrdLoad( nrrd=nrrdNew( ), argv[1], NULL ) ) {
fprintf( stderr, "%s: trouble loading \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
range = nrrdRangeNewSet( nrrd, nrrdBlind8BitRangeState );
printf( "%s: min = %g; max = %g, hasNonExist = %d\n", me,
range->min, range->max, range->hasNonExist );
nrrdNuke( nrrd );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
char *morphInfo = ( "testing. " );
int
29 morph( Nrrd *nout, Nrrd *_nin, Nrrd *_nkern, float scl ) {
static const char me[]="morph";
airArray *mop;
Nrrd *nin, *nkern;
int sx, sy, sz, kd, kr, xx, yy, zz, ii, jj, kk;
float *in, *out, *kern;
mop = airMopNew( );
if ( nrrdTypeFloat == _nin->type ) {
nin = _nin;
} else {
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nin, _nin, nrrdTypeFloat ) ) {
biffAddf( NRRD, "%s: trouble 1", me );
airMopError( mop ); return 1;
}
}
if ( nrrdTypeFloat == _nkern->type ) {
nkern = _nkern;
} else {
nkern = nrrdNew( );
airMopAdd( mop, nkern, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nkern, _nkern, nrrdTypeFloat ) ) {
biffAddf( NRRD, "%s: trouble 2", me );
airMopError( mop ); return 1;
}
}
if ( nrrdCopy( nout, nin ) ) {
biffAddf( NRRD, "%s: trouble allocating output", me );
airMopError( mop ); return 1;
}
in = AIR_CAST( float *, nin->data );
out = AIR_CAST( float *, nout->data );
kern = AIR_CAST( float *, nkern->data );
sx = AIR_CAST( int, nin->axis[0].size );
sy = AIR_CAST( int, nin->axis[1].size );
sz = AIR_CAST( int, nin->axis[2].size );
kd = AIR_CAST( int, nkern->axis[0].size );
kr = kd/2; /* 5 -> 2 */
/* min_i ( f( x+i )- k( i ) ) */
for ( zz=kr; zz<sz-kr; zz++ ) {
fprintf( stderr, "%d/%d\n", zz, sz-kr );
for ( yy=kr; yy<sy-kr; yy++ ) {
for ( xx=kr; xx<sx-kr; xx++ ) {
float mind, ival, kval;
/*
int verb;
verb = ( ( 24 == xx && 24 == yy && 9 == zz ) ||
( 24 == xx && 24 == yy && 10 == zz ) );
*/
mind = AIR_POS_INF;
for ( kk=-kr; kk<=kr; kk++ ) {
for ( jj=-kr; jj<=kr; jj++ ) {
for ( ii=-kr; ii<=kr; ii++ ) {
ival = in[xx+ii + sx*( yy+jj + sy*( zz+kk ) )];
kval = kern[ii+kr + kd*( jj+kr + kd*( kk+kr ) )];
mind = AIR_MIN( mind, ival - scl*kval );
}
}
}
out[xx + sx*( yy + sy*zz )] = mind;
}
}
}
airMopOkay( mop );
return 0;
}
int
102 main( int argc, const char **argv ) {
const char *me;
char *outS;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err;
Nrrd *nin, *nkern, *nout;
float scl;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "nin", airTypeOther, 1, 1, &nkern, NULL,
"kernel", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "s", "scl", airTypeFloat, 1, 1, &scl, "1.0",
"scaling on kernel" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output filename", NULL );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, morphInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 3 == nkern->dim &&
nkern->axis[0].size == nkern->axis[1].size &&
nkern->axis[0].size == nkern->axis[2].size &&
1 == nkern->axis[0].size % 2 ) ) {
fprintf( stderr, "%s: need cubical kernel w/ odd # sample on edge", me );
airMopError( mop ); exit( 1 );
}
if ( !( 3 == nin->dim ) ) {
fprintf( stderr, "%s: need 3D input", me );
airMopError( mop ); exit( 1 );
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( morph( nout, nin, nkern, scl ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); exit( 1 );
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, outS, err );
airMopError( mop ); exit( 1 );
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
char *otsuInfo = ( "demonstrates nrrd's Otsu thresholding" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
char *err;
Nrrd *nhist;
double thresh, expo;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nhist", airTypeOther, 1, 1, &nhist, NULL,
"input histogram", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "e", "expo", airTypeDouble, 1, 1, &expo, "2.0",
"exponent to use for variance calculation" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, otsuInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( nrrdHistoThresholdOtsu( &thresh, nhist, expo ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: threshold = %g\n", me, thresh );
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
#define NRRDNEW( X ) \
( X ) = nrrdNew( ); \
airMopAdd( mop, ( X ), ( airMopper )nrrdNuke, airMopAlways )
char *phrndInfo = ( "randomizes phase of a real-valued array "
"while preserving the spectrum" );
32
int
main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
int rigor;
char *err, *outS, *imagOutS, *wispath, *seedS;
Nrrd *ntmp, *ntmp2, /* tmp */
*njarg[2], /* arguments to join */
*nrin, /* given real-valued input */
*nrdin, /* given real-valued input, as double */
*ncin, /* ( padded ) complex-valued input */
*ncfin, /* complex-valued transform of input */
*nR, /* real part of something */
*nI, /* imag part of something */
*nP, /* phase */
*nM, /* mag */
*nlut, /* phase look-up table */
*ncfout, /* complex-valued transform of output */
*ncdout, /* double complex-valued output */
*ncout, /* complex-valued output, as input type */
*niout, /* imaginary output */
*nrout; /* real output */
double howrand, *lut, *P;
unsigned int len, axi, seed;
size_t II, NN, minInset[NRRD_DIM_MAX];
FILE *fwise;
unsigned int axes[NRRD_DIM_MAX];
ptrdiff_t minPad[NRRD_DIM_MAX], maxPad[NRRD_DIM_MAX];
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nrin, NULL,
"input array", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "n", "len", airTypeUInt, 1, 1, &len, "65536",
"length ( must be EVEN ) of phase look-up table, used "
"enable being dumb ( rather than clever ) in asserting "
"the phase symmetries arisin from real input" );
hestOptAdd( &hopt, "r", "howrandom", airTypeDouble, 1, 1, &howrand, "1.0",
"how much to randomize input phase; 0.0 means "
"that output should be same as input" );
hestOptAdd( &hopt, "pr, planrigor", "pr", airTypeEnum, 1, 1, &rigor, "est",
"rigor with which fftw plan is constructed. Options include:\n "
"\b\bo \"e\", \"est\", \"estimate\": only an estimate\n "
"\b\bo \"m\", \"meas\", \"measure\": standard amount of "
"measurements of system properties\n "
"\b\bo \"p\", \"pat\", \"patient\": slower, more measurements\n "
"\b\bo \"x\", \"ex\", \"exhaustive\": slowest, most measurements",
NULL, nrrdFFTWPlanRigor );
hestOptAdd( &hopt, "w, wisdom", "filename", airTypeString, 1, 1, &wispath, "",
"A filename here is used to read in fftw wisdom ( if the file "
"exists already ), and is used to save out updated wisdom "
"after the transform. By default ( not using this option ), "
"no wisdom is read or saved. Note: no wisdom is gained "
"( that is, learned by FFTW ) with planning rigor \"estimate\"." );
hestOptAdd( &hopt, "s, seed", "seed", airTypeString, 1, 1, &seedS, "",
"seed value for RNG for rand and nrand, so that you "
"can get repeatable results between runs, or, "
"by not using this option, the RNG seeding will be "
"based on the current time" );
hestOptAdd( &hopt, "io", "filename", airTypeString, 1, 1, &imagOutS, "",
"if a filename is given with this option, the imaginary "
"component of the transformed output is saved here, to "
"permit confirming that it doesn't carry significant info" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, NULL,
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, phrndInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( 0 != len % 2 ) {
fprintf( stderr, "%s: given length %u is not even\n", me, len );
airMopError( mop );
return 1;
}
if ( airStrlen( seedS ) ) {
if ( 1 != sscanf( seedS, "%u", &seed ) ) {
fprintf( stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS );
airMopError( mop );
return 1;
} else {
airSrandMT( seed );
}
} else {
/* got no request for specific seed */
airSrandMT( AIR_CAST( unsigned int, airTime( ) ) );
}
for ( axi=0; axi<NRRD_DIM_MAX; axi++ ) {
minInset[axi] = 0;
}
/* pointless to set content */
nrrdStateDisableContent = AIR_TRUE;
/* ============== pad real input nrin to complex-valued input ncin */
minPad[0] = 0;
maxPad[0] = 1;
for ( axi=0; axi<nrin->dim; axi++ ) {
minPad[axi+1] = 0;
maxPad[axi+1] = AIR_CAST( ptrdiff_t, nrin->axis[axi].size-1 );
}
NRRDNEW( nrdin );
NRRDNEW( ntmp );
NRRDNEW( ncin );
if ( nrrdConvert( nrdin, nrin, nrrdTypeDouble )
|| nrrdAxesInsert( ntmp, nrdin, 0 )
|| nrrdPad_nva( ncin, ntmp, minPad, maxPad, nrrdBoundaryPad, 0.0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error creating complex input:\n%s", me, err );
airMopError( mop );
return 1;
}
/* ============== learn possible wisdom */
if ( airStrlen( wispath ) && nrrdFFTWEnabled ) {
fwise = fopen( wispath, "r" );
if ( fwise ) {
if ( nrrdFFTWWisdomRead( fwise ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with fft wisdom:\n%s", me, err );
airMopError( mop );
return 1;
}
fclose( fwise );
} else {
fprintf( stderr, "%s: ( \"%s\" couldn't be opened, will try to save "
"wisdom afterwards )", me, wispath );
}
}
/* ============== transform input to phase and magnitude */
for ( axi=0; axi<nrin->dim; axi++ ) {
axes[axi] = axi+1;
}
NRRDNEW( ncfin );
NRRDNEW( nR );
NRRDNEW( nI );
NRRDNEW( nP );
NRRDNEW( nM );
if ( nrrdFFT( ncfin, ncin, axes, nrin->dim, +1, AIR_TRUE, rigor )
|| nrrdSlice( nR, ncfin, 0, 0 )
|| nrrdSlice( nI, ncfin, 0, 1 )
|| nrrdArithBinaryOp( nP, nrrdBinaryOpAtan2, nI, nR )
|| nrrdProject( nM, ncfin, 0, nrrdMeasureL2, nrrdTypeDefault ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error processing input:\n%s", me, err );
airMopError( mop );
return 1;
}
/* ============== randomize phase */
NRRDNEW( nlut );
if ( nrrdMaybeAlloc_va( nlut, nrrdTypeDouble, 1, AIR_CAST( size_t, len ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error making lut:\n%s", me, err );
airMopError( mop );
return 1;
}
lut = AIR_CAST( double *, nlut->data );
for ( II=0; II<len; II++ ) {
/* random phase */
if ( II < len/2 ) {
lut[II] = AIR_AFFINE( 0, airDrandMT( ), 1, -AIR_PI, AIR_PI );
} else {
lut[II] = -lut[len-1-II];
}
}
NN = nrrdElementNumber( nP );
P = AIR_CAST( double *, nP->data );
for ( II=0; II<NN; II++ ) {
/* pp is the original input phase */
double pp = P[II];
/* pi is the index for the input phase */
unsigned int pi = airIndex( -AIR_PI, pp, AIR_PI, len );
/* lin is the best approximation ( up to len tables ) of original value */
/* double lin = AIR_AFFINE( -0.5, pi, len-0.5, -AIR_PI, AIR_PI ); */
/* printf( "%g %u %g %g\n", P[II], pi, lut[pi], lin ); */
/* lut[pi] is the randomized phase */
P[II] = AIR_LERP( howrand, pp, lut[pi] );
}
/* ============== transform ( new ) phase and magnitude to output */
njarg[0] = nR;
njarg[1] = nI;
NRRDNEW( ncfout );
NRRDNEW( ncdout );
NRRDNEW( ncout );
NRRDNEW( ntmp2 );
if ( nrrdArithUnaryOp( nR, nrrdUnaryOpCos, nP )
|| nrrdArithBinaryOp( nR, nrrdBinaryOpMultiply, nR, nM )
|| nrrdArithUnaryOp( nI, nrrdUnaryOpSin, nP )
|| nrrdArithBinaryOp( nI, nrrdBinaryOpMultiply, nI, nM )
|| nrrdJoin( ncfout, AIR_CAST( const Nrrd*const*, njarg ), 2, 0, AIR_TRUE )
|| nrrdFFT( ncdout, ncfout, axes, nrin->dim, -1, AIR_TRUE, rigor )
|| nrrdConvert( ntmp, ncdout, nrin->type )
|| nrrdConvert( ntmp2, ncin, nrin->type )
|| nrrdInset( ncout, ntmp2, ntmp, minInset ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error creating output\n%s", me, err );
//airMopError( mop );
//return 1;
}
/*
if ( nrrdSave( "cfin.nrrd", ncfin, NULL ) ||
nrrdSave( "cout.nrrd", ncout, NULL ) ||
nrrdSave( "cin.nrrd", ncin, NULL ) ||
nrrdSave( "tmp.nrrd", ntmp, NULL ) ||
nrrdSave( "R.nrrd", nR, NULL ) ||
nrrdSave( "I.nrrd", nI, NULL ) ||
nrrdSave( "P.nrrd", nP, NULL ) ||
nrrdSave( "M.nrrd", nM, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error saving tmps:\n%s", me, err );
airMopError( mop );
return 1;
}
*/
/* ============== DONE. saving things to save */
if ( airStrlen( wispath ) && nrrdFFTWEnabled ) {
if ( !( fwise = fopen( wispath, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing: %s\n",
me, wispath, strerror( errno ) );
airMopError( mop );
return 1;
}
if ( nrrdFFTWWisdomWrite( fwise ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with fft wisdom:\n%s", me, err );
airMopError( mop );
return 1;
}
fclose( fwise );
}
if ( airStrlen( imagOutS ) ) {
NRRDNEW( niout );
if ( nrrdSlice( niout, ncout, 0, 1 )
|| nrrdSave( imagOutS, niout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error slicing/saving imaginary output:\n%s",
me, err );
airMopError( mop );
return 1;
}
}
NRRDNEW( nrout );
if ( nrrdSlice( nrout, ncout, 0, 0 )
|| nrrdSave( outS, nrout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem slicing/saving real output:\n%s\n",
me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <math.h>
#include "../nrrd.h"
char *quadInfo = ( "generates quadratic test volumes, with isosurfaces "
"which should resemble z = A*x^2 + B*y^2" );
float
31 quadFunc( float x, float y, float z, float A, float B, float off ) {
return A*x*x + B*y*y - z + off;
}
int
37 main( int argc, const char *argv[] ) {
const char *me;
char *err, *out;
int size[3], xi, yi, zi;
hestOpt *hopt;
hestParm *hparm;
airArray *mop;
float min[3], max[3], AB[2], x, y, z, *data, off;
Nrrd *nout;
me = argv[0];
mop = airMopNew( );
hparm = hestParmNew( );
hopt = NULL;
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "s", "sx sy sz", airTypeInt, 3, 3, size, "128 128 128",
"dimensions of output volume" );
hestOptAdd( &hopt, "min", "x y z", airTypeFloat, 3, 3, min, "-1 -1 -1",
"lower bounding corner of volume" );
hestOptAdd( &hopt, "max", "x y z", airTypeFloat, 3, 3, max, "1 1 1",
"upper bounding corner of volume" );
hestOptAdd( &hopt, "c", "A B", airTypeFloat, 2, 2, AB, NULL,
"A and B quadratic coefficients" );
hestOptAdd( &hopt, "off", "z offset", airTypeFloat, 1, 1, &off, "0.0",
"vertical offset" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &out, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, quadInfo, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAlloc_va( nout, nrrdTypeFloat, 3,
AIR_CAST( size_t, size[0] ),
AIR_CAST( size_t, size[1] ),
AIR_CAST( size_t, size[2] ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem allocating volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
data = ( float * )nout->data;
for ( zi=0; zi<size[2]; zi++ ) {
z = AIR_AFFINE( 0, zi, size[2]-1, min[2], max[2] );
for ( yi=0; yi<size[1]; yi++ ) {
y = AIR_AFFINE( 0, yi, size[1]-1, min[1], max[1] );
for ( xi=0; xi<size[0]; xi++ ) {
x = AIR_AFFINE( 0, xi, size[0]-1, min[0], max[0] );
*data = quadFunc( x, y, z, AB[0], AB[1], off );
data += 1;
}
}
}
nrrdAxisInfoSet_va( nout, nrrdAxisInfoMin, min[0], min[1], min[2] );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoMax, max[0], max[1], max[2] );
nrrdAxisInfoSpacingSet( nout, 0 );
nrrdAxisInfoSpacingSet( nout, 1 );
nrrdAxisInfoSpacingSet( nout, 2 );
if ( nrrdSave( out, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
void
28 usage( char *me ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <nin1> <nin2>\n", me );
exit( 1 );
}
int
35 main( int argc, char **argv ) {
char *me, *err;
Nrrd *nrrd, *n2;
size_t size[NRRD_DIM_MAX];
me = argv[0];
if ( 3 != argc ) {
usage( me );
}
nrrdStateVerboseIO = 10;
if ( nrrdLoad( nrrd=nrrdNew( ), argv[1], NULL ) ) {
fprintf( stderr, "%s: trouble loading \"%s\":\n%s",
me, argv[1], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
fprintf( stderr, "%s: data for \"%s\" at %p\n", me, argv[1], nrrd->data );
if ( nrrdLoad( nrrd, argv[2], NULL ) ) {
fprintf( stderr, "%s: trouble loading \"%s\":\n%s",
me, argv[2], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
fprintf( stderr, "%s: data for \"%s\" at %p\n", me, argv[2], nrrd->data );
n2 = nrrdNew( );
nrrdAxisInfoGet_nva( nrrd, nrrdAxisInfoSize, size );
if ( nrrdWrap_nva( n2, nrrd->data, nrrd->type, nrrd->dim, size )
|| nrrdAxesMerge( n2, nrrd, 0 ) ) {
fprintf( stderr, "%s: trouble wrapping or merging \"%s\":\n%s",
me, argv[2], err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
fprintf( stderr, "%s: data for axmerge( \"%s\", 0 ) at %p\n",
me, argv[2], n2->data );
nrrdNuke( nrrd );
nrrdNix( n2 );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
int
28 main( int argc, char **argv ) {
char *me, *err;
Nrrd *nrrd;
NrrdIoState *nio;
char hstr[] = "NRRD0001\n"
"# Complete NRRD file format specification at:\n"
"# http://teem.sourceforge.net/nrrd/format.html\n"
"# one comment\n"
"# two comment\n"
"# three comment\n"
"type: float\n"
"dimension: 2\n"
"sizes: 91 114\n"
"centerings: node node\n"
"endian: big\n"
"encoding: raw\n"
"bingo:=bob\n"
"foo:=super duper fancy bar with corona\n"
/* "data file: tmp.raw\n" */;
char *wstr;
AIR_UNUSED( argc );
me = argv[0];
nrrdStateVerboseIO = 10;
nio = nrrdIoStateNew( );
nrrd = nrrdNew( );
nio->path = airStrdup( "." );
if ( nrrdStringRead( nrrd, hstr, nio ) ) {
fprintf( stderr, "%s: trouble reading string:\n%s",
me, err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
fprintf( stderr, "%s: nrrd->data = %p\n", me, nrrd->data );
nrrdSave( "out.nrrd", nrrd, NULL );
if ( nrrdStringWrite( &wstr, nrrd, NULL ) ) {
fprintf( stderr, "%s: trouble writing string:\n%s",
me, err = biffGet( NRRD ) );
free( err );
exit( 1 );
}
fprintf( stderr, "%s: |%s|\n", me, wstr );
free( wstr );
nrrdIoStateNix( nio );
nrrdNuke( nrrd );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <teem/air.h>
#include <teem/biff.h>
#include "../nrrd.h"
void
30 usage( char *me ) { /* 0 1 2 3 4 ( 5 ) */
fprintf( stderr, "usage: %s <min> <N> <max> <fileOut>\n", me );
exit( 1 );
}
int
36 main( int argc, char *argv[] ) {
char *me;
unsigned int ii, NN;
double min, max, *out;
Nrrd *nout;
me = argv[0];
if ( 5 != argc ) {
usage( me );
}
if ( 3 != ( sscanf( argv[2], "%u", &NN )
+ sscanf( argv[1], "%lf", &min )
+ sscanf( argv[3], "%lf", &max ) ) ) {
fprintf( stderr, "%s: couldn't parse %s %s %s double uint double",
me, argv[1], argv[2], argv[3] );
usage( me );
}
nout = nrrdNew( );
if ( nrrdAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, 5 ),
AIR_CAST( size_t, NN ) ) ) {
fprintf( stderr, "%s: trouble allocating:\n%s", me,
biffGetDone( NRRD ) );
exit( 1 );
}
out = AIR_CAST( double *, nout->data );
for ( ii=0; ii<NN; ii++ ) {
double xx, rr, ff, gg;
xx = AIR_AFFINE( 0, ii, NN-1, min, max );
rr = exp( xx );
ff = airFastExp( xx );
gg = airExp( xx );
if ( rr < 0 || ff < 0 || gg < 0
|| !AIR_EXISTS( rr ) || !AIR_EXISTS( ff ) || !AIR_EXISTS( gg ) ) {
fprintf( stderr, "%s: problem: %f -> real %f, approx %f %f\n",
me, xx, rr, ff, gg );
exit( 1 );
}
out[0] = rr;
out[1] = ff;
out[2] = ( ff-rr )/rr;
out[3] = gg;
out[4] = ( gg-rr )/rr;
out += 5;
}
if ( nrrdSave( argv[4], nout, NULL ) ) {
fprintf( stderr, "%s: trouble saving:\n%s", me,
biffGetDone( NRRD ) );
exit( 1 );
}
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
/*
** this program demonstrates parsing a string into a kernel with parms,
** verifies that all the kernel methods are consistent and/or equal,
** and produces a text file of the kernel evaluated many times.
** The output can be "plotted" with unu jhisto, as with:
** tkernel ctmr -2 0.0001 2 - | unu jhisto -b 800 300 | unu flip -a 1 | unu quantize -b 8 -o tmp.png
*/
void
35 usage( char *me ) {
/* 0 1 2 3 4 5 ( 6 ) ( 7 )*/
fprintf( stderr, "usage: %s <kernel> <min> <step> <max> <txtOut> [dkern]\n", me );
exit( 1 );
}
#define CLOSE( a, b, eps ) ( fabs( ( a )-( b ) ) < eps )
int
45 main( int argc, char *argv[] ) {
char *me, *kernS[2], *minS, *stepS, *maxS, *outS, *err, kstr[AIR_STRLEN_LARGE];
const NrrdKernel *kern[2];
NrrdKernelSpec *ksp[2];
double parm[NRRD_KERNEL_PARMS_NUM], min, step, max, integral,
*dom_d, *ran_d;
float *dom_f, *ran_f, val, r_f, r_d;
FILE *fout;
int i, len;
airArray *mop;
unsigned int kii;
me = argv[0];
if ( !( 6 == argc || 7 == argc ) ) {
usage( me );
}
kernS[0] = argv[1];
minS = argv[2];
stepS = argv[3];
maxS = argv[4];
outS = argv[5];
if ( 7 == argc ) {
kernS[1] = argv[6];
} else {
kernS[1] = NULL;
}
if ( 3 != ( sscanf( minS, "%lf", &min ) +
sscanf( stepS, "%lf", &step ) +
sscanf( maxS, "%lf", &max ) ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\", \"%s\", \"%s\" as 3 doubles\n",
me, minS, stepS, maxS );
exit( 1 );
}
mop = airMopNew( );
for ( kii=0; kii<=( kernS[1] ? 1 : 0 ); kii++ ) {
if ( nrrdKernelParse( &( kern[kii] ), parm, kernS[kii] ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: ( kii %u ) trouble:\n%s\n", me, kii, err );
airMopError( mop );
exit( 1 );
}
ksp[kii] = nrrdKernelSpecNew( );
airMopAdd( mop, ksp[kii], ( airMopper )nrrdKernelSpecNix, airMopAlways );
nrrdKernelSpecSet( ksp[kii], kern[kii], parm );
if ( nrrdKernelSpecSprint( kstr, ksp[kii] ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
exit( 1 );
}
fprintf( stderr, "%s: printed kernel as \"%s\"\n", me, kstr );
if ( !( min <= -kern[kii]->support( parm )
&& max >= kern[kii]->support( parm ) ) ) {
fprintf( stderr, "%s: WARNING: support=%g => lower min ( %g ) or raise max ( %g )\n",
me, kern[kii]->support( parm ), min, max );
}
fprintf( stderr, "%s: support( %s ) = %g\n", me, kstr, kern[kii]->support( parm ) );
}
/* see how many values are in the interval */
len = 0;
for ( val=min; val<=max; val+=step ) {
len++;
}
/* allocate domain and range for both float and double */
if ( !( ( dom_d = ( double * )calloc( len, sizeof( double ) ) ) &&
( ran_d = ( double * )calloc( len, sizeof( double ) ) ) &&
( dom_f = ( float * )calloc( len, sizeof( float ) ) ) &&
( ran_f = ( float * )calloc( len, sizeof( float ) ) ) ) ) {
fprintf( stderr, "%s: PANIC: couldn't allocate buffers\n", me );
exit( 1 );
}
airMopAdd( mop, dom_d, airFree, airMopAlways );
airMopAdd( mop, ran_d, airFree, airMopAlways );
airMopAdd( mop, dom_f, airFree, airMopAlways );
airMopAdd( mop, ran_f, airFree, airMopAlways );
/* set values in both domains */
i=0;
for ( val=min; val<=max; val+=step ) {
/* note that the value stored in dom_d[i] is only a
single-precision float, so that it is really equal to dom_f[i] */
dom_d[i] = val;
dom_f[i] = val;
i++;
}
/* do the vector evaluations */
kern[0]->evalN_f( ran_f, dom_f, len, parm );
kern[0]->evalN_d( ran_d, dom_d, len, parm );
/* do the single evaluations, and make sure everything agrees */
i = 0;
integral = 0;
for ( val=min; val<=max; val+=step ) {
/* compare two single evaluations */
r_f = kern[0]->eval1_f( val, parm );
r_d = kern[0]->eval1_d( val, parm );
if ( !CLOSE( r_f, r_d, 0.00001 ) ) {
fprintf( stderr, "%s: ( eval1_f( %g )== %f ) != ( eval1_d( %g )== %f )\n",
me, val, r_f, val, r_d );
}
/* compare single float with vector float */
if ( !CLOSE( r_f, ran_f[i], 0.00001 ) ) {
fprintf( stderr, "%s: ( eval1_f( %g )== %f ) != ( evalN_f[%d]== %f )\n",
me, val, r_f, i, ran_f[i] );
}
/* compare single float with vector double */
r_d = ran_d[i];
if ( !CLOSE( r_f, r_d, 0.00001 ) ) {
fprintf( stderr, "%s: ( eval1_f( %g )== %f ) != ( evalN_d[%d]== %f )\n",
me, val, r_f, i, r_d );
}
integral += step*ran_d[i];
/* possibly check on given derivatives */
if ( kern[1] ) {
double numd;
numd = ( kern[0]->eval1_d( val+step/2, parm )
- kern[0]->eval1_d( val-step/2, parm ) )/step;
if ( !CLOSE( numd, kern[1]->eval1_d( val+step, parm ), 0.005 ) ) {
fprintf( stderr, "%s: |numerical f'( %g ) %g - true %g| = %g > 0.005\n",
me, val, numd, kern[1]->eval1_d( val+step, parm ),
fabs( numd - kern[1]->eval1_d( val+step, parm ) ) );
/* exit( 1 ); */
}
}
i++;
}
if ( !CLOSE( integral, kern[0]->integral( parm ), 0.0005 ) ) {
fprintf( stderr, "%s: HEY HEY HEY HEY HEY HEY!\n", me );
fprintf( stderr,
"%s: discrete integral %f != %f\n", me, integral, kern[0]->integral( parm ) );
/* exit( 1 ); */
}
/* it all checks out; write the file */
if ( !( fout = airFopen( outS, stdout, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing\n", me, outS );
exit( 1 );
}
for ( i=0; i<=len-1; i++ ) {
fprintf( fout, "%g %g\n", dom_f[i], ran_f[i] );
}
fclose( fout );
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
/* learned: C++ name mangling means that you can't simply declare the
function as extern, you need to do the same extern "C" wrapping as
is done in the header file
*/
#ifdef __cplusplus
extern "C" {
#endif
33 extern int _nrrdOneLine( unsigned int *lenP, NrrdIoState *io, FILE *file );
#ifdef __cplusplus
}
#endif
FILE *
39 myopen( char *name ) {
if ( !strcmp( name, "-" ) ) {
return stdin;
} else {
return fopen( name, "r" );
}
}
void
49 myclose( FILE *file ) {
if ( file != stdin ) {
fclose( file );
}
return;
}
int
59 main( int argc, char *argv[] ) {
char *me, *fileS;
FILE *file;
unsigned int llen;
NrrdIoState *io;
me = argv[0];
if ( 2 != argc ) {
/* 0 1 ( 2 ) */
fprintf( stderr, "usage: %s <file>\n", me );
exit( 1 );
}
fileS = argv[1];
if ( !( file = myopen( fileS ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for reading\n", me, fileS );
exit( 1 );
}
io = nrrdIoStateNew( );
do {
if ( _nrrdOneLine( &llen, io, file ) ) {
fprintf( stderr, "%s: trouble:\n%s", me, biffGet( NRRD ) );
exit( 1 );
}
if ( llen ) {
printf( "%2u |%s|\n", llen, io->line );
}
} while( llen > 0 );
nrrdIoStateNix( io );
myclose( file );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
#define BINS 1024
#define HGHT 800
int
30 main( int argc, char **argv ) {
Nrrd *nval, *nhist, *npgm;
double *val;
int i;
AIR_UNUSED( argc );
AIR_UNUSED( argv );
nrrdAlloc_va( nval=nrrdNew( ), nrrdTypeDouble, 1,
AIR_CAST( size_t, BINS*BINS ) );
val = ( double * )nval->data;
airSrandMT( ( int )airTime( ) );
for ( i=0; i<BINS*BINS; i++ ) {
val[i] = airDrandMT( );
}
nrrdHisto( nhist=nrrdNew( ), nval, NULL, NULL, BINS, nrrdTypeInt );
nrrdHistoDraw( npgm=nrrdNew( ), nhist, HGHT, AIR_FALSE, 0.0 );
nrrdSave( "hist.pgm", npgm, NULL );
nrrdNuke( nval );
nrrdNuke( nhist );
nrrdNuke( npgm );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
int
28 main( int argc, char *argv[] ) {
char *me, *ninName, *noutName, *err;
Nrrd *nin;
me = argv[0];
if ( 3 != argc ) {
/* 0 1 2 ( 3 ) */
fprintf( stderr, "usage: %s <nin> <nout>\n", me );
exit( 1 );
}
ninName = argv[1];
noutName = argv[2];
if ( nrrdLoad( nin=nrrdNew( ), ninName, NULL ) ) {
fprintf( stderr, "%s: couldn't open nrrd \"%s\":\n%s", me, ninName,
err = biffGetDone( NRRD ) );
free( err ); exit( 1 );
}
if ( nrrdSave( noutName, nin, NULL ) ) {
fprintf( stderr, "%s: trouble saving nrrd to \"%s\":\n%s", me, noutName,
err = biffGetDone( NRRD ) );
free( err ); exit( 1 );
}
nrrdNuke( nin );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../nrrd.h"
int
28 main( int argc, char *argv[] ) {
int tt;
/*
char str1[AIR_STRLEN_MED], str2[AIR_STRLEN_MED],
str3[AIR_STRLEN_MED];
*/
AIR_UNUSED( argc );
AIR_UNUSED( argv );
for ( tt=nrrdTypeChar; tt<=nrrdTypeDouble; tt++ ) {
printf( " ----- %s -----\n", airEnumStr( nrrdType, tt ) );
printf( "nrrdTypeSize: %d\n", ( int )nrrdTypeSize[tt] );
printf( "nrrdTypeIsUnsigned: %d\n", nrrdTypeIsUnsigned[tt] );
printf( "nrrdTypeIsIntegral: %d\n", nrrdTypeIsIntegral[tt] );
printf( "nrrdTypeMin: % 31.15f\n", nrrdTypeMin[tt] );
printf( "nrrdTypeMax: % 31.15f\n", nrrdTypeMax[tt] );
}
printf( "sizeof( size_t ) = %d\n", ( int )sizeof( size_t ) );
/*
c = -10;
uc = 10;
s = -10;
us = 10;
i = -10;
ui = 10;
lli = -10;
ulli = 10;
f = 3.14159324234098320948172304987123;
d = 3.14159324234098320948172304987123;
printf( "c: %d\n", c );
printf( "uc: %u\n", uc );
printf( "s: %hd\n", s );
printf( "us: %hu\n", us );
printf( "i: %d\n", i );
printf( "ui: %u\n", ui );
printf( "lli: %lld\n", lli );
printf( "ulli: %llu\n", ulli );
printf( "f: %f\n", f );
printf( "d: %lf\n", d );
sprintf( str1, "-10" );
sprintf( str2, "10" );
sprintf( str3, "3.14159324234098320948172304987123" );
sscanf( str1, "%d", &c );
sscanf( str2, "%u", &uc );
sscanf( str1, "%hd", &s );
sscanf( str2, "%hu", &us );
sscanf( str1, "%d", &i );
sscanf( str2, "%u", &ui );
sscanf( str1, "%lld", &lli );
sscanf( str2, "%llu", &ulli );
sscanf( str3, "%f", &f );
sscanf( str3, "%lf", &d );
printf( "\n" );
printf( "c: %d\n", c );
printf( "uc: %u\n", uc );
printf( "s: %hd\n", s );
printf( "us: %hu\n", us );
printf( "i: %d\n", i );
printf( "ui: %u\n", ui );
printf( "lli: %lld\n", lli );
printf( "ulli: %llu\n", ulli );
printf( "f: %f\n", f );
printf( "d: %lf\n", d );
*/
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
### Written by Torsten Moeller
### sometime in March 1998
### email me if you find any errors
###
### March 2003 - modified to remove coeff part
*/
#pragma warning( disable:4244 )
#pragma warning( disable:4305 )
#define OVER_3 0.33333333
#define OVER_6 0.16666666
#define OVER_12 0.0833333333
#define OVER_2_3 0.6666666666
#include "tmFilters_raw.h"
/***************************************************************************/
/* Approximation Filters */
/***************************************************************************/
46 float dn_cn_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = 0.5; break;
case 1: result = 0.5; break;
default: result = 0;
}
return result;
}
62 float dn_cn_2ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
67 float dn_cn_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( 0.25*t +( 2*a-0.25 ) )*t - a ; break;
case 1: result = ( -0.25*t -( 6*a-1.25 ) )*t + 3*a ; break;
case 2: result = ( -0.25*t +( 6*a-0.75 ) )*t - 3*a+1; break;
case 3: result = ( 0.25*t -( 2*a+0.25 ) )*t + a ; break;
default: result = 0;
}
return result;
}
85 float dn_cn_4ef( float a, float t ) {
return d0_c0_4ef( a, t );
}
90 float dn_c0_1ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
95 float dn_c0_2ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
100 float dn_c0_3ef( float a, float t ) {
return d0_c0_3ef( a, t );
}
105 float dn_c0_4ef( float a, float t ) {
return d0_c0_4ef( a, t );
}
110 float dn_c1_1ef( float a, float t ) {
return d0_c1_1ef( a, t );
}
115 float dn_c1_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( t )*t/4; break;
case 1: result = ( -t +2 )*t/4 +0.25; break;
case 2: result = ( -t )*t/4 +0.5; break;
case 3: result = ( t -2 )*t/4 +0.25; break;
default: result = 0;
}
return result;
}
133 float dn_c1_3ef( float a, float t ) {
return d0_c1_3ef( a, t );
}
138 float dn_c1_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( -( a )*t +( 1.5*a- 1./24 ) )*t + 0 )*t + 0; break;
case 1: result = ( ( ( 5*a+OVER_6 )*t -( 7.5*a- 1./8 ) )*t - ( OVER_12 ) )*t +( 0.5*a-1./24 ); break;
case 2: result = ( ( -( 10*a+ 0.5 )*t +( 15*a+ 5./12 ) )*t +( OVER_2_3 ) )*t -( 2*a-OVER_6 ); break;
case 3: result = ( ( ( 10*a+ 0.5 )*t -( 15*a+13./12 ) )*t + 0 )*t +( 3*a+0.75 ); break;
case 4: result = ( ( -( 5*a+OVER_6 )*t +( 7.5*a+ 5./8 ) )*t -( OVER_2_3 ) )*t -( 2*a-OVER_6 ); break;
case 5: result = ( ( ( a )*t -( 1.5*a+ 1./24 ) )*t + ( OVER_12 ) )*t +( 0.5*a-1./24 ); break;
default: result = 0;
}
return result;
}
158 float dn_c2_1ef( float a, float t ) {
return d0_c2_1ef( a, t );
}
163 float dn_c2_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( OVER_6*t + 0 )*t + 0 )*t + 0; break;
case 1: result = ( ( -0.5*t +0.5 )*t +0.5 )*t + OVER_6; break;
case 2: result = ( ( 0.5*t - 1 )*t + 0 )*t +OVER_2_3; break;
case 3: result = ( ( -OVER_6*t +0.5 )*t -0.5 )*t + OVER_6; break;
default: result = 0;
}
return result;
}
181 float dn_c2_3ef( float a, float t ) {
return d0_c2_3ef( a, t );
}
186 float dn_c2_4ef( float a, float t ) {
return dn_c1_4ef( 1./36, t );
}
191 float dn_c3_1ef( float a, float t ) {
return d0_c3_1ef( a, t );
}
196 float dn_c3_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( -0.10*t +0.25 )*t*t*t*t; break;
case 1: result = ( ( ( 0.30*t -0.75 )*t*t +0.5 )*t +0.5 )*t +0.15; break;
case 2: result = ( ( ( -0.30*t +0.75 )*t*t -1 )*t +0 )*t +0.70; break;
case 3: result = ( ( ( 0.10*t -0.25 )*t*t +0.5 )*t -0.5 )*t +0.15; break;
default: result = 0;
}
return result;
}
214 float dn_c3_3ef( float a, float t ) {
return d0_c3_3ef( a, t );
}
219 float dn_c3_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( 1./30*t - 1./16 )*t + 0 )*t + 0 )*t + 0 )*t + 0; break;
case 1: result = ( ( ( ( -OVER_6*t +17./48 )*t +OVER_12 )*t - 1./24 )*t - OVER_12 )*t - 7./240; break;
case 2: result = ( ( ( ( OVER_3*t -19./24 )*t -OVER_6 )*t +OVER_2_3 )*t +OVER_2_3 )*t + 7./60; break;
case 3: result = ( ( ( ( -OVER_3*t + 7./8 )*t + 0 )*t - 1.25 )*t + 0 )*t +33./40; break;
case 4: result = ( ( ( ( OVER_6*t -23./48 )*t +OVER_6 )*t +OVER_2_3 )*t -OVER_2_3 )*t + 7./60; break;
case 5: result = ( ( ( ( - 1./30*t + 5./48 )*t -OVER_12 )*t - 1./24 )*t + OVER_12 )*t - 7./240; break;
default: result = 0;
}
return result;
}
/***************************************************************************/
/* Interpolation Filters */
/***************************************************************************/
243 float d0_cn_1ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
248 float d0_cn_2ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
253 float d0_cn_3ef( float a, float t ) {
return d0_c0_3ef( a, t );
}
258 float d0_cn_4ef( float a, float t ) {
return d0_c0_4ef( a, t );
}
263 float d0_c0_1ef( float a, float t ) {
return d0_c0_2ef( a, t );
}
268 float d0_c0_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = t; break;
case 1: result = 1-t; break;
default: result = 0;
}
return result;
}
284 float d0_c0_3ef( float a, float t ) {
return dn_cn_3ef( 0, t );
}
289 float d0_c0_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( OVER_6*t +0 )*t -OVER_6 )*t ; break;
case 1: result = ( ( -0.5*t +0.5 )*t +1 )*t ; break;
case 2: result = ( ( 0.5*t -1 )*t -0.5 )*t+1; break;
case 3: result = ( ( -OVER_6*t +0.5 )*t -OVER_3 )*t ; break;
default: result = 0;
}
return result;
}
308 float d0_c1_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( -2*t +3 )*t*t; break;
case 1: result = ( 2*t -3 )*t*t +1; break;
default: result = 0;
}
return result;
}
324 float d0_c1_2ef( float a, float t ) {
return d0_c1_3ef( a, t );
}
329 float d0_c1_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 0.5*t -0.5 )*t +0 )*t; break;
case 1: result = ( ( -1.5*t +2 )*t +0.5 )*t; break;
case 2: result = ( ( 1.5*t -2.5 )*t +0 )*t +1; break;
case 3: result = ( ( -0.5*t +1 )*t -0.5 )*t; break;
default: result = 0;
}
return result;
}
347 float d0_c1_4ef( float a, float t ) {
return dn_c1_4ef( 1./12, t );
}
352 float d0_c2_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 6*t -15 )*t +10 )*t*t*t; break;
case 1: result = ( ( -6*t +15 )*t -10 )*t*t*t +1; break;
default: result = 0;
}
return result;
}
368 float d0_c2_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( 0.5*t -0.5 )*t + 0 )*t + 0 )*t; break;
case 1: result = ( ( ( -0.5*t -0.5 )*t +1.5 )*t +0.5 )*t; break;
case 2: result = ( ( ( -0.5*t +2.5 )*t - 3 )*t + 0 )*t +1; break;
case 3: result = ( ( ( 0.5*t -1.5 )*t +1.5 )*t -0.5 )*t; break;
default: result = 0;
}
return result;
}
386 float d0_c2_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( -1*t +2.5 )*t -1.5 )*t +0 )*t +0 )*t ; break;
case 1: result = ( ( ( ( 3*t -7.5 )*t +4.5 )*t +0.5 )*t +0.5 )*t ; break;
case 2: result = ( ( ( ( -3*t +7.5 )*t -4.5 )*t -1 )*t +0 )*t +1; break;
case 3: result = ( ( ( ( 1*t -2.5 )*t +1.5 )*t +0.5 )*t -0.5 )*t ; break;
default: result = 0;
}
return result;
}
404 float d0_c2_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( -1./12*t + 1./12 )*t + 0 )*t + 0 )*t; break;
case 1: result = ( ( ( 1./4 *t + 1./12 )*t -1./4 )*t - OVER_12 )*t; break;
case 2: result = ( ( ( -1./6 *t - 1 )*t +3./2 )*t +OVER_2_3 )*t; break;
case 3: result = ( ( ( -1./6 *t + 5./3 )*t -5./2 )*t + 0 )*t +1; break;
case 4: result = ( ( ( 1./4 *t -13./12 )*t +3./2 )*t -OVER_2_3 )*t; break;
case 5: result = ( ( ( -1./12*t + 1./4 )*t -1./4 )*t + OVER_12 )*t; break;
default: result = 0;
}
return result;
}
424 float d0_c3_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( -20*t +70 )*t -84 )*t +35 )*t*t*t*t; break;
case 1: result = ( ( ( 20*t -70 )*t +84 )*t -35 )*t*t*t*t +1; break;
default: result = 0;
}
return result;
}
440 float d0_c3_2ef( float a, float t ) {
return d0_c3_3ef( a, t );
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( -0.75*t +2 )*t -1.25 )*t*t +0 )*t +0 )*t ; break;
case 1: result = ( ( ( ( 0.75*t -1.5 )*t +0 )*t*t +1.25 )*t +0.5 )*t ; break;
case 2: result = ( ( ( ( 0.75*t -3 )*t +3.75 )*t*t -2.5 )*t +0 )*t +1; break;
case 3: result = ( ( ( ( -0.75*t +2.5 )*t -2.5 )*t*t +1.25 )*t -0.5 )*t ; break;
default: result = 0;
}
return result;
}
459 float d0_c3_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( ( 3*t -10.5 )*t +12.5 )*t - 5 )*t*t +0 )*t +0 )*t ; break;
case 1: result = ( ( ( ( ( -9*t +31.5 )*t -37.5 )*t +15 )*t*t +0.5 )*t +0.5 )*t ; break;
case 2: result = ( ( ( ( ( 9*t -31.5 )*t +37.5 )*t -15 )*t*t -1 )*t +0 )*t +1; break;
case 3: result = ( ( ( ( ( -3*t +10.5 )*t -12.5 )*t + 5 )*t*t +0.5 )*t -0.5 )*t ; break;
default: result = 0;
}
return result;
}
477 float d0_c3_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( ( 7./48*t - 3./8 )*t +11./48 )*t +0 )*t + 0 )*t + 0 )*t; break;
case 1: result = ( ( ( ( ( -7./16*t + 1 )*t - 3./8 )*t +1./12 )*t - 3./16 )*t - OVER_12 )*t; break;
case 2: result = ( ( ( ( ( 7./24*t - 1./4 )*t -19./24 )*t -1./6 )*t + 5./4 )*t +OVER_2_3 )*t; break;
case 3: result = ( ( ( ( ( 7./24*t - 3./2 )*t + 7./3 )*t +0 )*t -17./8 )*t + 0 )*t +1; break;
case 4: result = ( ( ( ( ( -7./16*t +13./8 )*t -31./16 )*t +1./6 )*t + 5./4 )*t -OVER_2_3 )*t; break;
case 5: result = ( ( ( ( ( 7./48*t - 1./2 )*t +13./24 )*t -1./12 )*t - 3./16 )*t + OVER_12 )*t; break;
default: result = 0;
}
return result;
}
/***************************************************************************/
/* First Derivative Filters */
/***************************************************************************/
502 float d1_cn_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = 1; break;
case 1: result = -1; break;
default: result = 0;
}
return result;
}
518 float d1_cn_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = 0.5*t +( a ); break;
case 1: result = -0.5*t -( 3*a-0.5 ); break;
case 2: result = -0.5*t +( 3*a ); break;
case 3: result = 0.5*t -( a+0.5 ); break;
default: result = 0;
}
return result;
}
536 float d1_cn_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( 0.5*t +0 )*t -OVER_6; break;
case 1: result = ( -1.5*t +1 )*t +1; break;
case 2: result = ( 1.5*t -2 )*t -0.5; break;
case 3: result = ( -0.5*t +1 )*t -OVER_3; break;
default: result = 0;
}
return result;
}
554 float d1_cn_4ef( float a, float t ) {
return d1_c0_4ef( a, t );
}
559 float d1_c0_1ef( float a, float t ) {
return d1_c0_2ef( a, t );
}
564 float d1_c0_2ef( float a, float t ) {
return d1_cn_2ef( 0, t );
}
569 float d1_c0_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( a )*t -( a+OVER_12 ) )*t +0; break;
case 1: result = ( -( 5*a-0.5 )*t +( 5*a+ 0.25 ) )*t -OVER_12; break;
case 2: result = ( ( 10*a-1.5 )*t -( 10*a- 5./6 ) )*t +OVER_2_3; break;
case 3: result = ( -( 10*a-1.5 )*t +( 10*a- 13./6 ) )*t +0; break;
case 4: result = ( ( 5*a-0.5 )*t -( 5*a- 1.25 ) )*t -OVER_2_3; break;
case 5: result = ( -( a )*t +( a-OVER_12 ) )*t +OVER_12; break;
default: result = 0;
}
return result;
}
589 float d1_c0_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( OVER_12*t +( a ) )*t -( a+ OVER_6 ) )*t +0; break;
case 1: result = ( ( -0.25*t -( 5*a-0.25 ) )*t +( 5*a+ 0.75 ) )*t -OVER_12; break;
case 2: result = ( ( OVER_6*t +( 10*a- 0.5 ) )*t -( 10*a+ OVER_3 ) )*t +OVER_2_3; break;
case 3: result = ( ( OVER_6*t -( 10*a ) )*t +( 10*a- 5./6 ) )*t +0; break;
case 4: result = ( ( -0.25*t +( 5*a+ 0.5 ) )*t -( 5*a- 0.5 ) )*t -OVER_2_3; break;
case 5: result = ( ( OVER_12*t -( a+0.25 ) )*t +( a+OVER_12 ) )*t +OVER_12; break;
default: result = 0;
}
return result;
}
609 float d1_c1_1ef( float a, float t ) {
return d1_c1_2ef( a, t );
}
614 float d1_c1_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( 0.5*t +0 )*t +0; break;
case 1: result = ( -1.5*t +1 )*t +0.5; break;
case 2: result = ( 1.5*t -2 )*t +0; break;
case 3: result = ( -0.5*t +1 )*t -0.5; break;
default: result = 0;
}
return result;
}
632 float d1_c1_3ef( float a, float t ) {
return d1_c0_3ef( -1./12, t );
}
637 float d1_c1_4ef( float a, float t ) {
return d1_c0_4ef( -1./6, t );
}
642 float d1_c2_1ef( float a, float t ) {
return d1_c2_2ef( a, t );
}
647 float d1_c2_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( -0.5*t +1 )*t*t +0 )*t +0; break;
case 1: result = ( ( 1.5*t -3 )*t*t +1 )*t +0.5; break;
case 2: result = ( ( -1.5*t +3 )*t*t -2 )*t +0; break;
case 3: result = ( ( 0.5*t -1 )*t*t +1 )*t -0.5; break;
default: result = 0;
}
return result;
}
665 float d1_c2_3ef( float a, float t ) {
return d1_c2_4ef( a, t );
}
670 float d1_c2_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( OVER_6*t - 0.25 )*t + 0 )*t + 0 )*t +0; break;
case 1: result = ( ( ( - 5./6*t +17./12 )*t +0.25 )*t -OVER_12 )*t -OVER_12; break;
case 2: result = ( ( ( 5./3*t - 19./6 )*t -0.5 )*t + 4./3 )*t +OVER_2_3; break;
case 3: result = ( ( ( - 5./3*t + 3.5 )*t +0 )*t - 2.5 )*t +0; break;
case 4: result = ( ( ( 5./6*t -23./12 )*t +0.5 )*t + 4./3 )*t -OVER_2_3; break;
case 5: result = ( ( ( -OVER_6*t + 5./12 )*t -0.25 )*t -OVER_12 )*t +OVER_12; break;
default: result = 0;
}
return result;
}
690 float d1_c3_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( -0.75*t +1.25 )*t + 0 )*t*t + 0 )*t +0; break;
case 1: result = ( ( ( 0.75*t + 0 )*t -2.5 )*t*t +1.25 )*t +0.5; break;
case 2: result = ( ( ( 0.75*t -3.75 )*t +5 )*t*t -2.5 )*t +0; break;
case 3: result = ( ( ( -0.75*t +2.5 )*t -2.5 )*t*t +1.25 )*t -0.5; break;
default: result = 0;
}
return result;
}
708 float d1_c3_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( 1*t -3 )*t +2.5 )*t*t*t +0 )*t +0; break;
case 1: result = ( ( ( -3*t +9 )*t -7.5 )*t*t*t +1 )*t +0.5; break;
case 2: result = ( ( ( 3*t -9 )*t +7.5 )*t*t*t -2 )*t +0; break;
case 3: result = ( ( ( -1*t +3 )*t -2.5 )*t*t*t +1 )*t -0.5; break;
default: result = 0;
}
return result;
}
726 float d1_c3_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( 3./16*t - 13./48 )*t + 0 )*t + 0 )*t + 0 )*t +0; break;
case 1: result = ( ( ( ( -9./16*t + 5./12 )*t +19./24 )*t +0.25 )*t - 7./48 )*t -OVER_12; break;
case 2: result = ( ( ( ( 3./8 *t + 25./24 )*t -19./6 )*t - 0.5 )*t +19./12 )*t +OVER_2_3; break;
case 3: result = ( ( ( ( 3./8 *t - 35./12 )*t +19./4 )*t + 0 )*t -23./8 )*t +0; break;
case 4: result = ( ( ( ( -9./16*t + 115./48 )*t -19./6 )*t + 0.5 )*t +19./12 )*t -OVER_2_3; break;
case 5: result = ( ( ( ( 3./16*t -OVER_2_3 )*t +19./24 )*t -0.25 )*t - 7./48 )*t +OVER_12; break;
default: result = 0;
}
return result;
}
746 float d1_c3_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( ( -0.25*t +0.75 )*t - 7./12 )*t + 0 )*t + 0 )*t + 0 )*t +0; break;
case 1: result = ( ( ( ( ( 1.25*t -3.75 )*t +35./12 )*t + OVER_6 )*t +0.25 )*t -OVER_12 )*t -OVER_12; break;
case 2: result = ( ( ( ( ( -2.5*t + 7.5 )*t -35./6 )*t -OVER_2_3 )*t - 0.5 )*t + 4./3 )*t +OVER_2_3; break;
case 3: result = ( ( ( ( ( 2.5*t - 7.5 )*t +35./6 )*t + 1 )*t + 0 )*t - 5./2 )*t +0; break;
case 4: result = ( ( ( ( ( -1.25*t +3.75 )*t -35./12 )*t -OVER_2_3 )*t + 0.5 )*t + 4./3 )*t -OVER_2_3; break;
case 5: result = ( ( ( ( ( 0.25*t -0.75 )*t + 7./12 )*t + OVER_6 )*t -0.25 )*t -OVER_12 )*t +OVER_12; break;
default: result = 0;
}
return result;
}
/***************************************************************************/
/* Second Derivative Filters */
/***************************************************************************/
772 float d2_cn_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = 0.5; break;
case 1: result = -0.5; break;
case 2: result = -0.5; break;
case 3: result = 0.5; break;
default: result = 0;
}
return result;
}
790 float d2_cn_2ef( float a, float t ) {
return d2_c0_2ef( a, t );
}
795 float d2_cn_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( 0.25*t +( a-30 )/120 )*t -( a+10 )/240; break;
case 1: result = ( -0.75*t -( a-42 )/24 )*t +( a+ 6 )/48; break;
case 2: result = ( 0.5 *t +( a-42 )/12 )*t -( a-22 )/24; break;
case 3: result = ( 0.5 *t -( a-30 )/12 )*t +( a-50 )/24; break;
case 4: result = ( -0.75*t +( a- 6 )/24 )*t -( a-54 )/48; break;
case 5: result = ( 0.25*t -( a+30 )/120 )*t +( a-10 )/240; break;
default: result = 0;
}
return result;
}
815 float d2_cn_4ef( float a, float t ) {
return d2_c0_4ef( a, t );
}
820 float d2_c0_1ef( float a, float t ) {
return d2_c0_2ef( a, t );
}
825 float d2_c0_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = t; break;
case 1: result = -3*t +1; break;
case 2: result = 3*t -2; break;
case 3: result = - t +1; break;
default: result = 0;
}
return result;
}
843 float d2_c0_3ef( float a, float t ) {
return d2_cn_3ef( -10, t );
}
848 float d2_c0_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 1./6*t +0 )*t -0.25 )*t + 0; break;
case 1: result = ( ( -5./6*t +0.5 )*t +1.75 )*t -1./12; break;
case 2: result = ( ( 5./3*t -2 )*t -3.5 )*t + 4./3; break;
case 3: result = ( ( -5./3*t +3 )*t +2.5 )*t - 2.5; break;
case 4: result = ( ( 5./6*t -2 )*t -0.25 )*t + 4./3; break;
case 5: result = ( ( -1./6*t +0.5 )*t -0.25 )*t -1./12; break;
default: result = 0;
}
return result;
}
868 float d2_c1_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( -2*t +3 )*t*t +0; break;
case 1: result = ( 6*t -9 )*t*t +1; break;
case 2: result = ( -6*t +9 )*t*t -2; break;
case 3: result = ( 2*t -3 )*t*t +1; break;
default: result = 0;
}
return result;
}
886 float d2_c1_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( 0.25*t +0 )*t; break;
case 1: result = ( -0.75*t +0.5 )*t +0.25; break;
case 2: result = ( 0.5 *t -1 )*t; break;
case 3: result = ( 0.5 *t +0 )*t -0.5; break;
case 4: result = ( -0.75*t +1 )*t; break;
case 5: result = ( 0.25*t -0.5 )*t +0.25; break;
default: result = 0;
}
return result;
}
905 float d2_c1_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 2./3*t - 0.75 )*t +0 )*t; break;
case 1: result = ( ( -10./3*t + 4.25 )*t +0.5 )*t -1./12; break;
case 2: result = ( ( 20./3*t - 9.5 )*t -1 )*t +4./3; break;
case 3: result = ( ( -20./3*t +10.5 )*t +0 )*t -2.5; break;
case 4: result = ( ( 10./3*t - 5.75 )*t +1 )*t +4./3; break;
case 5: result = ( ( - 2./3*t + 1.25 )*t -0.5 )*t -1./12; break;
default: result = 0;
}
return result;
}
925 float d2_c1_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( -( a+ 53 )/360*t +( a+ 38 )/240 )*t +0 )*t; break;
case 1: result = ( ( ( 7*a+431 )/360*t -( 7*a+296 )/240 )*t - 1./8 )*t +( a+ 8 )/720; break;
case 2: result = ( ( -( 7*a+471 )/120*t +( 7*a+366 )/80 )*t +1 )*t -( a+18 )/120; break;
case 3: result = ( ( ( 7*a+491 )/72 *t -( 7*a+452 )/48 )*t -13./8 )*t +( a+72 )/48 ; break;
case 4: result = ( ( -( 7*a+491 )/72 *t +( 7*a+530 )/48 )*t +0 )*t -( a+98 )/36 ; break;
case 5: result = ( ( ( 7*a+471 )/120*t -( 7*a+576 )/80 )*t +13./8 )*t +( a+72 )/48 ; break;
case 6: result = ( ( -( 7*a+431 )/360*t +( 7*a+566 )/240 )*t -1 )*t -( a+18 )/120; break;
case 7: result = ( ( ( a+ 53 )/360*t -( a+ 68 )/240 )*t + 1./8 )*t +( a+ 8 )/720; break;
default: result = 0;
}
return result;
}
947 float d2_c2_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 6*t -15 )*t +10 )*t*t*t; break;
case 1: result = ( ( -18*t +45 )*t -30 )*t*t*t +1; break;
case 2: result = ( ( 18*t -45 )*t +30 )*t*t*t -2; break;
case 3: result = ( ( - 6*t +15 )*t -10 )*t*t*t +1; break;
default: result = 0;
}
return result;
}
965 float d2_c2_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( 1./6*t +0 )*t +0 )*t; break;
case 1: result = ( ( -5./6*t +0.5 )*t +0.5 )*t +1./6; break;
case 2: result = ( ( 5./3*t -2 )*t -1 )*t +1./3; break;
case 3: result = ( ( -5./3*t +3 )*t +0 )*t -1; break;
case 4: result = ( ( 5./6*t -2 )*t +1 )*t +1./3; break;
case 5: result = ( ( -1./6*t +0.5 )*t -0.5 )*t +1./6; break;
default: result = 0;
}
return result;
}
985 float d2_c2_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( -1.5*t + 3.75 )*t - 7./3 )*t +0 )*t +0 )*t; break;
case 1: result = ( ( ( ( 7.5*t -18.75 )*t +35./3 )*t +0.5 )*t +0.5 )*t -1./12; break;
case 2: result = ( ( ( ( - 15*t +37.5 )*t -70./3 )*t -2 )*t -1 )*t +4./3 ; break;
case 3: result = ( ( ( ( 15*t -37.5 )*t +70./3 )*t +3 )*t +0 )*t -2.5 ; break;
case 4: result = ( ( ( ( -7.5*t +18.75 )*t -35./3 )*t -2 )*t +1 )*t +4./3 ; break;
case 5: result = ( ( ( ( 1.5*t - 3.75 )*t + 7./3 )*t +0.5 )*t -0.5 )*t -1./12; break;
default: result = 0;
}
return result;
}
1005 float d2_c2_4ef( float a, float t ) {
return d2_c1_4ef( -38, t );
}
1010 float d2_c3_1ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( -20*t + 70 )*t - 84 )*t + 35 )*t*t*t*t; break;
case 1: result = ( ( ( 60*t -210 )*t +252 )*t -105 )*t*t*t*t +1; break;
case 2: result = ( ( ( -60*t +210 )*t -252 )*t +105 )*t*t*t*t -2; break;
case 3: result = ( ( ( 20*t - 70 )*t + 84 )*t - 35 )*t*t*t*t +1; break;
default: result = 0;
}
return result;
}
1028 float d2_c3_2ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( -0.1*t +0.25 )*t*t + 0 )*t +0 )*t; break;
case 1: result = ( ( ( 0.5*t -1.25 )*t*t +0.5 )*t +0.5 )*t +3./20; break;
case 2: result = ( ( ( -1 *t +2.5 )*t*t -2 )*t -1 )*t +2./5; break;
case 3: result = ( ( ( 1 *t -2.5 )*t*t +3 )*t +0 )*t -11./10; break;
case 4: result = ( ( ( -0.5*t +1.25 )*t*t -2 )*t +1 )*t +2./5; break;
case 5: result = ( ( ( 0.1*t -0.25 )*t*t +0.5 )*t -0.5 )*t +3./20; break;
default: result = 0;
}
return result;
}
1048 float d2_c3_3ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( ( 14./3*t - 49./3 )*t + 39./2 )*t - 95./12 )*t*t +0 )*t +0 )*t; break;
case 1: result = ( ( ( ( ( - 70./3*t +245./3 )*t -195./2 )*t +475./12 )*t*t +0.5 )*t +0.5 )*t -1./12; break;
case 2: result = ( ( ( ( ( 140./3*t -490./3 )*t +195 )*t -475./6 )*t*t -2 )*t -1 )*t +4./3; break;
case 3: result = ( ( ( ( ( -140./3*t +490./3 )*t -195 )*t +475./6 )*t*t +3 )*t +0 )*t -5./2; break;
case 4: result = ( ( ( ( ( 70./3*t -245./3 )*t +195./2 )*t -475./12 )*t*t -2 )*t +1 )*t +4./3; break;
case 5: result = ( ( ( ( ( - 14./3*t + 49./3 )*t - 39./2 )*t + 95./12 )*t*t +0.5 )*t -0.5 )*t -1./12; break;
default: result = 0;
}
return result;
}
1068 float d2_c3_4ef( float a, float t ) {
float result;
int i;
i = ( t<0 ) ? ( int )t-1:( int )t;
t = t - i;
switch ( i ) {
case 0: result = ( ( ( ( 1./24*t - 1./12 )*t +0 )*t +0 )*t +0 )*t; break;
case 1: result = ( ( ( ( - 7./24*t + 5./8 )*t +1./12 )*t -1./12 )*t - 1./8 )*t - 1./24; break;
case 2: result = ( ( ( ( 7./8 *t - 2 )*t -1./3 )*t +1 )*t +1 )*t + 1./6; break;
case 3: result = ( ( ( ( -35./24*t +85./24 )*t +5./12 )*t -13./4 )*t -13./8 )*t +17./24; break;
case 4: result = ( ( ( ( 35./24*t -15./4 )*t +0 )*t +14./3 )*t +0 )*t - 5./3; break;
case 5: result = ( ( ( ( - 7./8 *t +19./8 )*t -5./12 )*t -13./4 )*t +13./8 )*t +17./24; break;
case 6: result = ( ( ( ( 7./24*t - 5./6 )*t +1./3 )*t +1 )*t -1 )*t + 1./6; break;
case 7: result = ( ( ( ( - 1./24*t + 1./8 )*t -1./12 )*t -1./12 )*t + 1./8 )*t - 1./24; break;
default: result = 0;
}
return result;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
/* ************************************************* */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* */
/* */
/* THIS FILE AUTOMATICALLY GENERATED FROM */
/* PERL SCRIPTS IN THE tmf SUBDIRECTORY */
/* EDIT THOSE SCRIPTS, NOT THIS FILE! */
/* */
/* */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* !! WARNING !!! WARNING !!! WARNING !!! WARNING !! */
/* ************************************************* */
static double
43 _nrrd_TMFBAD_Int( const double *parm ) {
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
return 0.0;
}
static double
50 _nrrd_TMFBAD_Sup( const double *parm ) {
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
return 0.0;
}
static double
57 _nrrd_TMFBAD_1_d( double x, const double *parm ) {
AIR_UNUSED( x );
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
return 0.0;
}
static float
65 _nrrd_TMFBAD_1_f( float x, const double *parm ) {
AIR_UNUSED( x );
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
return 0.0;
}
static void
73 _nrrd_TMFBAD_N_d( double *f, const double *x, size_t len, const double *parm ) {
AIR_UNUSED( f );
AIR_UNUSED( x );
AIR_UNUSED( len );
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
}
static void
82 _nrrd_TMFBAD_N_f( float *f, const float *x, size_t len, const double *parm ) {
AIR_UNUSED( f );
AIR_UNUSED( x );
AIR_UNUSED( len );
AIR_UNUSED( parm );
fprintf( stderr, "_nrrd_TMFBAD: Invalid TMF indexing: ef == 0\n" );
}
static NrrdKernel
_nrrdKernel_TMFBAD = {
"TMFBAD",
1, _nrrd_TMFBAD_Sup, _nrrd_TMFBAD_Int,
_nrrd_TMFBAD_1_f, _nrrd_TMFBAD_N_f,
_nrrd_TMFBAD_1_d, _nrrd_TMFBAD_N_d
};
#define OVER_3 0.33333333
#define OVER_6 0.16666666
#define OVER_12 0.0833333333
#define OVER_2_3 0.6666666666
/* ------------------------ TMF_dn_cn_1ef --------------------- */
#define TMF_dn_cn_1ef( a, i, t ) ( \
( i == 0 ? 0.5 : \
( i == 1 ? 0.5 : \
0 ) ) )
/* ------------------------ TMF_dn_cn_2ef --------------------- */
#define TMF_dn_cn_2ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_cn_3ef --------------------- */
#define TMF_dn_cn_3ef( a, i, t ) ( \
( i == 0 ? ( 0.25*t +( 2*a-0.25 ) )*t - a : \
( i == 1 ? ( -0.25*t -( 6*a-1.25 ) )*t + 3*a : \
( i == 2 ? ( -0.25*t +( 6*a-0.75 ) )*t - 3*a+1 : \
( i == 3 ? ( 0.25*t -( 2*a+0.25 ) )*t + a : \
0 ) ) ) ) )
/* ------------------------ TMF_dn_cn_4ef --------------------- */
#define TMF_dn_cn_4ef( a, i, t ) ( \
TMF_d0_c0_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c0_1ef --------------------- */
#define TMF_dn_c0_1ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c0_2ef --------------------- */
#define TMF_dn_c0_2ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c0_3ef --------------------- */
#define TMF_dn_c0_3ef( a, i, t ) ( \
TMF_d0_c0_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c0_4ef --------------------- */
#define TMF_dn_c0_4ef( a, i, t ) ( \
TMF_d0_c0_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c1_1ef --------------------- */
#define TMF_dn_c1_1ef( a, i, t ) ( \
TMF_d0_c1_1ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c1_2ef --------------------- */
#define TMF_dn_c1_2ef( a, i, t ) ( \
( i == 0 ? ( t )*t/4 : \
( i == 1 ? ( -t +2 )*t/4 +0.25 : \
( i == 2 ? ( -t )*t/4 +0.5 : \
( i == 3 ? ( t -2 )*t/4 +0.25 : \
0 ) ) ) ) )
/* ------------------------ TMF_dn_c1_3ef --------------------- */
#define TMF_dn_c1_3ef( a, i, t ) ( \
TMF_d0_c1_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c1_4ef --------------------- */
#define TMF_dn_c1_4ef( a, i, t ) ( \
( i == 0 ? ( ( -( a )*t +( 1.5*a- 1./24 ) )*t + 0 )*t + 0 : \
( i == 1 ? ( ( ( 5*a+OVER_6 )*t -( 7.5*a- 1./8 ) )*t - ( OVER_12 ) )*t +( 0.5*a-1./24 ) : \
( i == 2 ? ( ( -( 10*a+ 0.5 )*t +( 15*a+ 5./12 ) )*t +( OVER_2_3 ) )*t -( 2*a-OVER_6 ) : \
( i == 3 ? ( ( ( 10*a+ 0.5 )*t -( 15*a+13./12 ) )*t + 0 )*t +( 3*a+0.75 ) : \
( i == 4 ? ( ( -( 5*a+OVER_6 )*t +( 7.5*a+ 5./8 ) )*t -( OVER_2_3 ) )*t -( 2*a-OVER_6 ) : \
( i == 5 ? ( ( ( a )*t -( 1.5*a+ 1./24 ) )*t + ( OVER_12 ) )*t +( 0.5*a-1./24 ) : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_dn_c2_1ef --------------------- */
#define TMF_dn_c2_1ef( a, i, t ) ( \
TMF_d0_c2_1ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c2_2ef --------------------- */
#define TMF_dn_c2_2ef( a, i, t ) ( \
( i == 0 ? ( ( OVER_6*t + 0 )*t + 0 )*t + 0 : \
( i == 1 ? ( ( -0.5*t +0.5 )*t +0.5 )*t + OVER_6 : \
( i == 2 ? ( ( 0.5*t - 1 )*t + 0 )*t +OVER_2_3 : \
( i == 3 ? ( ( -OVER_6*t +0.5 )*t -0.5 )*t + OVER_6 : \
0 ) ) ) ) )
/* ------------------------ TMF_dn_c2_3ef --------------------- */
#define TMF_dn_c2_3ef( a, i, t ) ( \
TMF_d0_c2_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c2_4ef --------------------- */
#define TMF_dn_c2_4ef( a, i, t ) ( \
TMF_dn_c1_4ef( ( double )( 1./36 ), i, t ) )
/* ------------------------ TMF_dn_c3_1ef --------------------- */
#define TMF_dn_c3_1ef( a, i, t ) ( \
TMF_d0_c3_1ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c3_2ef --------------------- */
#define TMF_dn_c3_2ef( a, i, t ) ( \
( i == 0 ? ( -0.10*t +0.25 )*t*t*t*t : \
( i == 1 ? ( ( ( 0.30*t -0.75 )*t*t +0.5 )*t +0.5 )*t +0.15 : \
( i == 2 ? ( ( ( -0.30*t +0.75 )*t*t -1 )*t +0 )*t +0.70 : \
( i == 3 ? ( ( ( 0.10*t -0.25 )*t*t +0.5 )*t -0.5 )*t +0.15 : \
0 ) ) ) ) )
/* ------------------------ TMF_dn_c3_3ef --------------------- */
#define TMF_dn_c3_3ef( a, i, t ) ( \
TMF_d0_c3_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_dn_c3_4ef --------------------- */
#define TMF_dn_c3_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( 1./30*t - 1./16 )*t + 0 )*t + 0 )*t + 0 )*t + 0 : \
( i == 1 ? ( ( ( ( -OVER_6*t +17./48 )*t +OVER_12 )*t - 1./24 )*t - OVER_12 )*t - 7./240 : \
( i == 2 ? ( ( ( ( OVER_3*t -19./24 )*t -OVER_6 )*t +OVER_2_3 )*t +OVER_2_3 )*t + 7./60 : \
( i == 3 ? ( ( ( ( -OVER_3*t + 7./8 )*t + 0 )*t - 1.25 )*t + 0 )*t +33./40 : \
( i == 4 ? ( ( ( ( OVER_6*t -23./48 )*t +OVER_6 )*t +OVER_2_3 )*t -OVER_2_3 )*t + 7./60 : \
( i == 5 ? ( ( ( ( - 1./30*t + 5./48 )*t -OVER_12 )*t - 1./24 )*t + OVER_12 )*t - 7./240 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d0_cn_1ef --------------------- */
#define TMF_d0_cn_1ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_cn_2ef --------------------- */
#define TMF_d0_cn_2ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_cn_3ef --------------------- */
#define TMF_d0_cn_3ef( a, i, t ) ( \
TMF_d0_c0_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_cn_4ef --------------------- */
#define TMF_d0_cn_4ef( a, i, t ) ( \
TMF_d0_c0_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_c0_1ef --------------------- */
#define TMF_d0_c0_1ef( a, i, t ) ( \
TMF_d0_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_c0_2ef --------------------- */
#define TMF_d0_c0_2ef( a, i, t ) ( \
( i == 0 ? t : \
( i == 1 ? 1-t : \
0 ) ) )
/* ------------------------ TMF_d0_c0_3ef --------------------- */
#define TMF_d0_c0_3ef( a, i, t ) ( \
TMF_dn_cn_3ef( ( double )( 0 ), i, t ) )
/* ------------------------ TMF_d0_c0_4ef --------------------- */
#define TMF_d0_c0_4ef( a, i, t ) ( \
( i == 0 ? ( ( OVER_6*t +0 )*t -OVER_6 )*t : \
( i == 1 ? ( ( -0.5*t +0.5 )*t +1 )*t : \
( i == 2 ? ( ( 0.5*t -1 )*t -0.5 )*t+1 : \
( i == 3 ? ( ( -OVER_6*t +0.5 )*t -OVER_3 )*t : \
0 ) ) ) ) )
/* ------------------------ TMF_d0_c1_1ef --------------------- */
#define TMF_d0_c1_1ef( a, i, t ) ( \
( i == 0 ? ( -2*t +3 )*t*t : \
( i == 1 ? ( 2*t -3 )*t*t +1 : \
0 ) ) )
/* ------------------------ TMF_d0_c1_2ef --------------------- */
#define TMF_d0_c1_2ef( a, i, t ) ( \
TMF_d0_c1_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_c1_3ef --------------------- */
#define TMF_d0_c1_3ef( a, i, t ) ( \
( i == 0 ? ( ( 0.5*t -0.5 )*t +0 )*t : \
( i == 1 ? ( ( -1.5*t +2 )*t +0.5 )*t : \
( i == 2 ? ( ( 1.5*t -2.5 )*t +0 )*t +1 : \
( i == 3 ? ( ( -0.5*t +1 )*t -0.5 )*t : \
0 ) ) ) ) )
/* ------------------------ TMF_d0_c1_4ef --------------------- */
#define TMF_d0_c1_4ef( a, i, t ) ( \
TMF_dn_c1_4ef( ( double )( 1./12 ), i, t ) )
/* ------------------------ TMF_d0_c2_1ef --------------------- */
#define TMF_d0_c2_1ef( a, i, t ) ( \
( i == 0 ? ( ( 6*t -15 )*t +10 )*t*t*t : \
( i == 1 ? ( ( -6*t +15 )*t -10 )*t*t*t +1 : \
0 ) ) )
/* ------------------------ TMF_d0_c2_2ef --------------------- */
#define TMF_d0_c2_2ef( a, i, t ) ( \
( i == 0 ? ( ( ( 0.5*t -0.5 )*t + 0 )*t + 0 )*t : \
( i == 1 ? ( ( ( -0.5*t -0.5 )*t +1.5 )*t +0.5 )*t : \
( i == 2 ? ( ( ( -0.5*t +2.5 )*t - 3 )*t + 0 )*t +1 : \
( i == 3 ? ( ( ( 0.5*t -1.5 )*t +1.5 )*t -0.5 )*t : \
0 ) ) ) ) )
/* ------------------------ TMF_d0_c2_3ef --------------------- */
#define TMF_d0_c2_3ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( -1*t +2.5 )*t -1.5 )*t +0 )*t +0 )*t : \
( i == 1 ? ( ( ( ( 3*t -7.5 )*t +4.5 )*t +0.5 )*t +0.5 )*t : \
( i == 2 ? ( ( ( ( -3*t +7.5 )*t -4.5 )*t -1 )*t +0 )*t +1 : \
( i == 3 ? ( ( ( ( 1*t -2.5 )*t +1.5 )*t +0.5 )*t -0.5 )*t : \
0 ) ) ) ) )
/* ------------------------ TMF_d0_c2_4ef --------------------- */
#define TMF_d0_c2_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( -1./12*t + 1./12 )*t + 0 )*t + 0 )*t : \
( i == 1 ? ( ( ( 1./4 *t + 1./12 )*t -1./4 )*t - OVER_12 )*t : \
( i == 2 ? ( ( ( -1./6 *t - 1 )*t +3./2 )*t +OVER_2_3 )*t : \
( i == 3 ? ( ( ( -1./6 *t + 5./3 )*t -5./2 )*t + 0 )*t +1 : \
( i == 4 ? ( ( ( 1./4 *t -13./12 )*t +3./2 )*t -OVER_2_3 )*t : \
( i == 5 ? ( ( ( -1./12*t + 1./4 )*t -1./4 )*t + OVER_12 )*t : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d0_c3_1ef --------------------- */
#define TMF_d0_c3_1ef( a, i, t ) ( \
( i == 0 ? ( ( ( -20*t +70 )*t -84 )*t +35 )*t*t*t*t : \
( i == 1 ? ( ( ( 20*t -70 )*t +84 )*t -35 )*t*t*t*t +1 : \
0 ) ) )
/* ------------------------ TMF_d0_c3_2ef --------------------- */
#define TMF_d0_c3_2ef( a, i, t ) ( \
TMF_d0_c3_3ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d0_c3_3ef --------------------- */
#define TMF_d0_c3_3ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( ( 3*t -10.5 )*t +12.5 )*t - 5 )*t*t +0 )*t +0 )*t : \
( i == 1 ? ( ( ( ( ( -9*t +31.5 )*t -37.5 )*t +15 )*t*t +0.5 )*t +0.5 )*t : \
( i == 2 ? ( ( ( ( ( 9*t -31.5 )*t +37.5 )*t -15 )*t*t -1 )*t +0 )*t +1 : \
( i == 3 ? ( ( ( ( ( -3*t +10.5 )*t -12.5 )*t + 5 )*t*t +0.5 )*t -0.5 )*t : \
0 ) ) ) ) )
/* ------------------------ TMF_d0_c3_4ef --------------------- */
#define TMF_d0_c3_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( ( 7./48*t - 3./8 )*t +11./48 )*t +0 )*t + 0 )*t + 0 )*t : \
( i == 1 ? ( ( ( ( ( -7./16*t + 1 )*t - 3./8 )*t +1./12 )*t - 3./16 )*t - OVER_12 )*t : \
( i == 2 ? ( ( ( ( ( 7./24*t - 1./4 )*t -19./24 )*t -1./6 )*t + 5./4 )*t +OVER_2_3 )*t : \
( i == 3 ? ( ( ( ( ( 7./24*t - 3./2 )*t + 7./3 )*t +0 )*t -17./8 )*t + 0 )*t +1 : \
( i == 4 ? ( ( ( ( ( -7./16*t +13./8 )*t -31./16 )*t +1./6 )*t + 5./4 )*t -OVER_2_3 )*t : \
( i == 5 ? ( ( ( ( ( 7./48*t - 1./2 )*t +13./24 )*t -1./12 )*t - 3./16 )*t + OVER_12 )*t : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d1_cn_1ef --------------------- */
#define TMF_d1_cn_1ef( a, i, t ) ( \
( i == 0 ? 1 : \
( i == 1 ? -1 : \
0 ) ) )
/* ------------------------ TMF_d1_cn_2ef --------------------- */
#define TMF_d1_cn_2ef( a, i, t ) ( \
( i == 0 ? 0.5*t +( a ) : \
( i == 1 ? -0.5*t -( 3*a-0.5 ) : \
( i == 2 ? -0.5*t +( 3*a ) : \
( i == 3 ? 0.5*t -( a+0.5 ) : \
0 ) ) ) ) )
/* ------------------------ TMF_d1_cn_3ef --------------------- */
#define TMF_d1_cn_3ef( a, i, t ) ( \
( i == 0 ? ( 0.5*t +0 )*t -OVER_6 : \
( i == 1 ? ( -1.5*t +1 )*t +1 : \
( i == 2 ? ( 1.5*t -2 )*t -0.5 : \
( i == 3 ? ( -0.5*t +1 )*t -OVER_3 : \
0 ) ) ) ) )
/* ------------------------ TMF_d1_cn_4ef --------------------- */
#define TMF_d1_cn_4ef( a, i, t ) ( \
TMF_d1_c0_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d1_c0_1ef --------------------- */
#define TMF_d1_c0_1ef( a, i, t ) ( \
TMF_d1_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d1_c0_2ef --------------------- */
#define TMF_d1_c0_2ef( a, i, t ) ( \
TMF_d1_cn_2ef( ( double )( 0 ), i, t ) )
/* ------------------------ TMF_d1_c0_3ef --------------------- */
#define TMF_d1_c0_3ef( a, i, t ) ( \
( i == 0 ? ( ( a )*t -( a+OVER_12 ) )*t +0 : \
( i == 1 ? ( -( 5*a-0.5 )*t +( 5*a+ 0.25 ) )*t -OVER_12 : \
( i == 2 ? ( ( 10*a-1.5 )*t -( 10*a- 5./6 ) )*t +OVER_2_3 : \
( i == 3 ? ( -( 10*a-1.5 )*t +( 10*a- 13./6 ) )*t +0 : \
( i == 4 ? ( ( 5*a-0.5 )*t -( 5*a- 1.25 ) )*t -OVER_2_3 : \
( i == 5 ? ( -( a )*t +( a-OVER_12 ) )*t +OVER_12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d1_c0_4ef --------------------- */
#define TMF_d1_c0_4ef( a, i, t ) ( \
( i == 0 ? ( ( OVER_12*t +( a ) )*t -( a+ OVER_6 ) )*t +0 : \
( i == 1 ? ( ( -0.25*t -( 5*a-0.25 ) )*t +( 5*a+ 0.75 ) )*t -OVER_12 : \
( i == 2 ? ( ( OVER_6*t +( 10*a- 0.5 ) )*t -( 10*a+ OVER_3 ) )*t +OVER_2_3 : \
( i == 3 ? ( ( OVER_6*t -( 10*a ) )*t +( 10*a- 5./6 ) )*t +0 : \
( i == 4 ? ( ( -0.25*t +( 5*a+ 0.5 ) )*t -( 5*a- 0.5 ) )*t -OVER_2_3 : \
( i == 5 ? ( ( OVER_12*t -( a+0.25 ) )*t +( a+OVER_12 ) )*t +OVER_12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d1_c1_1ef --------------------- */
#define TMF_d1_c1_1ef( a, i, t ) ( \
TMF_d1_c1_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d1_c1_2ef --------------------- */
#define TMF_d1_c1_2ef( a, i, t ) ( \
( i == 0 ? ( 0.5*t +0 )*t +0 : \
( i == 1 ? ( -1.5*t +1 )*t +0.5 : \
( i == 2 ? ( 1.5*t -2 )*t +0 : \
( i == 3 ? ( -0.5*t +1 )*t -0.5 : \
0 ) ) ) ) )
/* ------------------------ TMF_d1_c1_3ef --------------------- */
#define TMF_d1_c1_3ef( a, i, t ) ( \
TMF_d1_c0_3ef( ( double )( -1./12 ), i, t ) )
504 /* ------------------------ TMF_d1_c1_4ef --------------------- */
#define TMF_d1_c1_4ef( a, i, t ) ( \
TMF_d1_c0_4ef( ( double )( -1./6 ), i, t ) )
509
/* ------------------------ TMF_d1_c2_1ef --------------------- */
#define TMF_d1_c2_1ef( a, i, t ) ( \
TMF_d1_c2_2ef( ( double )( a ), i, t ) )
515
/* ------------------------ TMF_d1_c2_2ef --------------------- */
#define TMF_d1_c2_2ef( a, i, t ) ( \
( i == 0 ? ( ( -0.5*t +1 )*t*t +0 )*t +0 : \
( i == 1 ? ( ( 1.5*t -3 )*t*t +1 )*t +0.5 : \
( i == 2 ? ( ( -1.5*t +3 )*t*t -2 )*t +0 : \
( i == 3 ? ( ( 0.5*t -1 )*t*t +1 )*t -0.5 : \
0 ) ) ) ) )
526 /* ------------------------ TMF_d1_c2_3ef --------------------- */
#define TMF_d1_c2_3ef( a, i, t ) ( \
TMF_d1_c2_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d1_c2_4ef --------------------- */
#define TMF_d1_c2_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( OVER_6*t - 0.25 )*t + 0 )*t + 0 )*t +0 : \
( i == 1 ? ( ( ( - 5./6*t +17./12 )*t +0.25 )*t -OVER_12 )*t -OVER_12 : \
537 ( i == 2 ? ( ( ( 5./3*t - 19./6 )*t -0.5 )*t + 4./3 )*t +OVER_2_3 : \
( i == 3 ? ( ( ( - 5./3*t + 3.5 )*t +0 )*t - 2.5 )*t +0 : \
( i == 4 ? ( ( ( 5./6*t -23./12 )*t +0.5 )*t + 4./3 )*t -OVER_2_3 : \
( i == 5 ? ( ( ( -OVER_6*t + 5./12 )*t -0.25 )*t -OVER_12 )*t +OVER_12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d1_c3_1ef --------------------- */
#define TMF_d1_c3_1ef( a, i, t ) ( \
( i == 0 ? ( ( ( -0.75*t +1.25 )*t + 0 )*t*t + 0 )*t +0 : \
( i == 1 ? ( ( ( 0.75*t + 0 )*t -2.5 )*t*t +1.25 )*t +0.5 : \
( i == 2 ? ( ( ( 0.75*t -3.75 )*t +5 )*t*t -2.5 )*t +0 : \
( i == 3 ? ( ( ( -0.75*t +2.5 )*t -2.5 )*t*t +1.25 )*t -0.5 : \
0 ) ) ) ) )
552
/* ------------------------ TMF_d1_c3_2ef --------------------- */
#define TMF_d1_c3_2ef( a, i, t ) ( \
( i == 0 ? ( ( ( 1*t -3 )*t +2.5 )*t*t*t +0 )*t +0 : \
( i == 1 ? ( ( ( -3*t +9 )*t -7.5 )*t*t*t +1 )*t +0.5 : \
( i == 2 ? ( ( ( 3*t -9 )*t +7.5 )*t*t*t -2 )*t +0 : \
( i == 3 ? ( ( ( -1*t +3 )*t -2.5 )*t*t*t +1 )*t -0.5 : \
0 ) ) ) ) )
/* ------------------------ TMF_d1_c3_3ef --------------------- */
#define TMF_d1_c3_3ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( 3./16*t - 13./48 )*t + 0 )*t + 0 )*t + 0 )*t +0 : \
( i == 1 ? ( ( ( ( -9./16*t + 5./12 )*t +19./24 )*t +0.25 )*t - 7./48 )*t -OVER_12 : \
( i == 2 ? ( ( ( ( 3./8 *t + 25./24 )*t -19./6 )*t - 0.5 )*t +19./12 )*t +OVER_2_3 : \
( i == 3 ? ( ( ( ( 3./8 *t - 35./12 )*t +19./4 )*t + 0 )*t -23./8 )*t +0 : \
( i == 4 ? ( ( ( ( -9./16*t + 115./48 )*t -19./6 )*t + 0.5 )*t +19./12 )*t -OVER_2_3 : \
( i == 5 ? ( ( ( ( 3./16*t -OVER_2_3 )*t +19./24 )*t -0.25 )*t - 7./48 )*t +OVER_12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d1_c3_4ef --------------------- */
577
#define TMF_d1_c3_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( ( -0.25*t +0.75 )*t - 7./12 )*t + 0 )*t + 0 )*t + 0 )*t +0 : \
( i == 1 ? ( ( ( ( ( 1.25*t -3.75 )*t +35./12 )*t + OVER_6 )*t +0.25 )*t -OVER_12 )*t -OVER_12 : \
( i == 2 ? ( ( ( ( ( -2.5*t + 7.5 )*t -35./6 )*t -OVER_2_3 )*t - 0.5 )*t + 4./3 )*t +OVER_2_3 : \
582 ( i == 3 ? ( ( ( ( ( 2.5*t - 7.5 )*t +35./6 )*t + 1 )*t + 0 )*t - 5./2 )*t +0 : \
( i == 4 ? ( ( ( ( ( -1.25*t +3.75 )*t -35./12 )*t -OVER_2_3 )*t + 0.5 )*t + 4./3 )*t -OVER_2_3 : \
( i == 5 ? ( ( ( ( ( 0.25*t -0.75 )*t + 7./12 )*t + OVER_6 )*t -0.25 )*t -OVER_12 )*t +OVER_12 : \
0 ) ) ) ) ) ) )
588 /* ------------------------ TMF_d2_cn_1ef --------------------- */
#define TMF_d2_cn_1ef( a, i, t ) ( \
( i == 0 ? 0.5 : \
( i == 1 ? -0.5 : \
( i == 2 ? -0.5 : \
( i == 3 ? 0.5 : \
0 ) ) ) ) )
/* ------------------------ TMF_d2_cn_2ef --------------------- */
599
#define TMF_d2_cn_2ef( a, i, t ) ( \
TMF_d2_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d2_cn_3ef --------------------- */
#define TMF_d2_cn_3ef( a, i, t ) ( \
( i == 0 ? ( 0.25*t +( a-30 )/120 )*t -( a+10 )/240 : \
( i == 1 ? ( -0.75*t -( a-42 )/24 )*t +( a+ 6 )/48 : \
( i == 2 ? ( 0.5 *t +( a-42 )/12 )*t -( a-22 )/24 : \
610 ( i == 3 ? ( 0.5 *t -( a-30 )/12 )*t +( a-50 )/24 : \
( i == 4 ? ( -0.75*t +( a- 6 )/24 )*t -( a-54 )/48 : \
( i == 5 ? ( 0.25*t -( a+30 )/120 )*t +( a-10 )/240 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_cn_4ef --------------------- */
#define TMF_d2_cn_4ef( a, i, t ) ( \
TMF_d2_c0_4ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d2_c0_1ef --------------------- */
#define TMF_d2_c0_1ef( a, i, t ) ( \
625 TMF_d2_c0_2ef( ( double )( a ), i, t ) )
/* ------------------------ TMF_d2_c0_2ef --------------------- */
#define TMF_d2_c0_2ef( a, i, t ) ( \
( i == 0 ? t : \
( i == 1 ? -3*t +1 : \
( i == 2 ? 3*t -2 : \
( i == 3 ? - t +1 : \
0 ) ) ) ) )
/* ------------------------ TMF_d2_c0_3ef --------------------- */
#define TMF_d2_c0_3ef( a, i, t ) ( \
TMF_d2_cn_3ef( ( double )( -10 ), i, t ) )
/* ------------------------ TMF_d2_c0_4ef --------------------- */
#define TMF_d2_c0_4ef( a, i, t ) ( \
( i == 0 ? ( ( 1./6*t +0 )*t -0.25 )*t + 0 : \
( i == 1 ? ( ( -5./6*t +0.5 )*t +1.75 )*t -1./12 : \
( i == 2 ? ( ( 5./3*t -2 )*t -3.5 )*t + 4./3 : \
650 ( i == 3 ? ( ( -5./3*t +3 )*t +2.5 )*t - 2.5 : \
( i == 4 ? ( ( 5./6*t -2 )*t -0.25 )*t + 4./3 : \
( i == 5 ? ( ( -1./6*t +0.5 )*t -0.25 )*t -1./12 : \
0 ) ) ) ) ) ) )
655
/* ------------------------ TMF_d2_c1_1ef --------------------- */
#define TMF_d2_c1_1ef( a, i, t ) ( \
( i == 0 ? ( -2*t +3 )*t*t +0 : \
( i == 1 ? ( 6*t -9 )*t*t +1 : \
661 ( i == 2 ? ( -6*t +9 )*t*t -2 : \
( i == 3 ? ( 2*t -3 )*t*t +1 : \
0 ) ) ) ) )
/* ------------------------ TMF_d2_c1_2ef --------------------- */
#define TMF_d2_c1_2ef( a, i, t ) ( \
( i == 0 ? ( 0.25*t +0 )*t : \
( i == 1 ? ( -0.75*t +0.5 )*t +0.25 : \
( i == 2 ? ( 0.5 *t -1 )*t : \
672 ( i == 3 ? ( 0.5 *t +0 )*t -0.5 : \
( i == 4 ? ( -0.75*t +1 )*t : \
( i == 5 ? ( 0.25*t -0.5 )*t +0.25 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c1_3ef --------------------- */
#define TMF_d2_c1_3ef( a, i, t ) ( \
( i == 0 ? ( ( 2./3*t - 0.75 )*t +0 )*t : \
( i == 1 ? ( ( -10./3*t + 4.25 )*t +0.5 )*t -1./12 : \
683 ( i == 2 ? ( ( 20./3*t - 9.5 )*t -1 )*t +4./3 : \
( i == 3 ? ( ( -20./3*t +10.5 )*t +0 )*t -2.5 : \
( i == 4 ? ( ( 10./3*t - 5.75 )*t +1 )*t +4./3 : \
( i == 5 ? ( ( - 2./3*t + 1.25 )*t -0.5 )*t -1./12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c1_4ef --------------------- */
#define TMF_d2_c1_4ef( a, i, t ) ( \
( i == 0 ? ( ( -( a+ 53 )/360*t +( a+ 38 )/240 )*t +0 )*t : \
( i == 1 ? ( ( ( 7*a+431 )/360*t -( 7*a+296 )/240 )*t - 1./8 )*t +( a+ 8 )/720 : \
( i == 2 ? ( ( -( 7*a+471 )/120*t +( 7*a+366 )/80 )*t +1 )*t -( a+18 )/120 : \
( i == 3 ? ( ( ( 7*a+491 )/72 *t -( 7*a+452 )/48 )*t -13./8 )*t +( a+72 )/48 : \
( i == 4 ? ( ( -( 7*a+491 )/72 *t +( 7*a+530 )/48 )*t +0 )*t -( a+98 )/36 : \
698 ( i == 5 ? ( ( ( 7*a+471 )/120*t -( 7*a+576 )/80 )*t +13./8 )*t +( a+72 )/48 : \
( i == 6 ? ( ( -( 7*a+431 )/360*t +( 7*a+566 )/240 )*t -1 )*t -( a+18 )/120 : \
( i == 7 ? ( ( ( a+ 53 )/360*t -( a+ 68 )/240 )*t + 1./8 )*t +( a+ 8 )/720 : \
0 ) ) ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c2_1ef --------------------- */
#define TMF_d2_c2_1ef( a, i, t ) ( \
( i == 0 ? ( ( 6*t -15 )*t +10 )*t*t*t : \
( i == 1 ? ( ( -18*t +45 )*t -30 )*t*t*t +1 : \
( i == 2 ? ( ( 18*t -45 )*t +30 )*t*t*t -2 : \
( i == 3 ? ( ( - 6*t +15 )*t -10 )*t*t*t +1 : \
0 ) ) ) ) )
/* ------------------------ TMF_d2_c2_2ef --------------------- */
#define TMF_d2_c2_2ef( a, i, t ) ( \
( i == 0 ? ( ( 1./6*t +0 )*t +0 )*t : \
( i == 1 ? ( ( -5./6*t +0.5 )*t +0.5 )*t +1./6 : \
( i == 2 ? ( ( 5./3*t -2 )*t -1 )*t +1./3 : \
( i == 3 ? ( ( -5./3*t +3 )*t +0 )*t -1 : \
( i == 4 ? ( ( 5./6*t -2 )*t +1 )*t +1./3 : \
( i == 5 ? ( ( -1./6*t +0.5 )*t -0.5 )*t +1./6 : \
723 0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c2_3ef --------------------- */
728 #define TMF_d2_c2_3ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( -1.5*t + 3.75 )*t - 7./3 )*t +0 )*t +0 )*t : \
( i == 1 ? ( ( ( ( 7.5*t -18.75 )*t +35./3 )*t +0.5 )*t +0.5 )*t -1./12 : \
( i == 2 ? ( ( ( ( - 15*t +37.5 )*t -70./3 )*t -2 )*t -1 )*t +4./3 : \
( i == 3 ? ( ( ( ( 15*t -37.5 )*t +70./3 )*t +3 )*t +0 )*t -2.5 : \
( i == 4 ? ( ( ( ( -7.5*t +18.75 )*t -35./3 )*t -2 )*t +1 )*t +4./3 : \
734 ( i == 5 ? ( ( ( ( 1.5*t - 3.75 )*t + 7./3 )*t +0.5 )*t -0.5 )*t -1./12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c2_4ef --------------------- */
#define TMF_d2_c2_4ef( a, i, t ) ( \
TMF_d2_c1_4ef( ( double )( -38 ), i, t ) )
/* ------------------------ TMF_d2_c3_1ef --------------------- */
745
#define TMF_d2_c3_1ef( a, i, t ) ( \
( i == 0 ? ( ( ( -20*t + 70 )*t - 84 )*t + 35 )*t*t*t*t : \
( i == 1 ? ( ( ( 60*t -210 )*t +252 )*t -105 )*t*t*t*t +1 : \
( i == 2 ? ( ( ( -60*t +210 )*t -252 )*t +105 )*t*t*t*t -2 : \
( i == 3 ? ( ( ( 20*t - 70 )*t + 84 )*t - 35 )*t*t*t*t +1 : \
0 ) ) ) ) )
/* ------------------------ TMF_d2_c3_2ef --------------------- */
756 #define TMF_d2_c3_2ef( a, i, t ) ( \
( i == 0 ? ( ( ( -0.1*t +0.25 )*t*t + 0 )*t +0 )*t : \
( i == 1 ? ( ( ( 0.5*t -1.25 )*t*t +0.5 )*t +0.5 )*t +3./20 : \
( i == 2 ? ( ( ( -1 *t +2.5 )*t*t -2 )*t -1 )*t +2./5 : \
( i == 3 ? ( ( ( 1 *t -2.5 )*t*t +3 )*t +0 )*t -11./10 : \
( i == 4 ? ( ( ( -0.5*t +1.25 )*t*t -2 )*t +1 )*t +2./5 : \
( i == 5 ? ( ( ( 0.1*t -0.25 )*t*t +0.5 )*t -0.5 )*t +3./20 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c3_3ef --------------------- */
#define TMF_d2_c3_3ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( ( 14./3*t - 49./3 )*t + 39./2 )*t - 95./12 )*t*t +0 )*t +0 )*t : \
( i == 1 ? ( ( ( ( ( - 70./3*t +245./3 )*t -195./2 )*t +475./12 )*t*t +0.5 )*t +0.5 )*t -1./12 : \
771 ( i == 2 ? ( ( ( ( ( 140./3*t -490./3 )*t +195 )*t -475./6 )*t*t -2 )*t -1 )*t +4./3 : \
( i == 3 ? ( ( ( ( ( -140./3*t +490./3 )*t -195 )*t +475./6 )*t*t +3 )*t +0 )*t -5./2 : \
( i == 4 ? ( ( ( ( ( 70./3*t -245./3 )*t +195./2 )*t -475./12 )*t*t -2 )*t +1 )*t +4./3 : \
( i == 5 ? ( ( ( ( ( - 14./3*t + 49./3 )*t - 39./2 )*t + 95./12 )*t*t +0.5 )*t -0.5 )*t -1./12 : \
0 ) ) ) ) ) ) )
/* ------------------------ TMF_d2_c3_4ef --------------------- */
#define TMF_d2_c3_4ef( a, i, t ) ( \
( i == 0 ? ( ( ( ( 1./24*t - 1./12 )*t +0 )*t +0 )*t +0 )*t : \
( i == 1 ? ( ( ( ( - 7./24*t + 5./8 )*t +1./12 )*t -1./12 )*t - 1./8 )*t - 1./24 : \
( i == 2 ? ( ( ( ( 7./8 *t - 2 )*t -1./3 )*t +1 )*t +1 )*t + 1./6 : \
( i == 3 ? ( ( ( ( -35./24*t +85./24 )*t +5./12 )*t -13./4 )*t -13./8 )*t +17./24 : \
( i == 4 ? ( ( ( ( 35./24*t -15./4 )*t +0 )*t +14./3 )*t +0 )*t - 5./3 : \
( i == 5 ? ( ( ( ( - 7./8 *t +19./8 )*t -5./12 )*t -13./4 )*t +13./8 )*t +17./24 : \
( i == 6 ? ( ( ( ( 7./24*t - 5./6 )*t +1./3 )*t +1 )*t -1 )*t + 1./6 : \
( i == 7 ? ( ( ( ( - 1./24*t + 1./8 )*t -1./12 )*t -1./12 )*t + 1./8 )*t - 1./24 : \
0 ) ) ) ) ) ) ) ) )
/* ------------------------ TMF_dn_cn_1ef --------------------- */
static double _nrrd_TMF_dn_cn_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
796 return 1.0;
}
static double _nrrd_TMF_dn_cn_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
801 return 1;
}
static double
_nrrd_TMF_dn_cn_1ef_1_d( double x, const double *parm ) {
int i;
807
AIR_UNUSED( parm ); /* TMF_dn_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_cn_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_cn_1ef_1_f( float x, const double *parm ) {
int i;
818
AIR_UNUSED( parm ); /* TMF_dn_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_cn_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_cn_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
829 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_cn_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_cn_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
844 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_cn_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_cn_1ef = {
"TMF_dn_cn_1ef",
1, _nrrd_TMF_dn_cn_1ef_Sup, _nrrd_TMF_dn_cn_1ef_Int,
_nrrd_TMF_dn_cn_1ef_1_f, _nrrd_TMF_dn_cn_1ef_N_f,
_nrrd_TMF_dn_cn_1ef_1_d, _nrrd_TMF_dn_cn_1ef_N_d
};
/* ------------------------ TMF_dn_cn_2ef --------------------- */
static double _nrrd_TMF_dn_cn_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
869 return 1.0;
}
static double _nrrd_TMF_dn_cn_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
874 return 1;
}
static double
_nrrd_TMF_dn_cn_2ef_1_d( double x, const double *parm ) {
int i;
880
AIR_UNUSED( parm ); /* TMF_dn_cn_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_cn_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_cn_2ef_1_f( float x, const double *parm ) {
int i;
891
AIR_UNUSED( parm ); /* TMF_dn_cn_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_cn_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_cn_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
902 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_cn_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_cn_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
917 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_cn_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_cn_2ef = {
"TMF_dn_cn_2ef",
1, _nrrd_TMF_dn_cn_2ef_Sup, _nrrd_TMF_dn_cn_2ef_Int,
_nrrd_TMF_dn_cn_2ef_1_f, _nrrd_TMF_dn_cn_2ef_N_f,
_nrrd_TMF_dn_cn_2ef_1_d, _nrrd_TMF_dn_cn_2ef_N_d
};
/* ------------------------ TMF_dn_cn_3ef --------------------- */
static double _nrrd_TMF_dn_cn_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
942 return 1.0;
}
static double _nrrd_TMF_dn_cn_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
947 return 2;
}
static double
_nrrd_TMF_dn_cn_3ef_1_d( double x, const double *parm ) {
int i;
953
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_cn_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_cn_3ef_1_f( float x, const double *parm ) {
int i;
964
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_cn_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_cn_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
975 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_cn_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_cn_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
990 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_cn_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_cn_3ef = {
"TMF_dn_cn_3ef",
1, _nrrd_TMF_dn_cn_3ef_Sup, _nrrd_TMF_dn_cn_3ef_Int,
_nrrd_TMF_dn_cn_3ef_1_f, _nrrd_TMF_dn_cn_3ef_N_f,
_nrrd_TMF_dn_cn_3ef_1_d, _nrrd_TMF_dn_cn_3ef_N_d
};
/* ------------------------ TMF_dn_cn_4ef --------------------- */
static double _nrrd_TMF_dn_cn_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1015 return 1.0;
}
static double _nrrd_TMF_dn_cn_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1020 return 2;
}
static double
_nrrd_TMF_dn_cn_4ef_1_d( double x, const double *parm ) {
int i;
1026
AIR_UNUSED( parm ); /* TMF_dn_cn_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_cn_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_cn_4ef_1_f( float x, const double *parm ) {
int i;
1037
AIR_UNUSED( parm ); /* TMF_dn_cn_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_cn_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_cn_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1048 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_cn_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_cn_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1063 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_cn_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_cn_4ef = {
"TMF_dn_cn_4ef",
1, _nrrd_TMF_dn_cn_4ef_Sup, _nrrd_TMF_dn_cn_4ef_Int,
_nrrd_TMF_dn_cn_4ef_1_f, _nrrd_TMF_dn_cn_4ef_N_f,
_nrrd_TMF_dn_cn_4ef_1_d, _nrrd_TMF_dn_cn_4ef_N_d
};
/* ------------------------ TMF_dn_c0_1ef --------------------- */
static double _nrrd_TMF_dn_c0_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1088 return 1.0;
}
static double _nrrd_TMF_dn_c0_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1093 return 1;
}
static double
_nrrd_TMF_dn_c0_1ef_1_d( double x, const double *parm ) {
int i;
1099
AIR_UNUSED( parm ); /* TMF_dn_c0_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c0_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c0_1ef_1_f( float x, const double *parm ) {
int i;
1110
AIR_UNUSED( parm ); /* TMF_dn_c0_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c0_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c0_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1121 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c0_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c0_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1136 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c0_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c0_1ef = {
"TMF_dn_c0_1ef",
1, _nrrd_TMF_dn_c0_1ef_Sup, _nrrd_TMF_dn_c0_1ef_Int,
_nrrd_TMF_dn_c0_1ef_1_f, _nrrd_TMF_dn_c0_1ef_N_f,
_nrrd_TMF_dn_c0_1ef_1_d, _nrrd_TMF_dn_c0_1ef_N_d
};
/* ------------------------ TMF_dn_c0_2ef --------------------- */
static double _nrrd_TMF_dn_c0_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1161 return 1.0;
}
static double _nrrd_TMF_dn_c0_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1166 return 1;
}
static double
_nrrd_TMF_dn_c0_2ef_1_d( double x, const double *parm ) {
int i;
1172
AIR_UNUSED( parm ); /* TMF_dn_c0_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c0_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c0_2ef_1_f( float x, const double *parm ) {
int i;
1183
AIR_UNUSED( parm ); /* TMF_dn_c0_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c0_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c0_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1194 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c0_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c0_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1209 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c0_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c0_2ef = {
"TMF_dn_c0_2ef",
1, _nrrd_TMF_dn_c0_2ef_Sup, _nrrd_TMF_dn_c0_2ef_Int,
_nrrd_TMF_dn_c0_2ef_1_f, _nrrd_TMF_dn_c0_2ef_N_f,
_nrrd_TMF_dn_c0_2ef_1_d, _nrrd_TMF_dn_c0_2ef_N_d
};
/* ------------------------ TMF_dn_c0_3ef --------------------- */
static double _nrrd_TMF_dn_c0_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1234 return 1.0;
}
static double _nrrd_TMF_dn_c0_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1239 return 2;
}
static double
_nrrd_TMF_dn_c0_3ef_1_d( double x, const double *parm ) {
int i;
1245
AIR_UNUSED( parm ); /* TMF_dn_c0_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c0_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c0_3ef_1_f( float x, const double *parm ) {
int i;
1256
AIR_UNUSED( parm ); /* TMF_dn_c0_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c0_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c0_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1267 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c0_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c0_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1282 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c0_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c0_3ef = {
"TMF_dn_c0_3ef",
1, _nrrd_TMF_dn_c0_3ef_Sup, _nrrd_TMF_dn_c0_3ef_Int,
_nrrd_TMF_dn_c0_3ef_1_f, _nrrd_TMF_dn_c0_3ef_N_f,
_nrrd_TMF_dn_c0_3ef_1_d, _nrrd_TMF_dn_c0_3ef_N_d
};
/* ------------------------ TMF_dn_c0_4ef --------------------- */
static double _nrrd_TMF_dn_c0_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1307 return 1.0;
}
static double _nrrd_TMF_dn_c0_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1312 return 2;
}
static double
_nrrd_TMF_dn_c0_4ef_1_d( double x, const double *parm ) {
int i;
1318
AIR_UNUSED( parm ); /* TMF_dn_c0_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c0_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c0_4ef_1_f( float x, const double *parm ) {
int i;
1329
AIR_UNUSED( parm ); /* TMF_dn_c0_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c0_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c0_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1340 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c0_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c0_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1355 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c0_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c0_4ef = {
"TMF_dn_c0_4ef",
1, _nrrd_TMF_dn_c0_4ef_Sup, _nrrd_TMF_dn_c0_4ef_Int,
_nrrd_TMF_dn_c0_4ef_1_f, _nrrd_TMF_dn_c0_4ef_N_f,
_nrrd_TMF_dn_c0_4ef_1_d, _nrrd_TMF_dn_c0_4ef_N_d
};
/* ------------------------ TMF_dn_c1_1ef --------------------- */
static double _nrrd_TMF_dn_c1_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1380 return 1.0;
}
static double _nrrd_TMF_dn_c1_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1385 return 1;
}
static double
_nrrd_TMF_dn_c1_1ef_1_d( double x, const double *parm ) {
int i;
1391
AIR_UNUSED( parm ); /* TMF_dn_c1_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c1_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c1_1ef_1_f( float x, const double *parm ) {
int i;
1402
AIR_UNUSED( parm ); /* TMF_dn_c1_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c1_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c1_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1413 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c1_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c1_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1428 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c1_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c1_1ef = {
"TMF_dn_c1_1ef",
1, _nrrd_TMF_dn_c1_1ef_Sup, _nrrd_TMF_dn_c1_1ef_Int,
_nrrd_TMF_dn_c1_1ef_1_f, _nrrd_TMF_dn_c1_1ef_N_f,
_nrrd_TMF_dn_c1_1ef_1_d, _nrrd_TMF_dn_c1_1ef_N_d
};
/* ------------------------ TMF_dn_c1_2ef --------------------- */
static double _nrrd_TMF_dn_c1_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1453 return 1.0;
}
static double _nrrd_TMF_dn_c1_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1458 return 2;
}
static double
_nrrd_TMF_dn_c1_2ef_1_d( double x, const double *parm ) {
int i;
1464
AIR_UNUSED( parm ); /* TMF_dn_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c1_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c1_2ef_1_f( float x, const double *parm ) {
int i;
1475
AIR_UNUSED( parm ); /* TMF_dn_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c1_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c1_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1486 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c1_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c1_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1501 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c1_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c1_2ef = {
"TMF_dn_c1_2ef",
1, _nrrd_TMF_dn_c1_2ef_Sup, _nrrd_TMF_dn_c1_2ef_Int,
_nrrd_TMF_dn_c1_2ef_1_f, _nrrd_TMF_dn_c1_2ef_N_f,
_nrrd_TMF_dn_c1_2ef_1_d, _nrrd_TMF_dn_c1_2ef_N_d
};
/* ------------------------ TMF_dn_c1_3ef --------------------- */
static double _nrrd_TMF_dn_c1_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1526 return 1.0;
}
static double _nrrd_TMF_dn_c1_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1531 return 2;
}
static double
_nrrd_TMF_dn_c1_3ef_1_d( double x, const double *parm ) {
int i;
1537
AIR_UNUSED( parm ); /* TMF_dn_c1_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c1_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c1_3ef_1_f( float x, const double *parm ) {
int i;
1548
AIR_UNUSED( parm ); /* TMF_dn_c1_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c1_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c1_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1559 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c1_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c1_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1574 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c1_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c1_3ef = {
"TMF_dn_c1_3ef",
1, _nrrd_TMF_dn_c1_3ef_Sup, _nrrd_TMF_dn_c1_3ef_Int,
_nrrd_TMF_dn_c1_3ef_1_f, _nrrd_TMF_dn_c1_3ef_N_f,
_nrrd_TMF_dn_c1_3ef_1_d, _nrrd_TMF_dn_c1_3ef_N_d
};
/* ------------------------ TMF_dn_c1_4ef --------------------- */
static double _nrrd_TMF_dn_c1_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1599 return 1.0;
}
static double _nrrd_TMF_dn_c1_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1604 return 3;
}
static double
_nrrd_TMF_dn_c1_4ef_1_d( double x, const double *parm ) {
int i;
1610
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c1_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c1_4ef_1_f( float x, const double *parm ) {
int i;
1621
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c1_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c1_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1632 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c1_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c1_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1647 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c1_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c1_4ef = {
"TMF_dn_c1_4ef",
1, _nrrd_TMF_dn_c1_4ef_Sup, _nrrd_TMF_dn_c1_4ef_Int,
_nrrd_TMF_dn_c1_4ef_1_f, _nrrd_TMF_dn_c1_4ef_N_f,
_nrrd_TMF_dn_c1_4ef_1_d, _nrrd_TMF_dn_c1_4ef_N_d
};
/* ------------------------ TMF_dn_c2_1ef --------------------- */
static double _nrrd_TMF_dn_c2_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1672 return 1.0;
}
static double _nrrd_TMF_dn_c2_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1677 return 1;
}
static double
_nrrd_TMF_dn_c2_1ef_1_d( double x, const double *parm ) {
int i;
1683
AIR_UNUSED( parm ); /* TMF_dn_c2_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c2_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c2_1ef_1_f( float x, const double *parm ) {
int i;
1694
AIR_UNUSED( parm ); /* TMF_dn_c2_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c2_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c2_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1705 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c2_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c2_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1720 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c2_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c2_1ef = {
"TMF_dn_c2_1ef",
1, _nrrd_TMF_dn_c2_1ef_Sup, _nrrd_TMF_dn_c2_1ef_Int,
_nrrd_TMF_dn_c2_1ef_1_f, _nrrd_TMF_dn_c2_1ef_N_f,
_nrrd_TMF_dn_c2_1ef_1_d, _nrrd_TMF_dn_c2_1ef_N_d
};
/* ------------------------ TMF_dn_c2_2ef --------------------- */
static double _nrrd_TMF_dn_c2_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1745 return 1.0;
}
static double _nrrd_TMF_dn_c2_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1750 return 2;
}
static double
_nrrd_TMF_dn_c2_2ef_1_d( double x, const double *parm ) {
int i;
1756
AIR_UNUSED( parm ); /* TMF_dn_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c2_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c2_2ef_1_f( float x, const double *parm ) {
int i;
1767
AIR_UNUSED( parm ); /* TMF_dn_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c2_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c2_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1778 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c2_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c2_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1793 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c2_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c2_2ef = {
"TMF_dn_c2_2ef",
1, _nrrd_TMF_dn_c2_2ef_Sup, _nrrd_TMF_dn_c2_2ef_Int,
_nrrd_TMF_dn_c2_2ef_1_f, _nrrd_TMF_dn_c2_2ef_N_f,
_nrrd_TMF_dn_c2_2ef_1_d, _nrrd_TMF_dn_c2_2ef_N_d
};
/* ------------------------ TMF_dn_c2_3ef --------------------- */
static double _nrrd_TMF_dn_c2_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1818 return 1.0;
}
static double _nrrd_TMF_dn_c2_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1823 return 2;
}
static double
_nrrd_TMF_dn_c2_3ef_1_d( double x, const double *parm ) {
int i;
1829
AIR_UNUSED( parm ); /* TMF_dn_c2_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c2_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c2_3ef_1_f( float x, const double *parm ) {
int i;
1840
AIR_UNUSED( parm ); /* TMF_dn_c2_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c2_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c2_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1851 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c2_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c2_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1866 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c2_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c2_3ef = {
"TMF_dn_c2_3ef",
1, _nrrd_TMF_dn_c2_3ef_Sup, _nrrd_TMF_dn_c2_3ef_Int,
_nrrd_TMF_dn_c2_3ef_1_f, _nrrd_TMF_dn_c2_3ef_N_f,
_nrrd_TMF_dn_c2_3ef_1_d, _nrrd_TMF_dn_c2_3ef_N_d
};
/* ------------------------ TMF_dn_c2_4ef --------------------- */
static double _nrrd_TMF_dn_c2_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1891 return 1.0;
}
static double _nrrd_TMF_dn_c2_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1896 return 3;
}
static double
_nrrd_TMF_dn_c2_4ef_1_d( double x, const double *parm ) {
int i;
1902
AIR_UNUSED( parm ); /* TMF_dn_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c2_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c2_4ef_1_f( float x, const double *parm ) {
int i;
1913
AIR_UNUSED( parm ); /* TMF_dn_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c2_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c2_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1924 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c2_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c2_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
1939 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c2_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c2_4ef = {
"TMF_dn_c2_4ef",
1, _nrrd_TMF_dn_c2_4ef_Sup, _nrrd_TMF_dn_c2_4ef_Int,
_nrrd_TMF_dn_c2_4ef_1_f, _nrrd_TMF_dn_c2_4ef_N_f,
_nrrd_TMF_dn_c2_4ef_1_d, _nrrd_TMF_dn_c2_4ef_N_d
};
/* ------------------------ TMF_dn_c3_1ef --------------------- */
static double _nrrd_TMF_dn_c3_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
1964 return 1.0;
}
static double _nrrd_TMF_dn_c3_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
1969 return 1;
}
static double
_nrrd_TMF_dn_c3_1ef_1_d( double x, const double *parm ) {
int i;
1975
AIR_UNUSED( parm ); /* TMF_dn_c3_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c3_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c3_1ef_1_f( float x, const double *parm ) {
int i;
1986
AIR_UNUSED( parm ); /* TMF_dn_c3_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c3_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c3_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
1997 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c3_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c3_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2012 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c3_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c3_1ef = {
"TMF_dn_c3_1ef",
1, _nrrd_TMF_dn_c3_1ef_Sup, _nrrd_TMF_dn_c3_1ef_Int,
_nrrd_TMF_dn_c3_1ef_1_f, _nrrd_TMF_dn_c3_1ef_N_f,
_nrrd_TMF_dn_c3_1ef_1_d, _nrrd_TMF_dn_c3_1ef_N_d
};
/* ------------------------ TMF_dn_c3_2ef --------------------- */
static double _nrrd_TMF_dn_c3_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2037 return 1.0;
}
static double _nrrd_TMF_dn_c3_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2042 return 2;
}
static double
_nrrd_TMF_dn_c3_2ef_1_d( double x, const double *parm ) {
int i;
2048
AIR_UNUSED( parm ); /* TMF_dn_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c3_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c3_2ef_1_f( float x, const double *parm ) {
int i;
2059
AIR_UNUSED( parm ); /* TMF_dn_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c3_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c3_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2070 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c3_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c3_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2085 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c3_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c3_2ef = {
"TMF_dn_c3_2ef",
1, _nrrd_TMF_dn_c3_2ef_Sup, _nrrd_TMF_dn_c3_2ef_Int,
_nrrd_TMF_dn_c3_2ef_1_f, _nrrd_TMF_dn_c3_2ef_N_f,
_nrrd_TMF_dn_c3_2ef_1_d, _nrrd_TMF_dn_c3_2ef_N_d
};
/* ------------------------ TMF_dn_c3_3ef --------------------- */
static double _nrrd_TMF_dn_c3_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2110 return 1.0;
}
static double _nrrd_TMF_dn_c3_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2115 return 2;
}
static double
_nrrd_TMF_dn_c3_3ef_1_d( double x, const double *parm ) {
int i;
2121
AIR_UNUSED( parm ); /* TMF_dn_c3_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c3_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c3_3ef_1_f( float x, const double *parm ) {
int i;
2132
AIR_UNUSED( parm ); /* TMF_dn_c3_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c3_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c3_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2143 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c3_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c3_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2158 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c3_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c3_3ef = {
"TMF_dn_c3_3ef",
1, _nrrd_TMF_dn_c3_3ef_Sup, _nrrd_TMF_dn_c3_3ef_Int,
_nrrd_TMF_dn_c3_3ef_1_f, _nrrd_TMF_dn_c3_3ef_N_f,
_nrrd_TMF_dn_c3_3ef_1_d, _nrrd_TMF_dn_c3_3ef_N_d
};
/* ------------------------ TMF_dn_c3_4ef --------------------- */
static double _nrrd_TMF_dn_c3_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2183 return 1.0;
}
static double _nrrd_TMF_dn_c3_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2188 return 3;
}
static double
_nrrd_TMF_dn_c3_4ef_1_d( double x, const double *parm ) {
int i;
2194
AIR_UNUSED( parm ); /* TMF_dn_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_dn_c3_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_dn_c3_4ef_1_f( float x, const double *parm ) {
int i;
2205
AIR_UNUSED( parm ); /* TMF_dn_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_dn_c3_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_dn_c3_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2216 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_dn_c3_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_dn_c3_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2231 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_dn_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_dn_c3_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_dn_c3_4ef = {
"TMF_dn_c3_4ef",
1, _nrrd_TMF_dn_c3_4ef_Sup, _nrrd_TMF_dn_c3_4ef_Int,
_nrrd_TMF_dn_c3_4ef_1_f, _nrrd_TMF_dn_c3_4ef_N_f,
_nrrd_TMF_dn_c3_4ef_1_d, _nrrd_TMF_dn_c3_4ef_N_d
};
/* ------------------------ TMF_d0_cn_1ef --------------------- */
static double _nrrd_TMF_d0_cn_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2256 return 1.0;
}
static double _nrrd_TMF_d0_cn_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2261 return 1;
}
static double
_nrrd_TMF_d0_cn_1ef_1_d( double x, const double *parm ) {
int i;
2267
AIR_UNUSED( parm ); /* TMF_d0_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_cn_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_cn_1ef_1_f( float x, const double *parm ) {
int i;
2278
AIR_UNUSED( parm ); /* TMF_d0_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_cn_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_cn_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2289 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_cn_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_cn_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2304 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_cn_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_cn_1ef = {
"TMF_d0_cn_1ef",
1, _nrrd_TMF_d0_cn_1ef_Sup, _nrrd_TMF_d0_cn_1ef_Int,
_nrrd_TMF_d0_cn_1ef_1_f, _nrrd_TMF_d0_cn_1ef_N_f,
_nrrd_TMF_d0_cn_1ef_1_d, _nrrd_TMF_d0_cn_1ef_N_d
};
/* ------------------------ TMF_d0_cn_2ef --------------------- */
static double _nrrd_TMF_d0_cn_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2329 return 1.0;
}
static double _nrrd_TMF_d0_cn_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2334 return 1;
}
static double
_nrrd_TMF_d0_cn_2ef_1_d( double x, const double *parm ) {
int i;
2340
AIR_UNUSED( parm ); /* TMF_d0_cn_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_cn_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_cn_2ef_1_f( float x, const double *parm ) {
int i;
2351
AIR_UNUSED( parm ); /* TMF_d0_cn_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_cn_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_cn_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2362 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_cn_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_cn_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2377 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_cn_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_cn_2ef = {
"TMF_d0_cn_2ef",
1, _nrrd_TMF_d0_cn_2ef_Sup, _nrrd_TMF_d0_cn_2ef_Int,
_nrrd_TMF_d0_cn_2ef_1_f, _nrrd_TMF_d0_cn_2ef_N_f,
_nrrd_TMF_d0_cn_2ef_1_d, _nrrd_TMF_d0_cn_2ef_N_d
};
/* ------------------------ TMF_d0_cn_3ef --------------------- */
static double _nrrd_TMF_d0_cn_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2402 return 1.0;
}
static double _nrrd_TMF_d0_cn_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2407 return 2;
}
static double
_nrrd_TMF_d0_cn_3ef_1_d( double x, const double *parm ) {
int i;
2413
AIR_UNUSED( parm ); /* TMF_d0_cn_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_cn_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_cn_3ef_1_f( float x, const double *parm ) {
int i;
2424
AIR_UNUSED( parm ); /* TMF_d0_cn_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_cn_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_cn_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2435 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_cn_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_cn_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2450 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_cn_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_cn_3ef = {
"TMF_d0_cn_3ef",
1, _nrrd_TMF_d0_cn_3ef_Sup, _nrrd_TMF_d0_cn_3ef_Int,
_nrrd_TMF_d0_cn_3ef_1_f, _nrrd_TMF_d0_cn_3ef_N_f,
_nrrd_TMF_d0_cn_3ef_1_d, _nrrd_TMF_d0_cn_3ef_N_d
};
/* ------------------------ TMF_d0_cn_4ef --------------------- */
static double _nrrd_TMF_d0_cn_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2475 return 1.0;
}
static double _nrrd_TMF_d0_cn_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2480 return 2;
}
static double
_nrrd_TMF_d0_cn_4ef_1_d( double x, const double *parm ) {
int i;
2486
AIR_UNUSED( parm ); /* TMF_d0_cn_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_cn_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_cn_4ef_1_f( float x, const double *parm ) {
int i;
2497
AIR_UNUSED( parm ); /* TMF_d0_cn_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_cn_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_cn_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2508 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_cn_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_cn_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2523 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_cn_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_cn_4ef = {
"TMF_d0_cn_4ef",
1, _nrrd_TMF_d0_cn_4ef_Sup, _nrrd_TMF_d0_cn_4ef_Int,
_nrrd_TMF_d0_cn_4ef_1_f, _nrrd_TMF_d0_cn_4ef_N_f,
_nrrd_TMF_d0_cn_4ef_1_d, _nrrd_TMF_d0_cn_4ef_N_d
};
/* ------------------------ TMF_d0_c0_1ef --------------------- */
static double _nrrd_TMF_d0_c0_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2548 return 1.0;
}
static double _nrrd_TMF_d0_c0_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2553 return 1;
}
static double
_nrrd_TMF_d0_c0_1ef_1_d( double x, const double *parm ) {
int i;
2559
AIR_UNUSED( parm ); /* TMF_d0_c0_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c0_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c0_1ef_1_f( float x, const double *parm ) {
int i;
2570
AIR_UNUSED( parm ); /* TMF_d0_c0_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c0_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c0_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2581 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c0_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c0_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2596 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c0_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c0_1ef = {
"TMF_d0_c0_1ef",
1, _nrrd_TMF_d0_c0_1ef_Sup, _nrrd_TMF_d0_c0_1ef_Int,
_nrrd_TMF_d0_c0_1ef_1_f, _nrrd_TMF_d0_c0_1ef_N_f,
_nrrd_TMF_d0_c0_1ef_1_d, _nrrd_TMF_d0_c0_1ef_N_d
};
/* ------------------------ TMF_d0_c0_2ef --------------------- */
static double _nrrd_TMF_d0_c0_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2621 return 1.0;
}
static double _nrrd_TMF_d0_c0_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2626 return 1;
}
static double
_nrrd_TMF_d0_c0_2ef_1_d( double x, const double *parm ) {
int i;
2632
AIR_UNUSED( parm ); /* TMF_d0_c0_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c0_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c0_2ef_1_f( float x, const double *parm ) {
int i;
2643
AIR_UNUSED( parm ); /* TMF_d0_c0_2ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c0_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c0_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2654 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c0_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c0_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2669 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c0_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c0_2ef = {
"TMF_d0_c0_2ef",
1, _nrrd_TMF_d0_c0_2ef_Sup, _nrrd_TMF_d0_c0_2ef_Int,
_nrrd_TMF_d0_c0_2ef_1_f, _nrrd_TMF_d0_c0_2ef_N_f,
_nrrd_TMF_d0_c0_2ef_1_d, _nrrd_TMF_d0_c0_2ef_N_d
};
/* ------------------------ TMF_d0_c0_3ef --------------------- */
static double _nrrd_TMF_d0_c0_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2694 return 1.0;
}
static double _nrrd_TMF_d0_c0_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2699 return 2;
}
static double
_nrrd_TMF_d0_c0_3ef_1_d( double x, const double *parm ) {
int i;
2705
AIR_UNUSED( parm ); /* TMF_d0_c0_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c0_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c0_3ef_1_f( float x, const double *parm ) {
int i;
2716
AIR_UNUSED( parm ); /* TMF_d0_c0_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c0_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c0_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2727 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c0_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c0_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2742 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c0_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c0_3ef = {
"TMF_d0_c0_3ef",
1, _nrrd_TMF_d0_c0_3ef_Sup, _nrrd_TMF_d0_c0_3ef_Int,
_nrrd_TMF_d0_c0_3ef_1_f, _nrrd_TMF_d0_c0_3ef_N_f,
_nrrd_TMF_d0_c0_3ef_1_d, _nrrd_TMF_d0_c0_3ef_N_d
};
/* ------------------------ TMF_d0_c0_4ef --------------------- */
static double _nrrd_TMF_d0_c0_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2767 return 1.0;
}
static double _nrrd_TMF_d0_c0_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2772 return 2;
}
static double
_nrrd_TMF_d0_c0_4ef_1_d( double x, const double *parm ) {
int i;
2778
AIR_UNUSED( parm ); /* TMF_d0_c0_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c0_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c0_4ef_1_f( float x, const double *parm ) {
int i;
2789
AIR_UNUSED( parm ); /* TMF_d0_c0_4ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c0_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c0_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2800 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c0_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c0_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2815 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c0_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c0_4ef = {
"TMF_d0_c0_4ef",
1, _nrrd_TMF_d0_c0_4ef_Sup, _nrrd_TMF_d0_c0_4ef_Int,
_nrrd_TMF_d0_c0_4ef_1_f, _nrrd_TMF_d0_c0_4ef_N_f,
_nrrd_TMF_d0_c0_4ef_1_d, _nrrd_TMF_d0_c0_4ef_N_d
};
/* ------------------------ TMF_d0_c1_1ef --------------------- */
static double _nrrd_TMF_d0_c1_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2840 return 1.0;
}
static double _nrrd_TMF_d0_c1_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2845 return 1;
}
static double
_nrrd_TMF_d0_c1_1ef_1_d( double x, const double *parm ) {
int i;
2851
AIR_UNUSED( parm ); /* TMF_d0_c1_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c1_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c1_1ef_1_f( float x, const double *parm ) {
int i;
2862
AIR_UNUSED( parm ); /* TMF_d0_c1_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c1_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c1_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2873 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c1_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c1_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2888 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c1_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c1_1ef = {
"TMF_d0_c1_1ef",
1, _nrrd_TMF_d0_c1_1ef_Sup, _nrrd_TMF_d0_c1_1ef_Int,
_nrrd_TMF_d0_c1_1ef_1_f, _nrrd_TMF_d0_c1_1ef_N_f,
_nrrd_TMF_d0_c1_1ef_1_d, _nrrd_TMF_d0_c1_1ef_N_d
};
/* ------------------------ TMF_d0_c1_2ef --------------------- */
static double _nrrd_TMF_d0_c1_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2913 return 1.0;
}
static double _nrrd_TMF_d0_c1_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2918 return 2;
}
static double
_nrrd_TMF_d0_c1_2ef_1_d( double x, const double *parm ) {
int i;
2924
AIR_UNUSED( parm ); /* TMF_d0_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c1_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c1_2ef_1_f( float x, const double *parm ) {
int i;
2935
AIR_UNUSED( parm ); /* TMF_d0_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c1_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c1_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
2946 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c1_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c1_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
2961 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c1_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c1_2ef = {
"TMF_d0_c1_2ef",
1, _nrrd_TMF_d0_c1_2ef_Sup, _nrrd_TMF_d0_c1_2ef_Int,
_nrrd_TMF_d0_c1_2ef_1_f, _nrrd_TMF_d0_c1_2ef_N_f,
_nrrd_TMF_d0_c1_2ef_1_d, _nrrd_TMF_d0_c1_2ef_N_d
};
/* ------------------------ TMF_d0_c1_3ef --------------------- */
static double _nrrd_TMF_d0_c1_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
2986 return 1.0;
}
static double _nrrd_TMF_d0_c1_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
2991 return 2;
}
static double
_nrrd_TMF_d0_c1_3ef_1_d( double x, const double *parm ) {
int i;
2997
AIR_UNUSED( parm ); /* TMF_d0_c1_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c1_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c1_3ef_1_f( float x, const double *parm ) {
int i;
3008
AIR_UNUSED( parm ); /* TMF_d0_c1_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c1_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c1_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3019 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c1_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c1_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3034 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c1_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c1_3ef = {
"TMF_d0_c1_3ef",
1, _nrrd_TMF_d0_c1_3ef_Sup, _nrrd_TMF_d0_c1_3ef_Int,
_nrrd_TMF_d0_c1_3ef_1_f, _nrrd_TMF_d0_c1_3ef_N_f,
_nrrd_TMF_d0_c1_3ef_1_d, _nrrd_TMF_d0_c1_3ef_N_d
};
/* ------------------------ TMF_d0_c1_4ef --------------------- */
static double _nrrd_TMF_d0_c1_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3059 return 1.0;
}
static double _nrrd_TMF_d0_c1_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3064 return 3;
}
static double
_nrrd_TMF_d0_c1_4ef_1_d( double x, const double *parm ) {
int i;
3070
AIR_UNUSED( parm ); /* TMF_d0_c1_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c1_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c1_4ef_1_f( float x, const double *parm ) {
int i;
3081
AIR_UNUSED( parm ); /* TMF_d0_c1_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c1_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c1_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3092 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c1_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c1_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3107 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c1_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c1_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c1_4ef = {
"TMF_d0_c1_4ef",
1, _nrrd_TMF_d0_c1_4ef_Sup, _nrrd_TMF_d0_c1_4ef_Int,
_nrrd_TMF_d0_c1_4ef_1_f, _nrrd_TMF_d0_c1_4ef_N_f,
_nrrd_TMF_d0_c1_4ef_1_d, _nrrd_TMF_d0_c1_4ef_N_d
};
/* ------------------------ TMF_d0_c2_1ef --------------------- */
static double _nrrd_TMF_d0_c2_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3132 return 1.0;
}
static double _nrrd_TMF_d0_c2_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3137 return 1;
}
static double
_nrrd_TMF_d0_c2_1ef_1_d( double x, const double *parm ) {
int i;
3143
AIR_UNUSED( parm ); /* TMF_d0_c2_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c2_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c2_1ef_1_f( float x, const double *parm ) {
int i;
3154
AIR_UNUSED( parm ); /* TMF_d0_c2_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c2_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c2_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3165 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c2_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c2_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3180 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c2_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c2_1ef = {
"TMF_d0_c2_1ef",
1, _nrrd_TMF_d0_c2_1ef_Sup, _nrrd_TMF_d0_c2_1ef_Int,
_nrrd_TMF_d0_c2_1ef_1_f, _nrrd_TMF_d0_c2_1ef_N_f,
_nrrd_TMF_d0_c2_1ef_1_d, _nrrd_TMF_d0_c2_1ef_N_d
};
/* ------------------------ TMF_d0_c2_2ef --------------------- */
static double _nrrd_TMF_d0_c2_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3205 return 1.0;
}
static double _nrrd_TMF_d0_c2_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3210 return 2;
}
static double
_nrrd_TMF_d0_c2_2ef_1_d( double x, const double *parm ) {
int i;
3216
AIR_UNUSED( parm ); /* TMF_d0_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c2_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c2_2ef_1_f( float x, const double *parm ) {
int i;
3227
AIR_UNUSED( parm ); /* TMF_d0_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c2_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c2_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3238 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c2_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c2_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3253 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c2_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c2_2ef = {
"TMF_d0_c2_2ef",
1, _nrrd_TMF_d0_c2_2ef_Sup, _nrrd_TMF_d0_c2_2ef_Int,
_nrrd_TMF_d0_c2_2ef_1_f, _nrrd_TMF_d0_c2_2ef_N_f,
_nrrd_TMF_d0_c2_2ef_1_d, _nrrd_TMF_d0_c2_2ef_N_d
};
/* ------------------------ TMF_d0_c2_3ef --------------------- */
static double _nrrd_TMF_d0_c2_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3278 return 1.0;
}
static double _nrrd_TMF_d0_c2_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3283 return 2;
}
static double
_nrrd_TMF_d0_c2_3ef_1_d( double x, const double *parm ) {
int i;
3289
AIR_UNUSED( parm ); /* TMF_d0_c2_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c2_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c2_3ef_1_f( float x, const double *parm ) {
int i;
3300
AIR_UNUSED( parm ); /* TMF_d0_c2_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c2_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c2_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3311 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c2_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c2_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3326 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c2_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c2_3ef = {
"TMF_d0_c2_3ef",
1, _nrrd_TMF_d0_c2_3ef_Sup, _nrrd_TMF_d0_c2_3ef_Int,
_nrrd_TMF_d0_c2_3ef_1_f, _nrrd_TMF_d0_c2_3ef_N_f,
_nrrd_TMF_d0_c2_3ef_1_d, _nrrd_TMF_d0_c2_3ef_N_d
};
/* ------------------------ TMF_d0_c2_4ef --------------------- */
static double _nrrd_TMF_d0_c2_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3351 return 1.0;
}
static double _nrrd_TMF_d0_c2_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3356 return 3;
}
static double
_nrrd_TMF_d0_c2_4ef_1_d( double x, const double *parm ) {
int i;
3362
AIR_UNUSED( parm ); /* TMF_d0_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c2_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c2_4ef_1_f( float x, const double *parm ) {
int i;
3373
AIR_UNUSED( parm ); /* TMF_d0_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c2_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c2_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3384 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c2_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c2_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3399 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c2_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c2_4ef = {
"TMF_d0_c2_4ef",
1, _nrrd_TMF_d0_c2_4ef_Sup, _nrrd_TMF_d0_c2_4ef_Int,
_nrrd_TMF_d0_c2_4ef_1_f, _nrrd_TMF_d0_c2_4ef_N_f,
_nrrd_TMF_d0_c2_4ef_1_d, _nrrd_TMF_d0_c2_4ef_N_d
};
/* ------------------------ TMF_d0_c3_1ef --------------------- */
static double _nrrd_TMF_d0_c3_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3424 return 1.0;
}
static double _nrrd_TMF_d0_c3_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3429 return 1;
}
static double
_nrrd_TMF_d0_c3_1ef_1_d( double x, const double *parm ) {
int i;
3435
AIR_UNUSED( parm ); /* TMF_d0_c3_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c3_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c3_1ef_1_f( float x, const double *parm ) {
int i;
3446
AIR_UNUSED( parm ); /* TMF_d0_c3_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c3_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c3_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3457 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c3_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c3_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3472 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c3_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c3_1ef = {
"TMF_d0_c3_1ef",
1, _nrrd_TMF_d0_c3_1ef_Sup, _nrrd_TMF_d0_c3_1ef_Int,
_nrrd_TMF_d0_c3_1ef_1_f, _nrrd_TMF_d0_c3_1ef_N_f,
_nrrd_TMF_d0_c3_1ef_1_d, _nrrd_TMF_d0_c3_1ef_N_d
};
/* ------------------------ TMF_d0_c3_2ef --------------------- */
static double _nrrd_TMF_d0_c3_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3497 return 1.0;
}
static double _nrrd_TMF_d0_c3_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3502 return 2;
}
static double
_nrrd_TMF_d0_c3_2ef_1_d( double x, const double *parm ) {
int i;
3508
AIR_UNUSED( parm ); /* TMF_d0_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c3_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c3_2ef_1_f( float x, const double *parm ) {
int i;
3519
AIR_UNUSED( parm ); /* TMF_d0_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c3_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c3_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3530 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c3_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c3_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3545 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c3_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c3_2ef = {
"TMF_d0_c3_2ef",
1, _nrrd_TMF_d0_c3_2ef_Sup, _nrrd_TMF_d0_c3_2ef_Int,
_nrrd_TMF_d0_c3_2ef_1_f, _nrrd_TMF_d0_c3_2ef_N_f,
_nrrd_TMF_d0_c3_2ef_1_d, _nrrd_TMF_d0_c3_2ef_N_d
};
/* ------------------------ TMF_d0_c3_3ef --------------------- */
static double _nrrd_TMF_d0_c3_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3570 return 1.0;
}
static double _nrrd_TMF_d0_c3_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3575 return 2;
}
static double
_nrrd_TMF_d0_c3_3ef_1_d( double x, const double *parm ) {
int i;
3581
AIR_UNUSED( parm ); /* TMF_d0_c3_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c3_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c3_3ef_1_f( float x, const double *parm ) {
int i;
3592
AIR_UNUSED( parm ); /* TMF_d0_c3_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c3_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c3_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3603 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c3_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c3_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3618 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c3_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c3_3ef = {
"TMF_d0_c3_3ef",
1, _nrrd_TMF_d0_c3_3ef_Sup, _nrrd_TMF_d0_c3_3ef_Int,
_nrrd_TMF_d0_c3_3ef_1_f, _nrrd_TMF_d0_c3_3ef_N_f,
_nrrd_TMF_d0_c3_3ef_1_d, _nrrd_TMF_d0_c3_3ef_N_d
};
/* ------------------------ TMF_d0_c3_4ef --------------------- */
static double _nrrd_TMF_d0_c3_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3643 return 1.0;
}
static double _nrrd_TMF_d0_c3_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3648 return 3;
}
static double
_nrrd_TMF_d0_c3_4ef_1_d( double x, const double *parm ) {
int i;
3654
AIR_UNUSED( parm ); /* TMF_d0_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d0_c3_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d0_c3_4ef_1_f( float x, const double *parm ) {
int i;
3665
AIR_UNUSED( parm ); /* TMF_d0_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d0_c3_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d0_c3_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3676 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d0_c3_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d0_c3_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3691 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d0_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d0_c3_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d0_c3_4ef = {
"TMF_d0_c3_4ef",
1, _nrrd_TMF_d0_c3_4ef_Sup, _nrrd_TMF_d0_c3_4ef_Int,
_nrrd_TMF_d0_c3_4ef_1_f, _nrrd_TMF_d0_c3_4ef_N_f,
_nrrd_TMF_d0_c3_4ef_1_d, _nrrd_TMF_d0_c3_4ef_N_d
};
/* ------------------------ TMF_d1_cn_1ef --------------------- */
static double _nrrd_TMF_d1_cn_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3716 return 0.0;
}
static double _nrrd_TMF_d1_cn_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3721 return 1;
}
static double
_nrrd_TMF_d1_cn_1ef_1_d( double x, const double *parm ) {
int i;
3727
AIR_UNUSED( parm ); /* TMF_d1_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_cn_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_cn_1ef_1_f( float x, const double *parm ) {
int i;
3738
AIR_UNUSED( parm ); /* TMF_d1_cn_1ef */
x += 1;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_cn_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_cn_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3749 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_cn_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_cn_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3764 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 1;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_cn_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_cn_1ef = {
"TMF_d1_cn_1ef",
1, _nrrd_TMF_d1_cn_1ef_Sup, _nrrd_TMF_d1_cn_1ef_Int,
_nrrd_TMF_d1_cn_1ef_1_f, _nrrd_TMF_d1_cn_1ef_N_f,
_nrrd_TMF_d1_cn_1ef_1_d, _nrrd_TMF_d1_cn_1ef_N_d
};
/* ------------------------ TMF_d1_cn_2ef --------------------- */
static double _nrrd_TMF_d1_cn_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3789 return 0.0;
}
static double _nrrd_TMF_d1_cn_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3794 return 2;
}
static double
_nrrd_TMF_d1_cn_2ef_1_d( double x, const double *parm ) {
int i;
3800
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_cn_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_cn_2ef_1_f( float x, const double *parm ) {
int i;
3811
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_cn_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_cn_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3822 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_cn_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_cn_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3837 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_cn_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_cn_2ef = {
"TMF_d1_cn_2ef",
1, _nrrd_TMF_d1_cn_2ef_Sup, _nrrd_TMF_d1_cn_2ef_Int,
_nrrd_TMF_d1_cn_2ef_1_f, _nrrd_TMF_d1_cn_2ef_N_f,
_nrrd_TMF_d1_cn_2ef_1_d, _nrrd_TMF_d1_cn_2ef_N_d
};
/* ------------------------ TMF_d1_cn_3ef --------------------- */
static double _nrrd_TMF_d1_cn_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3862 return 0.0;
}
static double _nrrd_TMF_d1_cn_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3867 return 2;
}
static double
_nrrd_TMF_d1_cn_3ef_1_d( double x, const double *parm ) {
int i;
3873
AIR_UNUSED( parm ); /* TMF_d1_cn_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_cn_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_cn_3ef_1_f( float x, const double *parm ) {
int i;
3884
AIR_UNUSED( parm ); /* TMF_d1_cn_3ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_cn_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_cn_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3895 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_cn_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_cn_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_cn_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3910 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_cn_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_cn_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_cn_3ef = {
"TMF_d1_cn_3ef",
1, _nrrd_TMF_d1_cn_3ef_Sup, _nrrd_TMF_d1_cn_3ef_Int,
_nrrd_TMF_d1_cn_3ef_1_f, _nrrd_TMF_d1_cn_3ef_N_f,
_nrrd_TMF_d1_cn_3ef_1_d, _nrrd_TMF_d1_cn_3ef_N_d
};
/* ------------------------ TMF_d1_cn_4ef --------------------- */
static double _nrrd_TMF_d1_cn_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
3935 return 0.0;
}
static double _nrrd_TMF_d1_cn_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
3940 return 3;
}
static double
_nrrd_TMF_d1_cn_4ef_1_d( double x, const double *parm ) {
int i;
3946
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_cn_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_cn_4ef_1_f( float x, const double *parm ) {
int i;
3957
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_cn_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_cn_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
3968 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_cn_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_cn_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
3983 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_cn_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_cn_4ef = {
"TMF_d1_cn_4ef",
1, _nrrd_TMF_d1_cn_4ef_Sup, _nrrd_TMF_d1_cn_4ef_Int,
_nrrd_TMF_d1_cn_4ef_1_f, _nrrd_TMF_d1_cn_4ef_N_f,
_nrrd_TMF_d1_cn_4ef_1_d, _nrrd_TMF_d1_cn_4ef_N_d
};
/* ------------------------ TMF_d1_c0_1ef --------------------- */
static double _nrrd_TMF_d1_c0_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4008 return 0.0;
}
static double _nrrd_TMF_d1_c0_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4013 return 2;
}
static double
_nrrd_TMF_d1_c0_1ef_1_d( double x, const double *parm ) {
int i;
4019
AIR_UNUSED( parm ); /* TMF_d1_c0_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c0_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c0_1ef_1_f( float x, const double *parm ) {
int i;
4030
AIR_UNUSED( parm ); /* TMF_d1_c0_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c0_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c0_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4041 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c0_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c0_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4056 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c0_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c0_1ef = {
"TMF_d1_c0_1ef",
1, _nrrd_TMF_d1_c0_1ef_Sup, _nrrd_TMF_d1_c0_1ef_Int,
_nrrd_TMF_d1_c0_1ef_1_f, _nrrd_TMF_d1_c0_1ef_N_f,
_nrrd_TMF_d1_c0_1ef_1_d, _nrrd_TMF_d1_c0_1ef_N_d
};
/* ------------------------ TMF_d1_c0_2ef --------------------- */
static double _nrrd_TMF_d1_c0_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4081 return 0.0;
}
static double _nrrd_TMF_d1_c0_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4086 return 2;
}
static double
_nrrd_TMF_d1_c0_2ef_1_d( double x, const double *parm ) {
int i;
4092
AIR_UNUSED( parm ); /* TMF_d1_c0_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c0_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c0_2ef_1_f( float x, const double *parm ) {
int i;
4103
AIR_UNUSED( parm ); /* TMF_d1_c0_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c0_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c0_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4114 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c0_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c0_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4129 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c0_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c0_2ef = {
"TMF_d1_c0_2ef",
1, _nrrd_TMF_d1_c0_2ef_Sup, _nrrd_TMF_d1_c0_2ef_Int,
_nrrd_TMF_d1_c0_2ef_1_f, _nrrd_TMF_d1_c0_2ef_N_f,
_nrrd_TMF_d1_c0_2ef_1_d, _nrrd_TMF_d1_c0_2ef_N_d
};
/* ------------------------ TMF_d1_c0_3ef --------------------- */
static double _nrrd_TMF_d1_c0_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4154 return 0.0;
}
static double _nrrd_TMF_d1_c0_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4159 return 3;
}
static double
_nrrd_TMF_d1_c0_3ef_1_d( double x, const double *parm ) {
int i;
4165
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c0_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c0_3ef_1_f( float x, const double *parm ) {
int i;
4176
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c0_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c0_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4187 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c0_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c0_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4202 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c0_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c0_3ef = {
"TMF_d1_c0_3ef",
1, _nrrd_TMF_d1_c0_3ef_Sup, _nrrd_TMF_d1_c0_3ef_Int,
_nrrd_TMF_d1_c0_3ef_1_f, _nrrd_TMF_d1_c0_3ef_N_f,
_nrrd_TMF_d1_c0_3ef_1_d, _nrrd_TMF_d1_c0_3ef_N_d
};
/* ------------------------ TMF_d1_c0_4ef --------------------- */
static double _nrrd_TMF_d1_c0_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4227 return 0.0;
}
static double _nrrd_TMF_d1_c0_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4232 return 3;
}
static double
_nrrd_TMF_d1_c0_4ef_1_d( double x, const double *parm ) {
int i;
4238
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c0_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c0_4ef_1_f( float x, const double *parm ) {
int i;
4249
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c0_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c0_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4260 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c0_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c0_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4275 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c0_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c0_4ef = {
"TMF_d1_c0_4ef",
1, _nrrd_TMF_d1_c0_4ef_Sup, _nrrd_TMF_d1_c0_4ef_Int,
_nrrd_TMF_d1_c0_4ef_1_f, _nrrd_TMF_d1_c0_4ef_N_f,
_nrrd_TMF_d1_c0_4ef_1_d, _nrrd_TMF_d1_c0_4ef_N_d
};
/* ------------------------ TMF_d1_c1_1ef --------------------- */
static double _nrrd_TMF_d1_c1_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4300 return 0.0;
}
static double _nrrd_TMF_d1_c1_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4305 return 2;
}
static double
_nrrd_TMF_d1_c1_1ef_1_d( double x, const double *parm ) {
int i;
4311
AIR_UNUSED( parm ); /* TMF_d1_c1_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c1_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c1_1ef_1_f( float x, const double *parm ) {
int i;
4322
AIR_UNUSED( parm ); /* TMF_d1_c1_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c1_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c1_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4333 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c1_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c1_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4348 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c1_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c1_1ef = {
"TMF_d1_c1_1ef",
1, _nrrd_TMF_d1_c1_1ef_Sup, _nrrd_TMF_d1_c1_1ef_Int,
_nrrd_TMF_d1_c1_1ef_1_f, _nrrd_TMF_d1_c1_1ef_N_f,
_nrrd_TMF_d1_c1_1ef_1_d, _nrrd_TMF_d1_c1_1ef_N_d
};
/* ------------------------ TMF_d1_c1_2ef --------------------- */
static double _nrrd_TMF_d1_c1_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4373 return 0.0;
}
static double _nrrd_TMF_d1_c1_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4378 return 2;
}
static double
_nrrd_TMF_d1_c1_2ef_1_d( double x, const double *parm ) {
int i;
4384
AIR_UNUSED( parm ); /* TMF_d1_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c1_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c1_2ef_1_f( float x, const double *parm ) {
int i;
4395
AIR_UNUSED( parm ); /* TMF_d1_c1_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c1_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c1_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4406 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c1_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c1_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4421 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c1_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c1_2ef = {
"TMF_d1_c1_2ef",
1, _nrrd_TMF_d1_c1_2ef_Sup, _nrrd_TMF_d1_c1_2ef_Int,
_nrrd_TMF_d1_c1_2ef_1_f, _nrrd_TMF_d1_c1_2ef_N_f,
_nrrd_TMF_d1_c1_2ef_1_d, _nrrd_TMF_d1_c1_2ef_N_d
};
/* ------------------------ TMF_d1_c1_3ef --------------------- */
static double _nrrd_TMF_d1_c1_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4446 return 0.0;
}
static double _nrrd_TMF_d1_c1_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4451 return 3;
}
static double
_nrrd_TMF_d1_c1_3ef_1_d( double x, const double *parm ) {
int i;
4457
AIR_UNUSED( parm ); /* TMF_d1_c1_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c1_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c1_3ef_1_f( float x, const double *parm ) {
int i;
4468
AIR_UNUSED( parm ); /* TMF_d1_c1_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c1_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c1_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4479 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c1_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c1_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4494 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c1_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c1_3ef = {
"TMF_d1_c1_3ef",
1, _nrrd_TMF_d1_c1_3ef_Sup, _nrrd_TMF_d1_c1_3ef_Int,
_nrrd_TMF_d1_c1_3ef_1_f, _nrrd_TMF_d1_c1_3ef_N_f,
_nrrd_TMF_d1_c1_3ef_1_d, _nrrd_TMF_d1_c1_3ef_N_d
};
/* ------------------------ TMF_d1_c1_4ef --------------------- */
static double _nrrd_TMF_d1_c1_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4519 return 0.0;
}
static double _nrrd_TMF_d1_c1_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4524 return 3;
}
static double
_nrrd_TMF_d1_c1_4ef_1_d( double x, const double *parm ) {
int i;
4530
AIR_UNUSED( parm ); /* TMF_d1_c1_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c1_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c1_4ef_1_f( float x, const double *parm ) {
int i;
4541
AIR_UNUSED( parm ); /* TMF_d1_c1_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c1_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c1_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4552 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c1_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c1_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4567 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c1_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c1_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c1_4ef = {
"TMF_d1_c1_4ef",
1, _nrrd_TMF_d1_c1_4ef_Sup, _nrrd_TMF_d1_c1_4ef_Int,
_nrrd_TMF_d1_c1_4ef_1_f, _nrrd_TMF_d1_c1_4ef_N_f,
_nrrd_TMF_d1_c1_4ef_1_d, _nrrd_TMF_d1_c1_4ef_N_d
};
/* ------------------------ TMF_d1_c2_1ef --------------------- */
static double _nrrd_TMF_d1_c2_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4592 return 0.0;
}
static double _nrrd_TMF_d1_c2_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4597 return 2;
}
static double
_nrrd_TMF_d1_c2_1ef_1_d( double x, const double *parm ) {
int i;
4603
AIR_UNUSED( parm ); /* TMF_d1_c2_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c2_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c2_1ef_1_f( float x, const double *parm ) {
int i;
4614
AIR_UNUSED( parm ); /* TMF_d1_c2_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c2_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c2_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4625 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c2_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c2_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4640 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c2_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c2_1ef = {
"TMF_d1_c2_1ef",
1, _nrrd_TMF_d1_c2_1ef_Sup, _nrrd_TMF_d1_c2_1ef_Int,
_nrrd_TMF_d1_c2_1ef_1_f, _nrrd_TMF_d1_c2_1ef_N_f,
_nrrd_TMF_d1_c2_1ef_1_d, _nrrd_TMF_d1_c2_1ef_N_d
};
/* ------------------------ TMF_d1_c2_2ef --------------------- */
static double _nrrd_TMF_d1_c2_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4665 return 0.0;
}
static double _nrrd_TMF_d1_c2_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4670 return 2;
}
static double
_nrrd_TMF_d1_c2_2ef_1_d( double x, const double *parm ) {
int i;
4676
AIR_UNUSED( parm ); /* TMF_d1_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c2_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c2_2ef_1_f( float x, const double *parm ) {
int i;
4687
AIR_UNUSED( parm ); /* TMF_d1_c2_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c2_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c2_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4698 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c2_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c2_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4713 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c2_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c2_2ef = {
"TMF_d1_c2_2ef",
1, _nrrd_TMF_d1_c2_2ef_Sup, _nrrd_TMF_d1_c2_2ef_Int,
_nrrd_TMF_d1_c2_2ef_1_f, _nrrd_TMF_d1_c2_2ef_N_f,
_nrrd_TMF_d1_c2_2ef_1_d, _nrrd_TMF_d1_c2_2ef_N_d
};
/* ------------------------ TMF_d1_c2_3ef --------------------- */
static double _nrrd_TMF_d1_c2_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4738 return 0.0;
}
static double _nrrd_TMF_d1_c2_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4743 return 3;
}
static double
_nrrd_TMF_d1_c2_3ef_1_d( double x, const double *parm ) {
int i;
4749
AIR_UNUSED( parm ); /* TMF_d1_c2_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c2_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c2_3ef_1_f( float x, const double *parm ) {
int i;
4760
AIR_UNUSED( parm ); /* TMF_d1_c2_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c2_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c2_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4771 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c2_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c2_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4786 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c2_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c2_3ef = {
"TMF_d1_c2_3ef",
1, _nrrd_TMF_d1_c2_3ef_Sup, _nrrd_TMF_d1_c2_3ef_Int,
_nrrd_TMF_d1_c2_3ef_1_f, _nrrd_TMF_d1_c2_3ef_N_f,
_nrrd_TMF_d1_c2_3ef_1_d, _nrrd_TMF_d1_c2_3ef_N_d
};
/* ------------------------ TMF_d1_c2_4ef --------------------- */
static double _nrrd_TMF_d1_c2_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4811 return 0.0;
}
static double _nrrd_TMF_d1_c2_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4816 return 3;
}
static double
_nrrd_TMF_d1_c2_4ef_1_d( double x, const double *parm ) {
int i;
4822
AIR_UNUSED( parm ); /* TMF_d1_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c2_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c2_4ef_1_f( float x, const double *parm ) {
int i;
4833
AIR_UNUSED( parm ); /* TMF_d1_c2_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c2_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c2_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4844 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c2_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c2_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4859 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c2_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c2_4ef = {
"TMF_d1_c2_4ef",
1, _nrrd_TMF_d1_c2_4ef_Sup, _nrrd_TMF_d1_c2_4ef_Int,
_nrrd_TMF_d1_c2_4ef_1_f, _nrrd_TMF_d1_c2_4ef_N_f,
_nrrd_TMF_d1_c2_4ef_1_d, _nrrd_TMF_d1_c2_4ef_N_d
};
/* ------------------------ TMF_d1_c3_1ef --------------------- */
static double _nrrd_TMF_d1_c3_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4884 return 0.0;
}
static double _nrrd_TMF_d1_c3_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4889 return 2;
}
static double
_nrrd_TMF_d1_c3_1ef_1_d( double x, const double *parm ) {
int i;
4895
AIR_UNUSED( parm ); /* TMF_d1_c3_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c3_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c3_1ef_1_f( float x, const double *parm ) {
int i;
4906
AIR_UNUSED( parm ); /* TMF_d1_c3_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c3_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c3_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4917 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c3_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c3_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
4932 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c3_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c3_1ef = {
"TMF_d1_c3_1ef",
1, _nrrd_TMF_d1_c3_1ef_Sup, _nrrd_TMF_d1_c3_1ef_Int,
_nrrd_TMF_d1_c3_1ef_1_f, _nrrd_TMF_d1_c3_1ef_N_f,
_nrrd_TMF_d1_c3_1ef_1_d, _nrrd_TMF_d1_c3_1ef_N_d
};
/* ------------------------ TMF_d1_c3_2ef --------------------- */
static double _nrrd_TMF_d1_c3_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
4957 return 0.0;
}
static double _nrrd_TMF_d1_c3_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
4962 return 2;
}
static double
_nrrd_TMF_d1_c3_2ef_1_d( double x, const double *parm ) {
int i;
4968
AIR_UNUSED( parm ); /* TMF_d1_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c3_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c3_2ef_1_f( float x, const double *parm ) {
int i;
4979
AIR_UNUSED( parm ); /* TMF_d1_c3_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c3_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c3_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
4990 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c3_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c3_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5005 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c3_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c3_2ef = {
"TMF_d1_c3_2ef",
1, _nrrd_TMF_d1_c3_2ef_Sup, _nrrd_TMF_d1_c3_2ef_Int,
_nrrd_TMF_d1_c3_2ef_1_f, _nrrd_TMF_d1_c3_2ef_N_f,
_nrrd_TMF_d1_c3_2ef_1_d, _nrrd_TMF_d1_c3_2ef_N_d
};
/* ------------------------ TMF_d1_c3_3ef --------------------- */
static double _nrrd_TMF_d1_c3_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5030 return 0.0;
}
static double _nrrd_TMF_d1_c3_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5035 return 3;
}
static double
_nrrd_TMF_d1_c3_3ef_1_d( double x, const double *parm ) {
int i;
5041
AIR_UNUSED( parm ); /* TMF_d1_c3_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c3_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c3_3ef_1_f( float x, const double *parm ) {
int i;
5052
AIR_UNUSED( parm ); /* TMF_d1_c3_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c3_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c3_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5063 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c3_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c3_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5078 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c3_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c3_3ef = {
"TMF_d1_c3_3ef",
1, _nrrd_TMF_d1_c3_3ef_Sup, _nrrd_TMF_d1_c3_3ef_Int,
_nrrd_TMF_d1_c3_3ef_1_f, _nrrd_TMF_d1_c3_3ef_N_f,
_nrrd_TMF_d1_c3_3ef_1_d, _nrrd_TMF_d1_c3_3ef_N_d
};
/* ------------------------ TMF_d1_c3_4ef --------------------- */
static double _nrrd_TMF_d1_c3_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5103 return 0.0;
}
static double _nrrd_TMF_d1_c3_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5108 return 3;
}
static double
_nrrd_TMF_d1_c3_4ef_1_d( double x, const double *parm ) {
int i;
5114
AIR_UNUSED( parm ); /* TMF_d1_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d1_c3_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d1_c3_4ef_1_f( float x, const double *parm ) {
int i;
5125
AIR_UNUSED( parm ); /* TMF_d1_c3_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d1_c3_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d1_c3_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5136 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d1_c3_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d1_c3_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5151 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d1_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d1_c3_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d1_c3_4ef = {
"TMF_d1_c3_4ef",
1, _nrrd_TMF_d1_c3_4ef_Sup, _nrrd_TMF_d1_c3_4ef_Int,
_nrrd_TMF_d1_c3_4ef_1_f, _nrrd_TMF_d1_c3_4ef_N_f,
_nrrd_TMF_d1_c3_4ef_1_d, _nrrd_TMF_d1_c3_4ef_N_d
};
/* ------------------------ TMF_d2_cn_1ef --------------------- */
static double _nrrd_TMF_d2_cn_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5176 return 0.0;
}
static double _nrrd_TMF_d2_cn_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5181 return 2;
}
static double
_nrrd_TMF_d2_cn_1ef_1_d( double x, const double *parm ) {
int i;
5187
AIR_UNUSED( parm ); /* TMF_d2_cn_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_cn_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_cn_1ef_1_f( float x, const double *parm ) {
int i;
5198
AIR_UNUSED( parm ); /* TMF_d2_cn_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_cn_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_cn_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5209 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_cn_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_cn_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5224 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_cn_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_cn_1ef = {
"TMF_d2_cn_1ef",
1, _nrrd_TMF_d2_cn_1ef_Sup, _nrrd_TMF_d2_cn_1ef_Int,
_nrrd_TMF_d2_cn_1ef_1_f, _nrrd_TMF_d2_cn_1ef_N_f,
_nrrd_TMF_d2_cn_1ef_1_d, _nrrd_TMF_d2_cn_1ef_N_d
};
/* ------------------------ TMF_d2_cn_2ef --------------------- */
static double _nrrd_TMF_d2_cn_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5249 return 0.0;
}
static double _nrrd_TMF_d2_cn_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5254 return 2;
}
static double
_nrrd_TMF_d2_cn_2ef_1_d( double x, const double *parm ) {
int i;
5260
AIR_UNUSED( parm ); /* TMF_d2_cn_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_cn_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_cn_2ef_1_f( float x, const double *parm ) {
int i;
5271
AIR_UNUSED( parm ); /* TMF_d2_cn_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_cn_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_cn_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5282 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_cn_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_cn_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5297 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_cn_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_cn_2ef = {
"TMF_d2_cn_2ef",
1, _nrrd_TMF_d2_cn_2ef_Sup, _nrrd_TMF_d2_cn_2ef_Int,
_nrrd_TMF_d2_cn_2ef_1_f, _nrrd_TMF_d2_cn_2ef_N_f,
_nrrd_TMF_d2_cn_2ef_1_d, _nrrd_TMF_d2_cn_2ef_N_d
};
/* ------------------------ TMF_d2_cn_3ef --------------------- */
static double _nrrd_TMF_d2_cn_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5322 return 0.0;
}
static double _nrrd_TMF_d2_cn_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5327 return 3;
}
static double
_nrrd_TMF_d2_cn_3ef_1_d( double x, const double *parm ) {
int i;
5333
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_cn_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_cn_3ef_1_f( float x, const double *parm ) {
int i;
5344
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_cn_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_cn_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5355 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_cn_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_cn_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5370 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_cn_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_cn_3ef = {
"TMF_d2_cn_3ef",
1, _nrrd_TMF_d2_cn_3ef_Sup, _nrrd_TMF_d2_cn_3ef_Int,
_nrrd_TMF_d2_cn_3ef_1_f, _nrrd_TMF_d2_cn_3ef_N_f,
_nrrd_TMF_d2_cn_3ef_1_d, _nrrd_TMF_d2_cn_3ef_N_d
};
/* ------------------------ TMF_d2_cn_4ef --------------------- */
static double _nrrd_TMF_d2_cn_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5395 return 0.0;
}
static double _nrrd_TMF_d2_cn_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5400 return 3;
}
static double
_nrrd_TMF_d2_cn_4ef_1_d( double x, const double *parm ) {
int i;
5406
AIR_UNUSED( parm ); /* TMF_d2_cn_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_cn_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_cn_4ef_1_f( float x, const double *parm ) {
int i;
5417
AIR_UNUSED( parm ); /* TMF_d2_cn_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_cn_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_cn_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5428 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_cn_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_cn_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5443 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_cn_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_cn_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_cn_4ef = {
"TMF_d2_cn_4ef",
1, _nrrd_TMF_d2_cn_4ef_Sup, _nrrd_TMF_d2_cn_4ef_Int,
_nrrd_TMF_d2_cn_4ef_1_f, _nrrd_TMF_d2_cn_4ef_N_f,
_nrrd_TMF_d2_cn_4ef_1_d, _nrrd_TMF_d2_cn_4ef_N_d
};
/* ------------------------ TMF_d2_c0_1ef --------------------- */
static double _nrrd_TMF_d2_c0_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5468 return 0.0;
}
static double _nrrd_TMF_d2_c0_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5473 return 2;
}
static double
_nrrd_TMF_d2_c0_1ef_1_d( double x, const double *parm ) {
int i;
5479
AIR_UNUSED( parm ); /* TMF_d2_c0_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c0_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c0_1ef_1_f( float x, const double *parm ) {
int i;
5490
AIR_UNUSED( parm ); /* TMF_d2_c0_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c0_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c0_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5501 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c0_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c0_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5516 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c0_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c0_1ef = {
"TMF_d2_c0_1ef",
1, _nrrd_TMF_d2_c0_1ef_Sup, _nrrd_TMF_d2_c0_1ef_Int,
_nrrd_TMF_d2_c0_1ef_1_f, _nrrd_TMF_d2_c0_1ef_N_f,
_nrrd_TMF_d2_c0_1ef_1_d, _nrrd_TMF_d2_c0_1ef_N_d
};
/* ------------------------ TMF_d2_c0_2ef --------------------- */
static double _nrrd_TMF_d2_c0_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5541 return 0.0;
}
static double _nrrd_TMF_d2_c0_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5546 return 2;
}
static double
_nrrd_TMF_d2_c0_2ef_1_d( double x, const double *parm ) {
int i;
5552
AIR_UNUSED( parm ); /* TMF_d2_c0_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c0_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c0_2ef_1_f( float x, const double *parm ) {
int i;
5563
AIR_UNUSED( parm ); /* TMF_d2_c0_2ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c0_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c0_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5574 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c0_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c0_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5589 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c0_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c0_2ef = {
"TMF_d2_c0_2ef",
1, _nrrd_TMF_d2_c0_2ef_Sup, _nrrd_TMF_d2_c0_2ef_Int,
_nrrd_TMF_d2_c0_2ef_1_f, _nrrd_TMF_d2_c0_2ef_N_f,
_nrrd_TMF_d2_c0_2ef_1_d, _nrrd_TMF_d2_c0_2ef_N_d
};
/* ------------------------ TMF_d2_c0_3ef --------------------- */
static double _nrrd_TMF_d2_c0_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5614 return 0.0;
}
static double _nrrd_TMF_d2_c0_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5619 return 3;
}
static double
_nrrd_TMF_d2_c0_3ef_1_d( double x, const double *parm ) {
int i;
5625
AIR_UNUSED( parm ); /* TMF_d2_c0_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c0_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c0_3ef_1_f( float x, const double *parm ) {
int i;
5636
AIR_UNUSED( parm ); /* TMF_d2_c0_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c0_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c0_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5647 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c0_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c0_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5662 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c0_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c0_3ef = {
"TMF_d2_c0_3ef",
1, _nrrd_TMF_d2_c0_3ef_Sup, _nrrd_TMF_d2_c0_3ef_Int,
_nrrd_TMF_d2_c0_3ef_1_f, _nrrd_TMF_d2_c0_3ef_N_f,
_nrrd_TMF_d2_c0_3ef_1_d, _nrrd_TMF_d2_c0_3ef_N_d
};
/* ------------------------ TMF_d2_c0_4ef --------------------- */
static double _nrrd_TMF_d2_c0_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5687 return 0.0;
}
static double _nrrd_TMF_d2_c0_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5692 return 3;
}
static double
_nrrd_TMF_d2_c0_4ef_1_d( double x, const double *parm ) {
int i;
5698
AIR_UNUSED( parm ); /* TMF_d2_c0_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c0_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c0_4ef_1_f( float x, const double *parm ) {
int i;
5709
AIR_UNUSED( parm ); /* TMF_d2_c0_4ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c0_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c0_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5720 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c0_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c0_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5735 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c0_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c0_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c0_4ef = {
"TMF_d2_c0_4ef",
1, _nrrd_TMF_d2_c0_4ef_Sup, _nrrd_TMF_d2_c0_4ef_Int,
_nrrd_TMF_d2_c0_4ef_1_f, _nrrd_TMF_d2_c0_4ef_N_f,
_nrrd_TMF_d2_c0_4ef_1_d, _nrrd_TMF_d2_c0_4ef_N_d
};
/* ------------------------ TMF_d2_c1_1ef --------------------- */
static double _nrrd_TMF_d2_c1_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5760 return 0.0;
}
static double _nrrd_TMF_d2_c1_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5765 return 2;
}
static double
_nrrd_TMF_d2_c1_1ef_1_d( double x, const double *parm ) {
int i;
5771
AIR_UNUSED( parm ); /* TMF_d2_c1_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c1_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c1_1ef_1_f( float x, const double *parm ) {
int i;
5782
AIR_UNUSED( parm ); /* TMF_d2_c1_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c1_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c1_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5793 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c1_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c1_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5808 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c1_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c1_1ef = {
"TMF_d2_c1_1ef",
1, _nrrd_TMF_d2_c1_1ef_Sup, _nrrd_TMF_d2_c1_1ef_Int,
_nrrd_TMF_d2_c1_1ef_1_f, _nrrd_TMF_d2_c1_1ef_N_f,
_nrrd_TMF_d2_c1_1ef_1_d, _nrrd_TMF_d2_c1_1ef_N_d
};
/* ------------------------ TMF_d2_c1_2ef --------------------- */
static double _nrrd_TMF_d2_c1_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5833 return 0.0;
}
static double _nrrd_TMF_d2_c1_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5838 return 3;
}
static double
_nrrd_TMF_d2_c1_2ef_1_d( double x, const double *parm ) {
int i;
5844
AIR_UNUSED( parm ); /* TMF_d2_c1_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c1_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c1_2ef_1_f( float x, const double *parm ) {
int i;
5855
AIR_UNUSED( parm ); /* TMF_d2_c1_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c1_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c1_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5866 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c1_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c1_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5881 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c1_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c1_2ef = {
"TMF_d2_c1_2ef",
1, _nrrd_TMF_d2_c1_2ef_Sup, _nrrd_TMF_d2_c1_2ef_Int,
_nrrd_TMF_d2_c1_2ef_1_f, _nrrd_TMF_d2_c1_2ef_N_f,
_nrrd_TMF_d2_c1_2ef_1_d, _nrrd_TMF_d2_c1_2ef_N_d
};
/* ------------------------ TMF_d2_c1_3ef --------------------- */
static double _nrrd_TMF_d2_c1_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5906 return 0.0;
}
static double _nrrd_TMF_d2_c1_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5911 return 3;
}
static double
_nrrd_TMF_d2_c1_3ef_1_d( double x, const double *parm ) {
int i;
5917
AIR_UNUSED( parm ); /* TMF_d2_c1_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c1_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c1_3ef_1_f( float x, const double *parm ) {
int i;
5928
AIR_UNUSED( parm ); /* TMF_d2_c1_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c1_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c1_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
5939 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c1_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c1_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
5954 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c1_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c1_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c1_3ef = {
"TMF_d2_c1_3ef",
1, _nrrd_TMF_d2_c1_3ef_Sup, _nrrd_TMF_d2_c1_3ef_Int,
_nrrd_TMF_d2_c1_3ef_1_f, _nrrd_TMF_d2_c1_3ef_N_f,
_nrrd_TMF_d2_c1_3ef_1_d, _nrrd_TMF_d2_c1_3ef_N_d
};
/* ------------------------ TMF_d2_c1_4ef --------------------- */
static double _nrrd_TMF_d2_c1_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
5979 return 0.0;
}
static double _nrrd_TMF_d2_c1_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
5984 return 4;
}
static double
_nrrd_TMF_d2_c1_4ef_1_d( double x, const double *parm ) {
int i;
5990
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c1_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c1_4ef_1_f( float x, const double *parm ) {
int i;
6001
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c1_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c1_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
6012 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c1_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c1_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
6027 size_t I;
int i;
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c1_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c1_4ef = {
"TMF_d2_c1_4ef",
1, _nrrd_TMF_d2_c1_4ef_Sup, _nrrd_TMF_d2_c1_4ef_Int,
_nrrd_TMF_d2_c1_4ef_1_f, _nrrd_TMF_d2_c1_4ef_N_f,
_nrrd_TMF_d2_c1_4ef_1_d, _nrrd_TMF_d2_c1_4ef_N_d
};
/* ------------------------ TMF_d2_c2_1ef --------------------- */
static double _nrrd_TMF_d2_c2_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
6052 return 0.0;
}
static double _nrrd_TMF_d2_c2_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
6057 return 2;
}
static double
_nrrd_TMF_d2_c2_1ef_1_d( double x, const double *parm ) {
int i;
6063
AIR_UNUSED( parm ); /* TMF_d2_c2_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c2_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c2_1ef_1_f( float x, const double *parm ) {
int i;
6074
AIR_UNUSED( parm ); /* TMF_d2_c2_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c2_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c2_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
6085 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c2_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c2_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
6100 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c2_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c2_1ef = {
"TMF_d2_c2_1ef",
1, _nrrd_TMF_d2_c2_1ef_Sup, _nrrd_TMF_d2_c2_1ef_Int,
_nrrd_TMF_d2_c2_1ef_1_f, _nrrd_TMF_d2_c2_1ef_N_f,
_nrrd_TMF_d2_c2_1ef_1_d, _nrrd_TMF_d2_c2_1ef_N_d
};
/* ------------------------ TMF_d2_c2_2ef --------------------- */
static double _nrrd_TMF_d2_c2_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
6125 return 0.0;
}
static double _nrrd_TMF_d2_c2_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
6130 return 3;
}
static double
_nrrd_TMF_d2_c2_2ef_1_d( double x, const double *parm ) {
int i;
6136
AIR_UNUSED( parm ); /* TMF_d2_c2_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c2_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c2_2ef_1_f( float x, const double *parm ) {
int i;
6147
AIR_UNUSED( parm ); /* TMF_d2_c2_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c2_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c2_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
6158 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c2_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c2_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
6173 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c2_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c2_2ef = {
"TMF_d2_c2_2ef",
1, _nrrd_TMF_d2_c2_2ef_Sup, _nrrd_TMF_d2_c2_2ef_Int,
_nrrd_TMF_d2_c2_2ef_1_f, _nrrd_TMF_d2_c2_2ef_N_f,
_nrrd_TMF_d2_c2_2ef_1_d, _nrrd_TMF_d2_c2_2ef_N_d
};
/* ------------------------ TMF_d2_c2_3ef --------------------- */
static double _nrrd_TMF_d2_c2_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
6198 return 0.0;
}
static double _nrrd_TMF_d2_c2_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
6203 return 3;
}
static double
_nrrd_TMF_d2_c2_3ef_1_d( double x, const double *parm ) {
int i;
6209
AIR_UNUSED( parm ); /* TMF_d2_c2_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c2_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c2_3ef_1_f( float x, const double *parm ) {
int i;
6220
AIR_UNUSED( parm ); /* TMF_d2_c2_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c2_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c2_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
6231 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c2_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c2_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
6246 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c2_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c2_3ef = {
"TMF_d2_c2_3ef",
1, _nrrd_TMF_d2_c2_3ef_Sup, _nrrd_TMF_d2_c2_3ef_Int,
_nrrd_TMF_d2_c2_3ef_1_f, _nrrd_TMF_d2_c2_3ef_N_f,
_nrrd_TMF_d2_c2_3ef_1_d, _nrrd_TMF_d2_c2_3ef_N_d
};
/* ------------------------ TMF_d2_c2_4ef --------------------- */
static double _nrrd_TMF_d2_c2_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
6271 return 0.0;
}
static double _nrrd_TMF_d2_c2_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
6276 return 4;
}
static double
_nrrd_TMF_d2_c2_4ef_1_d( double x, const double *parm ) {
int i;
6282
AIR_UNUSED( parm ); /* TMF_d2_c2_4ef */
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c2_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c2_4ef_1_f( float x, const double *parm ) {
int i;
6293
AIR_UNUSED( parm ); /* TMF_d2_c2_4ef */
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c2_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c2_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
6304 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c2_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c2_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
6319 size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c2_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c2_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c2_4ef = {
"TMF_d2_c2_4ef",
1, _nrrd_TMF_d2_c2_4ef_Sup, _nrrd_TMF_d2_c2_4ef_Int,
_nrrd_TMF_d2_c2_4ef_1_f, _nrrd_TMF_d2_c2_4ef_N_f,
_nrrd_TMF_d2_c2_4ef_1_d, _nrrd_TMF_d2_c2_4ef_N_d
};
/* ------------------------ TMF_d2_c3_1ef --------------------- */
6342 static double _nrrd_TMF_d2_c3_1ef_Int( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double _nrrd_TMF_d2_c3_1ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
return 2;
}
static double
_nrrd_TMF_d2_c3_1ef_1_d( double x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c3_1ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c3_1ef_1_f( float x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_1ef */
x += 2;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c3_1ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c3_1ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c3_1ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c3_1ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_1ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 2;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c3_1ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c3_1ef = {
"TMF_d2_c3_1ef",
1, _nrrd_TMF_d2_c3_1ef_Sup, _nrrd_TMF_d2_c3_1ef_Int,
_nrrd_TMF_d2_c3_1ef_1_f, _nrrd_TMF_d2_c3_1ef_N_f,
_nrrd_TMF_d2_c3_1ef_1_d, _nrrd_TMF_d2_c3_1ef_N_d
};
/* ------------------------ TMF_d2_c3_2ef --------------------- */
static double _nrrd_TMF_d2_c3_2ef_Int( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double _nrrd_TMF_d2_c3_2ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
return 3;
}
static double
_nrrd_TMF_d2_c3_2ef_1_d( double x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c3_2ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c3_2ef_1_f( float x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_2ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c3_2ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c3_2ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c3_2ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c3_2ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_2ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c3_2ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c3_2ef = {
"TMF_d2_c3_2ef",
1, _nrrd_TMF_d2_c3_2ef_Sup, _nrrd_TMF_d2_c3_2ef_Int,
_nrrd_TMF_d2_c3_2ef_1_f, _nrrd_TMF_d2_c3_2ef_N_f,
_nrrd_TMF_d2_c3_2ef_1_d, _nrrd_TMF_d2_c3_2ef_N_d
};
/* ------------------------ TMF_d2_c3_3ef --------------------- */
static double _nrrd_TMF_d2_c3_3ef_Int( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double _nrrd_TMF_d2_c3_3ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
return 3;
}
static double
_nrrd_TMF_d2_c3_3ef_1_d( double x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c3_3ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c3_3ef_1_f( float x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_3ef */
x += 3;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c3_3ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c3_3ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c3_3ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c3_3ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_3ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 3;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c3_3ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c3_3ef = {
"TMF_d2_c3_3ef",
1, _nrrd_TMF_d2_c3_3ef_Sup, _nrrd_TMF_d2_c3_3ef_Int,
_nrrd_TMF_d2_c3_3ef_1_f, _nrrd_TMF_d2_c3_3ef_N_f,
_nrrd_TMF_d2_c3_3ef_1_d, _nrrd_TMF_d2_c3_3ef_N_d
};
/* ------------------------ TMF_d2_c3_4ef --------------------- */
static double _nrrd_TMF_d2_c3_4ef_Int( const double *parm ) {
AIR_UNUSED( parm );
return 0.0;
}
static double _nrrd_TMF_d2_c3_4ef_Sup( const double *parm ) {
AIR_UNUSED( parm );
return 4;
}
static double
_nrrd_TMF_d2_c3_4ef_1_d( double x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_4ef */
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= i;
return TMF_d2_c3_4ef( parm[0], i, x );
}
static float
_nrrd_TMF_d2_c3_4ef_1_f( float x, const double *parm ) {
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_4ef */
x += 4;
i = ( int )( ( x<0 ) ? x-1 : x ); /* HEY scrutinize cast */
x -= AIR_CAST( float, i );
return AIR_CAST( float, TMF_d2_c3_4ef( parm[0], i, x ) );
}
static void
_nrrd_TMF_d2_c3_4ef_N_d( double *f, const double *x, size_t len, const double *parm ) {
double t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= i;
f[I] = TMF_d2_c3_4ef( parm[0], i, t );
}
}
static void
_nrrd_TMF_d2_c3_4ef_N_f( float *f, const float *x, size_t len, const double *parm ) {
float t;
size_t I;
int i;
AIR_UNUSED( parm ); /* TMF_d2_c3_4ef */
for ( I=0; I<len; I++ ) {
t = x[I] + 4;
i = ( int )( ( t<0 ) ? t-1 : t ); /* HEY scrutinize cast */
t -= AIR_CAST( float, i );
f[I] = AIR_CAST( float, TMF_d2_c3_4ef( parm[0], i, t ) );
}
}
static NrrdKernel
_nrrdKernel_TMF_d2_c3_4ef = {
"TMF_d2_c3_4ef",
1, _nrrd_TMF_d2_c3_4ef_Sup, _nrrd_TMF_d2_c3_4ef_Int,
_nrrd_TMF_d2_c3_4ef_1_f, _nrrd_TMF_d2_c3_4ef_N_f,
_nrrd_TMF_d2_c3_4ef_1_d, _nrrd_TMF_d2_c3_4ef_N_d
};
NrrdKernel *const
nrrdKernelTMF[4][5][5] = {
{ /* d = n */
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_dn_cn_1ef,
&_nrrdKernel_TMF_dn_cn_2ef,
&_nrrdKernel_TMF_dn_cn_3ef,
&_nrrdKernel_TMF_dn_cn_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_dn_c0_1ef,
&_nrrdKernel_TMF_dn_c0_2ef,
&_nrrdKernel_TMF_dn_c0_3ef,
&_nrrdKernel_TMF_dn_c0_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_dn_c1_1ef,
&_nrrdKernel_TMF_dn_c1_2ef,
&_nrrdKernel_TMF_dn_c1_3ef,
&_nrrdKernel_TMF_dn_c1_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_dn_c2_1ef,
&_nrrdKernel_TMF_dn_c2_2ef,
&_nrrdKernel_TMF_dn_c2_3ef,
&_nrrdKernel_TMF_dn_c2_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_dn_c3_1ef,
&_nrrdKernel_TMF_dn_c3_2ef,
&_nrrdKernel_TMF_dn_c3_3ef,
&_nrrdKernel_TMF_dn_c3_4ef,
},
},
{ /* d = 0 */
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d0_cn_1ef,
&_nrrdKernel_TMF_d0_cn_2ef,
&_nrrdKernel_TMF_d0_cn_3ef,
&_nrrdKernel_TMF_d0_cn_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d0_c0_1ef,
&_nrrdKernel_TMF_d0_c0_2ef,
&_nrrdKernel_TMF_d0_c0_3ef,
&_nrrdKernel_TMF_d0_c0_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d0_c1_1ef,
&_nrrdKernel_TMF_d0_c1_2ef,
&_nrrdKernel_TMF_d0_c1_3ef,
&_nrrdKernel_TMF_d0_c1_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d0_c2_1ef,
&_nrrdKernel_TMF_d0_c2_2ef,
&_nrrdKernel_TMF_d0_c2_3ef,
&_nrrdKernel_TMF_d0_c2_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d0_c3_1ef,
&_nrrdKernel_TMF_d0_c3_2ef,
&_nrrdKernel_TMF_d0_c3_3ef,
&_nrrdKernel_TMF_d0_c3_4ef,
},
},
{ /* d = 1 */
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d1_cn_1ef,
&_nrrdKernel_TMF_d1_cn_2ef,
&_nrrdKernel_TMF_d1_cn_3ef,
&_nrrdKernel_TMF_d1_cn_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d1_c0_1ef,
&_nrrdKernel_TMF_d1_c0_2ef,
&_nrrdKernel_TMF_d1_c0_3ef,
&_nrrdKernel_TMF_d1_c0_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d1_c1_1ef,
&_nrrdKernel_TMF_d1_c1_2ef,
&_nrrdKernel_TMF_d1_c1_3ef,
&_nrrdKernel_TMF_d1_c1_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d1_c2_1ef,
&_nrrdKernel_TMF_d1_c2_2ef,
&_nrrdKernel_TMF_d1_c2_3ef,
&_nrrdKernel_TMF_d1_c2_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d1_c3_1ef,
&_nrrdKernel_TMF_d1_c3_2ef,
&_nrrdKernel_TMF_d1_c3_3ef,
&_nrrdKernel_TMF_d1_c3_4ef,
},
},
{ /* d = 2 */
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d2_cn_1ef,
&_nrrdKernel_TMF_d2_cn_2ef,
&_nrrdKernel_TMF_d2_cn_3ef,
&_nrrdKernel_TMF_d2_cn_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d2_c0_1ef,
&_nrrdKernel_TMF_d2_c0_2ef,
&_nrrdKernel_TMF_d2_c0_3ef,
&_nrrdKernel_TMF_d2_c0_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d2_c1_1ef,
&_nrrdKernel_TMF_d2_c1_2ef,
&_nrrdKernel_TMF_d2_c1_3ef,
&_nrrdKernel_TMF_d2_c1_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d2_c2_1ef,
&_nrrdKernel_TMF_d2_c2_2ef,
&_nrrdKernel_TMF_d2_c2_3ef,
&_nrrdKernel_TMF_d2_c2_4ef,
},
{
&_nrrdKernel_TMFBAD,
&_nrrdKernel_TMF_d2_c3_1ef,
&_nrrdKernel_TMF_d2_c3_2ef,
&_nrrdKernel_TMF_d2_c3_3ef,
&_nrrdKernel_TMF_d2_c3_4ef,
},
},
};
const unsigned int nrrdKernelTMF_maxD = 2;
const unsigned int nrrdKernelTMF_maxC = 3;
const unsigned int nrrdKernelTMF_maxA = 4;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#define _SINC( x ) ( sin( AIR_PI*x )/( AIR_PI*x ) )
static double
29 _nrrdWindSincInt( const double *parm ) {
AIR_UNUSED( parm );
/* This isn't true, but there aren't good accurate, closed-form
approximations for these integrals ... */
return 1.0;
}
static double
38 _nrrdDWindSincInt( const double *parm ) {
AIR_UNUSED( parm );
/* ... or their derivatives */
return 0.0;
}
static double
46 _nrrdWindSincSup( const double *parm ) {
double S;
S = parm[0];
return parm[1]*S;
}
#define POW1( S ) ( S )
#define POW2( S ) ( ( S )*( S ) )
#define POW3( S ) ( ( S )*( S )*( S ) )
#define WS_1_F( name, mac, spow ) \
static float \
_nrrd##name##_1_f( float x, const double *parm ) { \
float R, S; \
\
S = AIR_CAST( float, parm[0] ); R = AIR_CAST( float, parm[1] ); \
x /= S; \
return AIR_CAST( float, mac( x, R )/spow( S ) ); \
}
#define WS_N_F( name, mac, spow ) \
static void \
69 _nrrd##name##_N_f( float *f, const float *x, size_t len, \
const double *parm ) { \
float S, R, t; \
size_t i; \
\
S = AIR_CAST( float, parm[0] ); R = AIR_CAST( float, parm[1] ); \
for ( i=0; i<len; i++ ) { \
t = x[i]/S; \
77 f[i] = AIR_CAST( float, mac( t, R )/spow( S ) ); \
} \
}
#define WS_1_D( name, mac, spow ) \
static double \
_nrrd##name##_1_d( double x, const double *parm ) { \
double R, S; \
\
S = parm[0]; R = parm[1]; \
x /= S; \
return mac( x, R )/spow( S ); \
}
#define WS_N_D( name, mac, spow ) \
static void \
_nrrd##name##_N_d( double *f, const double *x, size_t len, \
const double *parm ) { \
double S, R, t; \
size_t i; \
\
S = parm[0]; R = parm[1]; \
for ( i=0; i<len; i++ ) { \
t = x[i]/S; \
f[i] = mac( t, R )/spow( S ); \
} \
}
/* ------------------------------------------------------------ */
#define _HANN( x, R ) \
( x > R ? 0 : ( x < -R ? 0 : ( \
( x < R/50000 && x > -R/50000 ) \
? 1.1 - x*x*( AIR_PI*AIR_PI*( 3 + 2*R*R )/( 12*R*R ) \
+ AIR_PI*AIR_PI*AIR_PI*AIR_PI*( 5 + 2*R*R*( 5 + 2*R*R ) )*x*x/( 240*R*R*R*R ) ) \
: ( 1 + cos( AIR_PI*x/R ) )*_SINC( x )/2 ) \
) )
WS_1_D( Hann, _HANN, POW1 )
WS_1_F( Hann, _HANN, POW1 )
WS_N_F( Hann, _HANN, POW1 )
WS_N_D( Hann, _HANN, POW1 )
static NrrdKernel
_nrrdKernelHann = {
"hann",
2, _nrrdWindSincSup, _nrrdWindSincInt,
_nrrdHann_1_f, _nrrdHann_N_f, _nrrdHann_1_d, _nrrdHann_N_d
};
NrrdKernel *const
nrrdKernelHann = &_nrrdKernelHann;
/* ------------------------------------------------------------ */
#define _DHANN( x, R ) \
( x > R ? 0.0 : ( x < -R ? 0.0 : ( \
( x < R/50000 && x > -R/50000 ) \
? -x*AIR_PI*AIR_PI*( 3 + 2*R*R )/( 6*R*R ) \
: ( ( R*( 1 + cos( AIR_PI*x/R ) )*( AIR_PI*x*cos( AIR_PI*x ) - sin( AIR_PI*x ) ) \
- AIR_PI*x*sin( AIR_PI*x )*sin( AIR_PI*x/R ) )/( 2*R*AIR_PI*x*x ) ) \
) ) )
WS_1_D( DHann, _DHANN, POW2 )
WS_1_F( DHann, _DHANN, POW2 )
WS_N_F( DHann, _DHANN, POW2 )
WS_N_D( DHann, _DHANN, POW2 )
static NrrdKernel
_nrrdKernelDHann = {
"hannD",
2, _nrrdWindSincSup, _nrrdDWindSincInt,
_nrrdDHann_1_f, _nrrdDHann_N_f, _nrrdDHann_1_d, _nrrdDHann_N_d
};
NrrdKernel *const
nrrdKernelHannD = &_nrrdKernelDHann;
/* ------------------------------------------------------------ */
#define _DDHANN_A( x, R ) \
( 2*AIR_PI*R*cos( AIR_PI*x )*( R + R*cos( AIR_PI*x/R ) + AIR_PI*x*sin( AIR_PI*x/R ) ) )
#define _DDHANN_B( x, R ) \
( cos( AIR_PI*x/R )*( AIR_PI*AIR_PI*x*x + R*R*( AIR_PI*AIR_PI*x*x - 2 ) ) + \
R*( R*( AIR_PI*AIR_PI*x*x - 2 ) - 2*AIR_PI*x*sin( AIR_PI*x/R ) ) )
#define _DDHANN( x, R ) \
( x > R ? 0 : ( x < -R ? 0 : ( \
( x < R/50000 && x > -R/50000 ) \
? ( AIR_PI*AIR_PI/( 2*R*R ) )*( -( 3 + 2*R*R )/3 \
+ AIR_PI*AIR_PI*( 5 + 2*R*R*( 5 + R*R ) )*x*x/( 10*R*R ) ) \
: -( _DDHANN_A( x, R ) + sin( AIR_PI*x )*_DDHANN_B( x, R )/x )/( 2*AIR_PI*R*R*x*x ) \
) ) )
WS_1_D( DDHann, _DDHANN, POW3 )
WS_1_F( DDHann, _DDHANN, POW3 )
WS_N_F( DDHann, _DDHANN, POW3 )
WS_N_D( DDHann, _DDHANN, POW3 )
static NrrdKernel
_nrrdKernelDDHann = {
"hannDD",
2, _nrrdWindSincSup, _nrrdDWindSincInt,
_nrrdDDHann_1_f, _nrrdDDHann_N_f, _nrrdDDHann_1_d, _nrrdDDHann_N_d
};
NrrdKernel *const
nrrdKernelHannDD = &_nrrdKernelDDHann;
/* ------------------------------------------------------------ */
#define _BLACK( x, R ) \
( x > R ? 0 : ( x < -R ? 0 : ( \
( x < R/50000 && x > -R/50000 ) \
? 1.0 - x*x*( 1.6449340668482264 + 4.046537804446637/( R*R ) ) \
: ( 0.42 + cos( AIR_PI*x/R )/2 + 0.08*cos( 2*AIR_PI*x/R ) )*_SINC( x ) \
) ) )
WS_1_D( Black, _BLACK, POW1 )
WS_1_F( Black, _BLACK, POW1 )
WS_N_F( Black, _BLACK, POW1 )
WS_N_D( Black, _BLACK, POW1 )
static NrrdKernel
_nrrdKernelBlackman = {
"blackman",
2, _nrrdWindSincSup, _nrrdWindSincInt,
_nrrdBlack_1_f, _nrrdBlack_N_f, _nrrdBlack_1_d, _nrrdBlack_N_d
};
NrrdKernel *const
nrrdKernelBlackman = &_nrrdKernelBlackman;
/* ------------------------------------------------------------ */
#define _DBLACK_A( x, R ) \
R*x*cos( AIR_PI*x )*( 2.638937829015426 + AIR_PI*cos( AIR_PI*x/R ) \
+ 0.5026548245743669*cos( 2*AIR_PI*x/R ) )
#define _DBLACK_B( x, R ) \
sin( AIR_PI*x )*( -0.84*R - R*cos( AIR_PI*x/R ) - 0.16*R*cos( 2*AIR_PI*x/R ) - \
AIR_PI*x*sin( AIR_PI*x/R ) - 1.0053096491487339*x*sin( 2*AIR_PI*x/R ) )
#define _DBLACK( x, R ) \
( x > R ? 0.0 : ( x < -R ? 0.0 : ( \
( x < R/50000 && x > -R/50000 ) \
? -x*( 3.289868133696453 + 8.093075608893272/( R*R ) ) \
: ( _DBLACK_A( x, R ) + _DBLACK_B( x, R ) )/( 2*AIR_PI*R*x*x ) \
) ) )
WS_1_D( DBlack, _DBLACK, POW2 )
WS_1_F( DBlack, _DBLACK, POW2 )
WS_N_F( DBlack, _DBLACK, POW2 )
WS_N_D( DBlack, _DBLACK, POW2 )
static NrrdKernel
_nrrdKernelDBlack = {
"blackmanD",
2, _nrrdWindSincSup, _nrrdDWindSincInt,
_nrrdDBlack_1_f, _nrrdDBlack_N_f, _nrrdDBlack_1_d, _nrrdDBlack_N_d
};
NrrdKernel *const
nrrdKernelBlackmanD = &_nrrdKernelDBlack;
/* ------------------------------------------------------------ */
#define _DDBLACK( x, R ) \
( x > R ? 0.0 : ( x < -R ? 0.0 : ( \
( x < R/30 && x > -R/30 ) \
? ( -( 3.289868133696453 + 8.093075608893272/( R*R ) ) \
+ x*x*( 9.7409091034 + 86.694091020262/( R*R*R*R ) + 79.8754546479/( R*R ) ) ) \
: ( ( R*x*cos( AIR_PI*x )*( -2.638937829015426*R - AIR_PI*R*cos( ( AIR_PI*x )/R ) \
- 0.5026548245743669*R*cos( ( 2*AIR_PI*x )/R ) \
- AIR_PI*AIR_PI*x*sin( ( AIR_PI*x )/R ) \
- 3.158273408348595*x*sin( ( 2*AIR_PI*x )/R ) ) \
+ sin( AIR_PI*x )*( ( -4.934802200544679*x*x \
+ R*R*( 1 - 4.934802200544679*x*x ) )*cos( ( AIR_PI*x )/R ) \
+ ( -3.158273408348595*x*x \
+ R*R*( 0.16 - 0.7895683520871487*x*x ) )*cos( ( 2*AIR_PI*x )/R ) \
+ R*( 0.84*R - 4.14523384845753*R*x*x \
+ AIR_PI*x*sin( ( AIR_PI*x )/R ) \
+ 1.0053096491487339*x*sin( ( 2*AIR_PI*x )/R ) ) ) )/( AIR_PI*R*R*x*x*x ) ) \
) ) )
WS_1_D( DDBlack, _DDBLACK, POW3 )
WS_1_F( DDBlack, _DDBLACK, POW3 )
WS_N_F( DDBlack, _DDBLACK, POW3 )
WS_N_D( DDBlack, _DDBLACK, POW3 )
static NrrdKernel
_nrrdKernelDDBlack = {
"blackmanDD",
2, _nrrdWindSincSup, _nrrdDWindSincInt,
_nrrdDDBlack_1_f, _nrrdDDBlack_N_f, _nrrdDDBlack_1_d, _nrrdDDBlack_N_d
};
NrrdKernel *const
nrrdKernelBlackmanDD = &_nrrdKernelDDBlack;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "nrrd.h"
#include "privateNrrd.h"
/*
#include <sys/types.h>
#include <unistd.h>
*/
int
33 nrrdIoStateSet( NrrdIoState *nio, int parm, int value ) {
static const char me[]="nrrdIoStateSet";
if ( !nio ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_OP( nrrdIoStateUnknown, parm, nrrdIoStateLast ) ) ) {
biffAddf( NRRD, "%s: identifier %d not in valid range [%d, %d]", me,
parm, nrrdIoStateUnknown+1, nrrdIoStateLast-1 );
return 1;
}
switch ( parm ) {
case nrrdIoStateDetachedHeader:
nio->detachedHeader = !!value;
break;
case nrrdIoStateBareText:
nio->bareText = !!value;
break;
case nrrdIoStateCharsPerLine:
if ( value < 40 ) {
biffAddf( NRRD, "%s: %d charsPerLine is awfully small", me, value );
return 1;
}
/* cast won't lose info because "value" must be positive */
nio->charsPerLine = AIR_CAST( unsigned int, value );
break;
case nrrdIoStateValsPerLine:
if ( value < 4 ) {
biffAddf( NRRD, "%s: %d valsPerLine is awfully small", me, value );
return 1;
}
/* cast won't lose info because "value" must be positive */
nio->valsPerLine = AIR_CAST( unsigned int, value );
break;
case nrrdIoStateSkipData:
nio->skipData = !!value;
break;
case nrrdIoStateKeepNrrdDataFileOpen:
nio->keepNrrdDataFileOpen = !!value;
break;
case nrrdIoStateZlibLevel:
if ( !( AIR_IN_CL( -1, value, 9 ) ) ) {
biffAddf( NRRD, "%s: zlibLevel %d invalid", me, value );
return 1;
}
nio->zlibLevel = value;
break;
case nrrdIoStateZlibStrategy:
if ( !( AIR_IN_OP( nrrdZlibStrategyUnknown, value, nrrdZlibStrategyLast ) ) ) {
biffAddf( NRRD, "%s: zlibStrategy %d invalid", me, value );
return 1;
}
nio->zlibStrategy = value;
break;
case nrrdIoStateBzip2BlockSize:
if ( !( AIR_IN_CL( -1, value, 9 ) ) ) {
biffAddf( NRRD, "%s: bzip2BlockSize %d invalid", me, value );
return 1;
}
nio->bzip2BlockSize = value;
break;
default:
fprintf( stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm );
return 1;
}
return 0;
}
int
103 nrrdIoStateEncodingSet( NrrdIoState *nio, const NrrdEncoding *encoding ) {
static const char me[]="nrrdIoStateEncodingSet";
if ( !( nio && encoding ) ) {
if ( nio ) {
nio->encoding = nrrdEncodingUnknown;
}
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !encoding->available( ) ) {
nio->encoding = nrrdEncodingUnknown;
biffAddf( NRRD, "%s: %s encoding isn't actually available", me,
encoding->name );
return 1;
}
nio->encoding = encoding;
return 0;
}
int
124 nrrdIoStateFormatSet( NrrdIoState *nio, const NrrdFormat *format ) {
static const char me[]="nrrdIoStateFormatSet";
if ( !( nio && format ) ) {
if ( nio ) {
nio->format = nrrdFormatUnknown;
}
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !format->available( ) ) {
nio->format = nrrdFormatUnknown;
biffAddf( NRRD, "%s: %s format isn't actually available", me,
format->name );
return 1;
}
nio->format = format;
return 0;
}
/*
** no biff
*/
int
148 nrrdIoStateGet( NrrdIoState *nio, int parm ) {
static const char me[]="nrrdIoStateGet";
int value;
if ( !nio ) {
/* got NULL pointer */
return -1;
}
if ( !( AIR_IN_OP( nrrdIoStateUnknown, parm, nrrdIoStateLast ) ) ) {
/* got bogus parameter identifier */
return -1;
}
switch ( parm ) {
case nrrdIoStateDetachedHeader:
value = !!nio->detachedHeader;
break;
case nrrdIoStateBareText:
value = !!nio->bareText;
break;
case nrrdIoStateCharsPerLine:
/* HEY: this cast is a bad because nio->charsPerLine is unsigned */
value = AIR_CAST( int, nio->charsPerLine );
break;
case nrrdIoStateValsPerLine:
/* HEY: this cast is a bad because nio->valsPerLine is unsigned */
value = AIR_CAST( int, nio->valsPerLine );
break;
case nrrdIoStateSkipData:
value = !!nio->skipData;
break;
case nrrdIoStateKeepNrrdDataFileOpen:
value = !!nio->keepNrrdDataFileOpen;
break;
case nrrdIoStateZlibLevel:
value = nio->zlibLevel;
break;
case nrrdIoStateZlibStrategy:
value = nio->zlibStrategy;
break;
case nrrdIoStateBzip2BlockSize:
value = nio->bzip2BlockSize;
break;
default:
fprintf( stderr, "!%s: PANIC: didn't recognize parm %d\n", me, parm );
return -1;
}
return value;
}
/*
** no biff
*/
const NrrdEncoding *
201 nrrdIoStateEncodingGet( NrrdIoState *nio ) {
return nio ? nio->encoding : nrrdEncodingUnknown;
}
/*
** no biff
*/
const NrrdFormat *
210 nrrdIoStateFormatGet( NrrdIoState *nio ) {
return nio ? nio->format : nrrdFormatUnknown;
}
void
216 _nrrdStrcatSpaceVector( char *str, unsigned int spaceDim,
const double val[NRRD_SPACE_DIM_MAX] ) {
char buff[AIR_STRLEN_MED]; /* bad Gordon */
unsigned int dd;
if ( AIR_EXISTS( val[0] ) ) {
strcat( str, "( " );
for ( dd=0; dd<spaceDim; dd++ ) {
strcpy( buff, "" );
airSinglePrintf( NULL, buff, "%.17g", val[dd] );
strcat( str, buff );
sprintf( buff, "%s", dd+1 < spaceDim ? ", " : " )" );
strcat( str, buff );
}
} else {
strcat( str, _nrrdNoSpaceVector );
}
return;
}
int
237 _nrrdFieldInteresting( const Nrrd *nrrd, NrrdIoState *nio, int field ) {
int ret;
unsigned int ai;
if ( !( nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& nio
&& nio->encoding
&& AIR_IN_OP( nrrdField_unknown, field, nrrdField_last ) ) ) {
return 0;
}
ret = 0;
switch ( field ) {
case nrrdField_comment:
/* comments and key/value pairs are always handled differently ( by
being printed explicity ), so they are never "interesting" */
break;
case nrrdField_content:
ret = !!( airStrlen( nrrd->content ) );
break;
case nrrdField_number:
/* "number" is entirely redundant with "sizes", which is a
required field. Absolutely nothing is lost in eliding "number"
from the header, so "number" is NEVER interesting. Should this
judgement later be found in error, this is the one place where
the policy change can be implemented */
break;
case nrrdField_type:
/* this is vital */
ret = 1;
break;
case nrrdField_block_size:
ret = ( nrrdTypeBlock == nrrd->type );
break;
case nrrdField_dimension:
/* this is vital */
ret = 1;
break;
case nrrdField_space:
/* its interesting if its known */
ret = ( nrrdSpaceUnknown != nrrd->space );
break;
case nrrdField_space_dimension:
/* its interesting if its non-zero and if space is not known */
ret = ( nrrd->spaceDim > 0 && nrrdSpaceUnknown == nrrd->space );
break;
case nrrdField_sizes:
/* this is vital */
ret = 1;
break;
case nrrdField_spacings:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= AIR_EXISTS( nrrd->axis[ai].spacing );
}
break;
case nrrdField_thicknesses:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= AIR_EXISTS( nrrd->axis[ai].thickness );
}
break;
case nrrdField_axis_mins:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= AIR_EXISTS( nrrd->axis[ai].min );
}
break;
case nrrdField_axis_maxs:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= AIR_EXISTS( nrrd->axis[ai].max );
}
break;
case nrrdField_space_directions:
ret = nrrd->spaceDim > 0;
break;
case nrrdField_centers:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= ( nrrdCenterUnknown != nrrd->axis[ai].center );
}
break;
case nrrdField_kinds:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= ( nrrdKindUnknown != nrrd->axis[ai].kind );
}
break;
case nrrdField_labels:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= !!( airStrlen( nrrd->axis[ai].label ) );
}
break;
case nrrdField_units:
for ( ai=0; ai<nrrd->dim; ai++ ) {
ret |= !!( airStrlen( nrrd->axis[ai].units ) );
}
break;
case nrrdField_min:
case nrrdField_max:
/* these no longer exist in the Nrrd struct; we never write them */
ret = AIR_FALSE;
break;
case nrrdField_old_min:
ret = AIR_EXISTS( nrrd->oldMin );
break;
case nrrdField_old_max:
ret = AIR_EXISTS( nrrd->oldMax );
break;
case nrrdField_endian:
ret = nio->encoding->endianMatters && 1 < nrrdElementSize( nrrd );
break;
case nrrdField_encoding:
/* this is vital */
ret = 1;
break;
case nrrdField_line_skip:
ret = nio->lineSkip > 0;
break;
case nrrdField_byte_skip:
ret = nio->byteSkip != 0;
break;
case nrrdField_keyvalue:
/* comments and key/value pairs are always handled differently ( by
being printed explicity ), so they are never "interesting" */
break;
case nrrdField_sample_units:
ret = !!airStrlen( nrrd->sampleUnits );
break;
case nrrdField_space_units:
for ( ai=0; ai<nrrd->spaceDim; ai++ ) {
ret |= !!( airStrlen( nrrd->spaceUnits[ai] ) );
}
break;
case nrrdField_space_origin:
/* we're trusting other validity checks to ensure that
all the coeffs exist or not, together */
ret = ( nrrd->spaceDim > 0
&& AIR_EXISTS( nrrd->spaceOrigin[0] ) );
break;
case nrrdField_measurement_frame:
/* we're trusting other validity checks to ensure that
all the coeffs exist or not, together */
ret = ( nrrd->spaceDim > 0
&& AIR_EXISTS( nrrd->measurementFrame[0][0] ) );
break;
case nrrdField_data_file:
/* detached header was either requested or is required */
ret = ( nio->detachedHeader
|| nio->dataFNFormat
|| nio->dataFNArr->len > 1 );
break;
}
return ret;
}
/*
** _nrrdSprintFieldInfo
**
** this prints "<prefix><field>: <info>" into *strP ( after allocating it for
** big enough, usually with a stupidly big margin of error ), in a form
** suitable to be written to NRRD or other image headers. This will always
** print something ( for valid inputs ), even stupid <info>s like
** "( unknown endian )". It is up to the caller to decide which fields
** are worth writing, via _nrrdFieldInteresting( ).
**
** NOTE: some of these fields make sense in non-NRRD files ( e.g. all
** the per-axis information ), but many only make sense in NRRD files.
** This is just one example of NRRD-format-specific stuff that is not
** in formatNRRD.c
*/
void
406 _nrrdSprintFieldInfo( char **strP, const char *prefix,
const Nrrd *nrrd, NrrdIoState *nio, int field,
int dropAxis0 ) {
static const char me[]="_nrrdSprintFieldInfo";
char buff[AIR_STRLEN_MED], *fnb, stmp[AIR_STRLEN_SMALL],
*strtmp=NULL;
double colvec[NRRD_SPACE_DIM_MAX];
const char *fs;
unsigned int ii, dd,
uintStrlen = 11,
size_tStrlen = 33,
doubleStrlen = 513;
size_t fslen, fdlen, maxl;
int endi;
if ( !( strP && prefix
&& nrrd
&& AIR_IN_CL( 1, nrrd->dim, NRRD_DIM_MAX )
&& AIR_IN_OP( nrrdField_unknown, field, nrrdField_last ) ) ) {
return;
}
/* As of Sun Dec 2 01:57:48 CST 2012 ( revision 5832 ) the only
places where this function is called is when it has been guarded
by "if ( _nrrdFieldInteresting( ) )" ( except for in formatText.c when
its called on the dimension field, which is always interesting ).
So, the following:
if ( !_nrrdFieldInteresting( nrrd, nio, field ) ) {
*strP = airStrdup( "" );
}
was redundant and confusingly created the appearance of a memory
leak waiting to happen. We now let the default switch statement
set *strP to NULL ( all the other cases set it ), to smoke out
errors in how this function is called */
fs = airEnumStr( nrrdField, field );
fslen = strlen( prefix ) + strlen( fs ) + strlen( ": " ) + 1;
switch ( field ) {
case nrrdField_comment:
case nrrdField_keyvalue:
fprintf( stderr, "%s: CONFUSION: why are you calling me on \"%s\"?\n", me,
airEnumStr( nrrdField, nrrdField_comment ) );
*strP = airStrdup( "" );
break;
case nrrdField_content:
strtmp = airOneLinify( airStrdup( nrrd->content ) );
*strP = AIR_CALLOC( fslen + strlen( strtmp ), char );
sprintf( *strP, "%s%s: %s", prefix, fs, strtmp );
airFree( strtmp ); strtmp = NULL;
break;
case nrrdField_number:
*strP = AIR_CALLOC( fslen + size_tStrlen, char );
sprintf( *strP, "%s%s: %s", prefix, fs,
airSprintSize_t( stmp, nrrdElementNumber( nrrd ) ) );
break;
case nrrdField_type:
*strP = AIR_CALLOC( fslen + strlen( airEnumStr( nrrdType, nrrd->type ) ), char );
sprintf( *strP, "%s%s: %s", prefix, fs, airEnumStr( nrrdType, nrrd->type ) );
break;
case nrrdField_block_size:
*strP = AIR_CALLOC( fslen + size_tStrlen, char );
sprintf( *strP, "%s%s: %s", prefix, fs,
airSprintSize_t( stmp, nrrd->blockSize ) );
break;
case nrrdField_dimension:
*strP = AIR_CALLOC( fslen + uintStrlen, char );
sprintf( *strP, "%s%s: %d", prefix, fs, nrrd->dim );
break;
case nrrdField_space:
*strP = AIR_CALLOC( fslen
+ strlen( airEnumStr( nrrdSpace, nrrd->space ) ), char );
sprintf( *strP, "%s%s: %s", prefix, fs, airEnumStr( nrrdSpace, nrrd->space ) );
break;
case nrrdField_space_dimension:
*strP = AIR_CALLOC( fslen + uintStrlen, char );
sprintf( *strP, "%s%s: %d", prefix, fs, nrrd->spaceDim );
break;
/* ---- begin per-axis fields ---- */
case nrrdField_sizes:
*strP = AIR_CALLOC( fslen + nrrd->dim*( size_tStrlen + 1 ), char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
sprintf( buff, " %s", airSprintSize_t( stmp, nrrd->axis[ii].size ) );
strcat( *strP, buff );
}
break;
case nrrdField_spacings:
*strP = AIR_CALLOC( fslen + nrrd->dim*( doubleStrlen + 1 ), char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
airSinglePrintf( NULL, buff, " %.17g", nrrd->axis[ii].spacing );
strcat( *strP, buff );
}
break;
case nrrdField_thicknesses:
*strP = AIR_CALLOC( fslen + nrrd->dim*( doubleStrlen + 1 ), char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
airSinglePrintf( NULL, buff, " %.17g", nrrd->axis[ii].thickness );
strcat( *strP, buff );
}
break;
case nrrdField_axis_mins:
*strP = AIR_CALLOC( fslen + nrrd->dim*( doubleStrlen + 1 ), char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
airSinglePrintf( NULL, buff, " %.17g", nrrd->axis[ii].min );
strcat( *strP, buff );
}
break;
case nrrdField_axis_maxs:
*strP = AIR_CALLOC( fslen + nrrd->dim*( doubleStrlen + 1 ), char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
airSinglePrintf( NULL, buff, " %.17g", nrrd->axis[ii].max );
strcat( *strP, buff );
}
break;
case nrrdField_space_directions:
*strP = AIR_CALLOC( fslen
+ nrrd->dim*nrrd->spaceDim*( doubleStrlen
+ strlen( "( , ) " ) ), char );
sprintf( *strP, "%s%s: ", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
_nrrdStrcatSpaceVector( *strP, nrrd->spaceDim,
nrrd->axis[ii].spaceDirection );
if ( ii < nrrd->dim-1 ) {
strcat( *strP, " " );
}
}
break;
case nrrdField_centers:
fdlen = 0;
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
fdlen += 1 + airStrlen( nrrd->axis[ii].center
? airEnumStr( nrrdCenter, nrrd->axis[ii].center )
: NRRD_UNKNOWN );
}
*strP = AIR_CALLOC( fslen + fdlen, char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
sprintf( buff, " %s",
( nrrd->axis[ii].center
? airEnumStr( nrrdCenter, nrrd->axis[ii].center )
: NRRD_UNKNOWN ) );
strcat( *strP, buff );
}
break;
case nrrdField_kinds:
fdlen = 0;
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
fdlen += 1 + airStrlen( nrrd->axis[ii].kind
? airEnumStr( nrrdKind, nrrd->axis[ii].kind )
: NRRD_UNKNOWN );
}
*strP = AIR_CALLOC( fslen + fdlen, char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
sprintf( buff, " %s",
( nrrd->axis[ii].kind
? airEnumStr( nrrdKind, nrrd->axis[ii].kind )
: NRRD_UNKNOWN ) );
strcat( *strP, buff );
}
break;
case nrrdField_labels:
case nrrdField_units:
#define LABEL_OR_UNITS ( nrrdField_labels == field \
? nrrd->axis[ii].label \
: nrrd->axis[ii].units )
fdlen = 0;
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
/* The "2*" is because at worst every character needs escaping.
The "+ 3" for the |" "| between each part */
fdlen += 2*airStrlen( LABEL_OR_UNITS ) + 3;
}
fdlen += 1; /* for '\0' */
*strP = AIR_CALLOC( fslen + fdlen, char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=!!dropAxis0; ii<nrrd->dim; ii++ ) {
strcat( *strP, " \"" );
if ( nrrdField_labels == field
? airStrlen( nrrd->axis[ii].label )
: airStrlen( nrrd->axis[ii].units ) ) {
_nrrdWriteEscaped( NULL, *strP, LABEL_OR_UNITS,
"\"", _NRRD_WHITESPACE_NOTAB );
}
strcat( *strP, "\"" );
}
#undef LABEL_OR_UNITS
break;
/* ---- end per-axis fields ---- */
case nrrdField_min:
case nrrdField_max:
/* we're basically a no-op, now that these fields became meaningless */
*strP = AIR_CALLOC( fslen + doubleStrlen, char );
sprintf( *strP, "%s%s: 0.0", prefix, fs );
strcat( *strP, buff );
break;
case nrrdField_old_min:
*strP = AIR_CALLOC( fslen + doubleStrlen, char );
sprintf( *strP, "%s%s: ", prefix, fs );
airSinglePrintf( NULL, buff, "%.17g", nrrd->oldMin );
strcat( *strP, buff );
break;
case nrrdField_old_max:
*strP = AIR_CALLOC( fslen + doubleStrlen, char );
sprintf( *strP, "%s%s: ", prefix, fs );
airSinglePrintf( NULL, buff, "%.17g", nrrd->oldMax );
strcat( *strP, buff );
break;
case nrrdField_endian:
if ( airEndianUnknown != nio->endian ) {
/* we know a specific endianness because either it was recorded as
part of "unu make -h", or it was set ( and data was possibly
altered ) as part of "unu save" */
endi = nio->endian;
} else {
/* we record our current architecture's endian because we're
going to writing out data */
endi = airMyEndian( );
}
*strP = AIR_CALLOC( fslen + strlen( airEnumStr( airEndian, endi ) ), char );
sprintf( *strP, "%s%s: %s", prefix, fs, airEnumStr( airEndian, endi ) );
break;
case nrrdField_encoding:
*strP = AIR_CALLOC( fslen + strlen( nio->encoding->name ), char );
sprintf( *strP, "%s%s: %s", prefix, fs, nio->encoding->name );
break;
case nrrdField_line_skip:
*strP = AIR_CALLOC( fslen + uintStrlen, char );
sprintf( *strP, "%s%s: %d", prefix, fs, nio->lineSkip );
break;
case nrrdField_byte_skip:
*strP = AIR_CALLOC( fslen + uintStrlen, char );
sprintf( *strP, "%s%s: %ld", prefix, fs, nio->byteSkip );
break;
case nrrdField_sample_units:
strtmp = airOneLinify( airStrdup( nrrd->sampleUnits ) );
*strP = AIR_CALLOC( fslen + strlen( strtmp ), char );
sprintf( *strP, "%s%s: \"%s\"", prefix, fs, strtmp );
airFree( strtmp ); strtmp = NULL;
break;
case nrrdField_space_units:
fdlen = 0;
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
/* The "2*" is because at worst every character needs escaping.
See note in formatNRRD.c about how even though its not part
of the format, we have worst-case scenario of having to
escape a space units which is nothing but ". The "+ 3" for
the |" "| between each part */
fdlen += 2*airStrlen( nrrd->spaceUnits[ii] ) + 3;
}
fdlen += 1; /* for '\0' */
*strP = AIR_CALLOC( fslen + fdlen, char );
sprintf( *strP, "%s%s:", prefix, fs );
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
strcat( *strP, " \"" );
if ( airStrlen( nrrd->spaceUnits[ii] ) ) {
_nrrdWriteEscaped( NULL, *strP, nrrd->spaceUnits[ii],
"\"", _NRRD_WHITESPACE_NOTAB );
}
strcat( *strP, "\"" );
}
break;
case nrrdField_space_origin:
*strP = AIR_CALLOC( fslen + nrrd->spaceDim*( doubleStrlen
+ strlen( "( , ) " ) ), char );
sprintf( *strP, "%s%s: ", prefix, fs );
_nrrdStrcatSpaceVector( *strP, nrrd->spaceDim, nrrd->spaceOrigin );
break;
case nrrdField_measurement_frame:
*strP = AIR_CALLOC( fslen + ( nrrd->spaceDim*
nrrd->spaceDim*( doubleStrlen
+ strlen( "( , ) " ) ) ), char );
sprintf( *strP, "%s%s: ", prefix, fs );
for ( dd=0; dd<nrrd->spaceDim; dd++ ) {
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
colvec[ii] = nrrd->measurementFrame[dd][ii];
}
_nrrdStrcatSpaceVector( *strP, nrrd->spaceDim, colvec );
if ( dd < nrrd->spaceDim-1 ) {
strcat( *strP, " " );
}
}
break;
case nrrdField_data_file:
/* NOTE: this comes last ( nrrdField_data_file is the highest-valued
member of the nrrdField* enum ) because the "LIST" form of the
data file specification requires that the following lines be
the filenames */
/* error checking elsewhere: assumes there is data file info */
if ( nio->dataFNFormat ) {
*strP = AIR_CALLOC( fslen + strlen( nio->dataFNFormat ) + 4*uintStrlen,
char );
if ( nio->dataFileDim == nrrd->dim-1 ) {
sprintf( *strP, "%s%s: %s %d %d %d", prefix, fs, nio->dataFNFormat,
nio->dataFNMin, nio->dataFNMax, nio->dataFNStep );
} else {
sprintf( *strP, "%s%s: %s %d %d %d %u", prefix, fs, nio->dataFNFormat,
nio->dataFNMin, nio->dataFNMax, nio->dataFNStep,
nio->dataFileDim );
}
} else if ( nio->dataFNArr->len > 1 ) {
maxl = 0;
for ( ii=0; ii<nio->dataFNArr->len; ii++ ) {
maxl = AIR_MAX( maxl, strlen( nio->dataFN[ii] ) );
}
*strP = AIR_CALLOC( fslen + strlen( NRRD_LIST_FLAG )
+ uintStrlen + nio->dataFNArr->len * ( maxl + 1 ),
char );
fnb = AIR_CALLOC( fslen + strlen( NRRD_LIST_FLAG )
+ uintStrlen + maxl + 1, char );
if ( nio->dataFileDim == nrrd->dim-1 ) {
sprintf( *strP, "%s%s: LIST\n", prefix, fs );
} else {
sprintf( *strP, "%s%s: LIST %u\n", prefix, fs, nio->dataFileDim );
}
for ( ii=0; ii<nio->dataFNArr->len; ii++ ) {
sprintf( fnb, "%s%s", nio->dataFN[ii],
ii<nio->dataFNArr->len-1 ? "\n" : "" );
strcat( *strP, fnb );
}
free( fnb );
} else {
/* there is some ambiguity between a "LIST" of length one,
and a single explicit data filename, but that's harmless */
*strP = AIR_CALLOC( fslen + strlen( "./" )
+ strlen( nio->dataFN[0] ) + 1, char );
sprintf( *strP, "%s%s: %s%s", prefix, fs,
/* this is a favor to older readers that can deal with
this NRRD file because its being saved in a NRRD0003
( or below ) version, so we don't want to confuse them
by not having the old explicit header-relative flag */
( _nrrdFormatNRRD_whichVersion( nrrd, nio ) < 4 ? "./" : "" ),
nio->dataFN[0] );
}
break;
default:
fprintf( stderr, "%s: CONFUSION: field %d unrecognized\n", me, field );
*strP = NULL;
break;
}
return;
}
/*
** _nrrdFprintFieldInfo
**
** convenience wrapper around _nrrdSprintFieldInfo, for writing into
** a file. Same caveats here: use _nrrdFieldInteresting
759 */
void
_nrrdFprintFieldInfo( FILE *file, const char *prefix,
const Nrrd *nrrd, NrrdIoState *nio, int field,
int dropAxis0 ) {
char *line=NULL;
_nrrdSprintFieldInfo( &line, prefix, nrrd, nio, field, dropAxis0 );
if ( line ) {
fprintf( file, "%s\n", line );
free( line );
}
return;
}
773
int
_nrrdEncodingMaybeSet( NrrdIoState *nio ) {
static const char me[]="_nrrdEncodingMaybeSet";
if ( !nio ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !nio->encoding ) {
biffAddf( NRRD, "%s: invalid ( NULL ) encoding", me );
return 1;
}
if ( nrrdEncodingUnknown == nio->encoding ) {
nio->encoding = nrrdEncodingArray[nrrdDefaultWriteEncodingType];
}
if ( !nio->encoding->available( ) ) {
biffAddf( NRRD, "%s: %s encoding not available in this Teem build",
me, nio->encoding->name );
return 1;
}
return 0;
}
/*
** we can assume ( via action of caller nrrdSave ) that nio->encoding
** has been set
**
** we must set nio->format to something useful/non-trivial
802 */
int
_nrrdFormatMaybeGuess( const Nrrd *nrrd, NrrdIoState *nio,
const char *filename ) {
static const char me[]="_nrrdFormatMaybeGuess";
char mesg[AIR_STRLEN_MED];
int fi, guessed, available, fits;
if ( !nio->format ) {
biffAddf( NRRD, "%s: got invalid ( NULL ) format", me );
return 1;
}
if ( nrrdFormatUnknown == nio->format ) {
for ( fi = nrrdFormatTypeUnknown+1;
fi < nrrdFormatTypeLast;
fi++ ) {
if ( nrrdFormatArray[fi]->nameLooksLike( filename ) ) {
nio->format = nrrdFormatArray[fi];
break;
}
}
if ( nrrdFormatUnknown == nio->format ) {
/* no nameLooksLike( ) returned non-zero, punt */
nio->format = nrrdFormatNRRD;
}
guessed = AIR_TRUE;
} else {
guessed = AIR_FALSE;
}
available = nio->format->available( );
fits = nio->format->fitsInto( nrrd, nio->encoding, AIR_FALSE );
/* !available ==> !fits, by the nature of fitsInto( ) */
if ( !( available && fits ) ) {
sprintf( mesg, "can not use %s format: %s", nio->format->name,
( !available
? "not available in this Teem build"
: "array doesn\'t fit" ) );
if ( guessed ) {
if ( 1 <= nrrdStateVerboseIO ) {
fprintf( stderr, "( %s: %s --> saving to NRRD format )\n", me, mesg );
}
nio->format = nrrdFormatNRRD;
} else {
/* problem: this was the format someone explicitly requested */
biffAddf( NRRD, "%s: %s", me, mesg );
return 1;
}
}
return 0;
}
853
int
_nrrdFormatMaybeSet( NrrdIoState *nio ) {
static const char me[]="_nrrdFormatMaybeSet";
if ( !nio->format ) {
biffAddf( NRRD, "%s: invalid ( NULL ) format", me );
return 1;
}
if ( nrrdFormatUnknown == nio->format ) {
nio->format = nrrdFormatNRRD;
}
if ( !nio->format->available( ) ) {
biffAddf( NRRD, "%s: %s format not available in this Teem build",
me, nio->format->name );
return 1;
}
return 0;
}
/*
** _nrrdWrite
**
** Write a nrrd to given file or string ( allocated by nrrd ), using the
** format and and encoding indicated in nio. Cleverness should be
** isolated and collected here: by the time nio->format->write( ) is
** called, all writing parameters must be given explicitly, and their
** appropriateness is explicitly tested
881 */
int
_nrrdWrite( FILE *file, char **stringP, const Nrrd *nrrd, NrrdIoState *_nio ) {
static const char me[]="_nrrdWrite";
NrrdIoState *nio;
airArray *mop;
if ( !( ( file || stringP ) && nrrd ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( file && stringP ) {
biffAddf( NRRD, "%s: can't write to both file and string", me );
return 1;
}
if ( nrrdCheck( nrrd ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
mop = airMopNew( );
if ( _nio ) {
nio = _nio;
} else {
nio = nrrdIoStateNew( );
if ( !nio ) {
biffAddf( NRRD, "%s: couldn't alloc local NrrdIoState", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
}
if ( _nrrdEncodingMaybeSet( nio )
|| _nrrdFormatMaybeSet( nio ) ) {
biffAddf( NRRD, "%s: ", me );
airMopError( mop ); return 1;
}
if ( nio->byteSkip || nio->lineSkip ) {
/* NOTE: unu make bypasses this by calling nrrdFormatNRRD->write( )
directly */
biffAddf( NRRD, "%s: can't generate line or byte skips on data write", me );
airMopError( mop ); return 1;
}
if ( stringP ) {
if ( nrrdFormatNRRD != nio->format ) {
biffAddf( NRRD, "%s: sorry, can only write %s files to strings ( not %s )",
me, nrrdFormatNRRD->name, nio->format->name );
airMopError( mop ); return 1;
}
/* we do this in two passes; first see how much room is needed
for the header, then allocate, then write the header */
nio->learningHeaderStrlen = AIR_TRUE;
if ( nio->format->write( NULL, nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
*stringP = AIR_MALLOC( nio->headerStrlen + 1, char );
if ( !*stringP ) {
biffAddf( NRRD, "%s: couldn't allocate header string ( %u len )",
me, nio->headerStrlen );
airMopError( mop ); return 1;
}
nio->learningHeaderStrlen = AIR_FALSE;
nio->headerStringWrite = *stringP;
if ( nio->format->write( NULL, nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
} else {
/* call the writer appropriate for the format */
if ( nio->format->write( file, nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
/*
******** nrrdWrite
**
** wrapper around _nrrdWrite; writes to a FILE*
964 */
int
nrrdWrite( FILE *file, const Nrrd *nrrd, NrrdIoState *_nio ) {
static const char me[]="nrrdWrite";
if ( _nrrdWrite( file, NULL, nrrd, _nio ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdStringWrite
**
** wrapper around _nrrdWrite; *allocates* and writes to a string
980 */
int
nrrdStringWrite( char **stringP, const Nrrd *nrrd, NrrdIoState *_nio ) {
static const char me[]="nrrdStringWrite";
if ( _nrrdWrite( NULL, stringP, nrrd, _nio ) ) {
biffAddf( NRRD, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** nrrdSave
**
** save a given nrrd to a given filename, with cleverness to guess
** format if not specified by the caller
**
** currently, for NRRD format files, we play the detached header game
** whenever the filename ends in NRRD_EXT_NHDR, and when we play this
** game, the data file is ALWAYS header relative.
1001 */
int
nrrdSave( const char *filename, const Nrrd *nrrd, NrrdIoState *nio ) {
static const char me[]="nrrdSave";
FILE *file;
airArray *mop;
if ( !( nrrd && filename ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
if ( !nio ) {
nio = nrrdIoStateNew( );
if ( !nio ) {
biffAddf( NRRD, "%s: couldn't alloc local NrrdIoState", me );
return 1;
}
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
}
if ( _nrrdEncodingMaybeSet( nio )
|| _nrrdFormatMaybeGuess( nrrd, nio, filename ) ) {
biffAddf( NRRD, "%s: ", me );
airMopError( mop ); return 1;
}
if ( nrrdFormatNRRD == nio->format
&& airEndsWith( filename, NRRD_EXT_NHDR ) ) {
nio->detachedHeader = AIR_TRUE;
_nrrdSplitName( &( nio->path ), &( nio->base ), filename );
/* nix the ".nhdr" suffix */
nio->base[strlen( nio->base ) - strlen( NRRD_EXT_NHDR )] = 0;
/* nrrdFormatNRRD->write will do the rest */
} else {
nio->detachedHeader = AIR_FALSE;
}
if ( !( file = airFopen( filename, stdout, "wb" ) ) ) {
biffAddf( NRRD, "%s: couldn't fopen( \"%s\", \"wb\" ): %s",
me, filename, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( nrrdWrite( file, nrrd, nio ) ) {
biffAddf( NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1053
int
nrrdSaveMulti( const char *fnameFormat, const Nrrd *const *nin,
unsigned int ninLen, unsigned int numStart, NrrdIoState *nio ) {
static const char me[]="nrrdSaveMulti";
char *fname;
airArray *mop;
unsigned int nii;
if ( !( fnameFormat && nin ) ) {
biffAddf( NRRD, "%s: got NULL pointer", me );
return 1;
}
if ( !( _nrrdContainsPercentThisAndMore( fnameFormat, 'u' ) ) ) {
biffAddf( NRRD, "%s: given format \"%s\" doesn't seem to "
"have the \"%%u\" conversion specification to sprintf "
"an unsigned int\n", me, fnameFormat );
return 1;
}
mop = airMopNew( );
/* should be big enough for the number replacing the format sequence */
fname = AIR_CALLOC( strlen( fnameFormat ) + 128, char );
if ( !( fname ) ) {
biffAddf( NRRD, "%s: couldn't allocate local fname buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, fname, airFree, airMopAlways );
for ( nii=0; nii<ninLen; nii++ ) {
unsigned int num;
num = numStart + nii;
sprintf( fname, fnameFormat, num );
if ( nrrdSave( fname, nin[nii], nio ) ) {
biffAddf( NRRD, "%s: trouble saving nin[%u] to %s", me, nii, fname );
airMopError( mop ); return 1;
}
/* HEY: GLK hopes that the nio doesn't have any state that needs
resetting, but we can't call nrrdIoStateInit( ) because that
would negate the purpose of sending in the nio for all but the
first saved nrrd */
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/*
** issues:
** does everything work on the first iteration
** how to handle the needed extra probe for d strength / d scale
** how are force/energy along scale handled differently than in space?
*/
#define __IF_DEBUG if ( 0 )
static double
38 _pointDistSqrd( pullContext *pctx, pullPoint *AA, pullPoint *BB ) {
double diff[4];
ELL_4V_SUB( diff, AA->pos, BB->pos );
ELL_3V_SCALE( diff, 1/pctx->sysParm.radiusSpace, diff );
diff[3] /= pctx->sysParm.radiusScale;
return ELL_4V_DOT( diff, diff );
}
/*
** this sets, in task->neighPoint ( *NOT* point->neighPoint ), all the
** points in neighboring bins with which we might possibly interact,
** and returns the number of such points.
*/
static unsigned int
52 _neighBinPoints( pullTask *task, pullBin *bin, pullPoint *point,
double distTest ) {
static const char me[]="_neighBinPoints";
unsigned int nn, herPointIdx, herBinIdx;
pullBin *herBin;
pullPoint *herPoint;
nn = 0;
herBinIdx = 0;
while ( ( herBin = bin->neighBin[herBinIdx] ) ) {
for ( herPointIdx=0; herPointIdx<herBin->pointNum; herPointIdx++ ) {
herPoint = herBin->point[herPointIdx];
/*
printf( "!%s( %u ): neighbin %u has point %u\n", me,
point->idtag, herBinIdx, herPoint->idtag );
*/
/* can't interact with myself, or anything nixed */
if ( point != herPoint
&& !( herPoint->status & PULL_STATUS_NIXME_BIT ) ) {
if ( distTest
&& _pointDistSqrd( task->pctx, point, herPoint ) > distTest ) {
continue;
}
if ( nn+1 < _PULL_NEIGH_MAXNUM ) {
task->neighPoint[nn++] = herPoint;
/*
printf( "%s( %u ): neighPoint[%u] = %u\n",
me, point->idtag, nn-1, herPoint->idtag );
*/
} else {
fprintf( stderr, "%s: hit max# ( %u ) poss. neighbors ( from bins )\n",
me, _PULL_NEIGH_MAXNUM );
}
}
}
herBinIdx++;
}
/* also have to consider things in the add queue */
for ( herPointIdx=0; herPointIdx<task->addPointNum; herPointIdx++ ) {
herPoint = task->addPoint[herPointIdx];
if ( point != herPoint ) {
if ( distTest
&& _pointDistSqrd( task->pctx, point, herPoint ) > distTest ) {
continue;
}
if ( nn+1 < _PULL_NEIGH_MAXNUM ) {
task->neighPoint[nn++] = herPoint;
} else {
fprintf( stderr, "%s: hit max# ( %u ) poss neighs ( add queue len %u )\n",
me, _PULL_NEIGH_MAXNUM, task->addPointNum );
}
}
}
return nn;
}
/*
** compute the energy at "me" due to "she", and
** the gradient vector of her energy ( probably pointing towards her )
**
** we're passed spaceDist to save us work of recomputing sqrt( )
**
** egrad will be NULL if this is being called only to assess
** the energy at this point, rather than for learning how to move it
*/
double
118 _pullEnergyInterParticle( pullContext *pctx, pullPoint *me,
const pullPoint *she,
double spaceDist, double scaleDist,
/* output */
double egrad[4] ) {
char meme[]="pullEnergyInterParticle";
double diff[4], spaceRad, scaleRad, rr, ss, uu, beta,
en, den, enR, denR, enS, denS, enWR, enWS, denWR, denWS,
*parmR, *parmS, *parmW,
( *evalR )( double *, double, const double parm[PULL_ENERGY_PARM_NUM] ),
( *evalS )( double *, double, const double parm[PULL_ENERGY_PARM_NUM] ),
( *evalW )( double *, double, const double parm[PULL_ENERGY_PARM_NUM] );
int scaleSgn;
/* the vector "diff" goes from her, to me, in both space and scale */
ELL_4V_SUB( diff, me->pos, she->pos );
/* computed by caller: spaceDist = ELL_3V_LEN( diff ); */
/* computed by caller: scaleDist = AIR_ABS( diff[3] ); */
spaceRad = pctx->sysParm.radiusSpace;
scaleRad = pctx->sysParm.radiusScale;
rr = spaceDist/spaceRad;
if ( pctx->haveScale ) {
ss = scaleDist/scaleRad;
scaleSgn = airSgn( diff[3] );
} else {
ss = 0;
scaleSgn = 1;
}
if ( rr > 1 || ss > 1 ) {
if ( egrad ) {
ELL_4V_SET( egrad, 0, 0, 0, 0 );
}
return 0;
}
if ( rr == 0 && ss == 0 ) {
fprintf( stderr, "%s: pos( %u ) == pos( %u ) !! ( %g, %g, %g, %g )\n",
meme, me->idtag, she->idtag,
me->pos[0], me->pos[1], me->pos[2], me->pos[3] );
if ( egrad ) {
ELL_4V_SET( egrad, 0, 0, 0, 0 );
}
return 0;
}
#if PULL_HINTER
if ( pullProcessModeDescent == pctx->task[0]->processMode
&& pctx->nhinter && pctx->nhinter->data ) {
unsigned int ri, si, sz;
float *hint;
hint = AIR_CAST( float *, pctx->nhinter->data );
sz = pctx->nhinter->axis[0].size;
ri = airIndex( -1.0, rr, 1.0, sz );
si = airIndex( -1.0, ss*scaleSgn, 1.0, sz );
hint[ri + sz*si] += 1;
}
#endif
parmR = pctx->energySpecR->parm;
evalR = pctx->energySpecR->energy->eval;
parmS = pctx->energySpecS->parm;
evalS = pctx->energySpecS->energy->eval;
switch ( pctx->interType ) {
case pullInterTypeJustR:
/* _pullVolumeSetup makes sure that
!pctx->haveScale iff pullInterTypeJustR == pctx->interType */
en = evalR( &denR, rr, parmR );
if ( egrad ) {
denR *= 1.0/( spaceRad*spaceDist );
ELL_3V_SCALE( egrad, denR, diff );
egrad[3] = 0;
}
break;
case pullInterTypeUnivariate:
uu = sqrt( rr*rr + ss*ss );
en = evalR( &den, uu, parmR );
if ( egrad ) {
ELL_3V_SCALE( egrad, den/( uu*spaceRad*spaceRad ), diff );
egrad[3] = den*diff[3]/( uu*scaleRad*scaleRad );
}
break;
case pullInterTypeSeparable:
enR = evalR( &denR, rr, parmR );
enS = evalS( &denS, ss, parmS );
en = enR*enS;
if ( egrad ) {
ELL_3V_SCALE( egrad, denR*enS/( spaceRad*spaceDist ), diff );
egrad[3] = enR*airSgn( diff[3] )*denS/scaleRad;
}
break;
case pullInterTypeAdditive:
parmW = pctx->energySpecWin->parm;
evalW = pctx->energySpecWin->energy->eval;
enR = evalR( &denR, rr, parmR );
enS = evalS( &denS, ss, parmS );
enWR = evalW( &denWR, rr, parmW );
enWS = evalW( &denWS, ss, parmW );
beta = pctx->sysParm.beta;
en = AIR_LERP( beta, enR*enWS, enS*enWR );
if ( egrad ) {
double egradR[4], egradS[4];
ELL_3V_SCALE( egradR, denR*enWS/( spaceRad*spaceDist ), diff );
ELL_3V_SCALE( egradS, denWR*enS/( spaceRad*spaceDist ), diff );
egradR[3] = enR*scaleSgn*denWS/scaleRad;
egradS[3] = enWR*scaleSgn*denS/scaleRad;
ELL_4V_LERP( egrad, beta, egradR, egradS );
}
break;
default:
fprintf( stderr, "!%s: sorry, intertype %d unimplemented", meme,
pctx->interType );
en = AIR_NAN;
if ( egrad ) {
ELL_4V_SET( egrad, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
}
break;
}
/*
printf( "%s: %u <-- %u = %g, %g, %g -> egrad = %g, %g, %g, enr = %g\n",
meme, me->idtag, she->idtag,
diff[0], diff[1], diff[2],
egrad[0], egrad[1], egrad[2], enr );
*/
return en;
}
int
243 pullEnergyPlot( pullContext *pctx, Nrrd *nplot,
double xx, double yy, double zz,
unsigned int res ) {
static const char meme[]="pullEnergyPlot";
pullPoint *me, *she;
airArray *mop;
double dir[3], len, *plot, _rr, _ss, rr, ss, enr, egrad[4];
size_t size[3];
unsigned int ri, si;
if ( !( pctx && nplot ) ) {
biffAddf( PULL, "%s: got NULL pointer", meme );
return 1;
}
ELL_3V_SET( dir, xx, yy, zz );
if ( !ELL_3V_LEN( dir ) ) {
biffAddf( PULL, "%s: need non-zero length dir", meme );
return 1;
}
ELL_3V_NORM( dir, dir, len );
ELL_3V_SET( size, 3, res, res );
if ( nrrdMaybeAlloc_nva( nplot, nrrdTypeDouble, 3, size ) ) {
biffMovef( PULL, NRRD, "%s: trouble allocating output", meme );
return 1;
}
mop = airMopNew( );
me = pullPointNew( pctx );
she = pullPointNew( pctx );
airMopAdd( mop, me, ( airMopper )pullPointNix, airMopAlways );
airMopAdd( mop, she, ( airMopper )pullPointNix, airMopAlways );
ELL_4V_SET( me->pos, 0, 0, 0, 0 );
plot = AIR_CAST( double *, nplot->data );
for ( si=0; si<res; si++ ) {
_ss = AIR_AFFINE( 0, si, res-1, -1.0, 1.0 );
ss = _ss*pctx->sysParm.radiusScale;
for ( ri=0; ri<res; ri++ ) {
_rr = AIR_AFFINE( 0, ri, res-1, -1.0, 1.0 );
rr = _rr*pctx->sysParm.radiusSpace;
ELL_3V_SCALE( she->pos, rr, dir );
she->pos[3] = ss;
enr = _pullEnergyInterParticle( pctx, me, she,
AIR_ABS( rr ), AIR_ABS( ss ), egrad );
plot[0] = enr;
plot[1] = ELL_3V_DOT( egrad, dir );
plot[2] = egrad[3];
plot += 3;
}
}
airMopOkay( mop );
return 0;
}
/*
** computes energy from neighboring points. The non-NULLity of
** "egradSum" determines the energy *gradient* is computed ( with
** possible constraint modifications ) and stored there
**
** always computed:
** point->neighInterNum
** point->neighDistMean
**
** if pullProcessModeNeighLearn == task->processMode:
** point->neighCovar
** point->neighTanCovar
** point->neighNum
**
** 0=0 1=1 2=2 3=3
** ( 4 ) 4=5 5=6 6=7
** ( 8 ) ( 9 ) 7=10 8=11
** ( 12 ) ( 13 ) ( 14 ) 9=15
*/
double
317 _pullEnergyFromPoints( pullTask *task, pullBin *bin, pullPoint *point,
/* output */
double egradSum[4] ) {
static const char me[]="_pullEnergyFromPoints";
double energySum, spaDistSqMax; /* modeWghtSum */
int nlist, /* we enable the re-use of neighbor lists between inters, or,
at system start, creation of neighbor lists */
ntrue; /* we search all possible neighbors availble in the bins
( either because !nlist, or, this iter we learn true
subset of interacting neighbors ). This could also
be called "dontreuse" or something like that */
unsigned int nidx,
nnum; /* how much of task->neigh[] we use */
/* set nlist and ntrue */
if ( pullProcessModeNeighLearn == task->processMode ) {
/* we're here to both learn and store the true interacting neighbors */
nlist = AIR_TRUE;
ntrue = AIR_TRUE;
} else if ( pullProcessModeAdding == task->processMode
|| pullProcessModeNixing == task->processMode ) {
/* we assume that the work of learning neighbors has already been
done, so we can reuse them now */
nlist = AIR_TRUE;
ntrue = AIR_FALSE;
} else if ( pullProcessModeDescent == task->processMode ) {
if ( task->pctx->sysParm.neighborTrueProb < 1 ) {
nlist = AIR_TRUE;
if ( egradSum ) {
/* We allow the neighbor list optimization only when we're also
asked to compute the energy gradient, since that's the first
part of moving the particle. */
ntrue = ( 0 == task->pctx->iter
|| ( airDrandMT_r( task->rng )
< task->pctx->sysParm.neighborTrueProb ) );
} else {
/* When we're not getting the energy gradient, we're being
called to test the waters at possible new locations, in which
case we can't be changing the effective neighborhood */
ntrue = AIR_TRUE;
}
} else {
/* never trying neighborhood caching */
nlist = AIR_FALSE;
ntrue = AIR_TRUE;
}
} else {
fprintf( stderr, "%s: process mode %d unrecognized!\n", me,
task->processMode );
return AIR_NAN;
}
/* NOTE: can't have both nlist and ntrue false. possibilities are:
**
** nlist:
** true false
** true X X
** ntrue:
** false X
*/
/*
printf( "!%s( %u ), nlist = %d, ntrue = %d\n", me, point->idtag,
nlist, ntrue );
*/
/* set nnum and task->neigh[] */
if ( ntrue ) {
/* this finds the over-inclusive set of all possible interacting
points, based on bin membership as well the task's add queue */
nnum = _neighBinPoints( task, bin, point, 1.0 );
if ( nlist ) {
airArrayLenSet( point->neighPointArr, 0 );
}
} else {
/* ( nlist true ) this iter we re-use this point's existing neighbor
list, copying it into the the task's neighbor list to simulate
the action of _neighBinPoints( ) */
nnum = point->neighPointNum;
for ( nidx=0; nidx<nnum; nidx++ ) {
task->neighPoint[nidx] = point->neighPoint[nidx];
}
}
/* loop through neighbor points */
spaDistSqMax = ( task->pctx->sysParm.radiusSpace
* task->pctx->sysParm.radiusSpace );
/*
printf( "%s: radiusSpace = %g -> spaDistSqMax = %g\n", me,
task->pctx->sysParm.radiusSpace, spaDistSqMax );
*/
/* modeWghtSum = 0; */
energySum = 0;
point->neighInterNum = 0;
point->neighDistMean = 0.0;
if ( pullProcessModeNeighLearn == task->processMode ) {
ELL_10V_ZERO_SET( point->neighCovar );
point->stability = 0.0;
#if PULL_TANCOVAR
if ( task->pctx->ispec[pullInfoTangent1] ) {
double *tng;
float outer[9];
tng = point->info + task->pctx->infoIdx[pullInfoTangent1];
ELL_3MV_OUTER_TT( outer, float, tng, tng );
point->neighTanCovar[0] = outer[0];
point->neighTanCovar[1] = outer[1];
point->neighTanCovar[2] = outer[2];
point->neighTanCovar[3] = outer[4];
point->neighTanCovar[4] = outer[5];
point->neighTanCovar[5] = outer[8];
}
#endif
}
if ( egradSum ) {
ELL_4V_SET( egradSum, 0, 0, 0, 0 );
}
for ( nidx=0; nidx<nnum; nidx++ ) {
double diff[4], spaDistSq, spaDist, sclDist, enr, egrad[4];
pullPoint *herPoint;
herPoint = task->neighPoint[nidx];
if ( herPoint->status & PULL_STATUS_NIXME_BIT ) {
/* this point is not long for this world, pass over it */
continue;
}
ELL_4V_SUB( diff, point->pos, herPoint->pos ); /* me - her */
spaDistSq = ELL_3V_DOT( diff, diff );
/*
printf( "!%s: %u:%g, %g, %g <-- %u:%g, %g, %g = sqd %g %s %g\n", me,
point->idtag, point->pos[0], point->pos[1], point->pos[2],
herPoint->idtag,
herPoint->pos[0], herPoint->pos[1], herPoint->pos[2],
spaDistSq, spaDistSq > spaDistSqMax ? ">" : "<=", spaDistSqMax );
*/
if ( spaDistSq > spaDistSqMax ) {
continue;
}
sclDist = AIR_ABS( diff[3] );
if ( sclDist > task->pctx->sysParm.radiusScale ) {
continue;
}
spaDist = sqrt( spaDistSq );
/* we pass spaDist to avoid recomputing sqrt( ), and sclDist for
stupid consistency */
enr = _pullEnergyInterParticle( task->pctx, point, herPoint,
spaDist, sclDist,
egradSum ? egrad : NULL );
#if 0
/* sanity checking on energy derivatives */
if ( enr && egradSum ) {
double _pos[4], tdf[4], ee[2], eps=0.000001, apegrad[4], quot[4];
unsigned int cord, pan;
ELL_4V_COPY( _pos, point->pos );
for ( cord=0; cord<=3; cord++ ) {
for ( pan=0; pan<=1; pan++ ) {
point->pos[cord] = _pos[cord] + ( !pan ? -1 : +1 )*eps;
ELL_4V_SUB( tdf, point->pos, herPoint->pos );
ee[pan] = _pullEnergyInterParticle( task->pctx, point, herPoint,
ELL_3V_LEN( tdf ), AIR_ABS( tdf[3] ), NULL );
}
point->pos[cord] = _pos[cord];
apegrad[cord] = ( ee[1] - ee[0] )/( 2*eps );
quot[cord] = apegrad[cord]/egrad[cord];
}
if ( AIR_ABS( 1.0 - quot[0] ) > 0.01 ||
AIR_ABS( 1.0 - quot[1] ) > 0.01 ||
AIR_ABS( 1.0 - quot[2] ) > 0.01 ||
( task->pctx->haveScale && AIR_ABS( 1.0 - quot[3] ) > 0.01 ) ) {
printf( "!%s( %u<-%u ): ---------- claim egrad ( %g, %g, %g, %g )\n", me,
point->idtag, herPoint->idtag, egrad[0], egrad[1], egrad[2], egrad[3] );
printf( "!%s( %u<-%u ): measr egrad ( %g, %g, %g, %g )\n", me,
point->idtag, herPoint->idtag, apegrad[0], apegrad[1], apegrad[2], apegrad[3] );
printf( "!%s( %u<-%u ): quot ( %g, %g, %g, %g )\n", me,
point->idtag, herPoint->idtag, quot[0], quot[1], quot[2], quot[3] );
}
ELL_4V_COPY( point->pos, _pos );
}
#endif
if ( enr ) {
/* there is some non-zero energy due to her; and we assume that
its not just a fluke zero-crossing of the potential profile */
double ndist;
point->neighInterNum++;
if ( nlist && ntrue ) {
unsigned int ii;
/* we have to record that we had an interaction with this point */
ii = airArrayLenIncr( point->neighPointArr, 1 );
point->neighPoint[ii] = herPoint;
}
energySum += enr;
ELL_3V_SCALE( diff, 1.0/task->pctx->sysParm.radiusSpace, diff );
if ( task->pctx->haveScale ) {
diff[3] /= task->pctx->sysParm.radiusScale;
}
ndist = ELL_4V_LEN( diff );
point->neighDistMean += ndist;
if ( pullProcessModeNeighLearn == task->processMode ) {
float outer[16];
ELL_4MV_OUTER_TT( outer, float, diff, diff );
point->neighCovar[0] += outer[0];
point->neighCovar[1] += outer[1];
point->neighCovar[2] += outer[2];
point->neighCovar[3] += outer[3];
point->neighCovar[4] += outer[5];
point->neighCovar[5] += outer[6];
point->neighCovar[6] += outer[7];
point->neighCovar[7] += outer[10];
point->neighCovar[8] += outer[11];
point->neighCovar[9] += outer[15];
#if PULL_TANCOVAR
if ( task->pctx->ispec[pullInfoTangent1] ) {
double *tng;
tng = herPoint->info + task->pctx->infoIdx[pullInfoTangent1];
ELL_3MV_OUTER_TT( outer, float, tng, tng );
point->neighTanCovar[0] += outer[0];
point->neighTanCovar[1] += outer[1];
point->neighTanCovar[2] += outer[2];
point->neighTanCovar[3] += outer[4];
point->neighTanCovar[4] += outer[5];
point->neighTanCovar[5] += outer[8];
}
#endif
}
if ( egradSum ) {
ELL_4V_INCR( egradSum, egrad );
}
}
}
#define CNORM( M ) \
sqrt( M[0]*M[0] + 2*M[1]*M[1] + 2*M[2]*M[2] + 2*M[3]*M[3] \
+ M[4]*M[4] + 2*M[5]*M[5] + 2*M[6]*M[6] \
+ M[7]*M[7] + 2*M[8]*M[8] \
+ M[9]*M[9] )
#define CTRACE( M ) ( M[0] + M[4] + M[7] + M[9] )
/* finish computing things averaged over neighbors */
if ( point->neighInterNum ) {
point->neighDistMean /= point->neighInterNum;
if ( pullProcessModeNeighLearn == task->processMode ) {
float ncs[4], ncsLen, *cov, sxx, syy, scl;
cov = point->neighCovar;
ELL_10V_SCALE( cov, 1.0f/point->neighInterNum, cov );
/* Stability is related to the angle between S=( 0, 0, 0, 1 ) and the
projection of S onto the tangent surface; we approximate that
by finding the product neighCovar*S == last column of neighCovar*/
ELL_4V_SET( ncs, cov[3], cov[6], cov[8], cov[9] );
ELL_4V_NORM_TT( ncs, float, ncs, ncsLen );
if ( ncsLen ) {
syy = ncs[3];
scl = AIR_CAST( float, ( task->pctx->flag.scaleIsTau
? gageSigOfTau( point->pos[3] )
: point->pos[3] ) );
if ( scl ) {
sxx = AIR_CAST( float, ELL_3V_LEN( ncs ) )/scl;
point->stability = AIR_CAST( float, atan2( syy, sxx )/( AIR_PI/2 ) );
} else {
point->stability = 0;
}
} else {
/* HEY: probably bug that we can have *zero* last column in covar,
and yet have non-zero point->neighInterNum, right? */
point->stability = 0;
}
if ( !AIR_EXISTS( point->stability ) ) {
fprintf( stderr, "!%s( %u ): bad stability %g\n", me,
point->idtag, point->stability );
fprintf( stderr, "%g %g %g %g\n", cov[3], cov[6], cov[8], cov[9] );
fprintf( stderr, "%g %g %g %g\n", ncs[0], ncs[1], ncs[2], ncs[3] );
fprintf( stderr, "sxx %g syy %g\n", sxx, syy );
}
#if PULL_TANCOVAR
/* using 1 + # neigh because this includes tan1 of point itself */
ELL_6V_SCALE( point->neighTanCovar, 1.0f/( 1 + point->neighInterNum ),
point->neighTanCovar );
#endif
}
} else {
/* we had no neighbors at all */
point->neighDistMean = 0.0; /* shouldn't happen in any normal case */
/* point->neighCovar, neighTanCovar stay as initialized above */
}
601 return energySum;
}
static double
_energyFromImage( pullTask *task, pullPoint *point,
/* output */
double egradSum[4] ) {
static const char me[]="_energyFromImage";
double energy, grad3[3], _gamma;
int probed;
/* not sure I have the logic for this right
int ptrue;
if ( task->pctx->probeProb < 1 && allowProbeProb ) {
if ( egrad ) {
ptrue = ( 0 == task->pctx->iter
|| airDrandMT_r( task->rng ) < task->pctx->probeProb );
} else {
ptrue = AIR_FALSE;
}
} else {
ptrue = AIR_TRUE;
}
*/
probed = AIR_FALSE;
/* a better name for this would be
"probe only if you haven't already probed" */
#define MAYBEPROBE \
if ( !probed ) { \
if ( pullProbe( task, point ) ) { \
fprintf( stderr, "%s: problem probing!!!\n%s\n", me, biffGetDone( PULL ) ); \
} \
probed = AIR_TRUE; \
}
_gamma = task->pctx->sysParm.gamma;
energy = 0;
if ( egradSum ) {
ELL_4V_SET( egradSum, 0, 0, 0, 0 );
}
if ( task->pctx->flag.energyFromStrength
&& task->pctx->ispec[pullInfoStrength] ) {
double deltaScale, str0, str1, scl0, scl1, enr;
int sign;
if ( !egradSum ) {
/* just need the strength */
MAYBEPROBE;
enr = pullPointScalar( task->pctx, point, pullInfoStrength,
NULL, NULL );
energy += -_gamma*enr;
} else {
/* need strength and its gradient */
/* randomize choice between forward and backward difference */
/* NOTE: since you only need one bit of random, you could re-used
a random int and look through its bits to determine forw vs
back differences, but this is probably not the bottleneck */
sign = 2*AIR_CAST( int, airRandInt_r( task->rng, 2 ) ) - 1;
deltaScale = task->pctx->bboxMax[3] - task->pctx->bboxMin[3];
deltaScale *= sign*_PULL_STRENGTH_ENERGY_DELTA_SCALE;
scl1 = ( point->pos[3] += deltaScale );
pullProbe( task, point ); /* *not* MAYBEPROBE */
str1 = pullPointScalar( task->pctx, point, pullInfoStrength,
NULL, NULL );
scl0 = ( point->pos[3] -= deltaScale );
MAYBEPROBE;
str0 = pullPointScalar( task->pctx, point, pullInfoStrength,
NULL, NULL );
energy += -_gamma*str0;
egradSum[3] += -_gamma*( str1 - str0 )/( scl1 - scl0 );
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "%s( %u ): egrad[3] = %g*( ( %g-%g )/( %g-%g ) = %g/%g = %g )"
" = %g -> %g\n",
me, point->idtag, task->pctx->sysParm.gamma,
str1, str0, scl1, scl0,
str1 - str0, scl1 - scl0,
( str1 - str0 )/( scl1 - scl0 ),
task->pctx->sysParm.gamma*( str1 - str0 )/( scl1 - scl0 ),
egradSum[3] );
}
*/
}
}
/* Note that height doesn't contribute to the energy if there is
a constraint associated with it */
if ( task->pctx->ispec[pullInfoHeight]
&& !task->pctx->ispec[pullInfoHeight]->constraint
&& !( task->pctx->ispec[pullInfoHeightLaplacian]
&& task->pctx->ispec[pullInfoHeightLaplacian]->constraint ) ) {
MAYBEPROBE;
energy += pullPointScalar( task->pctx, point, pullInfoHeight,
grad3, NULL );
if ( egradSum ) {
ELL_3V_INCR( egradSum, grad3 );
}
}
if ( task->pctx->ispec[pullInfoIsovalue]
&& !task->pctx->ispec[pullInfoIsovalue]->constraint ) {
/* we're only going towards an isosurface, not constrained to it
==> set up a parabolic potential well around the isosurface */
double val;
MAYBEPROBE;
val = pullPointScalar( task->pctx, point, pullInfoIsovalue,
grad3, NULL );
energy += val*val;
if ( egradSum ) {
ELL_3V_SCALE_INCR( egradSum, 2*val, grad3 );
}
}
return energy;
}
#undef MAYBEPROBE
/*
** NOTE that the "egrad" being non-NULL has consequences for what gets
** computed in _energyFromImage and _pullEnergyFromPoints:
**
** NULL "egrad": we're simply learning the energy ( and want to know it
720 ** as truthfully as possible ) for the sake of inspecting system state
**
** non-NULL "egrad": we're learning the current energy, but the real point
** is to determine how to move the point to lower energy
**
** the ignoreImage flag is a hack, to allow _pullPointProcessAdding to
** do descent on a new point according to other points, but not the
** image.
*/
double
_pullPointEnergyTotal( pullTask *task, pullBin *bin, pullPoint *point,
int ignoreImage,
/* output */
double egrad[4] ) {
static const char me[]="_pullPointEnergyTotal";
double enrIm, enrPt, egradIm[4], egradPt[4], energy;
ELL_4V_SET( egradIm, 0, 0, 0, 0 );
ELL_4V_SET( egradPt, 0, 0, 0, 0 );
if ( !( ignoreImage || 1.0 == task->pctx->sysParm.alpha ) ) {
enrIm = _energyFromImage( task, point, egrad ? egradIm : NULL );
task->pctx->count[pullCountEnergyFromImage] += 1;
if ( egrad ) {
task->pctx->count[pullCountForceFromImage] += 1;
}
} else {
enrIm = 0;
}
if ( task->pctx->sysParm.alpha > 0.0 ) {
enrPt = _pullEnergyFromPoints( task, bin, point, egrad ? egradPt : NULL );
task->pctx->count[pullCountEnergyFromPoints] += 1;
if ( egrad ) {
task->pctx->count[pullCountForceFromPoints] += 1;
}
} else {
enrPt = 0;
}
energy = AIR_LERP( task->pctx->sysParm.alpha, enrIm, enrPt );
/*
printf( "!%s( %u ): energy = lerp( %g, im %g, pt %g ) = %g\n", me,
point->idtag, task->pctx->sysParm.alpha, enrIm, enrPt, energy );
*/
if ( egrad ) {
ELL_4V_LERP( egrad, task->pctx->sysParm.alpha, egradIm, egradPt );
/*
printf( "!%s( %u ): egradIm = %g %g %g %g\n", me, point->idtag,
egradIm[0], egradIm[1], egradIm[2], egradIm[3] );
printf( "!%s( %u ): egradPt = %g %g %g %g\n", me, point->idtag,
egradPt[0], egradPt[1], egradPt[2], egradPt[3] );
printf( "!%s( %u ): ---> force = %g %g %g %g\n", me,
point->idtag, force[0], force[1], force[2], force[3] );
*/
}
if ( task->pctx->sysParm.wall ) {
unsigned int axi;
double dwe; /* derivative of wall energy */
for ( axi=0; axi<4; axi++ ) {
dwe = point->pos[axi] - task->pctx->bboxMin[axi];
if ( dwe > 0 ) {
/* pos not below min */
dwe = point->pos[axi] - task->pctx->bboxMax[axi];
if ( dwe < 0 ) {
/* pos not above max */
dwe = 0;
}
}
energy += task->pctx->sysParm.wall*dwe*dwe/2;
if ( egrad ) {
egrad[axi] += task->pctx->sysParm.wall*dwe;
}
}
}
if ( !AIR_EXISTS( energy ) ) {
fprintf( stderr, "!%s( %u ): oops- non-exist energy %g\n", me, point->idtag,
energy );
}
return energy;
}
798
/*
** distance limit on a particles motion in both r and s,
** in rs-normalized space ( sqrt( ( r/radiusSpace )^2 + ( s/radiusScale )^2 ) )
**
** This means that if particles are jammed together in space,
** they aren't allowed to move very far in scale, either, which
** is a little weird, but probably okay.
*/
double
_pullDistLimit( pullTask *task, pullPoint *point ) {
double ret;
if ( point->neighDistMean == 0 /* no known neighbors from last iter */
|| pullEnergyZero == task->pctx->energySpecR->energy ) {
ret = 1;
} else {
ret = _PULL_DIST_CAP_RSNORM*point->neighDistMean;
816 }
/* HEY: maybe task->pctx->voxelSizeSpace or voxelSizeScale should
be considered here? */
return ret;
}
/*
** here is where the energy gradient is converted into force
*/
int
_pullPointProcessDescent( pullTask *task, pullBin *bin, pullPoint *point,
int ignoreImage ) {
static const char me[]="_pullPointProcessDescent";
double energyOld, energyNew, egrad[4], force[4], posOld[4];
int stepBad, giveUp, hailMary;
task->pctx->count[pullCountDescent] += 1;
if ( !point->stepEnergy ) {
fprintf( stderr, "\n\n\n%s: whoa, point %u step is zero!!\n\n\n\n",
me, point->idtag );
/* HEY: need to track down how this can originate! */
/*
biffAddf( PULL, "%s: point %u step is zero!", me, point->idtag );
return 1;
*/
point->stepEnergy = task->pctx->sysParm.stepInitial/100;
}
/* learn the energy at old location, and the energy gradient */
energyOld = _pullPointEnergyTotal( task, bin, point, ignoreImage, egrad );
ELL_4V_SCALE( force, -1, egrad );
if ( !( AIR_EXISTS( energyOld ) && ELL_4V_EXISTS( force ) ) ) {
biffAddf( PULL, "%s: point %u non-exist energy or force", me, point->idtag );
return 1;
}
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ): old pos = %g %g %g %g\n", me, point->idtag,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
printf( "!%s( %u ): energyOld = %g; force = %g %g %g %g\n", me,
point->idtag, energyOld, force[0], force[1], force[2], force[3] );
}
*/
if ( !ELL_4V_DOT( force, force ) ) {
/* this particle has no reason to go anywhere; BUT we still have to
enforce constraint if we have one */
int constrFail = 0;
__IF_DEBUG {
fprintf( stderr, "!%s: point %u unforced ...\n", me, point->idtag );
}
if ( task->pctx->constraint ) {
if ( _pullConstraintSatisfy( task, point,
100.0*_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
}
if ( constrFail ) {
/*
biffAddf( PULL, "%s: couldn't satisfy constraint on unforced %u: %s",
me, point->idtag, airEnumStr( pullConstraintFail, constrFail ) );
return 1;
*/
fprintf( stderr, "%s: *** constr sat fail on unfrced %u: "
"%s ( si# %u;%u )\n",
me, point->idtag, airEnumStr( pullConstraintFail, constrFail ),
point->stuckIterNum, task->pctx->iterParm.stuckMax );
point->status |= PULL_STATUS_STUCK_BIT;
point->stuckIterNum += 1;
if ( task->pctx->iterParm.stuckMax
&& point->stuckIterNum > task->pctx->iterParm.stuckMax ) {
point->status |= PULL_STATUS_NIXME_BIT;
}
}
point->energy = energyOld;
return 0;
}
if ( task->pctx->constraint
&& ( task->pctx->ispec[pullInfoTangent1]
|| task->pctx->ispec[pullInfoNegativeTangent1] ) ) {
/* we have a constraint, so do something to get the force more
tangential to the constraint manifold ( only in the spatial axes ) */
double proj[9], pfrc[3];
_pullConstraintTangent( task, point, proj );
ELL_3MV_MUL( pfrc, proj, force );
ELL_3V_COPY( force, pfrc );
/* force[3] untouched */
}
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ): post-constraint tan: force = %g %g %g %g\n", me,
point->idtag, force[0], force[1], force[2], force[3] );
printf( " precap stepEnergy = %g\n", point->stepEnergy );
}
*/
/* Cap particle motion. The point is only allowed to move at most unit
distance in rs-normalized space, which may mean that motion in r
or s is effectively cramped by crowding in the other axis, oh well.
Also, particle motion is limited in terms of spatial voxel size,
and ( if haveScale ) the average distance between scale samples */
if ( 1 ) {
double capvec[4], spcLen, sclLen, max, distLimit;
/* limits based on distLimit in rs-normalized space */
distLimit = _pullDistLimit( task, point );
ELL_4V_SCALE( capvec, point->stepEnergy, force );
spcLen = ELL_3V_LEN( capvec )/task->pctx->sysParm.radiusSpace;
sclLen = AIR_ABS( capvec[3] )/task->pctx->sysParm.radiusScale;
max = AIR_MAX( spcLen, sclLen );
if ( max > distLimit ) {
point->stepEnergy *= distLimit/max;
}
/* limits based on voxelSizeSpace and voxelSizeScale */
ELL_4V_SCALE( capvec, point->stepEnergy, force );
spcLen = ELL_3V_LEN( capvec )/task->pctx->voxelSizeSpace;
if ( task->pctx->haveScale ) {
sclLen = AIR_ABS( capvec[3] )/task->pctx->voxelSizeScale;
max = AIR_MAX( spcLen, sclLen );
} else {
max = spcLen;
}
if ( max > _PULL_DIST_CAP_VOXEL ) {
point->stepEnergy *= _PULL_DIST_CAP_VOXEL/max;
}
}
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( " postcap stepEnergy = %g\n", point->stepEnergy );
}
*/
/* turn off stuck bit, will turn it on again if needed */
point->status &= ~PULL_STATUS_STUCK_BIT;
ELL_4V_COPY( posOld, point->pos );
_pullPointHistInit( point );
_pullPointHistAdd( point, pullCondOld, AIR_NAN );
/* try steps along force until we succcessfully lower energy */
hailMary = AIR_FALSE;
do {
int constrFail, energyIncr;
giveUp = AIR_FALSE;
ELL_4V_SCALE_ADD2( point->pos, 1.0, posOld,
point->stepEnergy, force );
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ): ( iter %u ) try pos = %g %g %g %g%s\n",
me, point->idtag, task->pctx->iter,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3],
hailMary ? " ( Hail Mary )" : "" );
}
*/
if ( task->pctx->haveScale ) {
point->pos[3] = AIR_CLAMP( task->pctx->bboxMin[3],
point->pos[3],
task->pctx->bboxMax[3] );
}
task->pctx->count[pullCountTestStep] += 1;
_pullPointHistAdd( point, pullCondEnergyTry, AIR_NAN );
if ( task->pctx->constraint ) {
if ( _pullConstraintSatisfy( task, point,
_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
} else {
constrFail = AIR_FALSE;
}
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ): post constr = %g %g %g %g ( %d )\n", me,
point->idtag,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3], constrFail );
}
*/
if ( constrFail ) {
energyNew = AIR_NAN;
} else {
energyNew = _pullPointEnergyTotal( task, bin, point, ignoreImage, NULL );
}
energyIncr = energyNew > ( energyOld
+ task->pctx->sysParm.energyIncreasePermit );
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ): constrFail %d; enr Old New = %g %g -> enrIncr %d\n",
me, point->idtag, constrFail, energyOld, energyNew, energyIncr );
}
*/
stepBad = ( constrFail || energyIncr );
if ( stepBad ) {
point->stepEnergy *= task->pctx->sysParm.backStepScale;
if ( constrFail ) {
_pullPointHistAdd( point, pullCondConstraintFail, AIR_NAN );
} else {
_pullPointHistAdd( point, pullCondEnergyBad, AIR_NAN );
}
/* you have a problem if you had a non-trivial force, but you can't
ever seem to take a small enough step to reduce energy */
if ( point->stepEnergy < 0.00000001 ) {
/* this can happen if the force is due to a derivative of
feature strength with respect to scale, which is measured
WITHOUT enforcing the constraint, while particle updates
are done WITH the constraint, in which case the computed
force can be completely misleading. Thus, as a last-ditch
effort, we try moving in the opposite direction ( against
the force ) to see if that helps */
if ( task->pctx->verbose > 1 ) {
printf( "%s: %u %s ( %u ); ( %g, %g, %g, %g ) stepEnr %g\n", me,
point->idtag, hailMary ? "STUCK!" : "stuck?",
point->stuckIterNum,
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
point->stepEnergy );
}
if ( !hailMary ) {
ELL_4V_SCALE( force, -1, force );
/*
if ( 4819 == point->idtag || 4828 == point->idtag ) {
printf( "!%s( %u ): force now %g %g %g %g\n", me, point->idtag,
force[0], force[1], force[2], force[3] );
}
*/
hailMary = AIR_TRUE;
} else {
/* The hail Mary pass missed too; something is really odd.
This can happen when the previous iteration did a sloppy job
enforcing the constraint, so before we move on, we enforce
it, twice for good measure, so that things may be better next
time around */
if ( task->pctx->constraint ) {
if ( _pullConstraintSatisfy( task, point,
_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail )
|| _pullConstraintSatisfy( task, point,
_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
}
energyNew = _pullPointEnergyTotal( task, bin, point,
ignoreImage, NULL );
point->stepEnergy = task->pctx->sysParm.stepInitial;
point->status |= PULL_STATUS_STUCK_BIT;
point->stuckIterNum += 1;
giveUp = AIR_TRUE;
}
}
}
} while ( stepBad && !giveUp );
/* Hail Mary worked if ( hailMary && !stepBad ). It does sometimes work. */
/* now: unless we gave up, energy decreased, and,
if we have one, constraint has been met */
/*
if ( 1560 < task->pctx->iter && 2350 == point->idtag ) {
printf( "!%s( %u ):iter %u changed ( %g, %g, %g, %g )->( %g, %g, %g, %g )\n",
me, point->idtag, task->pctx->iter,
posOld[0], posOld[1], posOld[2], posOld[3],
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
}
*/
_pullPointHistAdd( point, pullCondNew, AIR_NAN );
ELL_4V_COPY( point->force, force );
/* not recorded for the sake of this function, but for system accounting */
point->energy = energyNew;
if ( !AIR_EXISTS( energyNew ) ) {
biffAddf( PULL, "%s: point %u has non-exist final energy %g\n",
me, point->idtag, energyNew );
return 1;
}
/* if its not stuck, reset stuckIterNum */
if ( !( point->status & PULL_STATUS_STUCK_BIT ) ) {
point->stuckIterNum = 0;
} else if ( task->pctx->iterParm.stuckMax
1096 && point->stuckIterNum > task->pctx->iterParm.stuckMax ) {
/* else if it is stuck then its up to us to set NIXME
based on point->stuckIterNum */
point->status |= PULL_STATUS_NIXME_BIT;
}
return 0;
}
int
_pullPointProcess( pullTask *task, pullBin *bin, pullPoint *point ) {
static const char me[]="_pullPointProcess";
int E;
/*
fprintf( stderr, "!%s( %u, %u ) mode %s\n", me, point->idtag, task->pctx->iter,
airEnumStr( pullProcessMode, task->processMode ) );
*/
E = 0;
switch ( task->processMode ) {
case pullProcessModeDescent:
E = _pullPointProcessDescent( task, bin, point,
AIR_FALSE
/* !task->pctx->haveScale ignoreImage */ );
break;
case pullProcessModeNeighLearn:
E = _pullPointProcessNeighLearn( task, bin, point );
break;
case pullProcessModeAdding:
if ( !task->pctx->flag.noAdd ) {
E = _pullPointProcessAdding( task, bin, point );
}
break;
case pullProcessModeNixing:
E = _pullPointProcessNixing( task, bin, point );
break;
default:
biffAddf( PULL, "%s: process mode %d unrecognized", me, task->processMode );
return 1;
break;
}
if ( E ) {
1138 biffAddf( PULL, "%s: trouble", me );
return 1;
}
if ( task->pctx->flag.zeroZ ) {
point->pos[2] = 0;
}
return 0;
}
int
pullBinProcess( pullTask *task, unsigned int myBinIdx ) {
static const char me[]="pullBinProcess";
pullBin *myBin;
unsigned int myPointIdx;
if ( task->pctx->verbose > 2 ) {
printf( "%s( %s ): doing bin %u\n", me,
airEnumStr( pullProcessMode, task->processMode ), myBinIdx );
}
myBin = task->pctx->bin + myBinIdx;
for ( myPointIdx=0; myPointIdx<myBin->pointNum; myPointIdx++ ) {
pullPoint *point;
if ( task->pctx->verbose > 1
&& task->pctx->pointNum > _PULL_PROGRESS_POINT_NUM_MIN
&& !task->pctx->flag.binSingle
&& task->pctx->progressBinMod
&& 0 == myBinIdx % task->pctx->progressBinMod ) {
printf( "." ); fflush( stdout );
}
point = myBin->point[myPointIdx];
if ( task->pctx->verbose > 2 ) {
printf( "%s( %s ) processing ( bin %u )->point[%u] %u\n", me,
airEnumStr( pullProcessMode, task->processMode ),
myBinIdx, myPointIdx, point->idtag );
}
if ( _pullPointProcess( task, myBin, point ) ) {
biffAddf( PULL, "%s: on point %u of bin %u\n", me,
1175 myPointIdx, myBinIdx );
return 1;
}
task->stuckNum += ( point->status & PULL_STATUS_STUCK_BIT );
} /* for myPointIdx */
return 0;
}
int
pullGammaLearn( pullContext *pctx ) {
static const char me[]="pullGammaLearn";
unsigned int binIdx, pointIdx, pointNum;
pullBin *bin;
pullPoint *point;
pullTask *task;
double deltaScale, scl, beta, wellX=0, wellY=0,
*strdd, *gmag, meanGmag, meanStrdd, wght, wghtSum;
airArray *mop;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !pctx->haveScale ) {
biffAddf( PULL, "%s: not using scale-space", me );
return 1;
}
if ( pullInterTypeAdditive == pctx->interType ) {
if ( pullEnergyButterworthParabola != pctx->energySpecS->energy ) {
biffAddf( PULL, "%s: want %s energy along scale, not %s", me,
pullEnergyButterworthParabola->name,
pctx->energySpecS->energy->name );
return 1;
}
} else if ( pullInterTypeSeparable == pctx->interType ) {
wellY = pctx->energySpecR->energy->well( &wellX,
pctx->energySpecR->parm );
if ( !( wellY < 0 ) ) {
biffAddf( PULL, "%s: spatial energy %s didn't have well",
me, pctx->energySpecR->energy->name );
return 1;
}
if ( pullEnergyBspln != pctx->energySpecS->energy ) {
biffAddf( PULL, "%s: want %s energy along scale, not %s", me,
pullEnergyBspln->name,
pctx->energySpecS->energy->name );
return 1;
}
} else {
biffAddf( PULL, "%s: need %s or %s inter type, not %s", me,
airEnumStr( pullInterType, pullInterTypeAdditive ),
airEnumStr( pullInterType, pullInterTypeSeparable ),
airEnumStr( pullInterType, pctx->interType ) );
return 1;
}
pointNum = pullPointNumber( pctx );
if ( !pointNum ) {
biffAddf( PULL, "%s: had no points!", me );
return 1;
}
mop = airMopNew( );
strdd = AIR_CALLOC( pointNum, double );
airMopAdd( mop, strdd, airFree, airMopAlways );
gmag = AIR_CALLOC( pointNum, double );
airMopAdd( mop, gmag, airFree, airMopAlways );
if ( !( strdd && gmag ) ) {
biffAddf( PULL, "%s: couldn't alloc two buffers of %u doubles",
me, pointNum );
airMopError( mop );
return 1;
}
task = pctx->task[0];
pointIdx = 0;
deltaScale = pctx->bboxMax[3] - pctx->bboxMin[3];
deltaScale *= _PULL_STRENGTH_ENERGY_DELTA_SCALE;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
unsigned int pidx;
bin = pctx->bin + binIdx;
for ( pidx=0; pidx<bin->pointNum; pidx++ ) {
double str[3], _ss, _gr;
point = bin->point[pidx];
point->pos[3] += deltaScale;
pullProbe( task, point );
str[2] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
point->pos[3] -= 2*deltaScale;
pullProbe( task, point );
str[0] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
point->pos[3] += deltaScale;
pullProbe( task, point );
str[1] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
_ss = ( str[0] - 2*str[1] + str[2] )/( deltaScale*deltaScale );
if ( _ss < 0.0 ) {
_gr = ( str[2] - str[0] )/( 2*deltaScale );
_gr = AIR_ABS( _gr );
strdd[pointIdx] = _ss;
gmag[pointIdx] = _gr;
pointIdx++;
}
}
}
if ( !pointIdx ) {
biffAddf( PULL, "%s: no points w/ 2nd deriv of strn wrt scale < 0", me );
airMopError( mop );
return 1;
}
/* resetting pointNum to actual number of points used */
pointNum = pointIdx;
/* learn meanGmag, with sqrt( ) sneakiness to discount high gmags */
meanGmag = 0.0;
for ( pointIdx=0; pointIdx<pointNum; pointIdx++ ) {
meanGmag += sqrt( gmag[pointIdx] );
}
meanGmag /= pointNum;
meanGmag *= meanGmag;
/* learn meanStrdd with a Gaussian weight on gmag; we want
to give more weight to the strdds that are near maximal strength
( hence 1st derivative near zero ) */
meanStrdd = wghtSum = 0.0;
for ( pointIdx=0; pointIdx<pointNum; pointIdx++ ) {
/* the "meanGmag/8" allowed the gamma learned from a
cone dataset immediately post-initialization to
nearly match the gamma learned post-phase-2 */
wght = airGaussian( gmag[pointIdx], 0.0, meanGmag/8 );
wghtSum += wght;
meanStrdd += wght*strdd[pointIdx];
}
meanStrdd /= wghtSum;
scl = pctx->sysParm.radiusScale;
if ( pullInterTypeAdditive == pctx->interType ) {
/* want to satisfy str''( s ) = enr''( s )
** ==> -gamma*strdd = 2*beta/( radiusScale )^2
** ==> gamma = -2*beta/( strdd*( radiusScale )^2 )
** ( beta = 1 ) ==> gamma = -2/( strdd*( radiusScale )^2 )
** NOTE: The difference from what is in the paper is a factor of 2,
** and the ability to include the influence of beta
*/
beta = ( pctx->flag.useBetaForGammaLearn
? pctx->sysParm.beta
: 1.0 );
pctx->sysParm.gamma = -2*beta/( meanStrdd*scl*scl );
} else if ( pullInterTypeSeparable == pctx->interType ) {
/* want to satisfy str''( s ) = enr''( s ); wellY < 0
** ==> gamma*strdd = wellY*8/( radiusScale )^2
** gamma = wellY*8/( strdd*( radiusScale )^2 )
*/
pctx->sysParm.gamma = wellY*8/( meanStrdd*scl*scl );
pctx->sysParm.gamma *= pctx->sysParm.separableGammaLearnRescale;
} else {
biffAddf( PULL, "%s: sorry %s inter type unimplemented", me,
airEnumStr( pullInterType, pctx->interType ) );
airMopError( mop );
return 1;
}
if ( pctx->verbose ) {
printf( "%s: learned gamma %g\n", me, pctx->sysParm.gamma );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/*
** because the pullContext keeps an array of bins ( not pointers to them )
** we have Init and Done functions ( not New and Nix )
*/
void
32 _pullBinInit( pullBin *bin ) {
bin->point = NULL;
bin->pointNum = 0;
bin->pointArr = NULL;
bin->neighBin = NULL;
return;
}
/*
** bins own the points they contain- so this frees them
*/
void
45 _pullBinDone( pullBin *bin ) {
unsigned int idx;
for ( idx=0; idx<bin->pointNum; idx++ ) {
bin->point[idx] = pullPointNix( bin->point[idx] );
}
bin->pointArr = airArrayNuke( bin->pointArr );
bin->neighBin = ( pullBin ** )airFree( bin->neighBin );
return;
}
int
57 _pullBinNeighborSet( pullContext *pctx, pullBin *bin ) {
static const char me[]="_pullBinNeighborSet";
unsigned int neiIdx, neiNum, be[4], binIdx;
unsigned int xi, yi, zi, si, xx, yy, zz, ss,
xmax, ymax, zmax, smax;
pullBin *nei[3*3*3*3];
int xmin, ymin, zmin, smin;
binIdx = AIR_UINT( bin - pctx->bin );
/* annoyingly, have to recover the bin coordinates */
ELL_4V_COPY( be, pctx->binsEdge );
xi = binIdx % be[0];
binIdx = ( binIdx - xi )/be[0];
yi = binIdx % be[1];
binIdx = ( binIdx - yi )/be[1];
zi = binIdx % be[2];
si = ( binIdx - zi )/be[2];
neiNum = 0;
bin->neighBin = ( pullBin ** )airFree( bin->neighBin );
smin = AIR_MAX( 0, ( int )si-1 );
smax = AIR_MIN( si+1, be[3]-1 );
zmin = AIR_MAX( 0, ( int )zi-1 );
zmax = AIR_MIN( zi+1, be[2]-1 );
ymin = AIR_MAX( 0, ( int )yi-1 );
ymax = AIR_MIN( yi+1, be[1]-1 );
xmin = AIR_MAX( 0, ( int )xi-1 );
xmax = AIR_MIN( xi+1, be[0]-1 );
for ( ss=smin; ss<=smax; ss++ ) {
for ( zz=zmin; zz<=zmax; zz++ ) {
for ( yy=ymin; yy<=ymax; yy++ ) {
for ( xx=xmin; xx<=xmax; xx++ ) {
nei[neiNum++] = pctx->bin + xx + be[0]*( yy + be[1]*( zz + be[2]*ss ) );
}
}
}
}
if ( !( bin->neighBin = AIR_CALLOC( 1+neiNum, pullBin* ) ) ) {
biffAddf( PULL, "%s: couldn't calloc array of %u neighbor pointers", me, 1+neiNum );
return 1;
}
for ( neiIdx=0; neiIdx<neiNum; neiIdx++ ) {
bin->neighBin[neiIdx] = nei[neiIdx];
}
/* NULL-terminate the bin array */
bin->neighBin[neiIdx] = NULL;
return 0;
}
/*
** bins on boundary now extend to infinity; so the only time this
** returns NULL ( indicating error ) is for non-existent positions
*/
pullBin *
110 _pullBinLocate( pullContext *pctx, double *posWorld ) {
static const char me[]="_pullBinLocate";
unsigned int axi, eidx[4], binIdx;
if ( !ELL_4V_EXISTS( posWorld ) ) {
biffAddf( PULL, "%s: non-existent position ( %g, %g, %g, %g )", me,
posWorld[0], posWorld[1], posWorld[2], posWorld[3] );
return NULL;
}
if ( pctx->flag.binSingle ) {
binIdx = 0;
} else {
for ( axi=0; axi<4; axi++ ) {
eidx[axi] = airIndexClamp( pctx->bboxMin[axi],
posWorld[axi],
pctx->bboxMax[axi],
pctx->binsEdge[axi] );
}
binIdx = ( eidx[0]
+ pctx->binsEdge[0]*(
eidx[1] + pctx->binsEdge[1]*(
eidx[2] + pctx->binsEdge[2] * eidx[3] ) ) );
}
return pctx->bin + binIdx;
}
/*
** this makes the bin the owner of the point
*/
int
142 _pullBinPointAdd( pullContext *pctx, pullBin *bin, pullPoint *point ) {
static const char me[]="_pullBinPointAdd";
int pntI;
pullPtrPtrUnion pppu;
AIR_UNUSED( pctx );
if ( !( bin->pointArr ) ) {
pppu.points = &( bin->point );
bin->pointArr = airArrayNew( pppu.v, &( bin->pointNum ),
sizeof( pullPoint * ), _PULL_BIN_INCR );
if ( !( bin->pointArr ) ) {
biffAddf( PULL, "%s: couldn't create point array", me );
return 1;
}
}
if ( !( bin->neighBin ) ) {
/* set up neighbor bin vector if not done so already */
if ( _pullBinNeighborSet( pctx, bin ) ) {
biffAddf( PULL, "%s: couldn't initialize neighbor bins", me );
return 1;
}
}
pntI = airArrayLenIncr( bin->pointArr, 1 );
bin->point[pntI] = point;
return 0;
}
/*
** the bin loses track of the point, caller responsible for ownership,
** even though caller never identifies it by pointer, which is weird
*/
void
174 _pullBinPointRemove( pullContext *pctx, pullBin *bin, int loseIdx ) {
AIR_UNUSED( pctx );
bin->point[loseIdx] = bin->point[bin->pointNum-1];
airArrayLenIncr( bin->pointArr, -1 );
return;
}
/*
** adds point to context
*/
int
186 pullBinsPointAdd( pullContext *pctx, pullPoint *point, pullBin **binP ) {
static const char me[]="pullBinsPointAdd";
pullBin *bin;
if ( binP ) {
*binP = NULL;
}
if ( !( bin = _pullBinLocate( pctx, point->pos ) ) ) {
biffAddf( PULL, "%s: can't locate point %p %u",
me, AIR_CAST( void*, point ), point->idtag );
return 1;
}
if ( binP ) {
*binP = bin;
}
if ( _pullBinPointAdd( pctx, bin, point ) ) {
biffAddf( PULL, "%s: trouble adding point %p %u",
me, AIR_CAST( void*, point ), point->idtag );
return 1;
}
return 0;
}
int
210 pullBinsPointMaybeAdd( pullContext *pctx, pullPoint *point,
/* output */
pullBin **binP, int *added ) {
static const char me[]="pullBinsPointMaybeAdd";
pullBin *bin;
unsigned int idx;
int okay;
if ( binP ) {
*binP = NULL;
}
if ( !( pctx && point && added ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !( bin = _pullBinLocate( pctx, point->pos ) ) ) {
biffAddf( PULL, "%s: can't locate point %p %u",
me, AIR_CAST( void*, point ), point->idtag );
return 1;
}
if ( binP ) {
*binP = bin;
}
if ( pctx->flag.restrictiveAddToBins ) {
okay = AIR_TRUE;
for ( idx=0; idx<bin->pointNum; idx++ ) {
double diff[4], len;
ELL_4V_SUB( diff, point->pos, bin->point[idx]->pos );
ELL_3V_SCALE( diff, 1/pctx->sysParm.radiusSpace, diff );
diff[3] /= pctx->sysParm.radiusScale;
len = ELL_4V_LEN( diff );
if ( len < _PULL_BINNING_MAYBE_ADD_THRESH ) {
okay = AIR_FALSE;
break;
}
}
if ( okay ) {
if ( _pullBinPointAdd( pctx, bin, point ) ) {
biffAddf( PULL, "%s: trouble adding point %p %u",
me, AIR_CAST( void*, point ), point->idtag );
return 1;
}
*added = AIR_TRUE;
} else {
*added = AIR_FALSE;
}
} else {
if ( _pullBinPointAdd( pctx, bin, point ) ) {
biffAddf( PULL, "%s: trouble adding point %p %u",
me, AIR_CAST( void*, point ), point->idtag );
return 1;
}
*added = AIR_TRUE;
}
return 0;
}
int
268 _pullBinSetup( pullContext *pctx ) {
static const char me[]="_pullBinSetup";
unsigned ii;
double volEdge[4], width;
/* the maximum distance of interaction is when one particle is sitting
on the edge of another particle's sphere of influence, *NOT*, when
the spheres of influence of two particles are tangent: particles
interact with potential fields of other particles, but there is
no interaction between potential fields. */
width = ( pctx->sysParm.radiusSpace ? pctx->sysParm.radiusSpace : 0.1 );
pctx->maxDistSpace = pctx->sysParm.binWidthSpace*width;
width = ( pctx->sysParm.radiusScale ? pctx->sysParm.radiusScale : 0.1 );
pctx->maxDistScale = 1*width;
if ( pctx->verbose ) {
printf( "%s: radiusSpace = %g -( %g )-> maxDistSpace = %g\n", me,
pctx->sysParm.radiusSpace, pctx->sysParm.binWidthSpace,
pctx->maxDistSpace );
printf( "%s: radiusScale = %g ----> maxDistScale = %g\n", me,
pctx->sysParm.radiusScale, pctx->maxDistScale );
}
if ( pctx->flag.binSingle ) {
pctx->binsEdge[0] = 1;
pctx->binsEdge[1] = 1;
pctx->binsEdge[2] = 1;
pctx->binsEdge[3] = 1;
pctx->binNum = 1;
} else {
volEdge[0] = pctx->bboxMax[0] - pctx->bboxMin[0];
volEdge[1] = pctx->bboxMax[1] - pctx->bboxMin[1];
volEdge[2] = pctx->bboxMax[2] - pctx->bboxMin[2];
volEdge[3] = pctx->bboxMax[3] - pctx->bboxMin[3];
if ( pctx->verbose ) {
printf( "%s: volEdge = %g %g %g %g\n", me,
volEdge[0], volEdge[1], volEdge[2], volEdge[3] );
}
pctx->binsEdge[0] = AIR_CAST( unsigned int,
floor( volEdge[0]/pctx->maxDistSpace ) );
pctx->binsEdge[0] = pctx->binsEdge[0] ? pctx->binsEdge[0] : 1;
pctx->binsEdge[1] = AIR_CAST( unsigned int,
floor( volEdge[1]/pctx->maxDistSpace ) );
pctx->binsEdge[1] = pctx->binsEdge[1] ? pctx->binsEdge[1] : 1;
pctx->binsEdge[2] = AIR_CAST( unsigned int,
floor( volEdge[2]/pctx->maxDistSpace ) );
pctx->binsEdge[2] = pctx->binsEdge[2] ? pctx->binsEdge[2] : 1;
pctx->binsEdge[3] = AIR_CAST( unsigned int,
floor( volEdge[3]/pctx->maxDistScale ) );
pctx->binsEdge[3] = pctx->binsEdge[3] ? pctx->binsEdge[3] : 1;
/* hack to observe things at bin boundaries
ELL_3V_SET( pctx->binsEdge, 3, 3, 3 );
*/
if ( pctx->verbose ) {
printf( "%s: binsEdge=( %u, %u, %u, %u )\n", me,
pctx->binsEdge[0], pctx->binsEdge[1],
pctx->binsEdge[2], pctx->binsEdge[3] );
}
pctx->binNum = ( pctx->binsEdge[0]*pctx->binsEdge[1]
*pctx->binsEdge[2]*pctx->binsEdge[3] );
}
if ( pctx->binNum > PULL_BIN_MAXNUM ) {
biffAddf( PULL, "%s: sorry, #bins %u > PULL_BIN_MAXNUM %u. Try "
"increasing pctx->sysParm.binWidthSpace ( %g )", me,
pctx->binNum, PULL_BIN_MAXNUM, pctx->sysParm.binWidthSpace );
return 1;
}
if ( pctx->verbose ) {
printf( "%s: trying to allocate %u bins . . . \n", me, pctx->binNum );
}
pctx->bin = AIR_CALLOC( pctx->binNum, pullBin );
if ( !( pctx->bin ) ) {
biffAddf( PULL, "%s: couln't allocate %u bins", me, pctx->binNum );
return 1;
}
if ( pctx->verbose ) {
printf( "%s: done allocating. Initializing . . . \n", me );
}
for ( ii=0; ii<pctx->binNum; ii++ ) {
_pullBinInit( pctx->bin + ii );
}
if ( pctx->verbose ) {
printf( "%s: done initializing.\n", me );
}
if ( pctx->flag.binSingle ) {
if ( !( pctx->bin[0].neighBin = AIR_CALLOC( 2, pullBin* ) ) ) {
biffAddf( PULL, "%s: trouble allocating for single bin?", me );
return 1;
}
pctx->bin[0].neighBin[0] = pctx->bin + 0;
pctx->bin[0].neighBin[1] = NULL;
}
return 0;
}
void
364 _pullBinFinish( pullContext *pctx ) {
unsigned int ii;
for ( ii=0; ii<pctx->binNum; ii++ ) {
_pullBinDone( pctx->bin + ii );
}
pctx->bin = ( pullBin * )airFree( pctx->bin );
ELL_4V_SET( pctx->binsEdge, 0, 0, 0, 0 );
pctx->binNum = 0;
}
/*
** sets pctx->stuckNum
** resets all task[]->stuckNum
** reallocates pctx->tmpPointPerm and pctx->tmpPointPtr
** the point of this is to do rebinning
**
** This function is only called by the master thread, this
** does *not* have to be thread-safe in any way
*/
int
385 _pullIterFinishDescent( pullContext *pctx ) {
static const char me[]="_pullIterFinishDescent";
unsigned int oldBinIdx, pointIdx, taskIdx, runIdx, pointNum;
pullBin *oldBin;
pullPoint *point;
int added;
_pullNixTheNixed( pctx );
pctx->stuckNum = 0;
for ( taskIdx=0; taskIdx<pctx->threadNum; taskIdx++ ) {
pctx->stuckNum += pctx->task[taskIdx]->stuckNum;
pctx->task[taskIdx]->stuckNum = 0;
}
/* even w/ a single bin, we may still have to permute the points */
pointNum = pullPointNumber( pctx );
if ( pointNum != pctx->tmpPointNum ) {
if ( pctx->verbose ) {
printf( "!%s: changing total point # %u --> %u\n", me,
pctx->tmpPointNum, pointNum );
}
airFree( pctx->tmpPointPerm );
airFree( pctx->tmpPointPtr );
pctx->tmpPointPtr = AIR_CAST( pullPoint **,
calloc( pointNum, sizeof( pullPoint* ) ) );
pctx->tmpPointPerm = AIR_CAST( unsigned int *,
calloc( pointNum, sizeof( unsigned int ) ) );
if ( !( pctx->tmpPointPtr && pctx->tmpPointPerm ) ) {
biffAddf( PULL, "%s: couldn't allocate tmp buffers %p %p", me,
AIR_VOIDP( pctx->tmpPointPtr ), AIR_VOIDP( pctx->tmpPointPerm ) );
return 1;
}
pctx->tmpPointNum = pointNum;
}
runIdx = 0;
for ( oldBinIdx=0; oldBinIdx<pctx->binNum; oldBinIdx++ ) {
oldBin = pctx->bin + oldBinIdx;
while ( oldBin->pointNum ) {
/* tricky: we can't traverse bin->point[], because of how it is
re-ordered on point removal, so we always grab point[0] */
pctx->tmpPointPtr[runIdx++] = oldBin->point[0];
_pullBinPointRemove( pctx, oldBin, 0 );
}
}
airShuffle_r( pctx->task[0]->rng,
pctx->tmpPointPerm, pointNum, pctx->flag.permuteOnRebin );
if ( pctx->flag.permuteOnRebin && pctx->verbose ) {
printf( "%s: permuting %u points\n", me, pointNum );
}
for ( pointIdx=0; pointIdx<pointNum; pointIdx++ ) {
point = pctx->tmpPointPtr[pctx->tmpPointPerm[pointIdx]];
/*
if ( 1 == pctx->iter && 102 == point->idtag ) {
_pullDebugSanity( pctx->task[0], point );
}
*/
/* Sun Jul 14 01:30:41 CDT 2013: the previous code was basically
just pullBinsPointAdd( ). But when working with codim-3
constraints ( like finding spatial maxima with maximal strength
along scale ), its easy for many points to start piling on top
of each other; that is the problem that
pullFlagRestrictiveAddToBins was designed to solve. */
if ( pctx->constraint && 0 == pctx->constraintDim ) {
if ( pullBinsPointMaybeAdd( pctx, point, NULL, &added ) ) {
biffAddf( PULL, "%s: trouble binning? point %u", me, point->idtag );
return 1;
}
if ( !added ) {
/* the point wasn't owned by any bin, and now it turns out
no bin wanted to own it, so we have to remove it */
/* in the case of point maxima searching; this mainly happened
because two points at the same spatial positition slowly
descended along scale towards each other at the scale of
maximal strength */
point = pullPointNix( point );
}
} else {
if ( pullBinsPointAdd( pctx, point, NULL ) ) {
biffAddf( PULL, "%s: trouble binning point %u", me, point->idtag );
return 1;
}
point = NULL;
}
pctx->tmpPointPtr[pctx->tmpPointPerm[pointIdx]] = NULL;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/*
** HEY: this messes with the points' idtag, and pctx->idtagNext,
** even though it really shouldn't have to
*/
int
32 pullCCFind( pullContext *pctx ) {
static const char me[]="pullCCFind";
airArray *mop, *eqvArr;
unsigned int passIdx, binIdx, pointIdx, neighIdx, eqvNum,
pointNum, *idmap;
pullBin *bin;
pullPoint *point, *her;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( _pullIterate( pctx, pullProcessModeNeighLearn ) ) {
biffAddf( PULL, "%s: trouble with %s for CC", me,
airEnumStr( pullProcessMode, pullProcessModeNeighLearn ) );
return 1;
}
mop = airMopNew( );
pointNum = pullPointNumber( pctx );
eqvArr = airArrayNew( NULL, NULL, 2*sizeof( unsigned int ), pointNum );
airMopAdd( mop, eqvArr, ( airMopper )airArrayNuke, airMopAlways );
idmap = AIR_CAST( unsigned int *, calloc( pointNum, sizeof( unsigned int ) ) );
airMopAdd( mop, idmap, airFree, airMopAlways );
/* to be safe, renumber all points, so that we know that the
idtags are contiguous, starting at 0. HEY: this should handled
by having a map from real point->idtag to a point number assigned
just for the sake of doing CCs */
pctx->idtagNext = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
point->idtag = pctx->idtagNext++;
}
}
/* same stupidity copied from limn/polymod.c:limnPolyDataCCFind */
eqvNum = 0;
for ( passIdx=0; passIdx<2; passIdx++ ) {
if ( passIdx ) {
airArrayLenPreSet( eqvArr, eqvNum );
}
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
for ( neighIdx=0; neighIdx<point->neighPointNum; neighIdx++ ) {
if ( 0 == passIdx ) {
++eqvNum;
} else {
her = point->neighPoint[neighIdx];
airEqvAdd( eqvArr, point->idtag, her->idtag );
}
}
}
}
}
/* do the CC analysis */
pctx->CCNum = airEqvMap( eqvArr, idmap, pointNum );
/* assign idcc's */
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
point->idCC = idmap[point->idtag];
}
}
airMopOkay( mop );
return 0;
}
/*
** measure something about connected componts already found
**
** measrInfo can be 0 to say "measure # particles in CC", or
** it can be a scalar pullInfo
**
** rho == 0: only size, rho == 1: only measrInfo
*/
int
117 pullCCMeasure( pullContext *pctx, Nrrd *nmeasr, int measrInfo, double rho ) {
static const char me[]="pullCCMeasure";
airArray *mop;
unsigned int binIdx, pointIdx, ii;
double *meas, *size;
pullBin *bin;
pullPoint *point;
if ( !( pctx && nmeasr ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !pctx->CCNum ) {
biffAddf( PULL, "%s: CCNum == 0: haven't yet learned CCs?", me );
return 1;
}
if ( measrInfo ) {
if ( airEnumValCheck( pullInfo, measrInfo ) ) {
biffAddf( PULL, "%s: measrInfo %d not a valid %s", me,
measrInfo, pullInfo->name );
return 1;
}
if ( 1 != pullInfoLen( measrInfo ) ) {
biffAddf( PULL, "%s: measrInfo %s ( %d ) isn't a scalar ( len %u )", me,
airEnumStr( pullInfo, measrInfo ), measrInfo,
pullInfoLen( measrInfo ) );
return 1;
}
if ( !pctx->ispec[measrInfo] ) {
biffAddf( PULL, "%s: no ispec set for measrInfo %s ( %d )", me,
airEnumStr( pullInfo, measrInfo ), measrInfo );
return 1;
}
} /* else measrInfo is zero, they want to know # points */
/* in any case nmeasr is allocated for doubles */
if ( nrrdMaybeAlloc_va( nmeasr, nrrdTypeDouble, 1,
AIR_CAST( size_t, pctx->CCNum ) ) ) {
biffMovef( PULL, NRRD, "%s: couldn't alloc nmeasr", me );
return 1;
}
meas = AIR_CAST( double *, nmeasr->data );
mop = airMopNew( );
/* HEY: don't actually need to allocate and set size[],
if measrInfo == 0 */
if ( !( size = AIR_CAST( double *,
calloc( pctx->CCNum, sizeof( double ) ) ) ) ) {
biffAddf( PULL, "%s: couldn't alloc size", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, size, airFree, airMopAlways );
/* finally, do measurement */
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
size[point->idCC]++;
meas[point->idCC] += ( measrInfo
? pullPointScalar( pctx, point, measrInfo,
NULL, NULL )
: 1 );
}
}
if ( measrInfo ) {
for ( ii=0; ii<pctx->CCNum; ii++ ) {
meas[ii] /= size[ii];
meas[ii] = AIR_LERP( rho, size[ii], meas[ii] );
}
}
airMopOkay( mop );
return 0;
}
typedef struct {
unsigned int i;
double d;
} ccpair;
/* we intend to sort in *descending* order */
static int
199 ccpairCompare( const void *_a, const void *_b ) {
const ccpair *a, *b;
a = AIR_CAST( const ccpair *, _a );
b = AIR_CAST( const ccpair *, _b );
return ( a->d < b->d
? +1
: ( a->d > b->d
? -1
: 0 ) );
}
/*
******** pullCCSort
**
** sort CCs in pull context
**
** measrInfo == 0: sort by size, else,
** sort by blend of size and measrInfo
** rho == 0: only size, rho == 1: only measrInfo
*/
int
221 pullCCSort( pullContext *pctx, int measrInfo, double rho ) {
static const char me[]="pullCCSort";
ccpair *pair;
Nrrd *nmeasr;
airArray *mop;
unsigned int ii, *revm, binIdx, pointIdx;
double *measr;
pullBin *bin;
pullPoint *point;
int E;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !pctx->CCNum ) {
biffAddf( PULL, "%s: haven't yet learned CCs?", me );
return 1;
}
#define CALLOC( T ) ( AIR_CAST( T *, calloc( pctx->CCNum, sizeof( T ) ) ) )
mop = airMopNew( );
if ( !( nmeasr = nrrdNew( ) )
|| airMopAdd( mop, nmeasr, ( airMopper )nrrdNuke, airMopAlways )
|| !( pair = CALLOC( ccpair ) )
|| airMopAdd( mop, pair, airFree, airMopAlways )
|| !( revm = CALLOC( unsigned int ) )
|| airMopAdd( mop, revm, airFree, airMopAlways ) ) {
biffAddf( PULL, "%s: couldn't allocate everything", me );
airMopError( mop ); return 1;
}
#undef CALLOC
if ( !measrInfo ) {
/* sorting by size */
E = pullCCMeasure( pctx, nmeasr, 0, 0.0 );
} else {
/* sorting by some blend of size and pullInfo */
E = pullCCMeasure( pctx, nmeasr, measrInfo, rho );
}
if ( E ) {
biffAddf( PULL, "%s: problem measuring CCs", me );
airMopError( mop ); return 1;
}
measr = AIR_CAST( double *, nmeasr->data );
for ( ii=0; ii<pctx->CCNum; ii++ ) {
pair[ii].i = ii;
pair[ii].d = measr[ii];
}
qsort( pair, pctx->CCNum, sizeof( ccpair ), ccpairCompare );
for ( ii=0; ii<pctx->CCNum; ii++ ) {
revm[pair[ii].i] = ii;
}
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
point->idCC = revm[point->idCC];
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
#define DEBUG ( 0 )
/* #define DEBUG ( 12 == point->idtag ) */
/*
typedef struct {
double val, absval, grad[3];
} stateIso;
static int
probeIso( pullTask *task, pullPoint *point, unsigned int iter, int cond,
double pos[3],
stateIso *state ) {
static const char me[]="probeIso";
ELL_3V_COPY( point->pos, pos ); / * NB: not touching point->pos[3] * /
_pullPointHistAdd( point, cond, AIR_NAN );
if ( pullProbe( task, point ) ) {
biffAddf( PULL, "%s: on iter %u", me, iter );
return 1;
}
state->val = pullPointScalar( task->pctx, point,
pullInfoIsovalue,
state->grad, NULL );
state->absval = AIR_ABS( state->val );
return 0;
}
*/
/* NOTE: this assumes variables "iter" ( uint ) and "me" ( char* ) */
#define NORMALIZE_ERR( dir, grad, len ) \
if ( task->pctx->flag.zeroZ ) grad[2]=0; \
ELL_3V_NORM( ( dir ), ( grad ), ( len ) ); \
if ( !( len ) ) { \
biffAddf( PULL, "%s: got zero grad at ( %g, %g, %g, %g ) on iter %u\n", me, \
point->pos[0], point->pos[1], point->pos[2], \
point->pos[3], iter ); \
return 1; \
}
#define NORMALIZE( dir, grad, len ) \
if ( task->pctx->flag.zeroZ ) grad[2]=0; \
ELL_3V_NORM( ( dir ), ( grad ), ( len ) ); \
if ( !( len ) ) { \
71 ELL_3V_SET( ( dir ), 0, 0, 0 ) ; \
}
/* ------------------------------- isosurface */
#define PROBE( v, av, g ) if ( pullProbe( task, point ) ) { \
biffAddf( PULL, "%s: on iter %u", me, iter ); \
return 1; \
} \
( v ) = pullPointScalar( task->pctx, point, \
pullInfoIsovalue, ( g ), NULL ); \
( av ) = AIR_ABS( v )
#define SAVE( state, aval, val, grad, pos ) \
state[0] = aval; \
state[1] = val; \
ELL_3V_COPY( state + 1 + 1, grad ); \
ELL_3V_COPY( state + 1 + 1 + 3, pos )
#define RESTORE( aval, val, grad, pos, state ) \
aval = state[0]; \
val = state[1]; \
ELL_3V_COPY( grad, state + 1 + 1 ); \
ELL_3V_COPY( pos, state + 1 + 1 + 3 )
static int
constraintSatIso( pullTask *task, pullPoint *point,
double stepMax, double constrEps,
unsigned int iterMax,
/* output */
int *constrFailP ) {
static const char me[]="constraintSatIso";
double
step, /* current step size */
val, aval, /* last and current function values */
hack, /* how to control re-tries in the context of a single
for-loop, instead of a nested do-while loop */
grad[4], dir[3], len, state[1 + 1 + 3 + 3];
unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */
PROBE( val, aval, grad );
SAVE( state, aval, val, grad, point->pos );
hack = 1;
for ( iter=1; iter<=iterMax; iter++ ) {
/* consider? http://en.wikipedia.org/wiki/Halley%27s_method */
NORMALIZE( dir, grad, len );
if ( !len ) {
/* no gradient; back off */
hack *= task->pctx->sysParm.backStepScale;
RESTORE( aval, val, grad, point->pos, state );
continue;
}
step = -val/len; /* the newton-raphson step */
step = step > 0 ? AIR_MIN( stepMax, step ) : AIR_MAX( -stepMax, step );
ELL_3V_SCALE_INCR( point->pos, hack*step, dir );
PROBE( val, aval, grad );
_pullPointHistAdd( point, pullCondConstraintSatA, val );
if ( aval <= state[0] ) { /* we're no further from the root */
if ( AIR_ABS( step ) < stepMax*constrEps ) { /* HEY stepMax*constrEps vs constrEps */
/* we have converged! */
break;
}
SAVE( state, aval, val, grad, point->pos );
hack = 1;
136 } else { /* oops, try again, don't update dir or len, reset val */
hack *= task->pctx->sysParm.backStepScale;
RESTORE( aval, val, grad, point->pos, state );
}
}
if ( iter > iterMax ) {
*constrFailP = pullConstraintFailIterMaxed;
} else {
*constrFailP = AIR_FALSE;
}
return 0;
}
#undef PROBE
#undef SAVE
#undef RESTORE
/* ------------------------------- laplacian */
#define PROBE( l ) if ( pullProbe( task, point ) ) { \
biffAddf( PULL, "%s: on iter %u", me, iter ); \
return 1; \
} \
( l ) = pullPointScalar( task->pctx, point, \
pullInfoHeightLaplacian, NULL, NULL );
#define PROBEG( l, g ) \
PROBE( l ); \
pullPointScalar( task->pctx, point, pullInfoHeight, ( g ), NULL );
static int
constraintSatLapl( pullTask *task, pullPoint *point,
double stepMax, double constrEps,
unsigned int iterMax,
/* output */
int *constrFailP ) {
static const char me[]="constraintSatLapl";
double
step, /* current step size */
valLast, val, /* last and current function values */
grad[4], dir[3], len,
posOld[3], posNew[3], tmpv[3];
double a=0, b=1, s, fa, fb, fs, tmp, diff;
int side = 0;
unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */
step = stepMax/2;
PROBEG( val, grad );
if ( 0 == val ) {
/* already exactly at the zero, we're done. This actually happens! */
/* printf( "!%s: a lapl == 0!\n", me ); */
return 0;
}
valLast = val;
NORMALIZE( dir, grad, len );
/* first phase: follow normalized gradient until laplacian sign change */
for ( iter=1; iter<=iterMax; iter++ ) {
double sgn;
ELL_3V_COPY( posOld, point->pos );
sgn = airSgn( val ); /* lapl < 0 => downhill; lapl > 0 => uphill */
ELL_3V_SCALE_INCR( point->pos, sgn*step, dir );
PROBEG( val, grad );
_pullPointHistAdd( point, pullCondConstraintSatA, val );
if ( val*valLast < 0 ) {
/* laplacian has changed sign; stop looking */
break;
}
valLast = val;
NORMALIZE( dir, grad, len );
}
if ( iter > iterMax ) {
*constrFailP = pullConstraintFailIterMaxed;
return 0;
}
/* second phase: find the zero-crossing, looking between
f( posOld )=valLast and f( posNew )=val */
ELL_3V_COPY( posNew, point->pos );
ELL_3V_SUB( tmpv, posNew, posOld );
len = ELL_3V_LEN( tmpv );
fa = valLast;
fb = val;
if ( AIR_ABS( fa ) < AIR_ABS( fb ) ) {
ELL_SWAP2( a, b, tmp ); ELL_SWAP2( fa, fb, tmp );
}
for ( iter=1; iter<=iterMax; iter++ ) {
s = AIR_AFFINE( fa, 0, fb, a, b );
ELL_3V_LERP( point->pos, s, posOld, posNew );
PROBE( fs );
_pullPointHistAdd( point, pullCondConstraintSatB, 0.0 );
if ( 0 == fs ) {
/* exactly nailed the zero, we're done. This actually happens! */
printf( "!%s: b lapl == 0!\n", me );
break;
}
/* "Illinois" false-position. Dumb, but it works. */
if ( fs*fb > 0 ) { /* not between s and b */
235 b = s;
fb = fs;
if ( +1 == side ) {
fa /= 2;
}
side = +1;
} else { /* not between a and s */
a = s;
fa = fs;
if ( -1 == side ) {
fb /= 2;
}
side = -1;
}
diff = ( b - a )*len;
if ( AIR_ABS( diff ) < stepMax*constrEps ) { /* HEY stepMax*constrEps vs constrEps */
/* converged! */
break;
}
}
if ( iter > iterMax ) {
*constrFailP = pullConstraintFailIterMaxed;
} else {
*constrFailP = AIR_FALSE;
}
return 0;
261 }
#undef PROBE
#undef PROBEG
/* ------------------------------------------- height ( line xor surf ) */
static int
probeHeight( pullTask *task, pullPoint *point,
/* output */
double *heightP, double grad[3], double hess[9] ) {
static const char me[]="probeHeight";
if ( pullProbe( task, point ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
*heightP = pullPointScalar( task->pctx, point, pullInfoHeight, grad, hess );
return 0;
}
/*
** creaseProj
**
** column-space of output posproj spans the directions along which
** particle is allowed to move *downward* ( in height ) for constraint
** satisfaction ( according to tangent 1 or tangents 1&2 ); for seeking
** minima where 2nd deriv is positive
**
** negproj is the same, but for points moving upwards ( according to
** negativetangent1 or negativetangent 1&2 ); for seeking
** maxima where 2nd deriv is negative
*/
static void
creaseProj( pullTask *task, pullPoint *point,
int tang1Use, int tang2Use,
int negtang1Use, int negtang2Use,
/* output */
double posproj[9], double negproj[9] ) {
static const char me[]="creaseProj";
double pp[9];
double *tng;
ELL_3M_ZERO_SET( posproj );
if ( tang1Use ) {
tng = point->info + task->pctx->infoIdx[pullInfoTangent1];
if ( DEBUG ) {
fprintf( stderr, "!%s: tng1 = %g %g %g\n", me, tng[0], tng[1], tng[2] );
}
ELL_3MV_OUTER( pp, tng, tng );
ELL_3M_ADD2( posproj, posproj, pp );
}
if ( tang2Use ) {
314 tng = point->info + task->pctx->infoIdx[pullInfoTangent2];
ELL_3MV_OUTER( pp, tng, tng );
ELL_3M_ADD2( posproj, posproj, pp );
}
ELL_3M_ZERO_SET( negproj );
if ( negtang1Use ) {
tng = point->info + task->pctx->infoIdx[pullInfoNegativeTangent1];
ELL_3MV_OUTER( pp, tng, tng );
ELL_3M_ADD2( negproj, negproj, pp );
}
if ( negtang2Use ) {
tng = point->info + task->pctx->infoIdx[pullInfoNegativeTangent2];
ELL_3MV_OUTER( pp, tng, tng );
ELL_3M_ADD2( negproj, negproj, pp );
}
if ( !tang1Use && !tang2Use && !negtang1Use && !negtang2Use ) {
/* we must be after points, and so need freedom to go after them */
/* for now we do this via posproj not negproj; see haveNada below */
ELL_3M_IDENTITY_SET( posproj );
}
return;
}
/* HEY: body of probeHeight could really be expanded in here */
#define PROBE( height, grad, hess, posproj, negproj ) \
if ( probeHeight( task, point, \
&( height ), ( grad ), ( hess ) ) ) { \
biffAddf( PULL, "%s: trouble on iter %u", me, iter ); \
return 1; \
} \
creaseProj( task, point, tang1Use, tang2Use, \
negtang1Use, negtang2Use, posproj, negproj )
#define SAVE( state, height, grad, hess, posproj, negproj, pos ) \
state[0] = height; \
ELL_3V_COPY( state + 1, grad ); \
ELL_3M_COPY( state + 1 + 3, hess ); \
ELL_3M_COPY( state + 1 + 3 + 9, posproj ); \
ELL_3M_COPY( state + 1 + 3 + 9 + 9, negproj ); \
ELL_3V_COPY( state + 1 + 3 + 9 + 9 + 9, pos )
#define RESTORE( height, grad, hess, posproj, negproj, pos, state ) \
height = state[0]; \
ELL_3V_COPY( grad, state + 1 ); \
ELL_3M_COPY( hess, state + 1 + 3 ); \
ELL_3M_COPY( posproj, state + 1 + 3 + 9 ); \
ELL_3M_COPY( negproj, state + 1 + 3 + 9 + 9 ); \
ELL_3V_COPY( pos, state + 1 + 3 + 9 + 9 + 9 )
#define DNORM( d1, d2, pdir, plen, pgrad, grad, hess, posproj ) \
ELL_3MV_MUL( pgrad, posproj, grad ); \
if ( task->pctx->flag.zeroZ ) pgrad[2]=0; \
ELL_3V_NORM( pdir, pgrad, plen ); \
d1 = ELL_3V_DOT( grad, pdir ); \
d2 = ELL_3MV_CONTR( hess, pdir )
#define PRINT( prefix ) \
fprintf( stderr, "-------------- probe results %s ( %u @ %g, %g, %g, %g ):\n", \
prefix, point->idtag, point->pos[0], point->pos[1], \
point->pos[2], point->pos[3] ); \
fprintf( stderr, "-- val = %g\n", val ); \
fprintf( stderr, "-- grad = %g %g %g\n", grad[0], grad[1], grad[2] ); \
fprintf( stderr, "-- hess = %g %g %g; %g %g %g; %g %g %g\n", \
hess[0], hess[1], hess[2], \
hess[3], hess[4], hess[5], \
hess[6], hess[7], hess[8] ); \
fprintf( stderr, "-- posproj = %g %g %g; %g %g %g; %g %g %g\n", \
posproj[0], posproj[1], posproj[2], \
posproj[3], posproj[4], posproj[5], \
posproj[6], posproj[7], posproj[8] ); \
fprintf( stderr, "-- negproj = %g %g %g; %g %g %g; %g %g %g\n", \
negproj[0], negproj[1], negproj[2], \
negproj[3], negproj[4], negproj[5], \
negproj[6], negproj[7], negproj[8] )
static int
constraintSatHght( pullTask *task, pullPoint *point,
int tang1Use, int tang2Use,
int negtang1Use, int negtang2Use,
double stepMax, double constrEps, unsigned int iterMax,
int *constrFailP ) {
static const char me[]="constraintSatHght";
double val, grad[3], hess[9], posproj[9], negproj[9],
state[1+3+9+9+9+3], hack, step,
d1, d2, pdir[3], plen, pgrad[3];
double _tmpv[3]={0, 0, 0};
/* #endif */
int havePos, haveNeg, haveNada, zeroGmagOkay;
unsigned int iter = 0; /* 0: initial probe, 1..iterMax: probes in loop */
/* http://en.wikipedia.org/wiki/Newton%27s_method_in_optimization */
zeroGmagOkay = ( 1 < task->pctx->iter && 0 == task->pctx->constraintDim );
havePos = tang1Use || tang2Use;
haveNeg = negtang1Use || negtang2Use;
haveNada = !havePos && !haveNeg;
if ( DEBUG ) {
double stpmin;
/* HEY: shouldn't stpmin also be used later in this function? */
stpmin = task->pctx->voxelSizeSpace*constrEps;
fprintf( stderr, "!%s( %u ): starting at %g %g %g %g\n", me, point->idtag,
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
fprintf( stderr, "!%s: pt %d %d nt %d %d ( nada %d ) "
"stepMax %g, iterMax %u\n", me,
tang1Use, tang2Use, negtang1Use, negtang2Use, haveNada,
stepMax, iterMax );
fprintf( stderr, "!%s: stpmin = %g = voxsize %g * parm.stepmin %g\n", me,
stpmin, task->pctx->voxelSizeSpace, constrEps );
}
PROBE( val, grad, hess, posproj, negproj );
_pullPointHistAdd( point, pullCondOld, val );
if ( DEBUG ) {
PRINT( "initial probe" );
}
SAVE( state, val, grad, hess, posproj, negproj, point->pos );
hack = 1;
for ( iter=1; iter<=iterMax; iter++ ) {
if ( DEBUG ) {
fprintf( stderr, "!%s: =-============= begin iter %u\n", me, iter );
}
/* HEY: no opportunistic increase of hack? */
if ( havePos || haveNada ) {
DNORM( d1, d2, pdir, plen, pgrad, grad, hess, posproj );
if ( !ELL_3M_FROB( hess ) ) {
*constrFailP = pullConstraintFailHessZeroA;
return 0;
}
if ( !plen ) {
if ( zeroGmagOkay ) {
/* getting to actual zero gradient is possible when looking for
point extrema ( or saddles ), and its not a problem, so as a
lousy hack we set step=0 and skip to the convergence test */
step = 0;
goto convtestA;
}
/* this use to be a biff error, which got to be annoying */
*constrFailP = pullConstraintFailProjGradZeroA;
return 0;
} else if ( !AIR_EXISTS( plen ) ) {
/* this use to be a biff error, which also got to be annoying */
*constrFailP = pullConstraintFailProjLenNonExist;
return 0;
}
step = ( d2 > 0 ? -d1/d2 : -plen );
if ( DEBUG ) {
fprintf( stderr, "!%s: ( + ) iter %u step = ( %g > 0 ? %g=-%g/%g : %g ) --> %g\n",
me, iter, d2, -d1/d2, d1, d2, -plen, step );
}
step = step > 0 ? AIR_MIN( stepMax, step ) : AIR_MAX( -stepMax, step );
convtestA:
if ( d2 > 0 && AIR_ABS( step ) < constrEps ) { /* HEY stepMax*constrEps vs constrEps */
/* we're converged because its concave up here
and we're close enough to the bottom */
if ( DEBUG ) {
fprintf( stderr, " |step| %g < %g*%g = %g ==> converged!\n",
AIR_ABS( step ),
stepMax, constrEps,
stepMax*constrEps );
}
if ( !haveNeg ) {
break;
} else {
goto nextstep;
}
}
/* else we have to take a significant step */
if ( DEBUG ) {
fprintf( stderr, " -> step %g, |pdir| = %g\n",
step, ELL_3V_LEN( pdir ) );
ELL_3V_COPY( _tmpv, point->pos );
fprintf( stderr, " -> pos ( %g, %g, %g, %g ) += "
"%g * %g * ( %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
hack, step, pdir[0], pdir[1], pdir[2] );
}
ELL_3V_SCALE_INCR( point->pos, hack*step, pdir );
if ( !ELL_4V_EXISTS( point->pos ) ) {
biffAddf( PULL, "%s: pos proj iter %u: pnt %u bad pos ( %g, %g, %g, %g ); "
"hack %g, step %g",
me, iter, point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3], hack, step );
return 1;
}
if ( DEBUG ) {
ELL_3V_SUB( _tmpv, _tmpv, point->pos );
fprintf( stderr, " -> moved to %g %g %g %g\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
fprintf( stderr, " ( moved %g )\n", ELL_3V_LEN( _tmpv ) );
}
PROBE( val, grad, hess, posproj, negproj );
_pullPointHistAdd( point, pullCondConstraintSatA, val );
if ( DEBUG ) {
fprintf( stderr, " ( + ) probed at ( %g, %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
PRINT( "after move" );
fprintf( stderr, " val( %g, %g, %g, %g )=%g %s state[0]=%g\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
val, val <= state[0] ? "<=" : ">", state[0] );
}
if ( val <= state[0] ) {
/* we made progress */
if ( DEBUG ) {
fprintf( stderr, " ( + ) progress!\n" );
}
SAVE( state, val, grad, hess, posproj, negproj, point->pos );
hack = 1;
} else {
/* oops, we went uphill instead of down; try again */
if ( DEBUG ) {
fprintf( stderr, " ( + ) val *increased* ( up from %.17g by %.17g ); "
"backing hack from %g to %g\n",
state[0], val - state[0],
hack, hack*task->pctx->sysParm.backStepScale );
}
hack *= task->pctx->sysParm.backStepScale;
RESTORE( val, grad, hess, posproj, negproj, point->pos, state );
if ( DEBUG ) {
fprintf( stderr, " restored to pos ( %g, %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
}
}
}
nextstep:
if ( haveNeg ) {
/* HEY: copy and paste from above, minus fluff, and with A->B */
DNORM( d1, d2, pdir, plen, pgrad, grad, hess, negproj );
if ( !ELL_3M_FROB( hess ) ) {
*constrFailP = pullConstraintFailHessZeroB;
return 0;
}
if ( !plen ) {
if ( zeroGmagOkay ) {
step = 0;
goto convtestB;
}
*constrFailP = pullConstraintFailProjGradZeroB;
return 0;
}
step = ( d2 < 0 ? -d1/d2 : plen );
if ( DEBUG ) {
fprintf( stderr, "!%s: -+ ) iter %u step = ( %g < 0 ? %g : %g ) --> %g\n",
me, iter, d2, -d1/d2, plen, step );
}
step = step > 0 ? AIR_MIN( stepMax, step ) : AIR_MAX( -stepMax, step );
convtestB:
if ( d2 < 0 && AIR_ABS( step ) < constrEps ) { /* HEY stepMax*constrEps vs constrEps */
/* we're converged because its concave down here
and we're close enough to the top */
if ( DEBUG ) {
fprintf( stderr, " |step| %g < %g*%g = %g ==> converged!\n",
AIR_ABS( step ),
stepMax, constrEps,
stepMax*constrEps );
}
/* no further iteration needed; we're converged */
break;
}
/* else we have to take a significant step */
if ( DEBUG ) {
fprintf( stderr, " -> step %g, |pdir| = %g\n",
step, ELL_3V_LEN( pdir ) );
ELL_3V_COPY( _tmpv, point->pos );
574 fprintf( stderr, " -> pos ( %g, %g, %g, %g ) += "
"%g * %g * ( %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
hack, step, pdir[0], pdir[1], pdir[2] );
}
ELL_3V_SCALE_INCR( point->pos, hack*step, pdir );
if ( !ELL_4V_EXISTS( point->pos ) ) {
biffAddf( PULL, "%s: neg proj iter %u: pnt %u bad pos ( %g, %g, %g, %g ); "
"hack %g, step %g",
me, iter, point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3], hack, step );
return 1;
}
if ( DEBUG ) {
ELL_3V_SUB( _tmpv, _tmpv, point->pos );
fprintf( stderr, " -> moved to %g %g %g %g\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
fprintf( stderr, " ( moved %g )\n", ELL_3V_LEN( _tmpv ) );
}
593 PROBE( val, grad, hess, posproj, negproj );
_pullPointHistAdd( point, pullCondConstraintSatB, val );
if ( DEBUG ) {
fprintf( stderr, " ( - ) probed at ( %g, %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
PRINT( "after move" );
fprintf( stderr, " val( %g, %g, %g, %g )=%g %s state[0]=%g\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
val, val >= state[0] ? ">=" : "<", state[0] );
}
if ( val >= state[0] ) {
/* we made progress */
if ( DEBUG ) {
fprintf( stderr, " ( - ) progress!\n" );
}
SAVE( state, val, grad, hess, posproj, negproj, point->pos );
hack = 1;
} else {
/* oops, we went downhill instead of up; try again */
if ( DEBUG ) {
fprintf( stderr, " ( - ) val *decreased* ( down from %.17g by %.17g ); "
"backing hack from %g to %g\n",
state[0], state[0] - val,
hack, hack*task->pctx->sysParm.backStepScale );
}
hack *= task->pctx->sysParm.backStepScale;
RESTORE( val, grad, hess, posproj, negproj, point->pos, state );
if ( DEBUG ) {
fprintf( stderr, " restored to pos ( %g, %g, %g, %g )\n",
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
}
}
}
}
if ( iter > iterMax ) {
*constrFailP = pullConstraintFailIterMaxed;
} else {
*constrFailP = AIR_FALSE;
}
_pullPointHistAdd( point, ( *constrFailP
? pullCondConstraintFail
: pullCondConstraintSuccess ), AIR_NAN );
if ( DEBUG ) {
printf( "!%s: Finished %s: %s ( %d )\n", me,
*constrFailP ? "with failure" : "OK",
*constrFailP ? airEnumStr( pullConstraintFail, *constrFailP ) : "",
*constrFailP );
}
return 0;
}
#undef PROBE
#undef DNORM
#undef SAVE
#undef RESTORE
double
_pullSigma( const pullContext *pctx, const double pos[4] ) {
double ret=0;
if ( pos && pos[3] ) {
ret = ( pctx->flag.scaleIsTau
? gageSigOfTau( pos[3] )
: pos[3] );
}
return ret;
}
/* ------------------------------------------- */
/* have to make sure that scale position point->pos[3]
** is not modified anywhere in here: constraints are ONLY spatial
**
** This uses biff, but only for showstopper problems
*/
int
_pullConstraintSatisfy( pullTask *task, pullPoint *point,
double travelMax,
/* output */
int *constrFailP ) {
static const char me[]="_pullConstraintSatisfy";
double stepMax, constrEps;
unsigned int iterMax;
double pos3Orig[3], pos3Diff[3], travel;
ELL_3V_COPY( pos3Orig, point->pos );
/* HEY the "10*" is based on isolated experiments; what's the principle? */
stepMax = 10*task->pctx->voxelSizeSpace;
iterMax = task->pctx->iterParm.constraintMax;
constrEps = task->pctx->voxelSizeSpace*task->pctx->sysParm.constraintStepMin;
/* * ( 1 + 0.2*_pullSigma( task->pctx, point->pos ) ); */
if ( DEBUG ) {
fprintf( stderr, "!%s( %d ): hi ==== %g %g %g, stepMax = %g, iterMax = %u\n",
me, point->idtag, point->pos[0], point->pos[1], point->pos[2],
stepMax, iterMax );
}
task->pctx->count[pullCountConstraintSatisfy] += 1;
switch ( task->pctx->constraint ) {
case pullInfoHeightLaplacian: /* zero-crossing edges */
if ( constraintSatLapl( task, point, stepMax/4, constrEps,
4*iterMax, constrFailP ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
break;
case pullInfoIsovalue:
if ( constraintSatIso( task, point, stepMax, constrEps,
iterMax, constrFailP ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
break;
704 case pullInfoHeight:
if ( constraintSatHght( task, point,
!!task->pctx->ispec[pullInfoTangent1],
!!task->pctx->ispec[pullInfoTangent2],
!!task->pctx->ispec[pullInfoNegativeTangent1],
!!task->pctx->ispec[pullInfoNegativeTangent2],
stepMax, constrEps, iterMax, constrFailP ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
break;
default:
fprintf( stderr, "%s: constraint on %s ( %d ) unimplemented!!\n", me,
airEnumStr( pullInfo, task->pctx->constraint ),
task->pctx->constraint );
}
ELL_3V_SUB( pos3Diff, pos3Orig, point->pos );
if ( travelMax ) {
travel = ELL_3V_LEN( pos3Diff )/task->pctx->voxelSizeSpace;
if ( travel > travelMax ) {
*constrFailP = pullConstraintFailTravel;
if ( DEBUG ) {
fprintf( stderr, "!%s: travel %g > travelMax %g\n", me,
travel, travelMax );
}
}
}
if ( DEBUG ) {
fprintf( stderr, "!%s( %u ) %s @ ( %g, %g, %g ) = ( %g, %g, %g ) + ( %g, %g, %g )\n", me,
point->idtag,
( *constrFailP
? airEnumStr( pullConstraintFail, *constrFailP )
: "#GOOD#" ),
point->pos[0], point->pos[1], point->pos[2],
pos3Diff[0], pos3Diff[1], pos3Diff[2],
pos3Orig[0], pos3Orig[1], pos3Orig[2] );
#if PULL_PHIST
if ( 1 ) {
Nrrd *nhist;
FILE *fhist;
char fname[AIR_STRLEN_LARGE];
nhist = nrrdNew( );
sprintf( fname, "%04u-%04u-phist.nrrd", task->pctx->iter, point->idtag );
if ( pullPositionHistoryNrrdGet( nhist, task->pctx, point ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
if ( ( fhist = fopen( fname, "w" ) ) ) {
752 if ( nrrdSave( fname, nhist, NULL ) ) {
biffMovef( PULL, NRRD, "%s: trouble", me );
return 1;
}
fclose( fhist );
}
nrrdNuke( nhist );
}
#endif
}
return 0;
}
#undef NORMALIZE
/*
** _pullConstraintTangent
**
** eigenvectors ( with non-zero eigenvalues ) of output proj are
** ( hopefully ) approximate tangents to the manifold to which particles
** are constrained. It is *not* the local tangent of the directions
** along which particles are allowed to move during constraint
** satisfaction ( that is given by creaseProj for creases )
**
** this can assume that probe( ) has just been called
*/
void
_pullConstraintTangent( pullTask *task, pullPoint *point,
/* output */
double proj[9] ) {
double vec[4], nvec[3], outer[9], len, posproj[9], negproj[9];
ELL_3M_IDENTITY_SET( proj ); /* NOTE: we are starting with identity . . . */
switch ( task->pctx->constraint ) {
case pullInfoHeight:
creaseProj( task, point,
!!task->pctx->ispec[pullInfoTangent1],
!!task->pctx->ispec[pullInfoTangent2],
!!task->pctx->ispec[pullInfoNegativeTangent1],
!!task->pctx->ispec[pullInfoNegativeTangent2],
posproj, negproj );
/* .. and subracting out output from creaseProj */
ELL_3M_SUB( proj, proj, posproj );
ELL_3M_SUB( proj, proj, negproj );
break;
case pullInfoHeightLaplacian:
case pullInfoIsovalue:
if ( pullInfoHeightLaplacian == task->pctx->constraint ) {
/* using gradient of height as approx normal to laplacian 0-crossing */
pullPointScalar( task->pctx, point, pullInfoHeight, vec, NULL );
} else {
pullPointScalar( task->pctx, point, pullInfoIsovalue, vec, NULL );
}
ELL_3V_NORM( nvec, vec, len );
if ( len ) {
/* .. or and subracting out tensor product of normal with itself */
ELL_3MV_OUTER( outer, nvec, nvec );
ELL_3M_SUB( proj, proj, outer );
}
break;
}
return;
}
/*
** returns the *dimension* ( not codimension ) of the constraint manifold:
** 0 for points
** 1 for lines
** 2 for surfaces
** This is nontrivial because of the different ways that constraints
** can be expressed, combined with the possibility of pctx->flag.zeroZ
**
** a -1 return value represents a biff-able error
*/
int
_pullConstraintDim( const pullContext *pctx ) {
static const char me[]="_pullConstraintDim";
int ret, t1, t2, nt1, nt2;
switch ( pctx->constraint ) {
case pullInfoHeightLaplacian: /* zero-crossing edges */
ret = ( pctx->flag.zeroZ ? 1 : 2 );
break;
case pullInfoIsovalue:
ret = ( pctx->flag.zeroZ ? 1 : 2 );
break;
case pullInfoHeight:
t1 = !!pctx->ispec[pullInfoTangent1];
t2 = !!pctx->ispec[pullInfoTangent2];
nt1 = !!pctx->ispec[pullInfoNegativeTangent1];
nt2 = !!pctx->ispec[pullInfoNegativeTangent2];
switch ( t1 + t2 + nt1 + nt2 ) {
case 0:
ret = 0;
break;
case 3:
if ( pctx->flag.zeroZ ) {
biffAddf( PULL, "%s: can't have three of ( %s, %s, %s, %s ) tangents with "
"2-D data ( pctx->flag.zeroZ )", me,
airEnumStr( pullInfo, pullInfoTangent1 ),
airEnumStr( pullInfo, pullInfoTangent2 ),
airEnumStr( pullInfo, pullInfoNegativeTangent1 ),
airEnumStr( pullInfo, pullInfoNegativeTangent2 ) );
return -1;
} /* else we're in 3D; 3 constraints -> point features */
ret = 0;
break;
case 1:
ret = ( pctx->flag.zeroZ ? 1 : 2 );
break;
case 2:
ret = ( pctx->flag.zeroZ ? 0 : 1 );
break;
default:
biffAddf( PULL, "%s: can't simultaneously use all tangents "
"( %s, %s, %s, %s ) as this implies co-dimension of -1", me,
airEnumStr( pullInfo, pullInfoTangent1 ),
airEnumStr( pullInfo, pullInfoTangent2 ),
airEnumStr( pullInfo, pullInfoNegativeTangent1 ),
airEnumStr( pullInfo, pullInfoNegativeTangent2 ) );
return -1;
}
break;
default:
biffAddf( PULL, "%s: constraint on %s ( %d ) unimplemented", me,
airEnumStr( pullInfo, pctx->constraint ), pctx->constraint );
return -1;
}
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
pullContext *
29 pullContextNew( void ) {
pullContext *pctx;
unsigned int ii;
pctx = ( pullContext * )calloc( 1, sizeof( pullContext ) );
if ( !pctx ) {
return NULL;
}
_pullInitParmInit( &( pctx->initParm ) );
_pullIterParmInit( &( pctx->iterParm ) );
_pullSysParmInit( &( pctx->sysParm ) );
_pullFlagInit( &( pctx->flag ) );
pctx->verbose = 0;
pctx->threadNum = 1;
pctx->rngSeed = 42;
pctx->progressBinMod = 50;
pctx->iter_cb = NULL;
pctx->data_cb = NULL;
for ( ii=0; ii<PULL_VOLUME_MAXNUM; ii++ ) {
pctx->vol[ii] = NULL;
}
pctx->volNum = 0;
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
pctx->ispec[ii] = NULL;
pctx->infoIdx[ii] = UINT_MAX;
}
pctx->interType = pullInterTypeUnknown;
pctx->energySpecR = pullEnergySpecNew( );
pctx->energySpecS = pullEnergySpecNew( );
pctx->energySpecWin = pullEnergySpecNew( );
pctx->haltonOffset = 0;
ELL_4V_SET( pctx->bboxMin, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_4V_SET( pctx->bboxMax, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
pctx->infoTotalLen = 0; /* will be set later */
pctx->idtagNext = 0;
pctx->haveScale = AIR_FALSE;
pctx->constraint = 0;
pctx->constraintDim = -1;
pctx->targetDim = -1;
pctx->finished = AIR_FALSE;
pctx->maxDistSpace = AIR_NAN;
pctx->maxDistScale = AIR_NAN;
pctx->voxelSizeSpace = AIR_NAN;
pctx->voxelSizeScale = AIR_NAN;
pctx->eipScale = 1.0;
pctx->bin = NULL;
ELL_4V_SET( pctx->binsEdge, 0, 0, 0, 0 );
pctx->binNum = 0;
pctx->binNextIdx = 0;
pctx->tmpPointPerm = NULL;
pctx->tmpPointPtr = NULL;
pctx->tmpPointNum = 0;
/* pctx->binMutex setup my pullStart */
pctx->task = NULL;
pctx->iterBarrierA = NULL;
pctx->iterBarrierB = NULL;
#if PULL_HINTER
pctx->nhinter = nrrdNew( );
#endif
pctx->logAdd = NULL;
pctx->timeIteration = 0;
pctx->timeRun = 0;
pctx->energy = AIR_NAN;
pctx->addNum = 0;
pctx->nixNum = 0;
pctx->stuckNum = 0;
pctx->pointNum = 0;
pctx->iter = 0;
for ( ii=pullCountUnknown; ii<pullCountLast; ii++ ) {
pctx->count[ii] = 0;
}
return pctx;
}
/*
** this should only nix things created by pullContextNew, or the things
** ( vols and ispecs ) that were explicitly added to this context
*/
pullContext *
115 pullContextNix( pullContext *pctx ) {
unsigned int ii;
if ( pctx ) {
for ( ii=0; ii<pctx->volNum; ii++ ) {
pctx->vol[ii] = pullVolumeNix( pctx->vol[ii] );
}
pctx->volNum = 0;
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
if ( pctx->ispec[ii] ) {
pctx->ispec[ii] = pullInfoSpecNix( pctx->ispec[ii] );
}
}
pctx->energySpecR = pullEnergySpecNix( pctx->energySpecR );
pctx->energySpecS = pullEnergySpecNix( pctx->energySpecS );
pctx->energySpecWin = pullEnergySpecNix( pctx->energySpecWin );
#if PULL_HINTER
nrrdNuke( pctx->nhinter );
#endif
/* handled elsewhere: bin, task, iterBarrierA, iterBarrierB */
airFree( pctx );
}
return NULL;
}
int
141 _pullMiscParmCheck( pullContext *pctx ) {
static const char me[]="_pullMiscParmCheck";
double denr;
if ( !( AIR_IN_CL( 1, pctx->threadNum, PULL_THREAD_MAXNUM ) ) ) {
biffAddf( PULL, "%s: pctx->threadNum ( %d ) outside valid range [1, %d]", me,
pctx->threadNum, PULL_THREAD_MAXNUM );
return 1;
}
if ( airEnumValCheck( pullInterType, pctx->interType ) ) {
biffAddf( PULL, "%s: pctx->interType %d not a valid %s", me,
pctx->interType, pullInterType->name );
return 1;
}
/* HEY: error checking on energySpec's seems rather spotty . . . */
if ( pullEnergyUnknown == pctx->energySpecR->energy ) {
biffAddf( PULL, "%s: need to set space energy", me );
return 1;
}
if ( pullInterTypeJustR == pctx->interType
|| pullInterTypeUnivariate == pctx->interType ) {
if ( pullEnergyZero != pctx->energySpecS->energy ) {
biffAddf( PULL, "%s: can't use scale energy %s with inter type %s", me,
pctx->energySpecS->energy->name,
airEnumStr( pullInterType, pctx->interType ) );
return 1;
}
} else {
if ( pullEnergyZero == pctx->energySpecS->energy ) {
biffAddf( PULL, "%s: need a non-zero scale energy for inter type %s", me,
airEnumStr( pullInterType, pctx->interType ) );
return 1;
}
}
/* make sure that spatial repulsion is really repulsive at r=0 */
pctx->energySpecR->energy->eval( &denr, 0.0000001, pctx->energySpecR->parm );
if ( !( denr < 0 ) ) {
biffAddf( PULL, "%s: spatial energy doesn't have negative slope near r=0",
me );
return 1;
}
return 0;
}
int
187 _pullContextCheck( pullContext *pctx ) {
static const char me[]="_pullContextCheck";
unsigned int ii, ccount;
int gotIspec, gotConstr;
const pullInfoSpec *lthr, *strn;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( _pullInitParmCheck( &( pctx->initParm ) )
|| _pullIterParmCheck( &( pctx->iterParm ) )
|| _pullSysParmCheck( &( pctx->sysParm ) )
|| _pullMiscParmCheck( pctx ) ) {
biffAddf( PULL, "%s: problem with parameters", me );
return 1;
}
if ( !pctx->volNum ) {
biffAddf( PULL, "%s: have no volumes set", me );
return 1;
}
gotConstr = 0;
gotIspec = AIR_FALSE;
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
if ( pctx->ispec[ii] ) {
if ( pctx->ispec[ii]->constraint ) {
if ( 1 != pullInfoLen( ii ) ) {
biffAddf( PULL, "%s: can't use non-scalar ( len %u ) %s as constraint",
me, pullInfoLen( ii ), airEnumStr( pullInfo, ii ) );
return 1;
}
if ( pullSourceGage != pctx->ispec[ii]->source ) {
biffAddf( PULL, "%s: sorry, constraints can currently only "
"come from %s", me,
airEnumStr( pullSource, pullSourceGage ) );
return 1;
}
if ( gotConstr ) {
biffAddf( PULL, "%s: can't also have %s constraint, already have "
"constraint on %s ", me, airEnumStr( pullInfo, ii ),
airEnumStr( pullInfo, gotConstr ) );
return 1;
}
/* elso no problems having constraint on ii */
gotConstr = ii;
}
/* make sure we have extra info as necessary */
switch ( ii ) {
case pullInfoInside:
case pullInfoHeight:
case pullInfoHeightLaplacian:
case pullInfoSeedThresh:
case pullInfoLiveThresh:
case pullInfoLiveThresh2:
case pullInfoLiveThresh3:
case pullInfoIsovalue:
case pullInfoStrength:
if ( !( AIR_EXISTS( pctx->ispec[ii]->scale )
&& AIR_EXISTS( pctx->ispec[ii]->zero ) ) ) {
biffAddf( PULL, "%s: %s info needs scale ( %g ) and zero ( %g )", me,
airEnumStr( pullInfo, ii ),
pctx->ispec[ii]->scale, pctx->ispec[ii]->zero );
return 1;
}
break;
}
gotIspec = AIR_TRUE;
}
}
if ( !gotIspec ) {
biffAddf( PULL, "%s: have no infos set", me );
return 1;
}
if ( pctx->ispec[pullInfoStrength] ) {
if ( pullSourceGage != pctx->ispec[pullInfoStrength]->source ) {
biffAddf( PULL, "%s: %s info must come from %s ( not %s )", me,
airEnumStr( pullInfo, pullInfoStrength ),
airEnumStr( pullSource, pullSourceGage ),
airEnumStr( pullSource, pctx->ispec[pullInfoStrength]->source ) );
return 1;
}
}
if ( pctx->ispec[pullInfoInside] ) {
if ( !pctx->ispec[pullInfoInsideGradient] ) {
biffAddf( PULL, "%s: want %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoInside ),
airEnumStr( pullInfo, pullInfoInsideGradient ) );
return 1;
}
}
if ( pctx->ispec[pullInfoTangent2] ) {
if ( !pctx->ispec[pullInfoTangent1] ) {
biffAddf( PULL, "%s: want %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoTangent2 ),
airEnumStr( pullInfo, pullInfoTangent1 ) );
return 1;
}
}
if ( pctx->ispec[pullInfoNegativeTangent2] ) {
if ( !pctx->ispec[pullInfoNegativeTangent1] ) {
biffAddf( PULL, "%s: want %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoNegativeTangent2 ),
airEnumStr( pullInfo, pullInfoNegativeTangent1 ) );
return 1;
}
}
ccount = 0;
ccount += !!( pctx->ispec[pullInfoTangent1] );
ccount += !!( pctx->ispec[pullInfoTangent2] );
ccount += !!( pctx->ispec[pullInfoNegativeTangent1] );
ccount += !!( pctx->ispec[pullInfoNegativeTangent2] );
if ( 4 == ccount ) {
biffAddf( PULL, "%s: can't specify all 4 tangents together", me );
return 1;
}
if ( 3 == ccount && !pctx->flag.allowCodimension3Constraints ) {
biffAddf( PULL, "%s: must turn on allowCodimension3Constraints "
"with 3 tangents", me );
return 1;
}
if ( pctx->ispec[pullInfoHeight] ) {
if ( !( pctx->ispec[pullInfoHeightGradient] ) ) {
biffAddf( PULL, "%s: want %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoHeight ),
airEnumStr( pullInfo, pullInfoHeightGradient ) );
return 1;
}
if ( pctx->ispec[pullInfoHeight]->constraint ) {
if ( !pctx->ispec[pullInfoHeightHessian] ) {
biffAddf( PULL, "%s: want constrained %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoHeight ),
airEnumStr( pullInfo, pullInfoHeightHessian ) );
return 1;
}
if ( !( pctx->ispec[pullInfoTangent1]
|| pctx->ispec[pullInfoNegativeTangent1] ) ) {
if ( !pctx->flag.allowCodimension3Constraints ) {
biffAddf( PULL, "%s: want constrained %s but need ( at least ) "
"%s or %s set ( maybe enable "
"pullFlagAllowCodimension3Constraints? )",
me,
airEnumStr( pullInfo, pullInfoHeight ),
airEnumStr( pullInfo, pullInfoTangent1 ),
airEnumStr( pullInfo, pullInfoNegativeTangent1 ) );
return 1;
}
}
}
}
if ( pctx->ispec[pullInfoHeightLaplacian] ) {
if ( !( pctx->ispec[pullInfoHeight] ) ) {
biffAddf( PULL, "%s: want %s but don't have %s set", me,
airEnumStr( pullInfo, pullInfoHeightLaplacian ),
airEnumStr( pullInfo, pullInfoHeight ) );
return 1;
}
}
if ( pctx->ispec[pullInfoIsovalue] ) {
if ( !( pctx->ispec[pullInfoIsovalueGradient]
&& pctx->ispec[pullInfoIsovalueHessian] ) ) {
biffAddf( PULL, "%s: want %s but don't have %s and %s set", me,
airEnumStr( pullInfo, pullInfoIsovalue ),
airEnumStr( pullInfo, pullInfoIsovalueGradient ),
airEnumStr( pullInfo, pullInfoIsovalueHessian ) );
return 1;
}
}
if ( ( lthr = pctx->ispec[pullInfoLiveThresh] )
&& ( strn = pctx->ispec[pullInfoStrength] )
&& lthr->volIdx == strn->volIdx
&& lthr->item == strn->item
&& lthr->scale*strn->scale < 0 ) {
biffAddf( PULL, "%s: %s and %s refer to same item ( %s in %s ), but have "
"scaling factors with different signs ( %g and %g ); really?", me,
airEnumStr( pullInfo, pullInfoLiveThresh ),
airEnumStr( pullInfo, pullInfoStrength ),
airEnumStr( pctx->vol[lthr->volIdx]->kind->enm, lthr->item ),
lthr->volName, lthr->scale, strn->scale );
return 1;
}
if ( pullInitMethodPointPerVoxel == pctx->initParm.method ) {
if ( !( pctx->ispec[pullInfoSeedThresh] ) ) {
biffAddf( PULL, "%s: sorry, need to have %s info set with %s init",
me, airEnumStr( pullInfo, pullInfoSeedThresh ),
"point-per-voxel" /* HEY no airEnum for this */ );
return 1;
}
}
return 0;
}
/*
** the API for this is most certainly going to change; the
** tensor output at this point is a hack created for vis purposes
*/
int
386 pullOutputGetFilter( Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut,
const double _scaleVec[3], double scaleRad,
pullContext *pctx,
unsigned int idtagMin, unsigned int idtagMax ) {
static const char me[]="pullOutputGetFilter";
unsigned int binIdx, pointNum, pointIdx, outIdx;
int E;
double *posOut, *tenOut, *strnOut, scaleVec[3], scaleDir[3], scaleMag;
pullBin *bin;
pullPoint *point;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( nStrengthOut && !pctx->ispec[pullInfoStrength] ) {
biffAddf( PULL, "%s: can't save out %s info that hasn't been set",
me, airEnumStr( pullInfo, pullInfoStrength ) );
return 1;
}
if ( !AIR_EXISTS( scaleRad ) ) {
biffAddf( PULL, "%s: got non-existent scaleRad %g", me, scaleRad );
return 1;
}
if ( !_scaleVec ) {
ELL_3V_SET( scaleVec, 0, 0, 0 );
ELL_3V_SET( scaleDir, 0, 0, 0 );
scaleMag = 0;
} else {
ELL_3V_COPY( scaleVec, _scaleVec );
if ( ELL_3V_LEN( scaleVec ) ) {
ELL_3V_NORM( scaleDir, scaleVec, scaleMag );
} else {
ELL_3V_SET( scaleDir, 0, 0, 0 );
scaleMag = 0;
}
}
pointNum = pullPointNumberFilter( pctx, idtagMin, idtagMax );
E = AIR_FALSE;
if ( nPosOut ) {
E |= nrrdMaybeAlloc_va( nPosOut, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, pointNum ) );
}
if ( nTenOut ) {
E |= nrrdMaybeAlloc_va( nTenOut, nrrdTypeDouble, 2,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, pointNum ) );
}
if ( nStrengthOut ) {
E |= nrrdMaybeAlloc_va( nStrengthOut, nrrdTypeDouble, 1,
AIR_CAST( size_t, pointNum ) );
}
if ( E ) {
biffMovef( PULL, NRRD, "%s: trouble allocating outputs", me );
return 1;
}
posOut = nPosOut ? ( double* )( nPosOut->data ) : NULL;
tenOut = nTenOut ? ( double* )( nTenOut->data ) : NULL;
strnOut = nStrengthOut ? ( double* )( nStrengthOut->data ) : NULL;
outIdx = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
if ( !( idtagMin <= point->idtag
&& ( 0 == idtagMax
|| point->idtag <= idtagMax ) ) ) {
continue;
}
/** to find idtag of point at particular location **/
/*
if ( AIR_ABS( 514.113 - point->pos[0] ) < 0.5 &&
AIR_ABS( 453.519 - point->pos[1] ) < 0.5 &&
AIR_ABS( 606.723 - point->pos[ 2 ] ) < 0.5 ) {
printf( "!%s: point %u at ( %g, %g, %g, %g ) ##############\n",
me, point->idtag,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
}
*/
if ( nPosOut ) {
ELL_4V_COPY( posOut + 4*outIdx, point->pos );
if ( pctx->haveScale && scaleMag ) {
double *tpos, tvec[3], sc;
tpos = posOut + 4*outIdx;
sc = ELL_3V_DOT( tpos, scaleDir );
ELL_3V_SCALE( tvec, sc, scaleDir );
ELL_3V_SUB( tpos, tpos, tvec );
ELL_3V_SCALE( tvec, scaleMag*tpos[3], scaleDir );
ELL_3V_ADD2( tpos, tpos, tvec );
}
/*
if ( 4523 == point->idtag ) {
fprintf( stderr, "!%s: point %u at index %u and pos %g %g %g %g\n",
me, point->idtag, outIdx,
( posOut + 4*outIdx )[0], ( posOut + 4*outIdx )[1],
( posOut + 4*outIdx )[2], ( posOut + 4*outIdx )[3] );
}
*/
}
if ( nStrengthOut ) {
strnOut[outIdx] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
}
if ( nTenOut ) {
double scl, tout[7];
scl = 1;
if ( pctx->ispec[pullInfoTensor] ) {
TEN_T_COPY( tout, point->info + pctx->infoIdx[pullInfoTensor] );
} else if ( pctx->ispec[pullInfoHeightHessian] ) {
double *hess, eval[3], evec[9], eceil, maxeval, elen;
unsigned int maxi;
hess = point->info + pctx->infoIdx[pullInfoHeightHessian];
if ( 0 ) {
/* do this if using general symmetric tensor glyphs */
TEN_M2T( tout, hess );
tout[0] = 1.0;
} if ( 0 ) {
/* for spheres and only spheres */
TEN_T_SET( tout, 1, 1, 0, 0, 1, 0, 1 );
} else {
ell_3m_eigensolve_d( eval, evec, hess, 10 );
eval[0] = AIR_ABS( eval[0] );
eval[1] = AIR_ABS( eval[1] );
eval[2] = AIR_ABS( eval[2] );
/* elen = ELL_3V_LEN( eval ); */
elen = ( eval[0]+eval[1]+eval[2] );
eceil = elen ? 10/elen : 10;
eval[0] = eval[0] ? AIR_MIN( eceil, 1.0/eval[0] ) : eceil;
eval[1] = eval[1] ? AIR_MIN( eceil, 1.0/eval[1] ) : eceil;
eval[2] = eval[2] ? AIR_MIN( eceil, 1.0/eval[2] ) : eceil;
maxi = ELL_MAX3_IDX( eval[0], eval[1], eval[2] );
maxeval = eval[maxi];
ELL_3V_SCALE( eval, 1/maxeval, eval );
tenMakeSingle_d( tout, 1, eval, evec );
if ( scaleRad && pctx->ispec[pullInfoHeight]->constraint ) {
double emin, sig;
if ( pctx->flag.scaleIsTau ) {
sig = gageSigOfTau( point->pos[3] );
} else {
sig = point->pos[3];
}
tenEigensolve_d( eval, evec, tout ); /* lazy way to sort */
emin = eval[2];
if ( 1 == pctx->constraintDim ) {
eval[1] = scaleRad*sig + emin/2;
eval[2] = scaleRad*sig + emin/2;
} if ( 2 == pctx->constraintDim ) {
double eavg;
eavg = ( 2*eval[0] + eval[2] )/3;
eval[0] = eavg;
eval[1] = eavg;
eval[2] = scaleRad*sig + emin/2;
}
tenMakeSingle_d( tout, 1, eval, evec );
}
}
} else if ( 0 /* another hack for general symmetric tensor glyphs */
&& pctx->constraint
&& ( pctx->ispec[pullInfoIsovalueHessian] ) ) {
double *hess;
hess = point->info + pctx->infoIdx[pullInfoIsovalueHessian];
TEN_M2T( tout, hess );
tout[0] = 1.0;
} else if ( pctx->constraint
&& ( pctx->ispec[pullInfoHeightGradient]
|| pctx->ispec[pullInfoIsovalueGradient] ) ) {
double *grad, norm[3], len, mat[9], out[9];
grad = point->info + ( pctx->ispec[pullInfoHeightGradient]
? pctx->infoIdx[pullInfoHeightGradient]
: pctx->infoIdx[pullInfoIsovalueGradient] );
ELL_3V_NORM( norm, grad, len );
ELL_3MV_OUTER( out, norm, norm );
ELL_3M_IDENTITY_SET( mat );
ELL_3M_SCALE_INCR( mat, -0.95, out );
TEN_M2T( tout, mat );
tout[0] = 1;
} else {
TEN_T_SET( tout, 1, 1, 0, 0, 1, 0, 1 );
}
TEN_T_SCALE( tout, scl, tout );
TEN_T_COPY( tenOut + 7*outIdx, tout );
/*
if ( 4523 == point->idtag ) {
fprintf( stderr, "!%s: point %u at index %u and ten ( %g ) %g %g %g %g %g %g\n",
me, point->idtag, outIdx,
tout[0], tout[1], tout[2], tout[3],
tout[4], tout[5], tout[6] );
}
*/
} /* if ( nTenOut ) */
++outIdx;
}
}
return 0;
}
int
587 pullOutputGet( Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nStrengthOut,
const double scaleVec[3], double scaleRad,
pullContext *pctx ) {
static const char me[]="pullOutputGet";
if ( pullOutputGetFilter( nPosOut, nTenOut, nStrengthOut, scaleVec, scaleRad, pctx, 0, 0 ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
return 0;
}
int
601 pullPropGet( Nrrd *nprop, int prop, pullContext *pctx ) {
static const char me[]="pullPropGet";
int typeOut;
size_t size[2];
unsigned int dim, pointNum, pointIdx, binIdx, *out_ui, outIdx;
double *out_d, covar[16];
float *out_f, *pnc;
unsigned char *out_uc;
pullBin *bin;
pullPoint *point;
pointNum = pullPointNumber( pctx );
switch( prop ) {
case pullPropEnergy:
case pullPropStepEnergy:
case pullPropStepConstr:
case pullPropScale:
case pullPropNeighCovarTrace:
case pullPropNeighCovarDet:
case pullPropStability:
dim = 1;
size[0] = pointNum;
typeOut = nrrdTypeDouble;
break;
case pullPropIdtag:
case pullPropIdCC:
case pullPropNeighInterNum:
dim = 1;
size[0] = pointNum;
typeOut = nrrdTypeUInt;
break;
case pullPropStuck:
dim = 1;
size[0] = pointNum;
/* HEY should be nrrdTypeUInt, no? */
typeOut = nrrdTypeUChar;
break;
case pullPropPosition:
case pullPropForce:
dim = 2;
size[0] = 4;
size[1] = pointNum;
typeOut = nrrdTypeDouble;
break;
case pullPropNeighDistMean:
dim = 1;
size[0] = pointNum;
typeOut = nrrdTypeDouble;
break;
case pullPropNeighCovar:
dim = 2;
size[0] = 10;
size[1] = pointNum;
typeOut = nrrdTypeFloat;
break;
case pullPropNeighCovar7Ten:
case pullPropNeighTanCovar:
dim = 2;
size[0] = 7;
size[1] = pointNum;
typeOut = nrrdTypeFloat;
break;
default:
biffAddf( PULL, "%s: prop %d unrecognized", me, prop );
return 1;
break;
}
if ( nrrdMaybeAlloc_nva( nprop, typeOut, dim, size ) ) {
biffMovef( PULL, NRRD, "%s: trouble allocating output", me );
return 1;
}
out_d = AIR_CAST( double *, nprop->data );
out_f = AIR_CAST( float *, nprop->data );
out_ui = AIR_CAST( unsigned int *, nprop->data );
out_uc = AIR_CAST( unsigned char *, nprop->data );
outIdx = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
pnc = point->neighCovar;
switch( prop ) {
case pullPropEnergy:
out_d[outIdx] = point->energy;
break;
case pullPropStepEnergy:
out_d[outIdx] = point->stepEnergy;
break;
case pullPropStepConstr:
out_d[outIdx] = point->stepConstr;
break;
case pullPropIdtag:
out_ui[outIdx] = point->idtag;
break;
case pullPropIdCC:
out_ui[outIdx] = point->idCC;
break;
case pullPropNeighInterNum:
out_ui[outIdx] = point->neighInterNum;
break;
case pullPropStuck:
out_uc[outIdx] = ( ( point->status & PULL_STATUS_STUCK_BIT )
? point->stuckIterNum
: 0 );
break;
case pullPropPosition:
ELL_4V_COPY( out_d + 4*outIdx, point->pos );
break;
case pullPropForce:
ELL_4V_COPY( out_d + 4*outIdx, point->force );
break;
case pullPropNeighDistMean:
out_d[outIdx] = point->neighDistMean;
break;
case pullPropScale:
out_d[outIdx] = ( pctx->flag.scaleIsTau
? gageSigOfTau( point->pos[3] )
: point->pos[3] );
break;
/*
0:xx 1:xy 2:xz 3:xs
1 4:yy 5:yz 6:ys
2 5 7:zz 8:zs
3 6 8 9:ss
*/
case pullPropNeighCovar:
ELL_10V_COPY( out_f + 10*outIdx, point->neighCovar );
break;
case pullPropNeighCovar7Ten:
TEN_T_SET( out_f + 7*outIdx, 1.0f,
pnc[0],
pnc[1],
pnc[2],
pnc[4],
pnc[5],
pnc[7] );
break;
case pullPropNeighTanCovar:
#if PULL_TANCOVAR
TEN_T_SET( out_f + 7*outIdx, 1.0f,
point->neighTanCovar[0],
point->neighTanCovar[1],
point->neighTanCovar[2],
point->neighTanCovar[3],
point->neighTanCovar[4],
point->neighTanCovar[5] );
#else
TEN_T_SET( out_f + 7*outIdx, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 0.0f,
0.0f );
#endif
break;
case pullPropNeighCovarTrace:
out_d[outIdx] = pnc[0] + pnc[4] + pnc[7] + pnc[9];
break;
case pullPropNeighCovarDet:
if ( pctx->haveScale ) {
ELL_4V_SET( covar + 0, pnc[0], pnc[1], pnc[2], pnc[3] );
ELL_4V_SET( covar + 4, pnc[1], pnc[4], pnc[5], pnc[6] );
ELL_4V_SET( covar + 8, pnc[2], pnc[5], pnc[7], pnc[8] );
ELL_4V_SET( covar + 12, pnc[3], pnc[6], pnc[8], pnc[9] );
out_d[outIdx] = ELL_4M_DET( covar );
} else {
ELL_3V_SET( covar + 0, pnc[0], pnc[1], pnc[2] );
ELL_3V_SET( covar + 3, pnc[1], pnc[4], pnc[5] );
ELL_3V_SET( covar + 6, pnc[2], pnc[5], pnc[7] );
out_d[outIdx] = ELL_3M_DET( covar );
}
break;
case pullPropStability:
out_d[outIdx] = point->stability;
break;
default:
biffAddf( PULL, "%s: prop %d unrecognized", me, prop );
return 1;
break;
}
++outIdx;
} /* for ( pointIdx ) */
}
return 0;
}
/*
** extract history as single array, possibly specific to only a single point0
*/
int
791 pullPositionHistoryNrrdGet( Nrrd *nhist, pullContext *pctx, pullPoint *point0 ) {
static const char me[]="pullPositionHistoryNrrdGet";
#if PULL_PHIST
pullBin *bin;
unsigned int binIdx, pointIdx, stepNum, stepIdx, phistIdx, phistNum, ii;
double *hist;
if ( !( nhist && pctx ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !point0 ) {
stepNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
pullPoint *point;
point = bin->point[pointIdx];
stepNum += point->phistArr->len;
}
}
} else {
stepNum = point0->phistArr->len;
}
if ( nrrdMaybeAlloc_va( nhist, nrrdTypeDouble, 2,
AIR_CAST( size_t, 1 + _PHN ),
AIR_CAST( size_t, stepNum ) ) ) {
biffMovef( PULL, NRRD, "%s: couldn't allocate output", me );
return 1;
}
hist = AIR_CAST( double *, nhist->data );
stepIdx = 0;
if ( !point0 ) {
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
pullPoint *point;
point = bin->point[pointIdx];
phistNum = point->phistArr->len;
for ( phistIdx=0; phistIdx<phistNum; phistIdx++ ) {
double *hh;
hh = hist + stepIdx*( 1 + _PHN );
hh[0] = AIR_CAST( double, point->idtag );
for ( ii=0; ii<_PHN; ii++ ) {
hh[ii+1] = ( point->phist + _PHN*phistIdx )[ii];
}
stepIdx++;
}
}
}
} else {
for ( stepIdx=0; stepIdx<stepNum; stepIdx++ ) {
double *hh;
hh = hist + stepIdx*( 1 + _PHN );
hh[0] = AIR_CAST( double, point0->idtag );
for ( ii=0; ii<_PHN; ii++ ) {
hh[ii+1] = ( point0->phist + _PHN*stepIdx )[ii];
}
}
}
return 0;
#else
AIR_UNUSED( nhist );
AIR_UNUSED( pctx );
AIR_UNUSED( point0 );
biffAddf( PULL, "%s: sorry, not compiled with PULL_PHIST", me );
return 1;
#endif
}
int
864 pullPositionHistoryPolydataGet( limnPolyData *pld, pullContext *pctx ) {
static const char me[]="pullPositionHistoryPolydataGet";
#if PULL_PHIST
pullBin *bin;
pullPoint *point;
unsigned int binIdx, pointIdx, pointNum, vertNum, vertIdx,
primIdx, phistIdx, phistNum;
if ( !( pld && pctx ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pointNum = 0;
vertNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
vertNum += point->phistArr->len;
pointNum++;
}
}
if ( limnPolyDataAlloc( pld, 1 << limnPolyDataInfoRGBA,
vertNum, vertNum, pointNum ) ) {
biffMovef( PULL, LIMN, "%s: couldn't allocate output", me );
return 1;
}
primIdx = 0;
vertIdx = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
phistNum = point->phistArr->len;
for ( phistIdx=0; phistIdx<phistNum; phistIdx++ ) {
int cond;
unsigned char rgb[3];
ELL_3V_SET( rgb, 0, 0, 0 );
ELL_3V_COPY( pld->xyzw + 4*vertIdx, point->phist + 5*phistIdx );
( pld->xyzw + 4*vertIdx )[3] = 1;
cond = AIR_CAST( int, ( point->phist + 5*phistIdx )[4] );
switch ( cond ) {
case pullCondOld:
ELL_3V_SET( rgb, 128, 128, 128 );
break;
case pullCondConstraintSatA:
ELL_3V_SET( rgb, 0, 255, 0 );
break;
case pullCondConstraintSatB:
ELL_3V_SET( rgb, 0, 0, 255 );
break;
case pullCondEnergyTry:
ELL_3V_SET( rgb, 255, 255, 255 );
break;
case pullCondEnergyBad:
ELL_3V_SET( rgb, 255, 0, 0 );
break;
case pullCondConstraintFail:
ELL_3V_SET( rgb, 255, 0, 255 );
break;
case pullCondNew:
ELL_3V_SET( rgb, 128, 255, 128 );
break;
}
ELL_4V_SET( pld->rgba + 4*vertIdx, rgb[0], rgb[1], rgb[2], 255 );
pld->indx[vertIdx] = vertIdx;
vertIdx++;
}
pld->type[primIdx] = limnPrimitiveLineStrip;
pld->icnt[primIdx] = phistNum;
primIdx++;
}
}
return 0;
#else
AIR_UNUSED( pld );
AIR_UNUSED( pctx );
biffAddf( PULL, "%s: sorry, not compiled with PULL_PHIST", me );
return 1;
#endif
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
int
_pullVerbose = 0;
#define _DECREASE( ell, enn ) ( \
2*( ( ell ) - ( enn ) ) \
/ ( ( AIR_ABS( ell ) + AIR_ABS( enn ) ) \
? ( AIR_ABS( ell ) + AIR_ABS( enn ) ) \
: 1 ) \
)
36 /*
** this is the core of the worker threads: as long as there are bins
** left to process, get the next one, and process it
*/
int
_pullProcess( pullTask *task ) {
static const char me[]="_pullProcess";
unsigned int binIdx;
while ( task->pctx->binNextIdx < task->pctx->binNum ) {
/* get the index of the next bin to process */
if ( task->pctx->threadNum > 1 ) {
airThreadMutexLock( task->pctx->binMutex );
}
/* note that we entirely skip bins with no points */
do {
binIdx = task->pctx->binNextIdx;
if ( task->pctx->binNextIdx < task->pctx->binNum ) {
task->pctx->binNextIdx++;
}
} while ( binIdx < task->pctx->binNum
&& 0 == task->pctx->bin[binIdx].pointNum );
if ( task->pctx->threadNum > 1 ) {
airThreadMutexUnlock( task->pctx->binMutex );
}
if ( binIdx == task->pctx->binNum ) {
/* no more bins to process! */
break;
}
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): calling pullBinProcess( %u )\n",
me, task->threadIdx, binIdx );
}
if ( pullBinProcess( task, binIdx ) ) {
biffAddf( PULL, "%s( %u ): had trouble on bin %u", me,
task->threadIdx, binIdx );
return 1;
}
}
75 return 0;
}
/* the main loop for each worker thread */
void *
_pullWorker( void *_task ) {
static const char me[]="_pushWorker";
pullTask *task;
task = ( pullTask * )_task;
while ( 1 ) {
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): waiting on barrier A\n",
me, task->threadIdx );
}
/* pushFinish sets finished prior to the barriers */
airThreadBarrierWait( task->pctx->iterBarrierA );
if ( task->pctx->finished ) {
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): done!\n", me, task->threadIdx );
}
break;
}
/* else there's work to do . . . */
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): starting to process\n", me, task->threadIdx );
}
if ( _pullProcess( task ) ) {
/* HEY clearly not threadsafe to have errors . . . */
biffAddf( PULL, "%s: thread %u trouble", me, task->threadIdx );
task->pctx->finished = AIR_TRUE;
}
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): waiting on barrier B\n",
me, task->threadIdx );
}
airThreadBarrierWait( task->pctx->iterBarrierB );
}
114
return _task;
}
int
pullStart( pullContext *pctx ) {
static const char me[]="pullStart";
unsigned int tidx;
if ( pctx->verbose ) {
fprintf( stderr, "%s: hello %p\n", me, AIR_VOIDP( pctx ) );
}
pctx->iter = 0; /* have to initialize this here because of seedOnly hack */
/* the ordering of steps below is important! e.g. gage context has
to be set up ( _pullVolumeSetup ) by before its copied ( _pullTaskSetup ) */
if ( _pullContextCheck( pctx )
|| _pullVolumeSetup( pctx )
|| _pullInfoSetup( pctx )
|| _pullTaskSetup( pctx )
|| _pullBinSetup( pctx ) ) {
biffAddf( PULL, "%s: trouble starting to set up context", me );
return 1;
}
if ( !( pctx->flag.startSkipsPoints ) ) {
if ( _pullPointSetup( pctx ) ) {
biffAddf( PULL, "%s: trouble setting up points", me );
return 1;
}
}
if ( pctx->threadNum > 1 ) {
pctx->binMutex = airThreadMutexNew( );
pctx->iterBarrierA = airThreadBarrierNew( pctx->threadNum );
pctx->iterBarrierB = airThreadBarrierNew( pctx->threadNum );
/* start threads 1 and up running; they'll all hit iterBarrierA */
for ( tidx=1; tidx<pctx->threadNum; tidx++ ) {
if ( pctx->verbose > 1 ) {
fprintf( stderr, "%s: spawning thread %d\n", me, tidx );
}
airThreadStart( pctx->task[tidx]->thread, _pullWorker,
( void * )( pctx->task[tidx] ) );
}
} else {
pctx->binMutex = NULL;
pctx->iterBarrierA = NULL;
pctx->iterBarrierB = NULL;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: setup for %u threads done\n", me, pctx->threadNum );
}
pctx->timeIteration = 0;
pctx->timeRun = 0;
return 0;
}
/*
173 ** this is called *after* pullOutputGet
**
** should nix everything created by the many _pull*Setup( ) functions
*/
int
pullFinish( pullContext *pctx ) {
static const char me[]="pullFinish";
unsigned int tidx;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pctx->finished = AIR_TRUE;
if ( pctx->threadNum > 1 ) {
if ( pctx->verbose > 1 ) {
fprintf( stderr, "%s: finishing workers\n", me );
}
airThreadBarrierWait( pctx->iterBarrierA );
/* worker threads now pass barrierA and see that finished is AIR_TRUE,
and then bail, so now we collect them */
for ( tidx=pctx->threadNum; tidx>0; tidx-- ) {
if ( tidx-1 ) {
airThreadJoin( pctx->task[tidx-1]->thread,
&( pctx->task[tidx-1]->returnPtr ) );
}
}
pctx->binMutex = airThreadMutexNix( pctx->binMutex );
pctx->iterBarrierA = airThreadBarrierNix( pctx->iterBarrierA );
pctx->iterBarrierB = airThreadBarrierNix( pctx->iterBarrierB );
}
/* no need for _pullVolumeFinish( pctx ), at least not now */
/* no need for _pullInfoFinish( pctx ), at least not now */
_pullTaskFinish( pctx );
_pullBinFinish( pctx );
_pullPointFinish( pctx ); /* yes, nixed bins deleted pnts inside, but
other buffers still have to be freed */
return 0;
}
/*
** _pullIterate
**
218 ** ( documentation )
**
** NB: this implements the body of thread 0, the master thread
*/
int
_pullIterate( pullContext *pctx, int mode ) {
static const char me[]="_pullIterate";
double time0;
int myError, E;
unsigned int ti;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( pullProcessMode, mode ) ) {
biffAddf( PULL, "%s: process mode %d unrecognized", me, mode );
return 1;
}
if ( !pctx->task ) {
biffAddf( PULL, "%s: NULL task array, didn't call pullStart( )?", me );
return 1;
}
/* if this is descent, pull down eip a bit */
if ( pullProcessModeDescent == mode ) {
pctx->sysParm.energyIncreasePermit *= pctx->eipScale;
}
#if PULL_HINTER
/* zero-out/alloc hinter if need be */
if ( pullProcessModeDescent == mode && pctx->nhinter ) {
if ( nrrdMaybeAlloc_va( pctx->nhinter, nrrdTypeFloat, 2,
AIR_CAST( size_t, _PULL_HINTER_SIZE ),
AIR_CAST( size_t, _PULL_HINTER_SIZE ) ) ) {
biffMovef( PULL, NRRD, "%s: setting up nhinter", me );
return 1;
}
}
#endif
/* tell all tasks what mode they're in */
for ( ti=0; ti<pctx->threadNum; ti++ ) {
pctx->task[ti]->processMode = mode;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s( %s ): iter %d goes w/ eip %g, %u pnts, enr %g%s\n",
me, airEnumStr( pullProcessMode, mode ),
pctx->iter, pctx->sysParm.energyIncreasePermit,
pullPointNumber( pctx ), _pullEnergyTotal( pctx ),
( pctx->flag.permuteOnRebin ? " ( por )" : "" ) );
}
time0 = airTime( );
pctx->pointNum = pullPointNumber( pctx );
/* the _pullWorker checks finished after iterBarrierA */
pctx->finished = AIR_FALSE;
/* initialize index of next bin to be doled out to threads */
pctx->binNextIdx=0;
if ( pctx->threadNum > 1 ) {
airThreadBarrierWait( pctx->iterBarrierA );
}
myError = AIR_FALSE;
if ( _pullProcess( pctx->task[0] ) ) {
biffAddf( PULL, "%s: master thread trouble w/ iter %u", me, pctx->iter );
pctx->finished = AIR_TRUE;
myError = AIR_TRUE;
}
if ( pctx->threadNum > 1 ) {
airThreadBarrierWait( pctx->iterBarrierB );
}
if ( pctx->finished ) {
if ( !myError ) {
/* we didn't set finished- one of the workers must have */
biffAddf( PULL, "%s: worker error on iter %u", me, pctx->iter );
}
return 1;
}
if ( pctx->verbose ) {
if ( pctx->pointNum > _PULL_PROGRESS_POINT_NUM_MIN ) {
fprintf( stderr, ".\n" ); /* finishing line of progress indicators */
}
}
/* depending on mode, run one of the iteration finishers */
E = 0;
switch ( mode ) {
case pullProcessModeDescent:
E = _pullIterFinishDescent( pctx ); /* includes rebinning */
break;
case pullProcessModeNeighLearn:
E = _pullIterFinishNeighLearn( pctx );
break;
case pullProcessModeAdding:
E = _pullIterFinishAdding( pctx );
break;
case pullProcessModeNixing:
E = _pullIterFinishNixing( pctx );
break;
default:
biffAddf( PULL, "%s: process mode %d unrecognized", me, mode );
return 1;
break;
}
if ( E ) {
pctx->finished = AIR_TRUE;
biffAddf( PULL, "%s: trouble finishing iter %u", me, pctx->iter );
return 1;
}
pctx->timeIteration = airTime( ) - time0;
#if PULL_HINTER
if ( pullProcessModeDescent == mode && pctx->nhinter ) {
char fname[AIR_STRLEN_SMALL];
sprintf( fname, "hinter-%05u.nrrd", pctx->iter );
if ( nrrdSave( fname, pctx->nhinter, NULL ) ) {
biffMovef( PULL, NRRD, "%s: saving hinter to %s", me, fname );
return 1;
}
}
#endif
343
return 0;
}
int
pullRun( pullContext *pctx ) {
static const char me[]="pullRun";
char poutS[AIR_STRLEN_MED];
Nrrd *npos;
double time0, time1, enrLast,
enrNew=AIR_NAN, enrDecrease=AIR_NAN, enrDecreaseAvg=AIR_NAN;
int converged;
unsigned firstIter;
if ( pctx->verbose ) {
fprintf( stderr, "%s: hello\n", me );
}
time0 = airTime( );
firstIter = pctx->iter;
if ( pctx->verbose ) {
fprintf( stderr, "%s: doing priming iteration ( iter now %u )\n", me,
pctx->iter );
}
if ( _pullIterate( pctx, pullProcessModeDescent ) ) {
biffAddf( PULL, "%s: trouble on priming iter %u", me, pctx->iter );
return 1;
}
pctx->iter += 1;
enrLast = enrNew = _pullEnergyTotal( pctx );
if ( pctx->verbose ) {
fprintf( stderr, "%s: starting system energy = %g\n", me, enrLast );
}
enrDecrease = enrDecreaseAvg = 0;
converged = AIR_FALSE;
while ( ( pctx->iterParm.min && pctx->iter <= pctx->iterParm.min )
||
( ( !pctx->iterParm.max || pctx->iter < pctx->iterParm.max )
&& !converged ) ) {
/* this per iteration init had been missing for a very long time */
pctx->addNum = pctx->nixNum = 0;
if ( pctx->iterParm.snap && !( pctx->iter % pctx->iterParm.snap ) ) {
npos = nrrdNew( );
sprintf( poutS, "snap.%06d.pos.nrrd", pctx->iter );
if ( pullOutputGet( npos, NULL, NULL, NULL, 0.0, pctx ) ) {
biffAddf( PULL, "%s: couldn't get snapshot for iter %d",
me, pctx->iter );
return 1;
}
if ( nrrdSave( poutS, npos, NULL ) ) {
biffMovef( PULL, NRRD,
"%s: couldn't save snapshot for iter %d",
me, pctx->iter );
return 1;
}
npos = nrrdNuke( npos );
}
if ( _pullIterate( pctx, pullProcessModeDescent ) ) {
biffAddf( PULL, "%s: trouble on iter %d", me, pctx->iter );
return 1;
}
enrNew = _pullEnergyTotal( pctx );
enrDecrease = _DECREASE( enrLast, enrNew );
if ( firstIter + 1 == pctx->iter ) {
/* we need some way of artificially boosting enrDecreaseAvg when
we're just starting, so that we thwart the convergence test,
which we do because we don't have the history of iterations
that enrDecreaseAvg is supposed to describe. Using some scaling
of enrDecrease is one possible hack. */
enrDecreaseAvg = 3*enrDecrease;
} else {
enrDecreaseAvg = ( 2*enrDecreaseAvg + enrDecrease )/3;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: ___ done iter %u: "
"e=%g, %g, de=%g, %g ( %g ), s=%g, %g\n",
me, pctx->iter, enrLast, enrNew, enrDecrease, enrDecreaseAvg,
pctx->sysParm.energyDecreaseMin,
_pullStepInterAverage( pctx ), _pullStepConstrAverage( pctx ) );
}
if ( pctx->iterParm.popCntlPeriod ) {
if ( ( pctx->iterParm.popCntlPeriod - 1 )
== ( pctx->iter % pctx->iterParm.popCntlPeriod )
&& enrDecreaseAvg < pctx->sysParm.energyDecreasePopCntlMin
&& ( pctx->sysParm.alpha != 0
|| !pctx->flag.noPopCntlWithZeroAlpha ) ) {
if ( pctx->verbose ) {
fprintf( stderr, "%s: ***** enr decrease %g < %g: "
"trying pop cntl ***** \n",
me, enrDecreaseAvg, pctx->sysParm.energyDecreasePopCntlMin );
}
if ( _pullIterate( pctx, pullProcessModeNeighLearn )
|| _pullIterate( pctx, pullProcessModeAdding )
|| _pullIterate( pctx, pullProcessModeNixing ) ) {
biffAddf( PULL, "%s: trouble with %s for pop cntl on iter %u", me,
airEnumStr( pullProcessMode, pctx->task[0]->processMode ),
pctx->iter );
return 1;
}
} else {
if ( pctx->verbose > 2 ) {
fprintf( stderr, "%s: ***** no pop cntl:\n", me );
fprintf( stderr, " iter=%u %% period=%u = %u != %u\n",
pctx->iter, pctx->iterParm.popCntlPeriod,
pctx->iter % pctx->iterParm.popCntlPeriod,
pctx->iterParm.popCntlPeriod - 1 );
fprintf( stderr, " en dec avg = %g >= %g\n",
enrDecreaseAvg, pctx->sysParm.energyDecreasePopCntlMin );
fprintf( stderr, " npcwza %s && alpha = %g\n",
pctx->flag.noPopCntlWithZeroAlpha ? "true" : "false",
pctx->sysParm.alpha );
}
}
}
pctx->iter += 1;
enrLast = enrNew;
converged = ( ( pctx->flag.convergenceIgnoresPopCntl
|| ( !pctx->iterParm.popCntlPeriod
|| ( !pctx->addNum && !pctx->nixNum ) ) )
&& AIR_IN_CL( 0, enrDecreaseAvg,
pctx->sysParm.energyDecreaseMin ) );
if ( pctx->verbose ) {
fprintf( stderr, "%s: converged %d = ( %d || ( %d || ( %d && %d ) ) ) "
"&& ( 0 <= %g <= %g )=%d\n",
me, converged,
pctx->flag.convergenceIgnoresPopCntl,
!pctx->iterParm.popCntlPeriod,
!pctx->addNum, !pctx->nixNum,
enrDecreaseAvg, pctx->sysParm.energyDecreaseMin,
AIR_IN_OP( 0, enrDecreaseAvg, pctx->sysParm.energyDecreaseMin ) );
}
if ( converged && pctx->verbose ) {
printf( "%s: enrDecreaseAvg %g < %g: converged!!\n", me,
enrDecreaseAvg, pctx->sysParm.energyDecreaseMin );
}
_pullPointStepEnergyScale( pctx, pctx->sysParm.opporStepScale );
/* call the callback */
if ( !( pctx->iter % pctx->iterParm.callback )
&& pctx->iter_cb ) {
pctx->iter_cb( pctx->data_cb );
}
}
if ( pctx->verbose ) {
printf( "%s: done ( ( %d|%d )&%d ) @iter %u: enr %g, enrDec = %g, %g "
"%u stuck\n", me,
!pctx->iterParm.max, pctx->iter < pctx->iterParm.max,
!converged,
pctx->iter, enrNew, enrDecrease, enrDecreaseAvg, pctx->stuckNum );
}
time1 = airTime( );
pctx->timeRun += time1 - time0;
pctx->energy = enrNew;
/* we do one final neighbor-learn iteration, to set the fields
( like stability ) that are only learned then */
if ( _pullIterate( pctx, pullProcessModeNeighLearn ) ) {
biffAddf( PULL, "%s: trouble after-final iter", me );
return 1;
}
if ( 0 ) {
/* probe inter-particle energy function */
unsigned int szimg=300, ri, si;
Nrrd *nout;
pullPoint *pa, *pb;
double rdir[3], len, r, s, *out, enr, egrad[4];
airRandMTState *rng;
rng = pctx->task[0]->rng;
nout = nrrdNew( );
nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 3,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, szimg ),
AIR_CAST( size_t, szimg ) );
out = AIR_CAST( double *, nout->data );
pa = pullPointNew( pctx );
pb = pullPointNew( pctx );
airNormalRand_r( pa->pos + 0, pa->pos + 1, rng );
airNormalRand_r( pa->pos + 2, pa->pos + 3, rng );
airNormalRand_r( rdir + 0, rdir + 1, rng );
airNormalRand_r( rdir + 2, NULL, rng );
ELL_3V_NORM( rdir, rdir, len );
for ( si=0; si<szimg; si++ ) {
s = AIR_AFFINE( 0, si, szimg-1,
-1.5*pctx->sysParm.radiusScale,
1.5*pctx->sysParm.radiusScale );
for ( ri=0; ri<szimg; ri++ ) {
r = AIR_AFFINE( 0, ri, szimg-1,
-1.5*pctx->sysParm.radiusSpace,
1.5*pctx->sysParm.radiusSpace );
ELL_3V_SCALE_ADD2( pb->pos, 1.0, pa->pos, r, rdir );
pb->pos[3] = pa->pos[3] + s;
/* now points are in desired test positions */
enr = _pullEnergyInterParticle( pctx, pa, pb,
AIR_ABS( r ), AIR_ABS( s ), egrad );
ELL_3V_SET( out + 3*( ri + szimg*si ),
enr, ELL_3V_DOT( egrad, rdir ), egrad[3] );
}
}
nrrdSave( "eprobe.nrrd", nout, NULL );
pullPointNix( pa );
pullPointNix( pb );
nrrdNuke( nout );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
const int
pullPresent = 42;
#if PULL_PHIST
const int pullPhistEnabled = 1;
#else
const int pullPhistEnabled = 0;
#endif
const char *
pullBiffKey = "pull";
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
const char *
_pullInterTypeStr[PULL_INTER_TYPE_MAX+1] = {
"( unknown_inter )",
"justR",
"univariate",
"separable",
"additive"
};
const char *
_pullInterTypeStrEqv[] = {
"r", "justr",
"univariate", "univar", "uni",
"separable", "separ", "sep",
"additive", "add",
""
};
const int
_pullInterTypeValEqv[] = {
pullInterTypeJustR, pullInterTypeJustR,
pullInterTypeUnivariate, pullInterTypeUnivariate, pullInterTypeUnivariate,
pullInterTypeSeparable, pullInterTypeSeparable, pullInterTypeSeparable,
pullInterTypeAdditive, pullInterTypeAdditive
};
const airEnum
_pullInterType = {
"interaction type",
PULL_INTER_TYPE_MAX,
_pullInterTypeStr, NULL,
NULL,
_pullInterTypeStrEqv, _pullInterTypeValEqv,
AIR_FALSE
};
62 const airEnum *const
pullInterType = &_pullInterType;
#define SPRING "spring"
#define GAUSS "gauss"
#define BSPLN "bspln"
#define BUTTER "butter"
#define COTAN "cotan"
#define CUBIC "cubic"
#define QUARTIC "quartic"
#define CWELL "cwell"
#define BWELL "bwell"
#define QWELL "qwell"
#define HWELL "hwell"
#define ZERO "zero"
#define BPARAB "bparab"
const char *
_pullEnergyTypeStr[PULL_ENERGY_TYPE_MAX+1] = {
"( unknown_energy )",
SPRING,
GAUSS,
BSPLN,
BUTTER,
COTAN,
CUBIC,
QUARTIC,
CWELL,
BWELL,
QWELL,
HWELL,
ZERO,
BPARAB
};
const char *
_pullEnergyTypeDesc[PULL_ENERGY_TYPE_MAX+1] = {
"unknown_energy",
"Hooke's law-based potential, with a tunable region of attraction",
"Gaussian potential",
"uniform cubic B-spline basis function",
"like a Gaussian, but a lot wider",
"Cotangent-based potential ( from Meyer et al. SMI '05 )",
"Cubic thing",
"Quartic thing",
"Piecewice cubic with tunable well location and depth",
"Better piecewice cubic with tunable well location and depth",
"Single quartic with tunable well location",
"Single heptic with tunable well location",
"no energy",
"butterworth-windowed spatial repel and scale attract"
};
const airEnum
_pullEnergyType = {
"energy",
PULL_ENERGY_TYPE_MAX,
_pullEnergyTypeStr, NULL,
_pullEnergyTypeDesc,
NULL, NULL,
AIR_FALSE
};
124 const airEnum *const
pullEnergyType = &_pullEnergyType;
double
128 _pullEnergyNoWell( double *wx, const double *parm ) {
AIR_UNUSED( wx );
AIR_UNUSED( parm );
return 0.0;
}
/* ----------------------------------------------------------------
** ------------------------------ UNKNOWN -------------------------
** ----------------------------------------------------------------
*/
double
140 _pullEnergyUnknownEval( double *denr, double dist, const double *parm ) {
static const char me[]="_pullEnergyUnknownEval";
AIR_UNUSED( dist );
AIR_UNUSED( parm );
*denr = AIR_NAN;
fprintf( stderr, "%s: ERROR- using unknown energy.\n", me );
return AIR_NAN;
}
pullEnergy
_pullEnergyUnknown = {
"unknown",
0,
_pullEnergyNoWell,
_pullEnergyUnknownEval
};
157 const pullEnergy *const
pullEnergyUnknown = &_pullEnergyUnknown;
/* ----------------------------------------------------------------
** ------------------------------ SPRING --------------------------
** ----------------------------------------------------------------
** 1 parms:
** parm[0]: width of pull region. Used to be width beyond 1.0, but
** now things are scrunched to fit both repelling and attractive
** region inside [0, 1]
**
** learned: "1/2" is not 0.5 !!!!!
*/
double
171 _pullEnergySpringEval( double *denr, double dist, const double *parm ) {
/* static const char me[]="_pullEnergySpringEval"; */
double enr, xx, pull;
pull = parm[0];
/* support used to be [0, 1 + pull], but now is scrunched to [0, 1],
so hack "dist" to match old parameterization */
dist = AIR_AFFINE( 0, dist, 1, 0, 1+pull );
xx = dist - 1.0;
if ( xx > pull ) {
enr = 0;
*denr = 0;
} else if ( xx > 0 ) {
enr = xx*xx*( xx*xx/( 4*pull*pull ) - 2*xx/( 3*pull ) + 1.0/2.0 );
*denr = xx*( xx*xx/( pull*pull ) - 2*xx/pull + 1 );
} else {
enr = xx*xx/2;
*denr = xx;
}
/*
if ( !AIR_EXISTS( ret ) ) {
printf( "!%s: dist=%g, pull=%g, blah=%d --> ret=%g\n",
me, dist, pull, blah, ret );
}
*/
return enr;
}
int
200 _pullEnergySpringWell( const double *parm ) {
return ( parm[0] > 0.0 );
}
const pullEnergy
_pullEnergySpring = {
SPRING,
1,
_pullEnergyNoWell, /* HEY: is this true? */
_pullEnergySpringEval
};
212 const pullEnergy *const
pullEnergySpring = &_pullEnergySpring;
/* ----------------------------------------------------------------
** ------------------------------ GAUSS --------------------------
** ----------------------------------------------------------------
** 0 parms: for simplicity we're now always cutting off at 4 sigmas
*/
/* HEY: copied from teem/src/nrrd/kernel.c */
#define _GAUSS( x, sig, cut ) ( \
x >= sig*cut ? 0 \
: exp( -x*x/( 2.0*sig*sig ) ) )
#define _DGAUSS( x, sig, cut ) ( \
226 x >= sig*cut ? 0 \
: -exp( -x*x/( 2.0*sig*sig ) )*( x/( sig*sig ) ) )
double
_pullEnergyGaussEval( double *denr, double dist, const double *parm ) {
AIR_UNUSED( parm );
*denr = _DGAUSS( dist, 0.25, 4 );
return _GAUSS( dist, 0.25, 4 );
}
const pullEnergy
_pullEnergyGauss = {
GAUSS,
240 0,
_pullEnergyNoWell,
_pullEnergyGaussEval
};
const pullEnergy *const
pullEnergyGauss = &_pullEnergyGauss;
/* ----------------------------------------------------------------
** ------------------------------ BSPLN --------------------------
** ----------------------------------------------------------------
** 0 parms
*/
/* HEY: copied from teem/src/nrrd/bsplKernel.c */
#define BSPL( ret, t, x ) \
254 if ( x < 1 ) { \
ret = ( 4 + 3*( -2 + x )*x*x )/6; \
} else if ( x < 2 ) { \
t = ( -2 + x ); \
ret = -t*t*t/6; \
} else { \
ret = 0; \
}
#define DBSPL( ret, t, x ) \
if ( x < 1 ) { \
ret = ( -4 + 3*x )*x/2; \
} else if ( x < 2 ) { \
t = ( -2 + x ); \
ret = -t*t/2; \
} else { \
ret = 0; \
}
272
double
_pullEnergyBsplnEval( double *denr, double dist, const double *parm ) {
double tmp, ret;
AIR_UNUSED( parm );
dist *= 2;
DBSPL( *denr, tmp, dist );
*denr *= 2;
BSPL( ret, tmp, dist );
return ret;
283 }
const pullEnergy
_pullEnergyBspln = {
BSPLN,
0,
_pullEnergyNoWell,
_pullEnergyBsplnEval
};
const pullEnergy *const
pullEnergyBspln = &_pullEnergyBspln;
/* ----------------------------------------------------------------
** ------------------------------ BUTTER --------------------------
** ----------------------------------------------------------------
** 2 parms: order ( an integer ) and "cut-ff" ( where height==0.5 )
*/
302 double
_pullEnergyButterworthEval( double *denr, double x, const double *parm ) {
int n;
double cut, denom, enr;
n = AIR_CAST( int, parm[0] );
cut = parm[1];
denom = 1 + airIntPow( x/cut, 2*n );
enr = 1/denom;
311 *denr = -2*n*airIntPow( x/cut, 2*n - 1 )*enr*enr/cut;
return enr;
}
const pullEnergy
_pullEnergyButterworth= {
BUTTER,
2,
_pullEnergyNoWell,
_pullEnergyButterworthEval
};
const pullEnergy *const
pullEnergyButterworth = &_pullEnergyButterworth;
/* ----------------------------------------------------------------
** ------------------------------ COTAN ---------------------------
** ----------------------------------------------------------------
** 0 parms!
329 */
double
_pullEnergyCotanEval( double *denr, double dist, const double *parm ) {
double pot, cc, enr;
AIR_UNUSED( parm );
pot = AIR_PI/2.0;
cc = 1.0/( FLT_MIN + tan( dist*pot ) );
enr = dist > 1 ? 0 : cc + dist*pot - pot;
338 *denr = dist > 1 ? 0 : -cc*cc*pot;
return enr;
}
const pullEnergy
_pullEnergyCotan = {
COTAN,
0,
_pullEnergyNoWell,
_pullEnergyCotanEval
};
const pullEnergy *const
pullEnergyCotan = &_pullEnergyCotan;
/* ----------------------------------------------------------------
** ------------------------------ CUBIC ---------------------------
** ----------------------------------------------------------------
** 0 parms!
*/
double
_pullEnergyCubicEval( double *denr, double dist, const double *parm ) {
359 double omr, enr;
AIR_UNUSED( parm );
if ( dist <= 1 ) {
omr = 1 - dist;
enr = omr*omr*omr;
*denr = -3*omr*omr;
} else {
enr = *denr = 0;
368 }
return enr;
}
const pullEnergy
_pullEnergyCubic = {
CUBIC,
0,
_pullEnergyNoWell,
_pullEnergyCubicEval
};
const pullEnergy *const
pullEnergyCubic = &_pullEnergyCubic;
/* ----------------------------------------------------------------
** ----------------------------- QUARTIC --------------------------
** ----------------------------------------------------------------
** 0 parms!
*/
double
_pullEnergyQuarticEval( double *denr, double dist, const double *parm ) {
389 double omr, enr;
AIR_UNUSED( parm );
if ( dist <= 1 ) {
omr = 1 - dist;
enr = 2.132*omr*omr*omr*omr;
*denr = -4*2.132*omr*omr*omr;
} else {
enr = *denr = 0;
398 }
return enr;
}
const pullEnergy
_pullEnergyQuartic = {
QUARTIC,
0,
_pullEnergyNoWell,
_pullEnergyQuarticEval
};
const pullEnergy *const
pullEnergyQuartic = &_pullEnergyQuartic;
/* ----------------------------------------------------------------
** ------------------ tunable piece-wise cubic --------------------
** ----------------------------------------------------------------
** 2 parm: wellX, wellY
*/
double
_pullEnergyCubicWellEval( double *denr, double x, const double *parm ) {
double a, b, c, d, e, wx, wy, enr;
wx = parm[0];
wy = parm[1];
a = ( 3*( -1 + wy ) )/wx;
424 b = ( -3*( -1 + wy ) )/( wx*wx );
c = -( 1 - wy )/( wx*wx*wx );
d = ( -3*wy )/( ( wx-1 )*( wx-1 ) );
e = ( -2*wy )/( ( wx-1 )*( wx-1 )*( wx-1 ) );
if ( x < wx ) {
enr = 1 + x*( a + x*( b + c*x ) );
*denr = a + x*( 2*b + 3*c*x );
} else if ( x < 1 ) {
double _x;
_x = x - wx;
enr = wy + _x*_x*( d + e*_x );
*denr = _x*( 2*d + 3*e*_x );
} else {
437 enr = 0;
*denr = 0;
}
return enr;
}
double
_pullEnergyCubicWellWell( double *wx, const double *parm ) {
446 *wx = parm[0];
return AIR_MIN( 0.0, parm[1] );
}
const pullEnergy
_pullEnergyCubicWell = {
CWELL,
2,
_pullEnergyCubicWellWell,
_pullEnergyCubicWellEval
};
const pullEnergy *const
pullEnergyCubicWell = &_pullEnergyCubicWell;
/* ----------------------------------------------------------------
** --------------- better tunable piece-wise cubic ----------------
** ----------------------------------------------------------------
** 2 parm: wellX, wellY
*/
double
_pullEnergyBetterCubicWellEval( double *denr, double x, const double *parm ) {
double a, b, c, d, e, f, g, wx, wy, xmo, xmoo, xmooo, enr;
/* HEY: it would be good if there was a place to store these
intermediate values, so that they don't need to be computed
for *every*single* inter-particle interaction */
wx = parm[0];
wy = parm[1];
xmo = wx-1;
xmoo = xmo*xmo;
xmooo = xmoo*xmo;
a = -3*( xmoo + ( -1 + 2*wx )*wy )/( xmoo*wx );
478 b = 3*( xmoo + ( -1 + wx*( 2 + wx ) )*wy )/( xmoo*wx*wx );
c = ( -1 + wy - wx*( -2 + wx + 2*( 1 + wx )*wy ) )/( xmoo*wx*wx*wx );
d = ( ( -1 + 3*wx )*wy )/xmooo;
e = -( 6*wx*wy )/xmooo;
f = ( 3*( 1 + wx )*wy )/xmooo;
g = -( 2*wy )/xmooo;
if ( x < wx ) {
enr = 1 + x*( a + x*( b + x*c ) );
*denr = a + x*( 2*b + x*3*c );
} else if ( x < 1 ) {
enr = d + x*( e + x*( f + x*g ) );
*denr = e + x*( 2*f + x*3*g );
} else {
491 enr = 0;
*denr = 0;
}
return enr;
}
double
_pullEnergyBetterCubicWellWell( double *wx, const double *parm ) {
500 *wx = parm[0];
return AIR_MIN( 0.0, parm[1] );
}
const pullEnergy
_pullEnergyBetterCubicWell = {
BWELL,
2,
_pullEnergyBetterCubicWellWell,
_pullEnergyBetterCubicWellEval
};
const pullEnergy *const
pullEnergyBetterCubicWell = &_pullEnergyBetterCubicWell;
514 /* ----------------------------------------------------------------
** ----------------- tunable single quartic well ------------------
** ----------------------------------------------------------------
** 1 parm: well radius
*/
double
_pullEnergyQuarticWellEval( double *denr, double x, const double *parm ) {
double a, b, c, d, w, enr;
w = parm[0];
a = ( 12*w )/( 1 - 4*w );
b = 3 + 9/( -1 + 4*w );
c = ( 8 + 4*w )/( 1 - 4*w );
d = 3/( -1 + 4*w );
enr = 1 + x*( a + x*( b + x*( c + x*d ) ) );
529 *denr = a + x*( 2*b + x*( 3*c + x*4*d ) );
return enr;
}
double
_pullEnergyQuarticWellWell( double *wx, const double *parm ) {
double t;
*wx = parm[0];
538 t = *wx - 1;
return t*t*t*t/( 4*( *wx ) - 1 );
}
const pullEnergy
_pullEnergyQuarticWell = {
QWELL,
1,
_pullEnergyQuarticWellWell,
_pullEnergyQuarticWellEval
};
const pullEnergy *const
pullEnergyQuarticWell = &_pullEnergyQuarticWell;
/* ----------------------------------------------------------------
** ------------------ tunable single heptic well ------------------
** ----------------------------------------------------------------
555 ** 1 parm: well radius
*/
double
_pullEnergyHepticWellEval( double *denr, double x, const double *parm ) {
double a, b, c, d, e, f, g, w, enr;
w = parm[0];
a = ( 42*w )/( 1 - 7*w );
b = 15 + 36/( -1 + 7*w );
c = -20 + 90/( 1 - 7*w );
d = ( 105*( 1 + w ) )/( -1 + 7*w );
e = -( ( 42*( 2 + w ) )/( -1 + 7*w ) );
f = ( 7*( 5 + w ) )/( -1 + 7*w );
g = 6/( 1 - 7*w );
enr = 1 + x*( a + x*( b + x*( c + x*( d + x*( e + x*( f + g*x ) ) ) ) ) );
570 *denr = a + x*( 2*b + x*( 3*c + x*( 4*d + x*( 5*e + x*( 6*f + x*7*g ) ) ) ) );
return enr;
}
double
_pullEnergyHepticWellWell( double *wx, const double *parm ) {
double t;
*wx = parm[0];
579 t = *wx - 1;
return t*t*t*t*t*t*t/( 7*( *wx ) - 1 );
}
const pullEnergy
_pullEnergyHepticWell = {
HWELL,
1,
_pullEnergyHepticWellWell,
_pullEnergyHepticWellEval
};
const pullEnergy *const
pullEnergyHepticWell = &_pullEnergyHepticWell;
/* ----------------------------------------------------------------
594 ** ------------------------------- ZERO ---------------------------
** ----------------------------------------------------------------
** 0 parms:
*/
double
_pullEnergyZeroEval( double *denr, double dist, const double *parm ) {
AIR_UNUSED( dist );
AIR_UNUSED( parm );
*denr = 0;
604 return 0;
}
const pullEnergy
_pullEnergyZero = {
ZERO,
0,
_pullEnergyNoWell,
_pullEnergyZeroEval
};
const pullEnergy *const
pullEnergyZero = &_pullEnergyZero;
/* ----------------------------------------------------------------
** ------------------------------- BPARAB -------------------------
619 ** ----------------------------------------------------------------
** 3 parms, the first two are for butterworth,
** parm[2] is a shift ( probably negative ) on the parabola
*/
double
_pullEnergyBParabEval( double *denr, double x, const double *parm ) {
double ben, dben;
627 ben = _pullEnergyButterworthEval( &dben, x, parm );
*denr = 2*x*ben + x*x*dben;
return ( x*x + parm[2] )*ben;
}
const pullEnergy
_pullEnergyButterworthParabola = {
BPARAB,
3,
_pullEnergyNoWell,
_pullEnergyBParabEval
};
const pullEnergy *const
pullEnergyButterworthParabola = &_pullEnergyButterworthParabola;
/* ----------------------------------------------------------------
** ----------------------------------------------------------------
** ----------------------------------------------------------------
645 */
const pullEnergy *const pullEnergyAll[PULL_ENERGY_TYPE_MAX+1] = {
&_pullEnergyUnknown, /* 0 */
&_pullEnergySpring, /* 1 */
&_pullEnergyGauss, /* 2 */
&_pullEnergyBspln, /* 3 */
&_pullEnergyButterworth, /* 4 */
&_pullEnergyCotan, /* 5 */
&_pullEnergyCubic, /* 6 */
&_pullEnergyQuartic, /* 7 */
&_pullEnergyCubicWell, /* 8 */
&_pullEnergyBetterCubicWell, /* 9 */
&_pullEnergyQuarticWell, /* 10 */
&_pullEnergyHepticWell, /* 11 */
660 &_pullEnergyZero, /* 12 */
&_pullEnergyButterworthParabola /* 13 */
};
pullEnergySpec *
pullEnergySpecNew( ) {
pullEnergySpec *ensp;
int pi;
ensp = ( pullEnergySpec * )calloc( 1, sizeof( pullEnergySpec ) );
if ( ensp ) {
ensp->energy = pullEnergyUnknown;
for ( pi=0; pi<PULL_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = AIR_NAN;
674 }
}
return ensp;
}
void
pullEnergySpecSet( pullEnergySpec *ensp, const pullEnergy *energy,
const double parm[PULL_ENERGY_PARM_NUM] ) {
unsigned int pi;
683
if ( ensp && energy && parm ) {
ensp->energy = energy;
for ( pi=0; pi<PULL_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = parm[pi];
}
}
690 return;
}
void
pullEnergySpecCopy( pullEnergySpec *esDst, const pullEnergySpec *esSrc ) {
if ( esDst && esSrc ) {
pullEnergySpecSet( esDst, esSrc->energy, esSrc->parm );
}
return;
}
pullEnergySpec *
pullEnergySpecNix( pullEnergySpec *ensp ) {
airFree( ensp );
return NULL;
}
int
pullEnergySpecParse( pullEnergySpec *ensp, const char *_str ) {
static const char me[]="pullEnergySpecParse";
char *str, *col, *_pstr, *pstr;
int etype;
unsigned int pi, haveParm;
airArray *mop;
double pval;
if ( !( ensp && _str ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
/* see if its the name of something that needs no parameters */
etype = airEnumVal( pullEnergyType, _str );
if ( pullEnergyTypeUnknown != etype ) {
/* the string is the name of some energy */
ensp->energy = pullEnergyAll[etype];
if ( 0 != ensp->energy->parmNum ) {
biffAddf( PULL, "%s: need %u parm%s for %s energy, but got none", me,
ensp->energy->parmNum,
( 1 == ensp->energy->parmNum ? "" : "s" ),
ensp->energy->name );
return 1;
}
/* the energy needs 0 parameters */
for ( pi=0; pi<PULL_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = AIR_NAN;
}
return 0;
}
/* start parsing parms after ':' */
mop = airMopNew( );
str = airStrdup( _str );
airMopAdd( mop, str, ( airMopper )airFree, airMopAlways );
col = strchr( str, ':' );
if ( !col ) {
biffAddf( PULL, "%s: either \"%s\" is not a recognized energy, "
"or it is an energy with parameters, and there's no "
"\":\" separator to indicate parameters", me, str );
airMopError( mop ); return 1;
}
*col = '\0';
etype = airEnumVal( pullEnergyType, str );
if ( pullEnergyTypeUnknown == etype ) {
biffAddf( PULL, "%s: didn't recognize \"%s\" as a %s", me,
str, pullEnergyType->name );
airMopError( mop ); return 1;
}
ensp->energy = pullEnergyAll[etype];
if ( 0 == ensp->energy->parmNum ) {
biffAddf( PULL, "%s: \"%s\" energy has no parms, but got something", me,
ensp->energy->name );
return 1;
}
_pstr = pstr = col+1;
/* code lifted from teem/src/nrrd/kernel.c, should probably refactor. . . */
for ( haveParm=0; haveParm<ensp->energy->parmNum; haveParm++ ) {
if ( !pstr ) {
break;
}
if ( 1 != sscanf( pstr, "%lg", &pval ) ) {
biffAddf( PULL, "%s: trouble parsing \"%s\" as double ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
}
ensp->parm[haveParm] = pval;
if ( ( pstr = strchr( pstr, ', ' ) ) ) {
pstr++;
if ( !*pstr ) {
biffAddf( PULL, "%s: nothing after last comma in \"%s\" ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
}
}
}
789 /* haveParm is now the number of parameters that were parsed. */
if ( haveParm < ensp->energy->parmNum ) {
biffAddf( PULL, "%s: parsed only %u of %u required parms ( for %s energy )"
"from \"%s\" ( in \"%s\" )",
me, haveParm, ensp->energy->parmNum,
ensp->energy->name, _pstr, _str );
airMopError( mop ); return 1;
} else {
if ( pstr ) {
biffAddf( PULL, "%s: \"%s\" ( in \"%s\" ) has more than %u doubles",
me, _pstr, _str, ensp->energy->parmNum );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
int
_pullHestEnergyParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
static const char me[]="_pullHestForceParse";
pullEnergySpec **enspP;
char *perr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
enspP = ( pullEnergySpec ** )ptr;
*enspP = pullEnergySpecNew( );
if ( pullEnergySpecParse( *enspP, str ) ) {
perr = biffGetDone( PULL );
airStrcpy( err, AIR_STRLEN_HUGE, perr );
free( perr );
return 1;
}
return 0;
}
hestCB
_pullHestEnergySpec = {
sizeof( pullEnergySpec* ),
"energy specification",
_pullHestEnergyParse,
( airMopper )pullEnergySpecNix
};
hestCB *
pullHestEnergySpec = &_pullHestEnergySpec;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/* --------------------------------------------------------- */
static const char *
_pullSourceStr[] = {
"( unknown pullSource )",
"gage",
"prop"
};
static const airEnum
_pullSource = {
"pullSource",
PULL_SOURCE_MAX,
_pullSourceStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
45 const airEnum *const
pullSource = &_pullSource;
/* --------------------------------------------------------- */
static const char *
_pullInfoStr[] = {
"( unknown pullInfo )",
"ten",
"teni",
"hess",
"in",
"ingradvec",
"hght",
"hghtgradvec",
"hghthessian",
"hghtlapl",
"seedprethresh",
"seedthresh",
"livethresh",
"livethresh2",
"livethresh3",
"tan1",
"tan2",
"negtan1",
"negtan2",
"isoval",
"isogradvec",
"isohessian",
"strength",
"quality"
};
static const int
_pullInfoVal[] = {
pullInfoUnknown,
pullInfoTensor, /* [7] tensor here */
pullInfoTensorInverse, /* [7] inverse of tensor here */
pullInfoHessian, /* [9] hessian used for force distortion */
pullInfoInside, /* [1] containment scalar */
pullInfoInsideGradient, /* [3] containment vector */
pullInfoHeight, /* [1] for gravity */
pullInfoHeightGradient, /* [3] for gravity */
pullInfoHeightHessian, /* [9] for gravity */
pullInfoHeightLaplacian, /* [1] */
pullInfoSeedPreThresh, /* [1] */
pullInfoSeedThresh, /* [1] scalar for thresholding seeding */
pullInfoLiveThresh, /* [1] */
pullInfoLiveThresh2, /* [1] */
pullInfoLiveThresh3, /* [1] */
pullInfoTangent1, /* [3] first tangent to constraint surf */
pullInfoTangent2, /* [3] second tangent to constraint surf */
pullInfoNegativeTangent1, /* [3] */
pullInfoNegativeTangent2, /* [3] */
pullInfoIsovalue, /* [1] */
pullInfoIsovalueGradient, /* [3] */
pullInfoIsovalueHessian, /* [9] */
pullInfoStrength, /* [1] */
pullInfoQuality /* [1] */
};
static const char *
_pullInfoStrEqv[] = {
"ten",
"teni",
"hess",
"in",
"ingradvec",
"hght", "h",
"hghtgradvec", "hgvec",
"hghthessian", "hhess",
"hghtlapl", "hlapl",
"seedprethresh", "spthr",
"seedthresh", "sthr",
"livethresh", "lthr",
"livethresh2", "lthr2",
"livethresh3", "lthr3",
"tan1",
"tan2",
"ntan1", "negtan1",
"ntan2", "negtan2",
"isoval", "iso",
"isogradvec", "isogvec",
"isohessian", "isohess",
"strength", "strn",
"quality", "qual",
""
};
static const int
_pullInfoValEqv[] = {
pullInfoTensor,
pullInfoTensorInverse,
pullInfoHessian,
pullInfoInside,
pullInfoInsideGradient,
pullInfoHeight, pullInfoHeight,
pullInfoHeightGradient, pullInfoHeightGradient,
pullInfoHeightHessian, pullInfoHeightHessian,
pullInfoHeightLaplacian, pullInfoHeightLaplacian,
pullInfoSeedPreThresh, pullInfoSeedPreThresh,
pullInfoSeedThresh, pullInfoSeedThresh,
pullInfoLiveThresh, pullInfoLiveThresh,
pullInfoLiveThresh2, pullInfoLiveThresh2,
pullInfoLiveThresh3, pullInfoLiveThresh3,
pullInfoTangent1,
pullInfoTangent2,
pullInfoNegativeTangent1, pullInfoNegativeTangent1,
pullInfoNegativeTangent2, pullInfoNegativeTangent2,
pullInfoIsovalue, pullInfoIsovalue,
pullInfoIsovalueGradient, pullInfoIsovalueGradient,
pullInfoIsovalueHessian, pullInfoIsovalueHessian,
pullInfoStrength, pullInfoStrength,
pullInfoQuality, pullInfoQuality
};
static const airEnum
_pullInfo = {
"pullInfo",
PULL_INFO_MAX,
_pullInfoStr, _pullInfoVal,
NULL,
_pullInfoStrEqv, _pullInfoValEqv,
AIR_FALSE
};
170 const airEnum *const
pullInfo = &_pullInfo;
/* --------------------------------------------------------- */
const char *
_pullPropStr[] = {
"( unknown_prop )",
"idtag", /* pullPropIdtag */
"idcc", /* pullPropIdCC */
"energy", /* pullPropEnergy */
"stepEnergy", /* pullPropStepEnergy */
"stepConstr", /* pullPropStepConstr */
"stuck", /* pullPropStuck */
"position", /* pullPropPosition */
"force", /* pullPropForce */
"neighDistMean", /* pullPropNeighDistMean */
"scale", /* pullPropScale */
"neighCovar", /* pullPropNeighCovar */
"neighCovar7Ten", /* pullPropNeighCovar7Ten */
"neighTanCovar", /* pullPropNeighTanCovar */
"internum", /* pullPropNeighInterNum */
"neighCovarTrace", /* pullPropNeighCovarTrace */
"neighCovarDet", /* pullPropNeighCovarDet */
"stability" /* pullPropStability */
};
static const airEnum
_pullProp = {
"pullProp",
PULL_PROP_MAX,
_pullPropStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
206 const airEnum *const
pullProp = &_pullProp;
/* --------------------------------------------------------- */
const char *
_pullProcessModeStr[] = {
"( unknown_mode )",
"descent",
"nlearn",
"adding",
"nixing"
};
const airEnum
_pullProcessMode = {
"process mode",
PULL_PROCESS_MODE_MAX,
_pullProcessModeStr, NULL,
NULL, NULL, NULL,
AIR_FALSE
};
228 const airEnum *const
pullProcessMode = &_pullProcessMode;
/* --------------------------------------------------------- */
const char *
_pullTraceStopStr[] = {
"( unknown_trace_stop )",
"speeding",
"constrfail",
"bounds",
"length",
"stub",
"voledge",
"orientconstrfail"
};
const airEnum
_pullTraceStop = {
"trace stop",
PULL_TRACE_STOP_MAX,
_pullTraceStopStr, NULL,
NULL, NULL, NULL,
AIR_FALSE
};
253 const airEnum *const
pullTraceStop = &_pullTraceStop;
/* --------------------------------------------------------- */
const char *
_pullInitMethodStr[] = {
"( unknown_init_method )",
"random",
"halton",
"ppv",
"given"
};
const airEnum
_pullInitMethod = {
"init method",
PULL_INIT_METHOD_MAX,
_pullInitMethodStr, NULL,
NULL, NULL, NULL,
AIR_FALSE
};
275 const airEnum *const
pullInitMethod = &_pullInitMethod;
/* --------------------------------------------------------- */
const char *
_pullCountStr[] = {
"( unknown_countable )",
"descent",
"teststep",
"enrg( img )",
"frc( img )",
"enrg( pts )",
"frc( pts )",
"probe",
"constr",
"adding",
"nixing",
"pts stuck",
"pts",
"CC",
"iter"
};
const airEnum
_pullCount = {
"countable",
PULL_COUNT_MAX,
_pullCountStr, NULL,
NULL, NULL, NULL,
AIR_FALSE
};
307 const airEnum *const
pullCount = &_pullCount;
/* --------------------------------------------------------- */
const char *
_pullConstraintFailStr[PULL_CONSTRAINT_FAIL_MAX+1] = {
"( unknown or no contraint fail )",
"needed Hessian 0 ( A )",
"needed Hessian 0 ( B )",
"projected gradient 0 ( A )",
"projected gradient 0 ( B )",
"iter max exceeded",
"travel exceeded",
"plen doesn't exist"
};
const airEnum
_pullConstraintFail = {
"constraint fail",
PULL_CONSTRAINT_FAIL_MAX,
_pullConstraintFailStr, NULL,
NULL, NULL, NULL,
AIR_FALSE
};
332 const airEnum *const
pullConstraintFail = &_pullConstraintFail;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/* --------------------------------------------- */
unsigned int
_pullInfoLen[PULL_INFO_MAX+1] = {
0, /* pullInfoUnknown */
7, /* pullInfoTensor */
7, /* pullInfoTensorInverse */
9, /* pullInfoHessian */
1, /* pullInfoInside */
3, /* pullInfoInsideGradient */
1, /* pullInfoHeight */
3, /* pullInfoHeightGradient */
9, /* pullInfoHeightHessian */
1, /* pullInfoHeightLaplacian */
1, /* pullInfoSeedPreThresh */
1, /* pullInfoSeedThresh */
1, /* pullInfoLiveThresh */
1, /* pullInfoLiveThresh2 */
1, /* pullInfoLiveThresh3 */
3, /* pullInfoTangent1 */
3, /* pullInfoTangent2 */
3, /* pullInfoNegativeTangent1 */
3, /* pullInfoNegativeTangent2 */
1, /* pullInfoIsovalue */
3, /* pullInfoIsovalueGradient */
9, /* pullInfoIsovalueHessian */
1, /* pullInfoStrength */
1, /* pullInfoQuality */
};
unsigned int
58 pullInfoLen( int info ) {
unsigned int ret;
if ( !airEnumValCheck( pullInfo, info ) ) {
ret = _pullInfoLen[info];
} else {
ret = 0;
}
return ret;
}
unsigned int
70 pullPropLen( int prop ) {
unsigned int ret;
switch ( prop ) {
case pullPropIdtag:
case pullPropIdCC:
case pullPropEnergy:
case pullPropStepEnergy:
case pullPropStepConstr:
case pullPropStuck:
case pullPropNeighDistMean:
case pullPropScale:
case pullPropStability:
ret = 1;
break;
case pullPropPosition:
case pullPropForce:
ret = 4;
break;
case pullPropNeighCovar:
ret = 10;
break;
case pullPropNeighCovar7Ten:
ret = 7;
break;
case pullPropNeighTanCovar:
ret = 6;
break;
default:
ret = 0;
break;
}
return ret;
}
pullInfoSpec *
106 pullInfoSpecNew( void ) {
pullInfoSpec *ispec;
ispec = AIR_CAST( pullInfoSpec *, calloc( 1, sizeof( pullInfoSpec ) ) );
if ( ispec ) {
ispec->info = pullInfoUnknown;
ispec->source = pullSourceUnknown;
ispec->volName = NULL;
ispec->item = 0; /* should be the unknown item for any kind */
ispec->prop = pullPropUnknown;
ispec->scale = AIR_NAN;
ispec->zero = AIR_NAN;
ispec->constraint = AIR_FALSE;
ispec->volIdx = UINT_MAX;
}
return ispec;
}
pullInfoSpec *
125 pullInfoSpecNix( pullInfoSpec *ispec ) {
if ( ispec ) {
airFree( ispec->volName );
airFree( ispec );
}
return NULL;
}
int
135 pullInfoSpecAdd( pullContext *pctx, pullInfoSpec *ispec ) {
static const char me[]="pullInfoSpecAdd";
unsigned int ii, vi, haveLen, needLen;
const gageKind *kind;
if ( !( pctx && ispec ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( pullInfo, ispec->info ) ) {
biffAddf( PULL, "%s: %d not a valid %s value", me,
ispec->info, pullInfo->name );
return 1;
}
if ( airEnumValCheck( pullSource, ispec->source ) ) {
biffAddf( PULL, "%s: %d not a valid %s value", me,
ispec->source, pullSource->name );
return 1;
}
if ( pctx->ispec[ispec->info] ) {
biffAddf( PULL, "%s: already set info %s ( %d )", me,
airEnumStr( pullInfo, ispec->info ), ispec->info );
return 1;
}
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
if ( pctx->ispec[ii] == ispec ) {
biffAddf( PULL, "%s( %s ): already got ispec %p as ispec[%u]", me,
airEnumStr( pullInfo, ispec->info ), AIR_VOIDP( ispec ), ii );
return 1;
}
}
if ( pctx->verbose ) {
printf( "%s: ispec %s from vol %s\n", me,
airEnumStr( pullInfo, ispec->info ), ispec->volName );
}
needLen = pullInfoLen( ispec->info );
if ( pullSourceGage == ispec->source ) {
vi = _pullVolumeIndex( pctx, ispec->volName );
if ( UINT_MAX == vi ) {
biffAddf( PULL, "%s( %s ): no volume has name \"%s\"", me,
airEnumStr( pullInfo, ispec->info ), ispec->volName );
return 1;
}
kind = pctx->vol[vi]->kind;
if ( airEnumValCheck( kind->enm, ispec->item ) ) {
biffAddf( PULL, "%s( %s ): %d not a valid \"%s\" item", me,
airEnumStr( pullInfo, ispec->info ), ispec->item, kind->name );
return 1;
}
haveLen = kind->table[ispec->item].answerLength;
if ( needLen != haveLen ) {
biffAddf( PULL, "%s( %s ): need len %u, but \"%s\" item \"%s\" has len %u",
me, airEnumStr( pullInfo, ispec->info ), needLen,
kind->name, airEnumStr( kind->enm, ispec->item ), haveLen );
return 1;
}
/* very tricky: seedOnly is initialized to true for everything */
if ( pullInfoSeedThresh != ispec->info
&& pullInfoSeedPreThresh != ispec->info ) {
/* if the info is neither seedthresh nor seedprethresh, then the
volume will have to be probed after the first iter, so turn
*off* seedOnly */
pctx->vol[vi]->seedOnly = AIR_FALSE;
}
/* less tricky: turn on forSeedPreThresh as needed;
its initialized to false */
if ( pullInfoSeedPreThresh == ispec->info ) {
pctx->vol[vi]->forSeedPreThresh = AIR_TRUE;
if ( pctx->verbose ) {
printf( "%s: volume %u %s used for %s\n", me, vi, pctx->vol[vi]->name,
airEnumStr( pullInfo, pullInfoSeedPreThresh ) );
}
}
/* now set item in gage query */
if ( gageQueryItemOn( pctx->vol[vi]->gctx, pctx->vol[vi]->gpvl,
ispec->item ) ) {
biffMovef( PULL, GAGE, "%s: trouble adding item %u to vol %u", me,
ispec->item, vi );
return 1;
}
ispec->volIdx = vi;
} else if ( pullSourceProp == ispec->source ) {
haveLen = pullPropLen( ispec->prop );
if ( needLen != haveLen ) {
biffAddf( PULL, "%s: need len %u, but \"%s\" \"%s\" has len %u",
me, needLen, pullProp->name,
airEnumStr( pullProp, ispec->prop ), haveLen );
return 1;
}
} else {
biffAddf( PULL, "%s: sorry, source %s unsupported", me,
airEnumStr( pullSource, ispec->source ) );
return 1;
}
if ( haveLen > 9 ) {
biffAddf( PULL, "%s: sorry, answer length ( %u ) > 9 unsupported", me,
haveLen );
return 1;
}
pctx->ispec[ispec->info] = ispec;
return 0;
}
/*
** sets:
** pctx->infoIdx[]
** pctx->infoTotalLen
** pctx->constraint
** pctx->constraintDim
** pctx->targetDim ( non-trivial logic for scale-space! )
*/
int
250 _pullInfoSetup( pullContext *pctx ) {
static const char me[]="_pullInfoSetup";
unsigned int ii;
pctx->infoTotalLen = 0;
pctx->constraint = 0;
pctx->constraintDim = 0;
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
if ( pctx->ispec[ii] ) {
pctx->infoIdx[ii] = pctx->infoTotalLen;
if ( pctx->verbose ) {
printf( "!%s: infoIdx[%u] ( %s ) = %u\n", me,
ii, airEnumStr( pullInfo, ii ), pctx->infoIdx[ii] );
}
pctx->infoTotalLen += pullInfoLen( ii );
if ( !pullInfoLen( ii ) ) {
biffAddf( PULL, "%s: got zero-length answer for ispec[%u]", me, ii );
return 1;
}
if ( pctx->ispec[ii]->constraint ) {
/* pullVolume *cvol; */
pctx->constraint = ii;
/* cvol = pctx->vol[pctx->ispec[ii]->volIdx]; */
}
}
}
if ( pctx->constraint ) {
pctx->constraintDim = _pullConstraintDim( pctx );
if ( -1 == pctx->constraintDim ) {
biffAddf( PULL, "%s: problem learning constraint dimension", me );
return 1;
}
if ( !pctx->flag.allowCodimension3Constraints && !pctx->constraintDim ) {
biffAddf( PULL, "%s: got constr dim 0 but co-dim 3 not allowed", me );
return 1;
}
if ( pctx->haveScale ) {
double *parmS, denS,
( *evalS )( double *, double, const double parm[PULL_ENERGY_PARM_NUM] );
switch ( pctx->interType ) {
case pullInterTypeUnivariate:
pctx->targetDim = 1 + pctx->constraintDim;
break;
case pullInterTypeSeparable:
/* HEY! need to check if this is true given enr and ens! */
pctx->targetDim = pctx->constraintDim;
break;
case pullInterTypeAdditive:
parmS = pctx->energySpecS->parm;
evalS = pctx->energySpecS->energy->eval;
evalS( &denS, _PULL_TARGET_DIM_S_PROBE, parmS );
if ( denS > 0 ) {
/* at small positive s, derivative was positive ==> attractive */
pctx->targetDim = pctx->constraintDim;
} else {
/* derivative was negative ==> repulsive */
pctx->targetDim = 1 + pctx->constraintDim;
}
break;
default:
biffAddf( PULL, "%s: sorry, intertype %s not handled here", me,
airEnumStr( pullInterType, pctx->interType ) );
break;
}
} else {
pctx->targetDim = pctx->constraintDim;
}
} else {
pctx->constraintDim = 0;
pctx->targetDim = 0;
}
if ( pctx->verbose ) {
printf( "!%s: infoTotalLen=%u, constr=%d, constr, targetDim = %d, %d\n",
me, pctx->infoTotalLen, pctx->constraint,
pctx->constraintDim, pctx->targetDim );
}
return 0;
}
static void
330 _infoCopy1( double *dst, const double *src ) {
dst[0] = src[0];
}
static void
335 _infoCopy2( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1];
}
static void
340 _infoCopy3( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2];
}
static void
345 _infoCopy4( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
}
static void
350 _infoCopy5( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
dst[4] = src[4];
}
static void
356 _infoCopy6( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
dst[4] = src[4]; dst[5] = src[5];
}
static void
362 _infoCopy7( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6];
}
static void
368 _infoCopy8( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7];
}
static void
374 _infoCopy9( double *dst, const double *src ) {
dst[0] = src[0]; dst[1] = src[1]; dst[2] = src[2]; dst[3] = src[3];
dst[4] = src[4]; dst[5] = src[5]; dst[6] = src[6]; dst[7] = src[7];
dst[8] = src[8];
}
380 void ( *_pullInfoCopy[10] )( double *, const double * ) = {
NULL,
_infoCopy1,
_infoCopy2,
_infoCopy3,
_infoCopy4,
_infoCopy5,
_infoCopy6,
_infoCopy7,
_infoCopy8,
_infoCopy9
};
int
394 pullInfoGet( Nrrd *ninfo, int info, pullContext *pctx ) {
static const char me[]="pullInfoGet";
size_t size[2];
unsigned int dim, pointNum, pointIdx, binIdx, outIdx, alen, aidx;
double *out_d;
pullBin *bin;
pullPoint *point;
if ( airEnumValCheck( pullInfo, info ) ) {
biffAddf( PULL, "%s: info %d not valid", me, info );
return 1;
}
pointNum = pullPointNumber( pctx );
alen = pullInfoLen( info );
aidx = pctx->infoIdx[info];
if ( 1 == alen ) {
dim = 1;
size[0] = pointNum;
} else {
dim = 2;
size[0] = alen;
size[1] = pointNum;
}
if ( nrrdMaybeAlloc_nva( ninfo, nrrdTypeDouble, dim, size ) ) {
biffMovef( PULL, NRRD, "%s: trouble allocating output", me );
return 1;
}
out_d = AIR_CAST( double *, ninfo->data );
outIdx = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
_pullInfoCopy[alen]( out_d + outIdx, point->info + aidx );
outIdx += alen;
}
}
return 0;
}
/* HEY this was written in a hurry;
** needs to be checked against parsing code */
int
439 pullInfoSpecSprint( char str[AIR_STRLEN_LARGE],
const pullContext *pctx, const pullInfoSpec *ispec ) {
static const char me[]="pullInfoSpecSprint";
const pullVolume *pvol;
char stmp[AIR_STRLEN_LARGE];
if ( !( str && pctx && ispec ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
strcpy( str, "" );
/* HEY: no bounds checking! */
strcat( str, airEnumStr( pullInfo, ispec->info ) );
if ( ispec->constraint ) {
strcat( str, "-c" );
}
strcat( str, ":" );
if ( pullSourceGage == ispec->source ) {
if ( UINT_MAX == ispec->volIdx ) {
biffAddf( PULL, "%s: never learned volIdx for \"%s\"", me,
ispec->volName );
return 1;
}
strcat( str, ispec->volName );
strcat( str, ":" );
pvol = pctx->vol[ispec->volIdx];
strcat( str, airEnumStr( pvol->kind->enm, ispec->item ) );
} else if ( pullSourceProp == ispec->source ) {
strcat( str, airEnumStr( pullProp, ispec->prop ) );
} else {
biffAddf( PULL, "%s: unexplained source %d", me, ispec->source );
return 1;
}
if ( ( pullSourceGage == ispec->source
&& 1 == pullInfoLen( ispec->info ) )
||
( pullSourceProp == ispec->source
&& 1 == pullPropLen( ispec->prop ) ) ) {
sprintf( stmp, "%g", ispec->zero );
strcat( str, stmp );
strcat( str, ":" );
sprintf( stmp, "%g", ispec->scale );
strcat( str, stmp );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
void
29 _pullInitParmInit( pullInitParm *initParm ) {
initParm->method = pullInitMethodUnknown;
initParm->liveThreshUse = AIR_FALSE;
initParm->unequalShapesAllow = AIR_FALSE;
initParm->jitter = 1.0;
initParm->numInitial = 0;
initParm->haltonStartIndex = 0;
initParm->samplesAlongScaleNum = 0;
initParm->ppvZRange[0] = 1;
initParm->ppvZRange[1] = 0;
initParm->pointPerVoxel = 0;
initParm->npos = NULL;
return;
}
#define CHECK( thing, min, max ) \
if ( !( AIR_EXISTS( iparm->thing ) \
&& min <= iparm->thing && iparm->thing <= max ) ) { \
48 biffAddf( PULL, "%s: initParm->" #thing " %g not in range [%g, %g]", \
me, iparm->thing, min, max ); \
return 1; \
}
int
_pullInitParmCheck( pullInitParm *iparm ) {
static const char me[]="_pullInitParmCheck";
if ( !AIR_IN_OP( pullInitMethodUnknown, iparm->method, pullInitMethodLast ) ) {
biffAddf( PULL, "%s: init method %d not valid", me, iparm->method );
return 1;
}
CHECK( jitter, 0.0, 2.0 );
switch ( iparm->method ) {
case pullInitMethodGivenPos:
if ( nrrdCheck( iparm->npos ) ) {
biffMovef( PULL, NRRD, "%s: got a broken npos", me );
return 1;
}
if ( !( 2 == iparm->npos->dim
&& 4 == iparm->npos->axis[0].size
&& ( nrrdTypeDouble == iparm->npos->type
|| nrrdTypeFloat == iparm->npos->type ) ) ) {
biffAddf( PULL, "%s: npos not a 2-D 4-by-N array of %s or %s"
"( got %u-D %u-by-X of %s )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
iparm->npos->dim,
AIR_CAST( unsigned int, iparm->npos->axis[0].size ),
airEnumStr( nrrdType, iparm->npos->type ) );
return 1;
}
break;
case pullInitMethodPointPerVoxel:
if ( iparm->pointPerVoxel < -3001 || iparm->pointPerVoxel > 10 ) {
biffAddf( PULL, "%s: pointPerVoxel %d unreasonable", me,
iparm->pointPerVoxel );
return 1;
}
if ( -1 == iparm->pointPerVoxel ) {
biffAddf( PULL, "%s: pointPerVoxel should be < -1 or >= 1", me );
return 1;
}
if ( 0 == iparm->jitter && 1 < iparm->pointPerVoxel ) {
biffAddf( PULL, "%s: must have jitter > 0 if pointPerVoxel ( %d ) > 1", me,
iparm->pointPerVoxel );
return 1;
}
break;
case pullInitMethodRandom:
case pullInitMethodHalton:
if ( !( iparm->numInitial >= 1 ) ) {
biffAddf( PULL, "%s: iparm->numInitial ( %d ) not >= 1\n", me,
iparm->numInitial );
return 1;
}
break;
/* no check needed on haltonStartIndex */
default:
biffAddf( PULL, "%s: init method %d valid but not handled?", me,
iparm->method );
return 1;
111 }
return 0;
}
int
pullInitRandomSet( pullContext *pctx, unsigned int numInitial ) {
static const char me[]="pullInitRandomSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !numInitial ) {
biffAddf( PULL, "%s: need non-zero numInitial", me );
return 1;
}
129 pctx->initParm.method = pullInitMethodRandom;
pctx->initParm.numInitial = numInitial;
return 0;
}
int
pullInitHaltonSet( pullContext *pctx, unsigned int numInitial,
unsigned int startIndex ) {
static const char me[]="pullInitHaltonSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !numInitial ) {
biffAddf( PULL, "%s: need non-zero numInitial", me );
return 1;
}
pctx->initParm.method = pullInitMethodHalton;
149 pctx->initParm.numInitial = numInitial;
pctx->initParm.haltonStartIndex = startIndex;
return 0;
}
int
pullInitPointPerVoxelSet( pullContext *pctx, int pointPerVoxel,
unsigned int zSlcMin, unsigned int zSlcMax,
unsigned int alongScaleNum,
double jitter ) {
static const char me[]="pullInitPointPerVoxelSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !pointPerVoxel ) {
biffAddf( PULL, "%s: need non-zero pointPerVoxel", me );
return 1;
}
if ( !AIR_EXISTS( jitter ) ) {
biffAddf( PULL, "%s: got non-existent jitter %g", me, jitter );
return 1;
}
pctx->initParm.method = pullInitMethodPointPerVoxel;
pctx->initParm.pointPerVoxel = pointPerVoxel;
pctx->initParm.samplesAlongScaleNum = alongScaleNum;
pctx->initParm.ppvZRange[0] = zSlcMin;
178 pctx->initParm.ppvZRange[1] = zSlcMax;
pctx->initParm.jitter = jitter;
return 0;
}
int
pullInitGivenPosSet( pullContext *pctx, const Nrrd *npos ) {
static const char me[]="pullInitGivenPosSet";
if ( !( pctx && npos ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
192 pctx->initParm.method = pullInitMethodGivenPos;
pctx->initParm.npos = npos;
return 0;
}
int
pullInitLiveThreshUseSet( pullContext *pctx, int liveThreshUse ) {
static const char me[]="pullInitLiveThreshUseSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
205
pctx->initParm.liveThreshUse = liveThreshUse;
return 0;
}
int
pullInitUnequalShapesAllowSet( pullContext *pctx, int allow ) {
static const char me[]="pullInitUnequalShapesAllowSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pctx->initParm.unequalShapesAllow = allow;
return 0;
}
#undef CHECK
FILE *
_pullPointAddLog = NULL;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
/* the Init( ) functions are up here for easier reference */
void
31 _pullIterParmInit( pullIterParm *iterParm ) {
iterParm->min = 0;
iterParm->max = 0;
iterParm->stuckMax = 4;
iterParm->constraintMax = 15;
iterParm->popCntlPeriod = 10;
iterParm->addDescent = 10;
iterParm->callback = 1;
iterParm->snap = 0;
iterParm->energyIncreasePermitHalfLife = 0;
return;
}
void
46 _pullSysParmInit( pullSysParm *sysParm ) {
sysParm->alpha = 0.5;
sysParm->beta = 0.5;
sysParm->gamma = 1;
sysParm->separableGammaLearnRescale = 8;
sysParm->wall = 1;
sysParm->radiusSpace = 1;
sysParm->radiusScale = 1;
sysParm->binWidthSpace = 1.001; /* supersititious */
sysParm->neighborTrueProb = 1.0;
sysParm->probeProb = 1.0;
sysParm->stepInitial = 1;
sysParm->opporStepScale = 1.0;
sysParm->backStepScale = 0.5;
sysParm->constraintStepMin = 0.0001;
sysParm->energyDecreaseMin = 0.001;
sysParm->energyDecreasePopCntlMin = 0.02;
sysParm->energyIncreasePermit = 0.0;
sysParm->fracNeighNixedMax = 0.25;
return;
}
void
70 _pullFlagInit( pullFlag *flag ) {
flag->permuteOnRebin = AIR_FALSE;
flag->noPopCntlWithZeroAlpha = AIR_FALSE;
flag->useBetaForGammaLearn = AIR_FALSE;
flag->restrictiveAddToBins = AIR_TRUE;
flag->energyFromStrength = AIR_FALSE;
flag->nixAtVolumeEdgeSpace = AIR_FALSE;
flag->nixAtVolumeEdgeSpaceInitRorH = AIR_FALSE;
flag->constraintBeforeSeedThresh = AIR_FALSE;
flag->noAdd = AIR_FALSE;
flag->popCntlEnoughTest = AIR_TRUE; /* really needs to be true by default */
flag->convergenceIgnoresPopCntl = AIR_FALSE; /* false by default for
backwards compatibility,
even thought this was
probably a mistake */
flag->binSingle = AIR_FALSE;
flag->allowCodimension3Constraints = AIR_FALSE;
flag->scaleIsTau = AIR_FALSE;
flag->startSkipsPoints = AIR_FALSE; /* must be false by default */
flag->zeroZ = AIR_FALSE;
return;
}
int
95 _pullIterParmCheck( pullIterParm *iterParm ) {
static const char me[]="_pullIterParmCheck";
if ( !( 1 <= iterParm->constraintMax
&& iterParm->constraintMax <= 500 ) ) {
biffAddf( PULL, "%s: iterParm->constraintMax %u not in range [%u, %u]",
me, iterParm->constraintMax, 1, _PULL_CONSTRAINT_ITER_MAX );
return 1;
}
return 0;
}
int
108 pullIterParmSet( pullContext *pctx, int which, unsigned int pval ) {
static const char me[]="pullIterParmSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_OP( pullIterParmUnknown, which, pullIterParmLast ) ) {
biffAddf( PULL, "%s: iter parm %d not valid", me, which );
return 1;
}
switch( which ) {
case pullIterParmMin:
pctx->iterParm.min = pval;
break;
case pullIterParmMax:
pctx->iterParm.max = pval;
break;
case pullIterParmStuckMax:
pctx->iterParm.stuckMax = pval;
break;
case pullIterParmConstraintMax:
pctx->iterParm.constraintMax = pval;
break;
case pullIterParmPopCntlPeriod:
pctx->iterParm.popCntlPeriod = pval;
break;
case pullIterParmAddDescent:
pctx->iterParm.addDescent = pval;
break;
case pullIterParmCallback:
pctx->iterParm.callback = pval;
break;
case pullIterParmSnap:
pctx->iterParm.snap = pval;
break;
case pullIterParmEnergyIncreasePermitHalfLife:
pctx->iterParm.energyIncreasePermitHalfLife = pval;
if ( pval ) {
pctx->eipScale = pow( 0.5, 1.0/pval );
} else {
pctx->eipScale = 1;
}
break;
default:
biffAddf( me, "%s: sorry, iter parm %d valid but not handled?", me, which );
return 1;
}
return 0;
}
#define CHECK( thing, min, max ) \
if ( !( AIR_EXISTS( sysParm->thing ) \
&& min <= sysParm->thing && sysParm->thing <= max ) ) { \
162 biffAddf( PULL, "%s: sysParm->" #thing " %g not in range [%g, %g]", \
me, sysParm->thing, min, max ); \
return 1; \
}
int
_pullSysParmCheck( pullSysParm *sysParm ) {
static const char me[]="_pullSysParmCheck";
/* these reality-check bounds are somewhat arbitrary */
CHECK( alpha, 0.0, 1.0 );
CHECK( beta, 0.0, 1.0 );
/* HEY: no check on gamma? */
CHECK( wall, 0.0, 100.0 );
CHECK( radiusSpace, 0.000001, 80.0 );
CHECK( radiusScale, 0.000001, 80.0 );
CHECK( binWidthSpace, 1.0, 15.0 );
CHECK( neighborTrueProb, 0.02, 1.0 );
CHECK( probeProb, 0.02, 1.0 );
if ( !( AIR_EXISTS( sysParm->stepInitial )
&& sysParm->stepInitial > 0 ) ) {
biffAddf( PULL, "%s: sysParm->stepInitial %g not > 0", me,
sysParm->stepInitial );
return 1;
}
CHECK( opporStepScale, 1.0, 5.0 );
CHECK( backStepScale, 0.01, 0.99 );
CHECK( constraintStepMin, 0.00000000000000001, 0.1 );
CHECK( energyDecreaseMin, -0.2, 1.0 );
CHECK( energyDecreasePopCntlMin, -1.0, 1.0 );
CHECK( energyIncreasePermit, 0.0, 1.0 );
193 CHECK( fracNeighNixedMax, 0.01, 0.99 );
return 0;
}
#undef CHECK
int
pullSysParmSet( pullContext *pctx, int which, double pval ) {
static const char me[]="pullSysParmSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_OP( pullSysParmUnknown, which, pullSysParmLast ) ) {
biffAddf( PULL, "%s: sys parm %d not valid", me, which );
return 1;
}
switch( which ) {
case pullSysParmAlpha:
pctx->sysParm.alpha = pval;
break;
case pullSysParmBeta:
pctx->sysParm.beta = pval;
break;
case pullSysParmGamma:
pctx->sysParm.gamma = pval;
break;
case pullSysParmSeparableGammaLearnRescale:
pctx->sysParm.separableGammaLearnRescale = pval;
break;
case pullSysParmStepInitial:
pctx->sysParm.stepInitial = pval;
break;
case pullSysParmRadiusSpace:
pctx->sysParm.radiusSpace = pval;
break;
case pullSysParmRadiusScale:
pctx->sysParm.radiusScale = pval;
break;
case pullSysParmBinWidthSpace:
pctx->sysParm.binWidthSpace = pval;
break;
case pullSysParmNeighborTrueProb:
pctx->sysParm.neighborTrueProb = pval;
break;
case pullSysParmProbeProb:
pctx->sysParm.probeProb = pval;
break;
case pullSysParmOpporStepScale:
pctx->sysParm.opporStepScale = pval;
break;
case pullSysParmBackStepScale:
pctx->sysParm.backStepScale = pval;
break;
case pullSysParmEnergyDecreasePopCntlMin:
pctx->sysParm.energyDecreasePopCntlMin = pval;
break;
case pullSysParmEnergyDecreaseMin:
pctx->sysParm.energyDecreaseMin = pval;
break;
case pullSysParmConstraintStepMin:
pctx->sysParm.constraintStepMin = pval;
break;
case pullSysParmEnergyIncreasePermit:
pctx->sysParm.energyIncreasePermit = pval;
break;
case pullSysParmFracNeighNixedMax:
pctx->sysParm.fracNeighNixedMax = pval;
break;
case pullSysParmWall:
pctx->sysParm.wall = pval;
break;
default:
biffAddf( me, "%s: sorry, sys parm %d valid but not handled?", me, which );
return 1;
}
return 0;
}
272 /*
******** pullFlagSet
**
** uniform way of setting all the boolean-ish flags
*/
int
pullFlagSet( pullContext *pctx, int which, int flag ) {
static const char me[]="pullFlagSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_OP( pullFlagUnknown, which, pullFlagLast ) ) {
biffAddf( PULL, "%s: flag %d not valid", me, which );
return 1;
}
switch ( which ) {
case pullFlagPermuteOnRebin:
pctx->flag.permuteOnRebin = flag;
break;
case pullFlagNoPopCntlWithZeroAlpha:
pctx->flag.noPopCntlWithZeroAlpha = flag;
break;
case pullFlagUseBetaForGammaLearn:
pctx->flag.useBetaForGammaLearn = flag;
break;
case pullFlagRestrictiveAddToBins:
pctx->flag.restrictiveAddToBins = flag;
break;
case pullFlagEnergyFromStrength:
pctx->flag.energyFromStrength = flag;
break;
case pullFlagNixAtVolumeEdgeSpace:
pctx->flag.nixAtVolumeEdgeSpace = flag;
break;
case pullFlagNixAtVolumeEdgeSpaceInitRorH:
pctx->flag.nixAtVolumeEdgeSpaceInitRorH = flag;
break;
case pullFlagConstraintBeforeSeedThresh:
pctx->flag.constraintBeforeSeedThresh = flag;
break;
case pullFlagNoAdd:
pctx->flag.noAdd = flag;
break;
case pullFlagPopCntlEnoughTest:
pctx->flag.popCntlEnoughTest = flag;
break;
case pullFlagConvergenceIgnoresPopCntl:
pctx->flag.convergenceIgnoresPopCntl = flag;
break;
case pullFlagBinSingle:
pctx->flag.binSingle = flag;
break;
case pullFlagAllowCodimension3Constraints:
pctx->flag.allowCodimension3Constraints = flag;
break;
case pullFlagScaleIsTau:
pctx->flag.scaleIsTau = flag;
break;
case pullFlagStartSkipsPoints:
pctx->flag.startSkipsPoints = flag;
break;
case pullFlagZeroZ:
pctx->flag.zeroZ = flag;
break;
default:
biffAddf( me, "%s: sorry, flag %d valid but not handled?", me, which );
return 1;
}
return 0;
}
/*
** HEY: its really confusing to have the array of per-CONTEXT volumes.
347 ** I know they're there to be copied upon task creation to create the
** per-TASK volumes, but its easy to think that one is supposed to be
** doing something with them, or that changes to them will have some
** effect . . .
*/
int
pullVerboseSet( pullContext *pctx, int verbose ) {
static const char me[]="pullVerboseSet";
unsigned int volIdx, taskIdx;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pctx->verbose = verbose;
for ( volIdx=0; volIdx<pctx->volNum; volIdx++ ) {
int v;
v = verbose > 0 ? verbose - 1 : 0;
gageParmSet( pctx->vol[volIdx]->gctx, gageParmVerbose, v );
}
for ( taskIdx=0; taskIdx<pctx->threadNum; taskIdx++ ) {
for ( volIdx=0; volIdx<pctx->volNum; volIdx++ ) {
int v;
v = verbose > 0 ? verbose - 1 : 0;
gageParmSet( pctx->task[taskIdx]->vol[volIdx]->gctx, gageParmVerbose, v );
372 }
}
return 0;
}
int
pullThreadNumSet( pullContext *pctx, unsigned int threadNum ) {
static const char me[]="pullThreadNumSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
384 }
pctx->threadNum = threadNum;
return 0;
}
int
pullRngSeedSet( pullContext *pctx, unsigned int rngSeed ) {
static const char me[]="pullRngSeedSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
396 }
pctx->rngSeed = rngSeed;
return 0;
}
int
pullProgressBinModSet( pullContext *pctx, unsigned int bmod ) {
static const char me[]="pullProgressBinModSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
408 }
pctx->progressBinMod = bmod;
return 0;
}
int
pullCallbackSet( pullContext *pctx,
void ( *iter_cb )( void *data_cb ),
void *data_cb ) {
static const char me[]="pullCallbackSet";
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pctx->iter_cb = iter_cb;
pctx->data_cb = data_cb;
return 0;
}
/*
******** pullInterEnergySet
**
** This is the single function for setting the inter-particle energy,
** which is clumsy because which pullEnergySpecs are necessary is
** different depending on interType. Pass NULL for those not needed.
**
435 ** Note that all the pullEnergySpecs inside the pctx are created
** by pullContextNew, so they should never be NULL. When a pullEnergySpec
** is not needed for a given interType, we set it to pullEnergyZero
** and a vector of NaNs.
*/
int
pullInterEnergySet( pullContext *pctx, int interType,
const pullEnergySpec *enspR,
const pullEnergySpec *enspS,
const pullEnergySpec *enspWin ) {
static const char me[]="pullInterEnergySet";
unsigned int zpi;
double zeroParm[PULL_ENERGY_PARM_NUM];
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_IN_OP( pullInterTypeUnknown, interType, pullInterTypeLast ) ) {
biffAddf( PULL, "%s: interType %d not valid", me, interType );
return 1;
}
for ( zpi=0; zpi<PULL_ENERGY_PARM_NUM; zpi++ ) {
zeroParm[zpi] = AIR_NAN;
}
#define CHECK_N_COPY( X ) \
if ( !ensp##X ) { \
biffAddf( PULL, "%s: need non-NULL ensp" #X " for interType %s", me, \
airEnumStr( pullInterType, interType ) ); \
return 1; \
} \
pullEnergySpecCopy( pctx->energySpec##X, ensp##X )
switch ( interType ) {
case pullInterTypeJustR:
/* 1: phi( r, s ) = phi_r( r ) */
case pullInterTypeUnivariate:
/* 2: phi( r, s ) = phi_r( u ); u = sqrt( r*r+s*s ) */
CHECK_N_COPY( R );
pullEnergySpecSet( pctx->energySpecS, pullEnergyZero, zeroParm );
pullEnergySpecSet( pctx->energySpecWin, pullEnergyZero, zeroParm );
break;
case pullInterTypeSeparable:
/* 3: phi( r, s ) = phi_r( r )*phi_s( s ) */
CHECK_N_COPY( R );
CHECK_N_COPY( S );
pullEnergySpecSet( pctx->energySpecWin, pullEnergyZero, zeroParm );
break;
case pullInterTypeAdditive:
/* 4: phi( r, s ) = beta*phi_r( r )*win( s ) + ( 1-beta )*win( r )*phi_s( s ) */
CHECK_N_COPY( R );
CHECK_N_COPY( S );
CHECK_N_COPY( Win );
break;
default:
biffAddf( PULL, "%s: interType %d valid but no handled?", me, interType );
492 return 1;
}
#undef CHECK_N_COPY
pctx->interType = interType;
return 0;
}
/*
** you can pass in a NULL FILE* if you want
*/
int
pullLogAddSet( pullContext *pctx, FILE *flog ) {
static const char me[]="pullLogAddSet";
if ( !( pctx ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
pctx->logAdd = flog;
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
#define DEBUG 1
/*
** HEY: this has to be threadsafe, at least threadsafe when there
** are no errors, because this can now be called from multiple
** tasks during population control
*/
pullPoint *
36 pullPointNew( pullContext *pctx ) {
static const char me[]="pullPointNew";
pullPoint *pnt;
unsigned int ii;
size_t pntSize;
pullPtrPtrUnion pppu;
if ( !pctx ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return NULL;
}
if ( !pctx->infoTotalLen ) {
biffAddf( PULL, "%s: can't allocate points w/out infoTotalLen set\n", me );
return NULL;
}
/* Allocate the pullPoint so that it has pctx->infoTotalLen doubles.
The pullPoint declaration has info[1], hence the "- 1" below */
pntSize = sizeof( pullPoint ) + sizeof( double )*( pctx->infoTotalLen - 1 );
pnt = AIR_CAST( pullPoint *, calloc( 1, pntSize ) );
if ( !pnt ) {
biffAddf( PULL, "%s: couldn't allocate point ( info len %u )\n", me,
pctx->infoTotalLen - 1 );
return NULL;
}
pnt->idtag = pctx->idtagNext++;
pnt->idCC = 0;
pnt->neighPoint = NULL;
pnt->neighPointNum = 0;
pppu.points = &( pnt->neighPoint );
pnt->neighPointArr = airArrayNew( pppu.v, &( pnt->neighPointNum ),
sizeof( pullPoint * ),
PULL_POINT_NEIGH_INCR );
pnt->neighPointArr->noReallocWhenSmaller = AIR_TRUE;
pnt->neighDistMean = 0;
ELL_10V_ZERO_SET( pnt->neighCovar );
pnt->stability = 0.0;
#if PULL_TANCOVAR
ELL_6V_ZERO_SET( pnt->neighTanCovar );
#endif
pnt->neighInterNum = 0;
pnt->stuckIterNum = 0;
#if PULL_PHIST
pnt->phist = NULL;
pnt->phistNum = 0;
pnt->phistArr = airArrayNew( AIR_CAST( void**, &( pnt->phist ) ),
&( pnt->phistNum ),
_PHN*sizeof( double ), 32 );
#endif
pnt->status = 0;
ELL_4V_SET( pnt->pos, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
pnt->energy = AIR_NAN;
ELL_4V_SET( pnt->force, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
pnt->stepEnergy = pctx->sysParm.stepInitial;
pnt->stepConstr = pctx->sysParm.stepInitial;
for ( ii=0; ii<pctx->infoTotalLen; ii++ ) {
pnt->info[ii] = AIR_NAN;
}
return pnt;
}
pullPoint *
98 pullPointNix( pullPoint *pnt ) {
pnt->neighPointArr = airArrayNuke( pnt->neighPointArr );
#if PULL_PHIST
pnt->phistArr = airArrayNuke( pnt->phistArr );
#endif
airFree( pnt );
return NULL;
}
#if PULL_PHIST
void
110 _pullPointHistInit( pullPoint *point ) {
airArrayLenSet( point->phistArr, 0 );
return;
}
void
117 _pullPointHistAdd( pullPoint *point, int cond, double val ) {
static const char me[]="_pullPointHistAdd";
unsigned int phistIdx;
phistIdx = airArrayLenIncr( point->phistArr, 1 );
ELL_4V_COPY( point->phist + _PHN*phistIdx, point->pos );
fprintf( stderr, "!%s: point %p pos = %.17g %.17g %.17g %.17g ( %g )\n", me,
point, point->pos[0], point->pos[1], point->pos[2], point->pos[3],
val );
( point->phist + _PHN*phistIdx )[4] = cond;
( point->phist + _PHN*phistIdx )[5] = val;
return;
}
#endif
/*
** HEY: there should be something like a "map" over all the points,
** which could implement all these redundant functions
*/
unsigned int
140 pullPointNumberFilter( const pullContext *pctx,
unsigned int idtagMin,
unsigned int idtagMax ) {
unsigned int binIdx, pointNum;
const pullBin *bin;
const pullPoint *point;
pointNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
unsigned int pointIdx;
bin = pctx->bin + binIdx;
if ( 0 == idtagMin && 0 == idtagMax ) {
pointNum += bin->pointNum;
} else {
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
pointNum += ( idtagMin <= point->idtag
&& ( 0 == idtagMax
|| point->idtag <= idtagMax ) );
}
}
}
return pointNum;
}
unsigned int
166 pullPointNumber( const pullContext *pctx ) {
return pullPointNumberFilter( pctx, 0, 0 );
}
double
172 _pullEnergyTotal( const pullContext *pctx ) {
unsigned int binIdx, pointIdx;
const pullBin *bin;
const pullPoint *point;
double sum;
sum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
sum += point->energy;
}
}
return sum;
}
void
190 _pullPointStepEnergyScale( pullContext *pctx, double scale ) {
unsigned int binIdx, pointIdx;
const pullBin *bin;
pullPoint *point;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
point->stepEnergy *= scale;
point->stepEnergy = AIR_MIN( point->stepEnergy,
_PULL_STEP_ENERGY_MAX );
}
}
return;
}
double
208 _pullStepInterAverage( const pullContext *pctx ) {
unsigned int binIdx, pointIdx, pointNum;
const pullBin *bin;
const pullPoint *point;
double sum, avg;
sum = 0;
pointNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
pointNum += bin->pointNum;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
sum += point->stepEnergy;
}
}
avg = ( !pointNum ? AIR_NAN : sum/pointNum );
return avg;
}
/* ^^^ vvv HEY HEY HEY: COPY + PASTE COPY + PASTE COPY + PASTE */
double
229 _pullStepConstrAverage( const pullContext *pctx ) {
unsigned int binIdx, pointIdx, pointNum;
const pullBin *bin;
const pullPoint *point;
double sum, avg;
sum = 0;
pointNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
pointNum += bin->pointNum;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
sum += point->stepConstr;
}
}
avg = ( !pointNum ? AIR_NAN : sum/pointNum );
return avg;
}
/*
** convenience function for learning a scalar AND its gradient or hessian
**
*/
double
254 pullPointScalar( const pullContext *pctx, const pullPoint *point, int sclInfo,
/* output */
double grad[3], double hess[9] ) {
static const char me[]="pullPointScalar";
double scl;
const pullInfoSpec *ispec;
int gradInfo[1+PULL_INFO_MAX] = {
0, /* pullInfoUnknown */
0, /* pullInfoTensor */
0, /* pullInfoTensorInverse */
0, /* pullInfoHessian */
pullInfoInsideGradient, /* pullInfoInside */
0, /* pullInfoInsideGradient */
pullInfoHeightGradient, /* pullInfoHeight */
0, /* pullInfoHeightGradient */
0, /* pullInfoHeightHessian */
0, /* pullInfoHeightLaplacian */
0, /* pullInfoSeedPreThresh */
0, /* pullInfoSeedThresh */
0, /* pullInfoLiveThresh */
0, /* pullInfoLiveThresh2 */
0, /* pullInfoLiveThresh3 */
0, /* pullInfoTangent1 */
0, /* pullInfoTangent2 */
0, /* pullInfoNegativeTangent1 */
0, /* pullInfoNegativeTangent2 */
pullInfoIsovalueGradient, /* pullInfoIsovalue */
0, /* pullInfoIsovalueGradient */
0, /* pullInfoIsovalueHessian */
0, /* pullInfoStrength */
};
int hessInfo[1+PULL_INFO_MAX] = {
0, /* pullInfoUnknown */
0, /* pullInfoTensor */
0, /* pullInfoTensorInverse */
0, /* pullInfoHessian */
0, /* pullInfoInside */
0, /* pullInfoInsideGradient */
pullInfoHeightHessian, /* pullInfoHeight */
0, /* pullInfoHeightGradient */
0, /* pullInfoHeightHessian */
0, /* pullInfoHeightLaplacian */
0, /* pullInfoSeedPreThresh */
0, /* pullInfoSeedThresh */
0, /* pullInfoLiveThresh */
0, /* pullInfoLiveThresh2 */
0, /* pullInfoLiveThresh3 */
0, /* pullInfoTangent1 */
0, /* pullInfoTangent2 */
0, /* pullInfoNegativeTangent1 */
0, /* pullInfoNegativeTangent2 */
pullInfoIsovalueHessian, /* pullInfoIsovalue */
0, /* pullInfoIsovalueGradient */
0, /* pullInfoIsovalueHessian */
0, /* pullInfoStrength */
};
const unsigned int *infoIdx;
infoIdx = pctx->infoIdx;
ispec = pctx->ispec[sclInfo];
/* NB: this "scl" is not scale-space scale; its the scaling
of the scalar. this is getting confusing ... */
scl = point->info[infoIdx[sclInfo]];
scl = ( scl - ispec->zero )*ispec->scale;
/* if ( 289 == pctx->iter ) {
fprintf( stderr, "!%s( %04u, %s )@( %g, %g ): ( %g - %g )*%g == %g\n",
me, point->idtag, airEnumStr( pullInfo, sclInfo ),
point->pos[0], point->pos[1],
point->info[infoIdx[sclInfo]], ispec->zero, ispec->scale, scl );
} */
if ( 0 && _pullVerbose ) {
if ( pullInfoSeedThresh == sclInfo ) {
printf( "!%s: seed thresh ( %g - %g )*%g == %g\n", me,
point->info[infoIdx[sclInfo]], ispec->zero, ispec->scale, scl );
}
}
/*
learned: this wasn't thought through: the idea was that the height
*laplacian* answer should be transformed by the *height* zero and
scale. scale might make sense, but not zero. This cost a few
hours of tracking down the fact that the first zero-crossing
detection phase of the lapl constraint was failing because the
laplacian was vacillating around hspec->zero, not 0.0 . . .
if ( pullInfoHeightLaplacian == sclInfo ) {
const pullInfoSpec *hspec;
hspec = pctx->ispec[pullInfoHeight];
scl = ( scl - hspec->zero )*hspec->scale;
} else {
scl = ( scl - ispec->zero )*ispec->scale;
}
*/
/*
fprintf( stderr, "!%s: %s = ( %g - %g )*%g = %g*%g = %g = %g\n", me,
airEnumStr( pullInfo, sclInfo ),
point->info[infoIdx[sclInfo]],
ispec->zero, ispec->scale,
point->info[infoIdx[sclInfo]] - ispec->zero, ispec->scale,
( point->info[infoIdx[sclInfo]] - ispec->zero )*ispec->scale,
scl );
*/
if ( grad && gradInfo[sclInfo] ) {
const double *ptr = point->info + infoIdx[gradInfo[sclInfo]];
ELL_3V_SCALE( grad, ispec->scale, ptr );
}
if ( hess && hessInfo[sclInfo] ) {
const double *ptr = point->info + infoIdx[hessInfo[sclInfo]];
ELL_3M_SCALE( hess, ispec->scale, ptr );
}
return scl;
}
int
366 pullProbe( pullTask *task, pullPoint *point ) {
static const char me[]="pullProbe";
unsigned int ii, gret=0;
int edge;
/*
fprintf( stderr, "!%s: task->probeSeedPreThreshOnly = %d\n", me,
task->probeSeedPreThreshOnly );
*/
#if 0
static int opened=AIR_FALSE;
static FILE *flog;
#endif
/*
fprintf( stderr, "%s( %u, %u ): A volNum = %u\n", me, task->pctx->iter, point->idtag, task->pctx->volNum );
*/
#if 0
static int logIdx=0, logDone=AIR_FALSE, logStarted=AIR_FALSE;
static Nrrd *nlog;
static double *log=NULL;
if ( !logStarted ) {
if ( 81 == point->idtag ) {
printf( "\n\n%s: ###### HELLO begin logging . . .\n\n\n", me );
/* knowing the logIdx at the end of logging . . . */
nlog = nrrdNew( );
nrrdMaybeAlloc_va( nlog, nrrdTypeDouble, 2,
AIR_CAST( size_t, 25 ),
AIR_CAST( size_t, 2754 ) );
log = AIR_CAST( double*, nlog->data );
logStarted = AIR_TRUE;
}
}
#endif
if ( !ELL_4V_EXISTS( point->pos ) ) {
fprintf( stderr, "%s: pnt %u non-exist pos ( %g, %g, %g, %g )\n\n!!!\n\n\n",
me, point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
biffAddf( PULL, "%s: pnt %u non-exist pos ( %g, %g, %g, %g )",
me, point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
return 1;
/* can't probe, but make it go away as quickly as possible */
/*
ELL_4V_SET( point->pos, 0, 0, 0, 0 );
point->status |= PULL_STATUS_NIXME_BIT;
return 0;
*/
}
if ( task->pctx->verbose > 3 ) {
printf( "%s: hello; probing %u volumes\n", me, task->pctx->volNum );
}
edge = AIR_FALSE;
task->pctx->count[pullCountProbe] += 1;
/*
fprintf( stderr, "%s( %u, %u ): B volNum = %u\n", me, task->pctx->iter, point->idtag, task->pctx->volNum );
*/
for ( ii=0; ii<task->pctx->volNum; ii++ ) {
pullVolume *vol;
vol = task->vol[ii];
if ( task->probeSeedPreThreshOnly
&& !( vol->forSeedPreThresh ) ) {
/* we're here *only* to probe SeedPreThresh,
and this volume isn't used for that */
continue;
}
if ( task->pctx->iter && vol->seedOnly ) {
/* its after the 1st iteration ( #0 ), and this vol is only for seeding */
continue;
}
/* HEY should task->vol[ii]->scaleNum be the using-scale-space test? */
if ( !task->vol[ii]->ninScale ) {
gret = gageProbeSpace( task->vol[ii]->gctx,
point->pos[0], point->pos[1], point->pos[2],
AIR_FALSE /* index-space */,
AIR_TRUE /* clamp */ );
} else {
if ( task->pctx->verbose > 3 ) {
printf( "%s: vol[%u] has scale ( %u )-> "
"gageStackProbeSpace( %p ) ( v %d )\n",
me, ii, task->vol[ii]->scaleNum,
AIR_VOIDP( task->vol[ii]->gctx ),
task->vol[ii]->gctx->verbose );
}
/*
if ( 81 == point->idtag ) {
printf( "%s: probing vol[%u] @ %g %g %g %g\n", me, ii,
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
}
*/
gret = gageStackProbeSpace( task->vol[ii]->gctx,
point->pos[0], point->pos[1], point->pos[2],
( task->pctx->flag.scaleIsTau
? gageSigOfTau( point->pos[3] )
: point->pos[3] ),
AIR_FALSE /* index-space */,
AIR_TRUE /* clamp */ );
}
if ( gret ) {
biffAddf( PULL, "%s: probe failed on vol %u/%u: ( %d ) %s", me,
ii, task->pctx->volNum,
task->vol[ii]->gctx->errNum, task->vol[ii]->gctx->errStr );
return 1;
}
/*
if ( !edge
&& AIR_ABS( point->pos[1] - 67 ) < 1
&& AIR_ABS( point->pos[2] - 67 ) < 1
&& point->pos[3] > 3.13
&& !!task->vol[ii]->gctx->edgeFrac ) {
fprintf( stderr, "!%s( %u @ %g, %g, %g, %g ): "
"vol[%u]->gctx->edgeFrac %g => edge bit on\n",
me, point->idtag,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3],
ii, task->vol[ii]->gctx->edgeFrac );
}
*/
edge |= !!task->vol[ii]->gctx->edgeFrac;
}
if ( edge ) {
point->status |= PULL_STATUS_EDGE_BIT;
} else {
point->status &= ~PULL_STATUS_EDGE_BIT;
}
/* maybe is a little stupid to have the infos indexed this way,
since it means that we always have to loop through all indices,
but at least the compiler can unroll it . . . */
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
unsigned int alen, aidx;
const pullInfoSpec *ispec;
ispec = task->pctx->ispec[ii];
if ( ispec ) {
alen = _pullInfoLen[ii];
aidx = task->pctx->infoIdx[ii];
if ( pullSourceGage == ispec->source ) {
_pullInfoCopy[alen]( point->info + aidx, task->ans[ii] );
/* if ( 289 == task->pctx->iter ) {
fprintf( stderr, "!%s( %u ): copied info %u ( %s ) len %u\n", me, point->idtag,
ii, airEnumStr( pullInfo, ii ), alen );
if ( 1 == alen ) {
fprintf( stderr, "!%s( %u ): ( point->info + %u )[0] = %g\n", me, point->idtag,
aidx, ( point->info + aidx )[0] );
}
} */
/*
if ( 81 == point->idtag ) {
pullVolume *vol;
pullInfoSpec *isp;
isp = task->pctx->ispec[ii];
vol = task->pctx->vol[isp->volIdx];
if ( 1 == alen ) {
printf( "!%s: info[%u] %s: %s( \"%s\" ) = %g\n", me,
ii, airEnumStr( pullInfo, ii ),
airEnumStr( vol->kind->enm, isp->item ),
vol->name, task->ans[ii][0] );
} else {
unsigned int vali;
printf( "!%s: info[%u] %s: %s( \"%s\" ) =\n", me,
ii, airEnumStr( pullInfo, ii ),
airEnumStr( vol->kind->enm, isp->item ), vol->name );
for ( vali=0; vali<alen; vali++ ) {
printf( "!%s: [%u] %g\n", me, vali,
task->ans[ii][vali] );
}
}
}
*/
} else if ( pullSourceProp == ispec->source ) {
switch ( ispec->prop ) {
case pullPropIdtag:
point->info[aidx] = point->idtag;
break;
case pullPropIdCC:
point->info[aidx] = point->idCC;
break;
case pullPropEnergy:
point->info[aidx] = point->energy;
break;
case pullPropStepEnergy:
point->info[aidx] = point->stepEnergy;
break;
case pullPropStepConstr:
point->info[aidx] = point->stepConstr;
break;
case pullPropStuck:
point->info[aidx] = ( ( point->status & PULL_STATUS_STUCK_BIT )
? point->stuckIterNum
: 0 );
break;
case pullPropPosition:
ELL_4V_COPY( point->info + aidx, point->pos );
break;
case pullPropForce:
ELL_4V_COPY( point->info + aidx, point->force );
break;
case pullPropNeighDistMean:
point->info[aidx] = point->neighDistMean;
break;
case pullPropScale:
point->info[aidx] = point->pos[3];
break;
case pullPropNeighCovar:
ELL_10V_COPY( point->info + aidx, point->neighCovar );
break;
case pullPropNeighCovar7Ten:
TEN_T_SET( point->info + aidx, 1.0f,
point->neighCovar[0],
point->neighCovar[1],
point->neighCovar[2],
point->neighCovar[4],
point->neighCovar[5],
point->neighCovar[7] );
break;
#if PULL_TANCOVAR
case pullPropNeighTanCovar:
TEN_T_SET( point->info + aidx, 1.0f,
point->neighTanCovar[0],
point->neighTanCovar[1],
point->neighTanCovar[2],
point->neighTanCovar[3],
point->neighTanCovar[4],
point->neighTanCovar[5] );
break;
#endif
case pullPropStability:
point->info[aidx] = point->stability;
break;
default:
break;
}
}
}
}
#if 0
if ( logStarted && !logDone ) {
unsigned int ai;
/* the actual logging */
log[0] = point->idtag;
ELL_4V_COPY( log + 1, point->pos );
for ( ai=0; ai<20; ai++ ) {
log[5 + ai] = point->info[ai];
}
log += nlog->axis[0].size;
logIdx++;
if ( 1 == task->pctx->iter && 81 == point->idtag ) {
printf( "\n\n%s: ###### OKAY done logging ( %u ). . .\n\n\n", me, logIdx );
nrrdSave( "probelog.nrrd", nlog, NULL );
nlog = nrrdNuke( nlog );
logDone = AIR_TRUE;
}
}
#endif
#if 0
if ( !opened ) {
flog = fopen( "flog.txt", "w" );
opened = AIR_TRUE;
}
if ( opened ) {
fprintf( flog, "%s( %u ): spthr( %g, %g, %g, %g ) = %g\n", me, point->idtag,
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
point->info[task->pctx->infoIdx[pullInfoSeedPreThresh]] );
}
#endif
return 0;
}
static int
639 _threshFail( const pullContext *pctx, const pullPoint *point, int info ) {
/* static const char me[]="_threshFail"; */
double val;
int ret;
if ( pctx->ispec[info] ) {
val = pullPointScalar( pctx, point, info, NULL, NULL );
ret = ( val < 0 );
/*
fprintf( stderr, "%s( %s ): val=%g -> ret=%d\n", me,
airEnumStr( pullInfo, info ), val, ret );
*/
} else {
ret = AIR_FALSE;
}
return ret;
}
int
658 pullPointInitializePerVoxel( const pullContext *pctx,
const unsigned int pointIdx,
pullPoint *point, pullVolume *scaleVol,
/* output */
int *createFailP ) {
static const char me[]="pullPointInitializePerVoxel";
unsigned int vidx[3], pix;
double iPos[3];
airRandMTState *rng;
pullVolume *seedVol;
gageShape *seedShape;
int reject, rejectEdge, constrFail;
unsigned int k;
seedVol = pctx->vol[pctx->ispec[pullInfoSeedThresh]->volIdx];
seedShape = seedVol->gctx->shape;
rng = pctx->task[0]->rng;
/* Obtain voxel and indices from pointIdx */
/* axis ordering for this is x, y, z, scale */
pix = pointIdx;
if ( pctx->initParm.pointPerVoxel > 0 ) {
pix /= pctx->initParm.pointPerVoxel;
} else {
pix *= -pctx->initParm.pointPerVoxel;
}
vidx[0] = pix % seedShape->size[0];
pix = ( pix - vidx[0] )/seedShape->size[0];
vidx[1] = pix % seedShape->size[1];
pix = ( pix - vidx[1] )/seedShape->size[1];
if ( pctx->initParm.ppvZRange[0] <= pctx->initParm.ppvZRange[1] ) {
unsigned int zrn;
zrn = pctx->initParm.ppvZRange[1] - pctx->initParm.ppvZRange[0] + 1;
vidx[2] = ( pix % zrn ) + pctx->initParm.ppvZRange[0];
pix = ( pix - ( pix % zrn ) )/zrn;
} else {
vidx[2] = pix % seedShape->size[2];
pix = ( pix - vidx[2] )/seedShape->size[2];
}
for ( k=0; k<=2; k++ ) {
iPos[k] = vidx[k] + pctx->initParm.jitter*( airDrandMT_r( rng )-0.5 );
}
gageShapeItoW( seedShape, point->pos, iPos );
if ( pctx->flag.zeroZ ) {
point->pos[2] = 0.0;
}
if ( 0 && _pullVerbose ) {
printf( "!%s: pointIdx %u -> vidx %u %u %u ( %u )\n"
" -> iPos %g %g %g -> wPos %g %g %g\n",
me, pointIdx, vidx[0], vidx[1], vidx[2], pix,
iPos[0], iPos[1], iPos[2],
point->pos[0], point->pos[1], point->pos[2] );
}
/* Compute sigma coordinate from pix */
if ( pctx->haveScale ) {
int outside;
double aidx, bidx;
/* pix should already be integer in [0, pctx->samplesAlongScaleNum-1 )]. */
aidx = pix + pctx->initParm.jitter*( airDrandMT_r( rng )-0.5 );
bidx = AIR_AFFINE( -0.5, aidx, pctx->initParm.samplesAlongScaleNum-0.5,
0.0, scaleVol->scaleNum-1 );
point->pos[3] = gageStackItoW( scaleVol->gctx, bidx, &outside );
if ( pctx->flag.scaleIsTau ) {
point->pos[3] = gageTauOfSig( point->pos[3] );
}
if ( 0 && _pullVerbose ) {
printf( "!%s( %u ): pix %u -> a %g b %g -> wpos %g\n", me, point->idtag,
pix, aidx, bidx, point->pos[3] );
}
} else {
point->pos[3] = 0;
}
if ( pctx->ispec[pullInfoSeedPreThresh] ) {
/* we first do a special-purpose probe just for SeedPreThresh */
pctx->task[0]->probeSeedPreThreshOnly = AIR_TRUE;
if ( pullProbe( pctx->task[0], point ) ) {
biffAddf( PULL, "%s: pre-probing pointIdx %u of world", me, pointIdx );
return 1;
}
pctx->task[0]->probeSeedPreThreshOnly = AIR_FALSE;
if ( _threshFail( pctx, point, pullInfoSeedPreThresh ) ) {
reject = AIR_TRUE;
/* HEY! this obviously need to be re-written */
goto finish;
}
}
/* else, we didn't have a SeedPreThresh, or we did, and we passed
it. Now we REDO the probe, including possibly re-learning
SeedPreThresh, which is silly, but the idea is that this is a
small price compared to what has been saved by avoiding all the
gageProbe( )s on volumes unrelated to SeedPreThresh */
if ( pullProbe( pctx->task[0], point ) ) {
biffAddf( PULL, "%s: probing pointIdx %u of world", me, pointIdx );
return 1;
}
constrFail = AIR_FALSE;
reject = AIR_FALSE;
/* Check we pass pre-threshold */
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoSeedPreThresh );
if ( !pctx->flag.constraintBeforeSeedThresh ) {
/* we should be guaranteed to have a seed thresh info */
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoSeedThresh );
if ( pctx->initParm.liveThreshUse ) {
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh2 );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh3 );
}
}
if ( !reject && pctx->constraint ) {
if ( _pullConstraintSatisfy( pctx->task[0], point,
10*_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: on pnt %u",
me, pointIdx );
return 1;
}
reject |= constrFail;
/* post constraint-satisfaction, we certainly have to assert thresholds */
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoSeedThresh );
if ( pctx->initParm.liveThreshUse ) {
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh2 );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh3 );
}
if ( pctx->flag.nixAtVolumeEdgeSpace
&& ( point->status & PULL_STATUS_EDGE_BIT ) ) {
rejectEdge = AIR_TRUE;
} else {
rejectEdge = AIR_FALSE;
}
reject |= rejectEdge;
if ( pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): constr %d, seed %d, thresh %d %d %d, edge %d\n",
me, point->idtag, constrFail,
_threshFail( pctx, point, pullInfoSeedThresh ),
_threshFail( pctx, point, pullInfoLiveThresh ),
_threshFail( pctx, point, pullInfoLiveThresh2 ),
_threshFail( pctx, point, pullInfoLiveThresh3 ), rejectEdge );
}
} else {
constrFail = AIR_FALSE;
}
finish:
/* Gather consensus */
if ( reject ) {
*createFailP = AIR_TRUE;
} else {
*createFailP = AIR_FALSE;
}
return 0;
}
static void
820 _pullUnitToWorld( const pullContext *pctx, const pullVolume *scaleVol,
double wrld[4], const double unit[4] ) {
/* static const char me[]="_pullUnitToWorld"; */
wrld[0] = AIR_AFFINE( 0.0, unit[0], 1.0, pctx->bboxMin[0], pctx->bboxMax[0] );
wrld[1] = AIR_AFFINE( 0.0, unit[1], 1.0, pctx->bboxMin[1], pctx->bboxMax[1] );
wrld[2] = AIR_AFFINE( 0.0, unit[2], 1.0, pctx->bboxMin[2], pctx->bboxMax[2] );
if ( pctx->haveScale ) {
double sridx;
int outside;
sridx = AIR_AFFINE( 0.0, unit[3], 1.0, 0, scaleVol->scaleNum-1 );
wrld[3] = gageStackItoW( scaleVol->gctx, sridx, &outside );
if ( pctx->flag.scaleIsTau ) {
wrld[3] = gageTauOfSig( wrld[3] );
}
} else {
wrld[3] = 0.0;
}
/*
fprintf( stderr, "!%s: ( %g, %g, %g, %g ) -- [%g, %g]x[%g, %g]x[%g, %g]--> ( %g, %g, %g, %g )\n", me,
unit[0], unit[1], unit[2], unit[3],
pctx->bboxMin[0], pctx->bboxMin[1], pctx->bboxMin[2],
pctx->bboxMax[0], pctx->bboxMax[1], pctx->bboxMax[2],
wrld[0], wrld[1], wrld[2], wrld[3] );
*/
return;
}
int
849 pullPointInitializeRandomOrHalton( pullContext *pctx,
const unsigned int pointIdx,
pullPoint *point, pullVolume *scaleVol ) {
static const char me[]="pullPointInitializeRandomOrHalton";
int reject, verbo;
airRandMTState *rng;
unsigned int tryCount = 0, threshFailCount = 0, spthreshFailCount = 0,
constrFailCount = 0;
rng = pctx->task[0]->rng;
do {
double rpos[4];
/* fprintf( stderr, "!%s: starting doooo ( tryCount %u )!\n", me, tryCount ); */
tryCount++;
reject = AIR_FALSE;
_pullPointHistInit( point );
/* Populate tentative random point */
if ( pullInitMethodHalton == pctx->initParm.method ) {
/* we generate all 4 coordinates, even if we don't need them all */
airHalton( rpos, ( pointIdx + threshFailCount + constrFailCount
+ pctx->haltonOffset + pctx->initParm.haltonStartIndex ),
airPrimeList, 4 );
/*
fprintf( stderr, "!%s( %u/%u ): halton( %u=%u+%u+%u+%u+%u ) => %g %g %g %g\n",
me, pointIdx, pctx->idtagNext,
( pointIdx + threshFailCount + constrFailCount +
pctx->haltonOffset + pctx->initParm.haltonStartIndex ),
pointIdx, threshFailCount, constrFailCount,
pctx->haltonOffset, pctx->initParm.haltonStartIndex,
rpos[0], rpos[1], rpos[2], rpos[3] );
*/
/*
fprintf( stderr, "%g %g %g %g ",
rpos[0], rpos[1], rpos[2], rpos[3] );
*/
if ( !pctx->haveScale ) {
rpos[3] = 0;
}
} else {
ELL_3V_SET( rpos, airDrandMT_r( rng ), airDrandMT_r( rng ), airDrandMT_r( rng ) );
if ( pctx->haveScale ) {
rpos[3] = airDrandMT_r( rng );
} else {
rpos[3] = 0;
}
}
_pullUnitToWorld( pctx, scaleVol, point->pos, rpos );
if ( pctx->flag.zeroZ ) {
point->pos[2] = 0.0;
}
/*
verbo = ( AIR_ABS( -0.246015 - point->pos[0] ) < 0.1 &&
AIR_ABS( -144.78 - point->pos[0] ) < 0.1 &&
AIR_ABS( -85.3813 - point->pos[0] ) < 0.1 );
*/
verbo = AIR_FALSE;
if ( verbo ) {
fprintf( stderr, "%s: verbo on for point %u at %g %g %g %g\n", me,
point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
}
_pullPointHistAdd( point, pullCondOld, AIR_NAN );
/* HEY copy and paste */
if ( pctx->ispec[pullInfoSeedPreThresh] ) {
/* we first do a special-purpose probe just for SeedPreThresh */
pctx->task[0]->probeSeedPreThreshOnly = AIR_TRUE;
if ( pullProbe( pctx->task[0], point ) ) {
biffAddf( PULL, "%s: pre-probing pointIdx %u of world", me, pointIdx );
return 1;
}
pctx->task[0]->probeSeedPreThreshOnly = AIR_FALSE;
if ( _threshFail( pctx, point, pullInfoSeedPreThresh ) ) {
threshFailCount++;
spthreshFailCount++;
reject = AIR_TRUE;
goto reckoning;
}
}
if ( pullProbe( pctx->task[0], point ) ) {
biffAddf( PULL, "%s: probing pointIdx %u of world", me, pointIdx );
return 1;
}
/* Check we pass pre-threshold */
#define THRESH_TEST( INFO ) \
if ( pctx->ispec[INFO] && _threshFail( pctx, point, INFO ) ) { \
threshFailCount++; \
reject = AIR_TRUE; \
goto reckoning; \
}
/* fprintf( stderr, "!%s: bi ngo 0 ( %d ) %d %p\n", me,
!pctx->flag.constraintBeforeSeedThresh,
pctx->initParm.liveThreshUse, pctx->ispec[pullInfoLiveThresh] ); */
if ( !pctx->flag.constraintBeforeSeedThresh ) {
THRESH_TEST( pullInfoSeedThresh );
if ( pctx->initParm.liveThreshUse ) {
THRESH_TEST( pullInfoLiveThresh );
THRESH_TEST( pullInfoLiveThresh2 );
THRESH_TEST( pullInfoLiveThresh3 );
}
}
/* fprintf( stderr, "!%s: bi ngo 1\n", me ); */
if ( pctx->constraint ) {
int constrFail;
/* fprintf( stderr, "!%s: calling _pullConstraintSatisfy( %u )\n", me, pointIdx ); */
if ( _pullConstraintSatisfy( pctx->task[0], point,
_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: trying constraint on point %u", me, pointIdx );
return 1;
}
#if PULL_PHIST
if ( DEBUG ) {
Nrrd *nhist;
FILE *fhist;
char fname[AIR_STRLEN_SMALL];
nhist = nrrdNew( );
if ( pullPositionHistoryNrrdGet( nhist, pctx, point ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
sprintf( fname, "%04u-%04u-%04u-phist.nrrd", pctx->iter,
point->idtag, tryCount );
if ( ( fhist = fopen( fname, "w" ) ) ) {
if ( nrrdSave( fname, nhist, NULL ) ) {
biffMovef( PULL, NRRD, "%s: trouble", me );
return 1;
}
fclose( fhist );
}
nrrdNuke( nhist );
}
#endif
if ( constrFail ) {
constrFailCount++;
reject = AIR_TRUE;
goto reckoning;
}
/* fprintf( stderr, "!%s: bi ngo 2\n", me ); */
/* post constraint-satisfaction, we certainly have to assert thresholds */
THRESH_TEST( pullInfoSeedThresh );
if ( pctx->initParm.liveThreshUse ) {
THRESH_TEST( pullInfoLiveThresh );
THRESH_TEST( pullInfoLiveThresh2 );
THRESH_TEST( pullInfoLiveThresh3 );
}
/* fprintf( stderr, "!%s: bi ngo 3 ( reject=%d )\n", me, reject ); */
}
reckoning:
if ( reject ) {
if ( threshFailCount + constrFailCount >= _PULL_RANDOM_SEED_TRY_MAX ) {
/* Very bad luck; we've too many times */
biffAddf( PULL, "%s: failed too often ( %u times ) placing point %u: "
"%u fails on thresh ( %u on pre-thresh ), %u on constr",
me, _PULL_RANDOM_SEED_TRY_MAX, pointIdx,
threshFailCount, spthreshFailCount, constrFailCount );
return 1;
}
}
} while ( reject );
if ( pullInitMethodHalton == pctx->initParm.method ) {
pctx->haltonOffset += threshFailCount + constrFailCount;
}
1015
return 0;
}
int
pullPointInitializeGivenPos( pullContext *pctx,
const double *posData,
const unsigned int pointIdx,
pullPoint *point,
/* output */
int *createFailP ) {
static const char me[]="pullPointInitializeGivenPos";
int reject, rejectEdge;
/* Copy nrrd point into pullPoint */
ELL_4V_COPY( point->pos, posData + 4*pointIdx );
if ( pctx->flag.zeroZ ) {
point->pos[2] = 0.0;
}
/*
if ( AIR_ABS( 247.828 - point->pos[0] ) < 0.1 &&
AIR_ABS( 66.8817 - point->pos[1] ) < 0.1 &&
AIR_ABS( 67.0031 - point->pos[2] ) < 0.1 ) {
fprintf( stderr, "%s: --------- point %u at %g %g %g %g\n", me,
point->idtag,
point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
}
*/
/* we're dictating positions, but still have to do initial probe,
and possibly liveThresholding */
if ( pullProbe( pctx->task[0], point ) ) {
biffAddf( PULL, "%s: probing pointIdx %u of npos", me, pointIdx );
return 1;
}
reject = AIR_FALSE;
if ( pctx->flag.nixAtVolumeEdgeSpace
&& ( point->status & PULL_STATUS_EDGE_BIT ) ) {
rejectEdge = AIR_TRUE;
} else {
rejectEdge = AIR_FALSE;
}
reject |= rejectEdge;
if ( pctx->initParm.liveThreshUse ) {
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh2 );
if ( !reject ) reject |= _threshFail( pctx, point, pullInfoLiveThresh3 );
}
/*
if ( reject ) {
fprintf( stderr, "!%s( %u ): edge %d thresh %d %d %d\n",
me, point->idtag, rejectEdge,
_threshFail( pctx, point, pullInfoLiveThresh ),
_threshFail( pctx, point, pullInfoLiveThresh2 ),
_threshFail( pctx, point, pullInfoLiveThresh3 ) );
}
*/
if ( reject ) {
*createFailP = AIR_TRUE;
} else {
*createFailP = AIR_FALSE;
}
return 0;
}
/*
** _pullPointSetup sets:
**
** This is only called by the master thread
1087 **
** this should set stuff to be like after an update stage and
** just before the rebinning
*/
int
_pullPointSetup( pullContext *pctx ) {
static const char me[]="_pullPointSetup";
char doneStr[AIR_STRLEN_SMALL];
unsigned int pointIdx, binIdx, tick, pn;
pullPoint *point;
pullBin *bin;
int createFail, added;
airArray *mop;
Nrrd *npos;
pullVolume *seedVol, *scaleVol;
gageShape *seedShape;
double factor, *posData;
unsigned int totalNumPoints, voxNum, ii;
/* on using pullBinsPointMaybeAdd: This is used only in the context
of constraints; it makes sense to be a little more selective
about adding points, so that they aren't completely piled on top
of each other. This relies on _PULL_BINNING_MAYBE_ADD_THRESH: its
tempting to set this value high, to more aggressively limit the
number of points added, but that's really the job of population
control, and we can't always guarantee that constraint manifolds
will be well-sampled ( with respect to pctx->radiusSpace and
pctx->radiusScale ) to start with */
if ( pctx->verbose ) {
printf( "%s: beginning ( %s ). . . ", me,
airEnumStr( pullInitMethod, pctx->initParm.method ) );
fflush( stdout );
}
mop = airMopNew( );
switch ( pctx->initParm.method ) {
case pullInitMethodGivenPos:
npos = nrrdNew( );
airMopAdd( mop, npos, ( airMopper )nrrdNuke, airMopAlways );
/* even if npos came in as double, we have to copy it */
if ( nrrdConvert( npos, pctx->initParm.npos, nrrdTypeDouble ) ) {
biffMovef( PULL, NRRD, "%s: trouble converting npos", me );
airMopError( mop ); return 1;
}
posData = AIR_CAST( double *, npos->data );
if ( pctx->initParm.numInitial || pctx->initParm.pointPerVoxel ) {
printf( "%s: with npos, overriding both numInitial ( %u ) "
"and pointPerVoxel ( %d )\n", me, pctx->initParm.numInitial,
pctx->initParm.pointPerVoxel );
}
totalNumPoints = AIR_CAST( unsigned int, npos->axis[1].size );
break;
case pullInitMethodPointPerVoxel:
npos = NULL;
posData = NULL;
if ( pctx->initParm.numInitial && pctx->verbose ) {
printf( "%s: pointPerVoxel %d overrides numInitial ( %u )\n", me,
pctx->initParm.pointPerVoxel, pctx->initParm.numInitial );
}
/* Obtain number of voxels */
seedVol = pctx->vol[pctx->ispec[pullInfoSeedThresh]->volIdx];
seedShape = seedVol->gctx->shape;
if ( pctx->initParm.ppvZRange[0] <= pctx->initParm.ppvZRange[1] ) {
unsigned int zrn;
if ( !( pctx->initParm.ppvZRange[0] < seedShape->size[2]
&& pctx->initParm.ppvZRange[1] < seedShape->size[2] ) ) {
biffAddf( PULL, "%s: ppvZRange[%u, %u] outside volume [0, %u]", me,
pctx->initParm.ppvZRange[0], pctx->initParm.ppvZRange[1],
seedShape->size[2]-1 );
airMopError( mop ); return 1;
}
zrn = pctx->initParm.ppvZRange[1] - pctx->initParm.ppvZRange[0] + 1;
voxNum = seedShape->size[0]*seedShape->size[1]*zrn;
if ( pctx->verbose ) {
printf( "%s: vol size %u %u [%u, %u] -> voxNum %u\n", me,
seedShape->size[0], seedShape->size[1],
pctx->initParm.ppvZRange[0], pctx->initParm.ppvZRange[1],
voxNum );
}
} else {
voxNum = seedShape->size[0]*seedShape->size[1]*seedShape->size[2];
if ( pctx->verbose ) {
printf( "%s: vol size %u %u %u -> voxNum %u\n", me,
seedShape->size[0], seedShape->size[1], seedShape->size[2],
voxNum );
}
}
/* Compute total number of points */
if ( pctx->initParm.pointPerVoxel > 0 ) {
factor = pctx->initParm.pointPerVoxel;
} else {
factor = -1.0/pctx->initParm.pointPerVoxel;
}
if ( pctx->haveScale ) {
unsigned int sasn;
sasn = pctx->initParm.samplesAlongScaleNum;
totalNumPoints = AIR_CAST( unsigned int, voxNum * factor * sasn );
} else {
totalNumPoints = AIR_CAST( unsigned int, voxNum * factor );
}
break;
case pullInitMethodRandom:
case pullInitMethodHalton:
npos = NULL;
posData = NULL;
totalNumPoints = pctx->initParm.numInitial;
break;
default:
biffAddf( PULL, "%s: pullInitMethod %d not handled!", me,
pctx->initParm.method );
airMopError( mop ); return 1;
break;
}
if ( pctx->verbose ) {
printf( "%s: initializing/seeding %u pts. . . ", me, totalNumPoints );
fflush( stdout );
}
/* find first scale volume, if there is one; this is used by some
seeders to determine placement along the scale axis */
scaleVol = NULL;
for ( ii=0; ii<pctx->volNum; ii++ ) {
if ( pctx->vol[ii]->ninScale ) {
scaleVol = pctx->vol[ii];
break;
}
}
/* Start adding points */
tick = totalNumPoints/1000;
point = NULL;
unsigned int initRorHack = 0;
/* This loop would normally be:
for ( pointIdx = 0; pointIdx < totalNumPoints; pointIdx++ ) {
but because of pctx->flag.nixAtVolumeEdgeSpaceInitRorH we
need to be able to decrement pointIdx to force another redo,
even if pointIdx==0. So loop index is actually one greater
than the index value actually used */
for ( pointIdx = 1; pointIdx <= totalNumPoints; pointIdx++ ) {
int E;
if ( pctx->verbose ) {
if ( tick < 100 || 0 == ( pointIdx-1 ) % tick ) {
printf( "%s", airDoneStr( 0, pointIdx-1, totalNumPoints, doneStr ) );
fflush( stdout );
}
}
if ( pctx->verbose > 5 ) {
printf( "\n%s: setting up point = %u/%u\n", me,
pointIdx-1, totalNumPoints );
}
/* Create point */
if ( !point ) {
point = pullPointNew( pctx );
}
/* Filling array according to initialization method */
E = 0;
switch( pctx->initParm.method ) {
case pullInitMethodRandom:
case pullInitMethodHalton:
/* point index passed here always increments, even if
we failed last time because of being at edge */
E = pullPointInitializeRandomOrHalton( pctx, pointIdx-1 + initRorHack,
point, scaleVol );
if ( pctx->flag.nixAtVolumeEdgeSpaceInitRorH ) {
createFail = !!( point->status & PULL_STATUS_EDGE_BIT );
if ( createFail ) {
initRorHack++;
if ( initRorHack > 10*totalNumPoints ) {
biffAddf( PULL, "%s: ( point %u ) handling nixAtVolumeEdgeSpaceInitRorH, "
"have failed so often ( %u > 10*#pnts = %u ); something "
"must be wrong", me, point->idtag, initRorHack,
totalNumPoints );
airMopError( mop ); return 1;
}
/* this is a for-loop, post-body increment will always happen;
we need to subvert it. */
pointIdx--;
}
} else {
createFail = AIR_FALSE;
}
break;
case pullInitMethodPointPerVoxel:
E = pullPointInitializePerVoxel( pctx, pointIdx-1, point, scaleVol,
&createFail );
break;
case pullInitMethodGivenPos:
E = pullPointInitializeGivenPos( pctx, posData, pointIdx-1, point,
&createFail );
break;
}
if ( E ) {
biffAddf( PULL, "%s: trouble trying point %u ( id %u )", me,
pointIdx-1, point->idtag );
airMopError( mop ); return 1;
}
if ( createFail ) {
/* We were not successful in creating a point; not an error */
continue;
}
/* else, the point is ready for binning */
if ( pctx->constraint ) {
if ( pullBinsPointMaybeAdd( pctx, point, NULL, &added ) ) {
biffAddf( PULL, "%s: trouble binning point %u", me, point->idtag );
airMopError( mop ); return 1;
}
/*
if ( 4523 == point->idtag ) {
fprintf( stderr, "!%s( %u ): ----- added=%d at %g %g %g %g\n",
me, point->idtag, added,
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
}
*/
if ( added ) {
point = NULL;
}
} else {
if ( pullBinsPointAdd( pctx, point, NULL ) ) {
biffAddf( PULL, "%s: trouble binning point %u", me, point->idtag );
airMopError( mop ); return 1;
}
point = NULL;
}
} /* Done looping through total number of points */
if ( pctx->verbose ) {
printf( "%s\n", airDoneStr( 0, pointIdx-1, totalNumPoints,
doneStr ) );
}
if ( point ) {
/* we created a new test point, but it was never placed in the volume */
/* so, HACK: undo pullPointNew . . . */
point = pullPointNix( point );
/* pctx->idtagNext -= 1; */
}
/* Final check: do we have any points? */
pn = pullPointNumber( pctx );
if ( !pn ) {
char stmp1[AIR_STRLEN_MED], stmp2[AIR_STRLEN_MED];
int guess=AIR_FALSE;
sprintf( stmp1, "%s: seeded 0 points", me );
if ( pctx->ispec[pullInfoSeedThresh] ) {
guess=AIR_TRUE;
sprintf( stmp2, " ( ? bad seedthresh %g ? )",
pctx->ispec[pullInfoSeedThresh]->zero );
strcat( stmp1, stmp2 );
}
if ( pctx->flag.nixAtVolumeEdgeSpace ) {
guess=AIR_TRUE;
sprintf( stmp2, " ( ? flag.nixAtVolumeEdgeSpace true ? )" );
strcat( stmp1, stmp2 );
}
if ( !guess ) {
sprintf( stmp2, " ( no guess as to why )" );
strcat( stmp1, stmp2 );
}
biffAddf( PULL, "%s", stmp1 );
airMopError( mop ); return 1;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: initialized to %u points ( idtagNext = %u )\n",
me, pn, pctx->idtagNext );
}
/* */
if ( 1 ) {
Nrrd *ntmp;
ntmp = nrrdNew( );
pullOutputGet( ntmp, NULL, NULL, NULL, 0.0, pctx );
nrrdSave( "pos-init.nrrd", ntmp, NULL );
nrrdNuke( ntmp );
}
/* */
pctx->tmpPointPtr = AIR_CAST( pullPoint **,
calloc( pn, sizeof( pullPoint* ) ) );
pctx->tmpPointPerm = AIR_CAST( unsigned int *,
calloc( pn, sizeof( unsigned int ) ) );
if ( !( pctx->tmpPointPtr && pctx->tmpPointPerm ) ) {
biffAddf( PULL, "%s: couldn't allocate tmp buffers %p %p", me,
AIR_VOIDP( pctx->tmpPointPtr ), AIR_VOIDP( pctx->tmpPointPerm ) );
airMopError( mop ); return 1;
}
pctx->tmpPointNum = pn;
/* now that all points have been added, set their energy to help
inspect initial state */
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
point->energy = _pullPointEnergyTotal( pctx->task[0], bin, point,
/* ignoreImage */
!pctx->haveScale,
point->force );
}
}
if ( pctx->verbose ) {
printf( "%s: all done. ", me );
fflush( stdout );
}
1389 airMopOkay( mop );
return 0;
}
void
_pullPointFinish( pullContext *pctx ) {
airFree( pctx->tmpPointPtr );
airFree( pctx->tmpPointPerm );
return ;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
int
29 _pullPointProcessNeighLearn( pullTask *task, pullBin *bin, pullPoint *point ) {
/* sneaky: we just learn ( and discard ) the energy, and this function
will do the work of learning the neighbors */
_pullEnergyFromPoints( task, bin, point, NULL );
return 0;
}
static double
38 _pointEnergyOfNeighbors( pullTask *task, pullBin *bin, pullPoint *point,
double *fracNixed ) {
double enr;
unsigned int ii, xx;
pullPoint *her;
enr = 0;
xx = 0;
for ( ii=0; ii<point->neighPointNum; ii++ ) {
her = point->neighPoint[ii];
if ( her->status & PULL_STATUS_NIXME_BIT ) {
xx += 1;
} else {
enr += _pullEnergyFromPoints( task, bin, her, NULL );
}
}
*fracNixed = ( point->neighPointNum
? AIR_CAST( double, xx )/point->neighPointNum
: 0 );
return enr;
}
int
61 _pullPointProcessAdding( pullTask *task, pullBin *bin, pullPoint *point ) {
static const char me[]="_pullPointProcessAdding";
unsigned int npi, iter, api;
double noffavg[4], npos[4], enrWith, enrWithout,
fracNixed, newSpcDist, tmp;
pullPoint *newpnt;
int E;
task->pctx->count[pullCountAdding] += 1;
if ( point->neighPointNum && task->pctx->targetDim
&& task->pctx->flag.popCntlEnoughTest ) {
unsigned int plenty, tardim;
tardim = task->pctx->targetDim;
if ( task->pctx->flag.zeroZ && tardim > 1 ) {
/* GLK unsure of tardim == 1 logic here */
tardim -= 1;
}
plenty = ( 1 == tardim
? 3
: ( 2 == tardim
? 7
: ( 3 == tardim
? 13 /* = 1 + 12
= 1 + coordination number of 3D sphere packing */
: 0 /* shouldn't get here */ ) ) );
/*
if ( 0 == ( point->idtag % 100 ) ) {
printf( "!%s: #num %d >?= plenty %d\n", me, point->neighPointNum, plenty );
}
*/
if ( point->neighPointNum >= plenty ) {
/* there's little chance that adding points will reduce energy */
return 0;
}
}
/*
printf( "!%s: point->pos = ( %g, %g, %g, %g )\n", me,
point->pos[0], point->pos[1], point->pos[2], point->pos[3] );
printf( "!%s: point->neighPointNum = %u\n", me,
point->neighPointNum );
printf( "!%s: task->pctx->targetDim = %u\n", me,
task->pctx->targetDim );
printf( "!%s: task->pctx->flag.popCntlEnoughTest = %d\n", me,
task->pctx->flag.popCntlEnoughTest );
*/
ELL_4V_SET( noffavg, 0, 0, 0, 0 );
for ( npi=0; npi<point->neighPointNum; npi++ ) {
double off[4];
ELL_4V_SUB( off, point->pos, point->neighPoint[npi]->pos );
/* normalize the offset */
ELL_3V_SCALE( off, 1/task->pctx->sysParm.radiusSpace, off );
if ( task->pctx->haveScale ) {
off[3] /= task->pctx->sysParm.radiusScale;
}
ELL_4V_INCR( noffavg, off );
}
if ( point->neighPointNum ) {
/*
if ( 0 == ( point->idtag % 100 ) ) {
printf( "%s: len( offavg ) %g >?= thresh %g\n", me,
ELL_4V_LEN( noffavg )/point->neighPointNum,
_PULL_NEIGH_OFFSET_SUM_THRESH );
}
*/
if ( ELL_4V_LEN( noffavg )/point->neighPointNum
< _PULL_NEIGH_OFFSET_SUM_THRESH ) {
/* we have neighbors, and they seem to be balanced well enough;
don't try to add */
return 0;
}
}
if ( task->pctx->energySpecR->energy->well( &newSpcDist,
task->pctx->energySpecR->parm ) ) {
/* HEY: if we don't actually have a well, what is the point of
guessing a new distance? */
newSpcDist = _PULL_NEWPNT_DIST;
}
/* compute offset ( normalized ) direction from current point location */
if ( !point->neighPointNum ) {
/* we had no neighbors, have to pretend like we did */
airNormalRand_r( noffavg + 0, noffavg + 1, task->rng );
airNormalRand_r( noffavg + 2, noffavg + 3, task->rng );
if ( !task->pctx->flag.zeroZ ) {
noffavg[2] = 0;
}
if ( !task->pctx->haveScale ) {
noffavg[3] = 0;
}
}
if ( task->pctx->constraint ) {
double proj[9], tmpvec[3];
_pullConstraintTangent( task, point, proj );
ELL_3MV_MUL( tmpvec, proj, noffavg );
ELL_3V_COPY( noffavg, tmpvec );
}
ELL_4V_NORM( noffavg, noffavg, tmp );
ELL_3V_SCALE( noffavg, task->pctx->sysParm.radiusSpace, noffavg );
noffavg[3] *= task->pctx->sysParm.radiusScale;
ELL_4V_SCALE( noffavg, newSpcDist, noffavg );
/* set new point location */
ELL_4V_ADD2( npos, noffavg, point->pos );
/*
printf( "!%s: new test pos @ ( %g, %g, %g, %g )\n", me,
npos[0], npos[1], npos[2], npos[3] );
*/
if ( !_pullInsideBBox( task->pctx, npos ) ) {
if ( task->pctx->verbose > 2 ) {
printf( "%s: new pnt would start ( %g, %g, %g, %g ) outside bbox, nope\n",
me, npos[0], npos[1], npos[2], npos[3] );
}
return 0;
}
/* initial pos is good, now we start getting serious */
newpnt = pullPointNew( task->pctx );
if ( !newpnt ) {
biffAddf( PULL, "%s: couldn't spawn new point from %u", me, point->idtag );
return 1;
}
ELL_4V_COPY( newpnt->pos, npos );
/* set status to indicate this is an unbinned point, with no
knowledge of its neighbors */
newpnt->status |= PULL_STATUS_NEWBIE_BIT;
/* satisfy constraint if needed */
if ( task->pctx->constraint ) {
int constrFail;
if ( _pullConstraintSatisfy( task, newpnt,
_PULL_CONSTRAINT_TRAVEL_MAX,
&constrFail ) ) {
biffAddf( PULL, "%s: on newbie point %u ( spawned from %u )", me,
newpnt->idtag, point->idtag );
pullPointNix( newpnt ); return 1;
}
if ( constrFail ) {
/* constraint satisfaction failed, which isn't an error for us,
we just don't try to add this point. Can do immediate nix
because no neighbors know about this point. */
pullPointNix( newpnt );
return 0;
}
if ( !_pullInsideBBox( task->pctx, newpnt->pos ) ) {
if ( task->pctx->verbose > 2 ) {
printf( "%s: post constr newpnt %u ( %g, %g, %g, %g ) outside bbox; nope\n",
me, newpnt->idtag, newpnt->pos[0], newpnt->pos[1],
newpnt->pos[2], newpnt->pos[3] );
}
newpnt = pullPointNix( newpnt );
return 0;
}
}
/*
printf( "!%s: new test pos @ ( %g, %g, %g, %g )\n", me,
newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3] );
*/
/* do some descent, on this point only, which ( HACK! ) we do by
changing the per-task process mode . . . */
task->processMode = pullProcessModeDescent;
E = 0;
for ( iter=0; iter<task->pctx->iterParm.addDescent; iter++ ) {
double diff[4];
if ( !E ) E |= _pullPointProcessDescent( task, bin, newpnt,
/* ignoreImage; actually it is
this use which motivated the
creation of ignoreImage */
AIR_TRUE );
if ( newpnt->status & PULL_STATUS_STUCK_BIT ) {
if ( task->pctx->verbose > 2 ) {
printf( "%s: possible newpnt %u stuck @ iter %u; nope\n", me,
newpnt->idtag, iter );
}
newpnt = pullPointNix( newpnt );
/* if we don't change the mode back, then pullBinProcess( ) won't
know to try adding for the rest of the bins it sees, bad HACK */
task->processMode = pullProcessModeAdding;
return 0;
}
if ( !_pullInsideBBox( task->pctx, newpnt->pos ) ) {
if ( task->pctx->verbose > 2 ) {
printf( "%s: newpnt %u went ( %g, %g, %g, %g ) outside bbox; nope\n",
me, newpnt->idtag, newpnt->pos[0], newpnt->pos[1],
newpnt->pos[2], newpnt->pos[3] );
}
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
ELL_4V_SUB( diff, newpnt->pos, point->pos );
ELL_3V_SCALE( diff, 1/task->pctx->sysParm.radiusSpace, diff );
diff[3] /= task->pctx->sysParm.radiusScale;
if ( ELL_4V_LEN( diff ) > _PULL_NEWPNT_STRAY_DIST ) {
if ( task->pctx->verbose > 2 ) {
printf( "%s: newpnt %u went too far %g from old point %u; nope\n",
me, newpnt->idtag, ELL_4V_LEN( diff ), point->idtag );
}
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
/* still okay to continue descending */
newpnt->stepEnergy *= task->pctx->sysParm.opporStepScale;
}
/*
printf( "!%s: new test pos @ ( %g, %g, %g, %g )\n", me,
newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3] );
{
double posdiff[4];
ELL_4V_SUB( posdiff, newpnt->pos, point->pos );
printf( "!%s: at dist %g\n", me, ELL_4V_LEN( posdiff ) );
}
*/
/* now that newbie point is final test location, see if it meets
the live thresh, if there is one */
if ( task->pctx->ispec[pullInfoLiveThresh]
&& 0 > pullPointScalar( task->pctx, newpnt, pullInfoLiveThresh,
NULL, NULL ) ) {
/* didn't meet threshold */
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
if ( task->pctx->ispec[pullInfoLiveThresh2] /* HEY: copy & paste */
&& 0 > pullPointScalar( task->pctx, newpnt, pullInfoLiveThresh2,
NULL, NULL ) ) {
/* didn't meet threshold */
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
if ( task->pctx->ispec[pullInfoLiveThresh3] /* HEY: copy & paste */
&& 0 > pullPointScalar( task->pctx, newpnt, pullInfoLiveThresh3,
NULL, NULL ) ) {
/* didn't meet threshold */
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
/* see if the new point should be nixed because its at a volume edge */
if ( task->pctx->flag.nixAtVolumeEdgeSpace
&& ( newpnt->status & PULL_STATUS_EDGE_BIT ) ) {
newpnt = pullPointNix( newpnt );
task->processMode = pullProcessModeAdding;
return 0;
}
/* no problem with live thresh, test energy by first learn neighbors */
/* we have to add newpnt to task's add queue, just so that its neighbors
can see it as a possible neighbor */
api = airArrayLenIncr( task->addPointArr, 1 );
task->addPoint[api] = newpnt;
task->processMode = pullProcessModeNeighLearn;
_pullEnergyFromPoints( task, bin, newpnt, NULL );
/* and teach the neighbors their neighbors ( possibly including newpnt ) */
for ( npi=0; npi<newpnt->neighPointNum; npi++ ) {
_pullEnergyFromPoints( task, bin, newpnt->neighPoint[npi], NULL );
}
/* now back to the actual mode we came here with */
task->processMode = pullProcessModeAdding;
enrWith = ( _pullEnergyFromPoints( task, bin, newpnt, NULL )
+ _pointEnergyOfNeighbors( task, bin, newpnt, &fracNixed ) );
newpnt->status |= PULL_STATUS_NIXME_BIT; /* turn nixme on */
enrWithout = _pointEnergyOfNeighbors( task, bin, newpnt, &fracNixed );
newpnt->status &= ~PULL_STATUS_NIXME_BIT; /* turn nixme off */
if ( enrWith < enrWithout ) {
/* energy is clearly lower *with* newpnt, so we want to add it, which
means keeping it in the add queue where it already is */
if ( task->pctx->logAdd ) {
double posdiff[4];
ELL_4V_SUB( posdiff, newpnt->pos, point->pos );
fprintf( task->pctx->logAdd, "%u %g %g %g %g %g %g\n", newpnt->idtag,
ELL_3V_LEN( posdiff )/task->pctx->sysParm.radiusSpace,
AIR_ABS( posdiff[3] )/task->pctx->sysParm.radiusScale,
newpnt->pos[0], newpnt->pos[1], newpnt->pos[2], newpnt->pos[3] );
}
} else {
/* adding point is not an improvement, remove it from the add queue */
airArrayLenIncr( task->addPointArr, -1 );
/* ugh, have to signal to neighbors that its no longer their neighbor.
HEY this is the part that is totally screwed with multiple threads */
task->processMode = pullProcessModeNeighLearn;
newpnt->status |= PULL_STATUS_NIXME_BIT;
for ( npi=0; npi<newpnt->neighPointNum; npi++ ) {
_pullEnergyFromPoints( task, bin, newpnt->neighPoint[npi], NULL );
}
task->processMode = pullProcessModeAdding;
newpnt = pullPointNix( newpnt );
}
return 0;
}
int
349 _pullPointProcessNixing( pullTask *task, pullBin *bin, pullPoint *point ) {
static const char me[]="_pullPointProcessNixing";
double enrWith, enrNeigh, enrWithout, fracNixed;
task->pctx->count[pullCountNixing] += 1;
if ( 0 && 289 == task->pctx->iter ) {
fprintf( stderr, "!%s( %04u ): hello lthr %p -> %g %g %g\n", me, point->idtag,
task->pctx->ispec[pullInfoLiveThresh],
pullPointScalar( task->pctx, point, pullInfoLiveThresh, NULL, NULL ),
point->pos[0], point->pos[1]
);
}
/* if there's a live thresh, do we meet it? */
if ( task->pctx->ispec[pullInfoLiveThresh]
&& 0 > pullPointScalar( task->pctx, point, pullInfoLiveThresh,
NULL, NULL ) ) {
point->status |= PULL_STATUS_NIXME_BIT;
return 0;
}
/* HEY copy & paste */
if ( task->pctx->ispec[pullInfoLiveThresh2]
&& 0 > pullPointScalar( task->pctx, point, pullInfoLiveThresh2,
NULL, NULL ) ) {
point->status |= PULL_STATUS_NIXME_BIT;
return 0;
}
/* HEY copy & paste */
if ( task->pctx->ispec[pullInfoLiveThresh3]
&& 0 > pullPointScalar( task->pctx, point, pullInfoLiveThresh3,
NULL, NULL ) ) {
point->status |= PULL_STATUS_NIXME_BIT;
return 0;
}
/* if many neighbors have been nixed, then system is far from convergence,
so energy is not a very meaningful guide to whether to nix this point
NOTE that we use this function to *learn* fracNixed */
enrNeigh = _pointEnergyOfNeighbors( task, bin, point, &fracNixed );
if ( fracNixed < task->pctx->sysParm.fracNeighNixedMax ) {
/* is energy lower without us around? */
enrWith = enrNeigh + _pullEnergyFromPoints( task, bin, point, NULL );
point->status |= PULL_STATUS_NIXME_BIT; /* turn nixme on */
enrWithout = _pointEnergyOfNeighbors( task, bin, point, &fracNixed );
if ( enrWith <= enrWithout ) {
/* Energy isn't distinctly lowered without the point, so keep it;
turn off nixing. If enrWith == enrWithout == 0, as happens to
isolated points, then the difference between "<=" and "<"
keeps the isolated points from getting nixed */
point->status &= ~PULL_STATUS_NIXME_BIT; /* turn nixme off */
}
/* else energy is certainly higher with the point, do nix it */
}
return 0;
}
int
407 _pullIterFinishNeighLearn( pullContext *pctx ) {
static const char me[]="_pullIterFinishNeighLearn";
/* a no-op for now */
AIR_UNUSED( pctx );
AIR_UNUSED( me );
return 0;
}
int
418 _pullIterFinishAdding( pullContext *pctx ) {
static const char me[]="_pullIterFinishAdding";
unsigned int taskIdx;
pctx->addNum = 0;
for ( taskIdx=0; taskIdx<pctx->threadNum; taskIdx++ ) {
pullTask *task;
task = pctx->task[taskIdx];
if ( task->addPointNum ) {
unsigned int pointIdx;
int added;
for ( pointIdx=0; pointIdx<task->addPointNum; pointIdx++ ) {
pullPoint *point;
pullBin *bin;
point = task->addPoint[pointIdx];
point->status &= ~PULL_STATUS_NEWBIE_BIT;
if ( pullBinsPointMaybeAdd( pctx, point, &bin, &added ) ) {
biffAddf( PULL, "%s: trouble binning new point %u", me, point->idtag );
return 1;
}
if ( added ) {
pctx->addNum++;
} else {
unsigned int npi, xpi;
if ( pctx->verbose ) {
printf( "%s: decided NOT to add new point %u\n", me, point->idtag );
}
/* HEY: copied from above */
/* ugh, have to signal to neigs that its no longer their neighbor */
task->processMode = pullProcessModeNeighLearn;
point->status |= PULL_STATUS_NIXME_BIT;
for ( npi=0; npi<point->neighPointNum; npi++ ) {
_pullEnergyFromPoints( task, bin, point->neighPoint[npi], NULL );
}
task->processMode = pullProcessModeAdding;
/* can't do immediate nix for reasons GLK doesn't quite understand */
xpi = airArrayLenIncr( task->nixPointArr, 1 );
task->nixPoint[xpi] = point;
}
}
airArrayLenSet( task->addPointArr, 0 );
}
}
if ( pctx->verbose && pctx->addNum ) {
printf( "%s: ADDED %u\n", me, pctx->addNum );
}
return 0;
}
void
468 _pullNixTheNixed( pullContext *pctx ) {
unsigned int binIdx;
pctx->nixNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
pullBin *bin;
unsigned int pointIdx;
bin = pctx->bin + binIdx;
pointIdx = 0;
while ( pointIdx < bin->pointNum ) {
pullPoint *point;
point = bin->point[pointIdx];
if ( pctx->flag.nixAtVolumeEdgeSpace
&& ( point->status & PULL_STATUS_EDGE_BIT ) ) {
point->status |= PULL_STATUS_NIXME_BIT;
}
if ( point->status & PULL_STATUS_NIXME_BIT ) {
pullPointNix( point );
/* copy last point pointer to this slot */
bin->point[pointIdx] = bin->point[bin->pointNum-1];
airArrayLenIncr( bin->pointArr, -1 ); /* will decrement bin->pointNum */
pctx->nixNum++;
} else {
pointIdx++;
}
}
}
return;
}
int
499 _pullIterFinishNixing( pullContext *pctx ) {
static const char me[]="_pullIterFinishNixing";
unsigned int taskIdx;
_pullNixTheNixed( pctx );
/* finish nixing the things that we decided not to add */
for ( taskIdx=0; taskIdx<pctx->threadNum; taskIdx++ ) {
pullTask *task;
task = pctx->task[taskIdx];
if ( task->nixPointNum ) {
unsigned int xpi;
for ( xpi=0; xpi<task->nixPointNum; xpi++ ) {
pullPointNix( task->nixPoint[xpi] );
}
airArrayLenSet( task->nixPointArr, 0 );
}
}
if ( pctx->verbose && pctx->nixNum ) {
printf( "%s: NIXED %u\n", me, pctx->nixNum );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
pullTask *
28 _pullTaskNew( pullContext *pctx, int threadIdx ) {
static const char me[]="_pullTaskNew";
pullTask *task;
unsigned int ii;
pullPtrPtrUnion pppu;
task = AIR_CALLOC( 1, pullTask );
if ( !task ) {
biffAddf( PULL, "%s: couldn't allocate task", me );
return NULL;
}
task->pctx = pctx;
for ( ii=0; ii<pctx->volNum; ii++ ) {
if ( !( task->vol[ii] = _pullVolumeCopy( pctx, pctx->vol[ii] ) ) ) {
biffAddf( PULL, "%s: trouble copying vol %u/%u", me, ii, pctx->volNum );
return NULL;
}
}
if ( 0 ) {
gagePerVolume *pvl;
const double *ans;
double pos[3];
int gret;
for ( ii=0; ii<pctx->volNum; ii++ ) {
pvl = task->vol[ii]->gctx->pvl[0];
printf( "!%s: vol[%u] query:\n", me, ii );
gageQueryPrint( stdout, pvl->kind, pvl->query );
ans = gageAnswerPointer( task->vol[ii]->gctx, pvl, gageSclValue );
ELL_3V_SET( pos, 0.6, 0.6, 0.3 );
gret = gageProbeSpace( task->vol[ii]->gctx, pos[0], pos[1], pos[2],
AIR_FALSE, AIR_TRUE );
printf( "!%s: ( %d ) val( %g, %g, %g ) = %g\n", me, gret,
pos[0], pos[1], pos[2], *ans );
ELL_3V_SET( pos, 0.5, 0.0, 0.0 );
gret = gageProbeSpace( task->vol[ii]->gctx, pos[0], pos[1], pos[2],
AIR_FALSE, AIR_TRUE );
printf( "!%s: ( %d ) val( %g, %g, %g ) = %g\n", me, gret,
pos[0], pos[1], pos[2], *ans );
}
}
/* now set up all pointers for per-task pullInfos */
for ( ii=0; ii<=PULL_INFO_MAX; ii++ ) {
const pullVolume *vol;
if ( pctx->ispec[ii] ) {
if ( pullSourceGage == pctx->ispec[ii]->source ) {
vol = task->vol[pctx->ispec[ii]->volIdx];
task->ans[ii] = gageAnswerPointer( vol->gctx, vol->gpvl,
pctx->ispec[ii]->item );
if ( pctx->verbose ) {
printf( "%s: task->ans[%u] = ( %s ) %p\n", me, ii,
vol->kind->name, AIR_CVOIDP( task->ans[ii] ) );
}
} else {
task->ans[ii] = NULL;
}
} else {
task->ans[ii] = NULL;
}
}
/* HEY: any idea why there is so little error checking in the below? */
/* initialize to descent because that's what's needed for the end of point
initialization, when initial energy must be learned */
task->processMode = pullProcessModeDescent;
task->probeSeedPreThreshOnly = AIR_FALSE;
if ( pctx->threadNum > 1 ) {
task->thread = airThreadNew( );
}
task->threadIdx = threadIdx;
task->rng = airRandMTStateNew( pctx->rngSeed + threadIdx );
task->pointBuffer = pullPointNew( pctx );
pctx->idtagNext = 0; /* because pullPointNew incremented it */
task->neighPoint = AIR_CAST( pullPoint **, calloc( _PULL_NEIGH_MAXNUM,
sizeof( pullPoint* ) ) );
task->addPoint = NULL;
task->addPointNum = 0;
pppu.points = &( task->addPoint );
task->addPointArr = airArrayNew( pppu.v, &( task->addPointNum ),
sizeof( pullPoint* ),
/* not exactly the right semantics . . . */
PULL_POINT_NEIGH_INCR );
task->nixPoint = NULL;
task->nixPointNum = 0;
pppu.points = &( task->nixPoint );
task->nixPointArr = airArrayNew( pppu.v, &( task->nixPointNum ),
sizeof( pullPoint* ),
/* not exactly the right semantics . . . */
PULL_POINT_NEIGH_INCR );
task->returnPtr = NULL;
task->stuckNum = 0;
return task;
}
pullTask *
122 _pullTaskNix( pullTask *task ) {
unsigned int ii;
if ( task ) {
for ( ii=0; ii<task->pctx->volNum; ii++ ) {
task->vol[ii] = pullVolumeNix( task->vol[ii] );
}
if ( task->pctx->threadNum > 1 ) {
task->thread = airThreadNix( task->thread );
}
task->rng = airRandMTStateNix( task->rng );
task->pointBuffer = pullPointNix( task->pointBuffer );
airFree( task->neighPoint );
task->addPointArr = airArrayNuke( task->addPointArr );
task->nixPointArr = airArrayNuke( task->nixPointArr );
airFree( task );
}
return NULL;
}
/*
** _pullTaskSetup sets:
**** pctx->task
**** pctx->task[]
*/
int
148 _pullTaskSetup( pullContext *pctx ) {
static const char me[]="_pullTaskSetup";
unsigned int tidx;
pctx->task = ( pullTask ** )calloc( pctx->threadNum, sizeof( pullTask * ) );
if ( !( pctx->task ) ) {
biffAddf( PULL, "%s: couldn't allocate array of tasks", me );
return 1;
}
for ( tidx=0; tidx<pctx->threadNum; tidx++ ) {
if ( pctx->verbose ) {
printf( "%s: creating task %u/%u\n", me, tidx, pctx->threadNum );
}
pctx->task[tidx] = _pullTaskNew( pctx, tidx );
if ( !( pctx->task[tidx] ) ) {
biffAddf( PULL, "%s: couldn't allocate task %d", me, tidx );
return 1;
}
}
return 0;
}
void
171 _pullTaskFinish( pullContext *pctx ) {
unsigned int tidx;
for ( tidx=0; tidx<pctx->threadNum; tidx++ ) {
pctx->task[tidx] = _pullTaskNix( pctx->task[tidx] );
}
airFree( pctx->task );
pctx->task = NULL;
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../pull.h"
#define TOP_MARGIN 3
char *info = ( "Sees if \"adhoc\" is inefficient." );
enum {
stepStyleUnknown, /* 0 */
stepStyleSmall, /* 1 */
stepStyleDescent, /* 2 */
stepStyleRandomUniform, /* 3 */
stepStyleRandomCool, /* 4 */
stepStyleLast
};
#define STEP_STYLE_MAX 4
const char *
_stepStyleStr[STEP_STYLE_MAX+1] = {
"( unknown_style )",
"small",
"descent",
"runiform",
"rcool"
};
const airEnum
_stepStyle = {
"step style",
STEP_STYLE_MAX,
_stepStyleStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
59 const airEnum *const
stepStyle = &_stepStyle;
static double
64 tpimod( double phi ) {
if ( phi > 0 ) {
phi = fmod( phi, 2*AIR_PI );
} else {
phi = 2*AIR_PI + fmod( -phi, 2*AIR_PI );
}
return phi;
}
static double
75 enrPairwise( double *grad, double me, double she,
pullEnergySpec *ensp, double radius ) {
double ad, td, rr, *parm, enr, denr,
( *eval )( double *, double, const double parm[PULL_ENERGY_PARM_NUM] );
ad = AIR_ABS( me - she );
if ( 0 == ad ) {
if ( grad ) {
*grad = 0;
}
return 0;
}
td = AIR_ABS( me - ( she + 2*AIR_PI ) );
if ( td < ad ) {
she += 2*AIR_PI;
ad = td;
} else {
td = AIR_ABS( me - ( she - 2*AIR_PI ) );
if ( td < ad ) {
she -= 2*AIR_PI;
ad = td;
}
}
rr = ad/radius;
if ( rr > 1 ) {
if ( grad ) {
*grad = 0;
}
return 0;
}
parm = ensp->parm;
eval = ensp->energy->eval;
enr = eval( &denr, rr, parm );
if ( grad ) {
*grad = denr * airSgn( me - she )/radius;
}
return enr;
}
static double
115 enrSingle( double *gradP, double me, unsigned int meIdx,
double *pos, unsigned int posNum,
pullEnergySpec *ensp, double radius ) {
unsigned int ii;
double enr, gg, grad;
enr = 0;
grad = 0;
me = tpimod( me );
for ( ii=0; ii<posNum; ii++ ) {
if ( ii == meIdx ) {
continue;
}
enr += enrPairwise( &gg, me, pos[ii], ensp, radius );
grad += gg;
}
if ( gradP ) {
*gradP = grad;
}
return enr;
}
static double
138 enrStdv( double *pos, unsigned int posNum,
pullEnergySpec *ensp, double radius ) {
unsigned int ii;
double SS, S, enr;
SS = S = 0.0;
for ( ii=0; ii<posNum; ii++ ) {
enr = enrSingle( NULL, pos[ii], ii, pos, posNum, ensp, radius );
S += enr;
SS += enr*enr;
}
S /= posNum;
SS /= posNum;
return sqrt( SS - S*S );
}
static void
155 runIter( unsigned int iter,
double *posOut, int sstyle, const double *posIn, double *step,
unsigned int pntNum,
pullEnergySpec *ensp, double radius,
double cool, double backoff, double creepup ) {
unsigned int ii;
double me, enr0, enr1, frc, tpos;
int badstep;
for ( ii=0; ii<pntNum; ii++ ) {
posOut[ii] = posIn[ii];
}
if ( iter < TOP_MARGIN ) {
/* hack for better seeing initial locations */
return;
}
for ( ii=0; ii<pntNum; ii++ ) {
me = posOut[ii];
enr0 = enrSingle( &frc, me, ii, posOut, pntNum, ensp, radius );
switch( sstyle ) {
case stepStyleSmall:
tpos = me + AIR_AFFINE( 0.0, airDrandMT( ), 1.0, -radius/10, radius/10 );
enr1 = enrSingle( NULL, tpos, ii, posOut, pntNum, ensp, radius );
if ( enr1 < enr0 ) {
me = tpos;
}
break;
case stepStyleDescent:
do {
tpos = me - step[ii]*frc;
enr1 = enrSingle( NULL, tpos, ii, posOut, pntNum, ensp, radius );
badstep = enr1 > enr0;
if ( badstep ) {
step[ii] *= backoff;
}
} while ( badstep );
step[ii] *= creepup;
me = tpos;
break;
case stepStyleRandomUniform:
tpos = AIR_AFFINE( 0.0, airDrandMT( ), 1.0, 0.0, 2*AIR_PI );
enr1 = enrSingle( NULL, tpos, ii, posOut, pntNum, ensp, radius );
if ( enr1 < enr0 ) {
me = tpos;
}
break;
case stepStyleRandomCool:
airNormalRand( &tpos, NULL );
tpos = me + tpos*cool;
enr1 = enrSingle( NULL, tpos, ii, posOut, pntNum, ensp, radius );
if ( enr1 < enr0 ) {
me = tpos;
}
break;
}
posOut[ii] = tpimod( me );
}
return;
}
int
216 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
char *outS;
pullEnergySpec *ensp, *enspCharge;
unsigned int pntNum, pntIdx, iter, iterMax, rngSeed, posNum, coolHalfLife;
double stdvMin, radius, *pos, *step, backoff, creepup, stepInitial;
airArray *posArr;
Nrrd *npos;
int sstyle, verbose;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0",
"verbosity" );
hestOptAdd( &hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL,
"specification of force function to use",
NULL, NULL, pullHestEnergySpec );
hestOptAdd( &hopt, "ss", "step style", airTypeEnum, 1, 1, &sstyle, NULL,
"minimization step style", NULL, stepStyle );
hestOptAdd( &hopt, "rad", "radius", airTypeDouble, 1, 1, &radius, NULL,
"radius of particle" );
hestOptAdd( &hopt, "esm", "eng stdv min", airTypeDouble, 1, 1, &stdvMin,
"0.05", "minimum stdv of particle energies" );
hestOptAdd( &hopt, "bo", "backoff", airTypeDouble, 1, 1, &backoff, "0.1",
"backoff in gradient descent" );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1, &stepInitial, "1.0",
"initial step size in gradient descent" );
hestOptAdd( &hopt, "cu", "creepup", airTypeDouble, 1, 1, &creepup, "1.1",
"creepup in gradient descent" );
hestOptAdd( &hopt, "chl", "cool half life", airTypeUInt, 1, 1, &coolHalfLife,
"20", "cool half life" );
hestOptAdd( &hopt, "np", "# part", airTypeUInt, 1, 1, &pntNum, "42",
"# of particles in simulation" );
hestOptAdd( &hopt, "maxi", "max iter", airTypeUInt, 1, 1, &iterMax, "1000",
"max number of iterations" );
hestOptAdd( &hopt, "seed", "seed", airTypeUInt, 1, 1, &rngSeed, "42",
"random number generator seed" );
hestOptAdd( &hopt, "o", "out", airTypeString, 1, 1, &outS, "-",
"output filename" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
airSrandMT( rngSeed );
posNum = 0;
posArr = airArrayNew( AIR_CAST( void **, &pos ),
&posNum, pntNum*sizeof( double ), iterMax/10 );
airMopAdd( mop, posArr, ( airMopper )airArrayNuke, airMopAlways );
step = AIR_CALLOC( pntNum, double );
airMopAdd( mop, step, airFree, airMopAlways );
for ( pntIdx=0; pntIdx<pntNum; pntIdx++ ) {
step[pntIdx] = stepInitial;
}
enspCharge = pullEnergySpecNew( );
airMopAdd( mop, enspCharge, ( airMopper )pullEnergySpecNix, airMopAlways );
if ( pullEnergySpecParse( enspCharge, "qwell:1" ) ) {
char *err;
airMopAdd( mop, err = biffGetDone( PULL ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s", me, err );
airMopError( mop ); return 1;
}
for ( iter=0; iter<iterMax; iter++ ) {
double stdv, cool, fiter;
if ( !iter ) {
airArrayLenIncr( posArr, 1 );
for ( pntIdx=0; pntIdx<pntNum; pntIdx++ ) {
pos[pntIdx] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0,
0.0, 2*AIR_PI );
}
}
fiter = AIR_MAX( 0, ( ( double )iter )-TOP_MARGIN );
cool = ( AIR_PI/2 )*pow( 0.5, fiter/coolHalfLife );
airArrayLenIncr( posArr, 1 );
runIter( iter, pos + pntNum*( iter+1 ), sstyle,
pos + pntNum*iter, step, pntNum,
ensp, radius, cool, backoff, creepup );
stdv = enrStdv( pos + pntNum*( iter+1 ), pntNum, enspCharge, AIR_PI/2 );
if ( verbose > 1 ) {
fprintf( stderr, "%s: iter %u stdv %g cool %g\n", me, iter, stdv, cool );
}
if ( stdv < stdvMin ) {
fprintf( stderr, "%s: converged in %u iters ( stdv %g < %g )\n", me,
iter, stdv, stdvMin );
break;
}
}
npos = nrrdNew( );
airMopAdd( mop, npos, ( airMopper )nrrdNix, airMopAlways );
if ( nrrdWrap_va( npos, pos, nrrdTypeDouble, 2,
AIR_CAST( size_t, pntNum ),
AIR_CAST( size_t, posNum ) )
|| nrrdSave( outS, npos, NULL ) ) {
char *err;
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem saving output:\n%s", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../pull.h"
char *info = ( "Tests parsing of energy, and its methods." );
int
30 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
pullEnergySpec *ensp;
unsigned int pi, xi, nn;
double xx, supp;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL,
"specification of force function to use",
NULL, NULL, pullHestEnergySpec );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
fprintf( stderr, "%s: parsed energy \"%s\", with %u parms.\n", me,
ensp->energy->name, ensp->energy->parmNum );
for ( pi=0; pi<ensp->energy->parmNum; pi++ ) {
fprintf( stderr, "parm[%u] == %g\n", pi, ensp->parm[pi] );
}
fprintf( stderr, "\n" );
nn = 600;
supp = 1.0;
for ( xi=1; xi<nn; xi++ ) {
double x0, x1, ee, ff, e0, e1, dummy;
xx = AIR_AFFINE( 0, xi, nn, 0, supp );
x1 = AIR_AFFINE( 0, xi+1, nn, 0, supp );
x0 = AIR_AFFINE( 0, xi-1, nn, 0, supp );
e1 = ensp->energy->eval( &dummy, x1, ensp->parm );
e0 = ensp->energy->eval( &dummy, x0, ensp->parm );
ee = ensp->energy->eval( &ff, xx, ensp->parm );
printf( "%g %g %g %g\n", xx, ee, ff, ( e1 - e0 )/( x1 - x0 ) );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
pullTrace *
29 pullTraceNew( void ) {
pullTrace *ret;
ret = AIR_CALLOC( 1, pullTrace );
if ( ret ) {
ret->seedPos[0] = ret->seedPos[1] = AIR_NAN;
ret->seedPos[2] = ret->seedPos[3] = AIR_NAN;
ret->nvert = nrrdNew( );
ret->nstrn = nrrdNew( );
ret->nstab = nrrdNew( );
ret->norin = nrrdNew( );
ret->seedIdx = 0;
ret->whyStop[0] = ret->whyStop[1] = pullTraceStopUnknown;
ret->whyStopCFail[0] = ret->whyStopCFail[1] = pullConstraintFailUnknown;
ret->whyNowhere = pullTraceStopUnknown;
ret->whyNowhereCFail = pullConstraintFailUnknown;
}
return ret;
}
pullTrace *
50 pullTraceNix( pullTrace *pts ) {
if ( pts ) {
nrrdNuke( pts->nvert );
nrrdNuke( pts->nstrn );
nrrdNuke( pts->nstab );
nrrdNuke( pts->norin );
free( pts );
}
return NULL;
}
void
63 pullTraceStability( double *spcStab,
double *oriStab,
const double pos0[4],
const double pos1[4],
const double ori0[3],
const double ori1[3],
double sigma0,
const pullContext *pctx ) {
double sc, stb, dx, ds, diff[4];
ELL_4V_SUB( diff, pos1, pos0 );
dx = ELL_3V_LEN( diff )/( pctx->voxelSizeSpace );
sc = ( pos0[3] + pos1[3] )/2;
if ( pctx->flag.scaleIsTau ) {
sc = gageSigOfTau( sc );
}
sc += sigma0;
dx /= sc;
ds = diff[3];
stb = atan2( ds, dx )/( AIR_PI/2 ); /* [-1, 1]: 0 means least stable */
stb = AIR_ABS( stb ); /* [0, 1]: 0 means least stable */
*spcStab = AIR_CLAMP( 0, stb, 1 );
/*
static double maxvelo = 0;
if ( 1 && vv > maxvelo ) {
char me[]="pullTraceStability";
printf( "\n%s: [%g, %g, %g, %g] - [%g, %g, %g, %g] = [%g, %g, %g, %g]\n", me,
pos0[0], pos0[1], pos0[2], pos0[3],
pos1[0], pos1[1], pos1[2], pos1[3],
diff[0], diff[1], diff[2], diff[3] );
printf( "%s: dx = %g -> %g; ds = %g\n", me, dx0, dx, ds );
printf( "%s: vv = atan2( %g, %g )/pi = %g -> %g > %g\n", me, ds, dx, vv0, vv, maxvelo );
maxvelo = vv;
}
*/
if ( ori0 && ori1 ) {
/* dori = delta in orientation */
double dori = ell_3v_angle_d( ori0, ori1 );
/* dori in [0, pi]; 0 and pi mean no change */
if ( dori > AIR_PI/2 ) {
dori -= AIR_PI;
}
/* dori in [-pi/2, pi/2]; 0 means no change; +-pi/2 means most change */
dori = AIR_ABS( dori );
/* dori in [0, pi/2]; 0 means no change; pi/2 means most change */
*oriStab = atan2( ds, dori )/( AIR_PI/2 );
/* *oriStab in [0, 1]; 0 means stable, 1 means not stable */
} else {
*oriStab = 0;
}
return;
}
int
117 _pullConstrTanSlap( pullContext *pctx, pullPoint *point,
double tlen,
/* input+output */ double toff[3],
/* output */ int *constrFailP ) {
static const char me[]="_pullConstrTanSlap";
double pos0[4], tt;
if ( !( pctx && point && toff && constrFailP ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( pctx->flag.zeroZ ) {
toff[2] = 0;
}
ELL_3V_NORM( toff, toff, tt );
if ( !( tlen > 0 && tt > 0 ) ) {
biffAddf( PULL, "%s: tlen %g or |toff| %g not positive", me, tlen, tt );
return 1;
}
ELL_4V_COPY( pos0, point->pos ); /* pos0[3] should stay put; will test at end */
ELL_3V_SCALE_ADD2( point->pos, 1, pos0, tlen, toff );
if ( _pullConstraintSatisfy( pctx->task[0], point, 0 /* travmax */, constrFailP ) ) {
biffAddf( PULL, "%s: trouble", me );
ELL_4V_COPY( point->pos, pos0 ); return 1;
}
/* save offset to new position */
ELL_3V_SUB( toff, point->pos, pos0 );
if ( pos0[3] != point->pos[3] ) {
biffAddf( PULL, "%s: point->pos[3] %g was changed ( from %g )",
me, point->pos[3], pos0[3] );
ELL_4V_COPY( point->pos, pos0 ); return 1;
}
ELL_4V_COPY( point->pos, pos0 );
return 0;
}
int
156 _pullConstrOrientFind( pullContext *pctx, pullPoint *point,
int normalfind, /* find normal two 2D surface,
else find tangent to 1D curve */
double tlen,
const double tdir0[3], /* if non-NULL, try using this direction */
/* output */
double dir[3],
int *constrFailP ) {
static const char me[]="_pullConstrOrientFind";
double tt;
#define SLAP( LEN, DIR ) \
/* fprintf( stderr, "!%s: SLAP %g %g %g -->", me, ( DIR )[0], ( DIR )[1], ( DIR )[2] ); */ \
if ( _pullConstrTanSlap( pctx, point, ( LEN ), ( DIR ), constrFailP ) ) { \
biffAddf( PULL, "%s: looking for tangent, starting with ( %g, %g, %g )", \
me, ( DIR )[0], ( DIR )[1], ( DIR )[2] ); \
return 1; \
} \
if ( *constrFailP ) { \
/* unsuccessful in finding tangent, but not a biff error */ \
return 0; \
} \
/* fprintf( stderr, " %g %g %g\n", ( DIR )[0], ( DIR )[1], ( DIR )[2] ); */
if ( !( pctx && point && constrFailP ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
*constrFailP = pullConstraintFailUnknown;
if ( normalfind ) {
double tan0[3], tan1[3];
/* looking for a surface normal */
if ( tdir0 ) { /* have something to start with */
ell_3v_perp_d( tan0, tdir0 );
ELL_3V_CROSS( tan1, tan0, tdir0 );
SLAP( tlen, tan1 );
SLAP( tlen, tan0 );
ELL_3V_CROSS( dir, tan1, tan0 );
} else { /* have to start from scratch */
double tns[9] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
SLAP( tlen, tns+0 );
SLAP( tlen, tns+3 );
SLAP( tlen, tns+6 );
ell_3m_1d_nullspace_d( dir, tns );
}
} else {
/* looking for a curve tangent */
if ( tdir0 ) { /* have something to start with */
ELL_3V_COPY( dir, tdir0 );
SLAP( tlen, dir );
/* SLAP( tlen, dir ); ( didn't have much effect, in one test ) */
} else { /* have to start from scratch */
double tX[3] = {1, 0, 0}, tY[3] = {0, 1, 0}, tZ[3] = {0, 0, 1};
SLAP( tlen, tX );
SLAP( tlen, tY );
if ( ELL_3V_DOT( tX, tY ) < 0 ) { ELL_3V_SCALE( tY, -1, tY ); }
if ( pctx->flag.zeroZ ) {
ELL_3V_SET( tZ, 0, 0, 0 );
} else {
SLAP( tlen, tZ );
if ( ELL_3V_DOT( tX, tZ ) < 0 ) { ELL_3V_SCALE( tZ, -1, tZ ); }
if ( ELL_3V_DOT( tY, tZ ) < 0 ) { ELL_3V_SCALE( tY, -1, tZ ); }
}
ELL_3V_ADD3( dir, tX, tY, tZ );
}
}
ELL_3V_NORM( dir, dir, tt );
if ( !( tt > 0 ) ) {
biffAddf( PULL, "%s: computed direction is zero ( %g )?", me, tt );
return 1;
}
return 0;
}
/*
******** pullTraceSet
**
** computes a single trace, according to the given parameters,
** and store it in the pullTrace
int recordStrength: should strength be recorded along trace
double scaleDelta: discrete step along scale
double halfScaleWin: how far, along scale, trace should go in each direction
unsigned int arrIncr: increment for storing position ( maybe strength )
const double _seedPos[4]: starting position
*/
241 int
pullTraceSet( pullContext *pctx, pullTrace *pts,
int recordStrength,
double scaleDelta, double halfScaleWin,
double orientTestLen,
unsigned int arrIncr,
const double _seedPos[4] ) {
static const char me[]="pullTraceSet";
pullPoint *point;
airArray *mop, *trceArr[2], *hstrnArr[2], *horinArr[2];
double *trce[2], ssrange[2], *vert, *hstrn[2], *horin[2], *strn, *orin, *stab,
seedPos[4], polen, porin[3];
int constrFail;
unsigned int dirIdx, lentmp, tidx, oidx, vertNum;
if ( !( pctx && pts && _seedPos ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( scaleDelta ) && scaleDelta > 0.0 ) ) {
biffAddf( PULL, "%s: need existing scaleDelta > 0 ( not %g )",
me, scaleDelta );
return 1;
}
if ( !( halfScaleWin > 0 ) ) {
biffAddf( PULL, "%s: need halfScaleWin > 0", me );
return 1;
}
if ( !( pctx->constraint ) ) {
biffAddf( PULL, "%s: given context doesn't have constraint set", me );
return 1;
}
pts->fdim = _pullConstraintDim( pctx );
if ( 0 > pts->fdim ) {
biffAddf( PULL, "%s: couldn't learn dimension of feature", me );
return 1;
}
if ( pts->fdim == 2 && pctx->flag.zeroZ ) {
biffAddf( PULL, "%s: can't have feature dim 2 with zeroZ", me );
return 1;
}
if ( !( AIR_EXISTS( orientTestLen ) && orientTestLen >= 0 ) ) {
biffAddf( PULL, "%s: need non-negative orientTestLen ( not %g )\n",
me, orientTestLen );
return 1;
}
if ( pts->fdim && !orientTestLen ) {
/* not really an error */
/*
fprintf( stderr, "\n\n%s: WARNING: have a %d-D feature, but not "
"measuring its orientation\n\n\n", me, pts->fdim );
*/
}
/* The test for "should I measure orientation"
is "if ( pts->fdim && orientTestLen )" */
if ( recordStrength && !pctx->ispec[pullInfoStrength] ) {
biffAddf( PULL, "%s: want to record strength but %s not set in context",
me, airEnumStr( pullInfo, pullInfoStrength ) );
return 1;
}
if ( pullConstraintScaleRange( pctx, ssrange ) ) {
biffAddf( PULL, "%s: trouble getting scale range", me );
return 1;
}
/* re-initialize termination descriptions ( in case of trace re-use ) */
pts->whyStop[0] = pts->whyStop[1] = pullTraceStopUnknown;
pts->whyStopCFail[0] = pts->whyStopCFail[1] = pullConstraintFailUnknown;
pts->whyNowhere = pullTraceStopUnknown;
pts->whyNowhereCFail = pullConstraintFailUnknown;
/* enforce zeroZ */
ELL_4V_COPY( seedPos, _seedPos );
if ( pctx->flag.zeroZ ) {
seedPos[2] = 0.0;
}
/* save seedPos in any case */
ELL_4V_COPY( pts->seedPos, seedPos );
mop = airMopNew( );
point = pullPointNew( pctx ); /* we'll want to decrement idtagNext later */
airMopAdd( mop, point, ( airMopper )pullPointNix, airMopAlways );
ELL_4V_COPY( point->pos, seedPos );
/*
if ( pctx->verbose ) {
fprintf( stderr, "%s: trying at seed=( %g, %g, %g, %g )\n", me,
seedPos[0], seedPos[1], seedPos[2], seedPos[3] );
}
*/
/* The termination of the trace due to low stablity is handled here
( or could be ), not by _pullConstraintSatisfy, so we set 0 for the
travelMax arg of _pullConstraintSatisfy ( no travel limit ) */
if ( _pullConstraintSatisfy( pctx->task[0], point,
0 /* travmax */, &constrFail ) ) {
biffAddf( PULL, "%s: constraint sat on seed point", me );
airMopError( mop );
return 1;
}
/*
if ( pctx->verbose ) {
fprintf( stderr, "%s: seed=( %g, %g, %g, %g ) -> %s ( %g, %g, %g, %g )\n", me,
seedPos[0], seedPos[1], seedPos[2], seedPos[3],
constrFail ? "!NO!" : "( yes )",
point->pos[0] - seedPos[0], point->pos[1] - seedPos[1],
point->pos[2] - seedPos[2], point->pos[3] - seedPos[3] );
}
*/
if ( constrFail ) {
pts->whyNowhere = pullTraceStopConstrFail;
airMopOkay( mop );
/* pctx->idtagNext -= 1; / * HACK * / */
return 0;
}
if ( point->status & PULL_STATUS_EDGE_BIT ) {
pts->whyNowhere = pullTraceStopVolumeEdge;
airMopOkay( mop );
/* pctx->idtagNext -= 1; / * HACK * / */
return 0;
}
if ( pctx->flag.zeroZ && point->pos[2] != 0 ) {
biffAddf( PULL, "%s: zeroZ violated ( a )", me );
airMopError( mop );
return 1;
}
/* else constraint sat worked at seed point; we have work to do */
if ( pts->fdim && orientTestLen ) {
/* learn orientation at seed point */
polen = ( orientTestLen
*pctx->voxelSizeSpace
/* if used, the effect of this this last ( unprincipled ) factor
is gradually increase the test distance with scale
*( 1 + gageTauOfSig( _pullSigma( pctx, point->pos ) ) ) */ );
double pos0[4], dp[4];
int cf;
ELL_4V_COPY( pos0, point->pos );
if ( _pullConstrOrientFind( pctx, point, pts->fdim == 2,
polen, NULL /* no initial guess */, porin, &cf ) ) {
biffAddf( PULL, "%s: trying to find orientation at seed", me );
airMopError( mop );
return 1;
}
ELL_4V_SUB( dp, pos0, point->pos );
/*
fprintf( stderr, "!%s: cf = %d ( %s )\n", me, cf, airEnumStr( pullConstraintFail, cf ) );
fprintf( stderr, "!%s: ( fdim=%u ) pos=[%g, %g, %g, %g] polen=%g porin=[%g, %g, %g] |%g|\n",
me, pts->fdim,
point->pos[0], point->pos[1], point->pos[2], point->pos[3],
polen, porin[0], porin[1], porin[2], ELL_4V_LEN( dp ) );
*/
if ( cf ) {
pts->whyNowhere = pullTraceStopConstrFail;
pts->whyNowhereCFail = cf;
airMopOkay( mop );
/* pctx->idtagNext -= 1; / * HACK * / */
return 0;
}
} else {
/* either feature is 0D points, or don't care about orientation */
polen = AIR_NAN;
ELL_3V_SET( porin, AIR_NAN, AIR_NAN, AIR_NAN );
}
for ( dirIdx=0; dirIdx<2; dirIdx++ ) {
trceArr[dirIdx] = airArrayNew( ( void** )( trce + dirIdx ), NULL,
4*sizeof( double ), arrIncr );
airMopAdd( mop, trceArr[dirIdx], ( airMopper )airArrayNuke, airMopAlways );
if ( recordStrength ) {
hstrnArr[dirIdx] = airArrayNew( ( void** )( hstrn + dirIdx ), NULL,
sizeof( double ), arrIncr );
airMopAdd( mop, hstrnArr[dirIdx], ( airMopper )airArrayNuke, airMopAlways );
} else {
hstrnArr[dirIdx] = NULL;
hstrn[dirIdx] = NULL;
}
if ( pts->fdim && orientTestLen ) {
horinArr[dirIdx] = airArrayNew( ( void** )( horin + dirIdx ), NULL,
3*sizeof( double ), arrIncr );
airMopAdd( mop, horinArr[dirIdx], ( airMopper )airArrayNuke, airMopAlways );
} else {
horinArr[dirIdx] = NULL;
horin[dirIdx] = NULL;
}
}
for ( dirIdx=0; dirIdx<2; dirIdx++ ) {
unsigned int step;
double dscl;
dscl = ( !dirIdx ? -1 : +1 )*scaleDelta;
step = 0;
while ( 1 ) {
if ( !step ) {
/* first step in both directions requires special tricks */
if ( 0 == dirIdx ) {
/* this is done once, at the very start */
/* save constraint sat of seed point */
tidx = airArrayLenIncr( trceArr[0], 1 );
ELL_4V_COPY( trce[0] + 4*tidx, point->pos );
if ( recordStrength ) {
airArrayLenIncr( hstrnArr[0], 1 );
hstrn[0][0] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
}
if ( pts->fdim && orientTestLen ) {
airArrayLenIncr( horinArr[0], 1 );
ELL_3V_COPY( horin[0] + 3*0, porin );
}
} else {
/* re-set position from constraint sat of seed pos */
ELL_4V_COPY( point->pos, trce[0] + 4*0 );
if ( pts->fdim && orientTestLen ) {
ELL_3V_COPY( porin, horin[0] + 3*0 );
}
}
}
/* nudge position along scale */
point->pos[3] += dscl;
if ( !AIR_IN_OP( ssrange[0], point->pos[3], ssrange[1] ) ) {
/* if we've stepped outside the range of scale for the volume
containing the constraint manifold, we're done */
pts->whyStop[dirIdx] = pullTraceStopBounds;
break;
}
if ( AIR_ABS( point->pos[3] - seedPos[3] ) > halfScaleWin ) {
/* we've moved along scale as far as allowed */
pts->whyStop[dirIdx] = pullTraceStopLength;
break;
}
/* re-assert constraint */
/*
fprintf( stderr, "%s( %u ): pos = %g %g %g %g.... \n", me,
point->idtag, point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
*/
if ( _pullConstraintSatisfy( pctx->task[0], point,
0 /* travmax */, &constrFail ) ) {
biffAddf( PULL, "%s: dir %u, step %u", me, dirIdx, step );
airMopError( mop );
return 1;
}
/*
if ( pctx->verbose ) {
fprintf( stderr, "%s( %u ): ... %s( %d ); pos = %g %g %g %g\n", me,
point->idtag,
constrFail ? "FAIL" : "( ok )",
constrFail, point->pos[0], point->pos[1],
point->pos[2], point->pos[3] );
}
*/
if ( point->status & PULL_STATUS_EDGE_BIT ) {
pts->whyStop[dirIdx] = pullTraceStopVolumeEdge;
break;
}
if ( constrFail ) {
/* constraint sat failed; no error, we're just done
with stepping for this direction */
pts->whyStop[dirIdx] = pullTraceStopConstrFail;
pts->whyStopCFail[dirIdx] = constrFail;
break;
}
if ( pctx->flag.zeroZ && point->pos[2] != 0 ) {
biffAddf( PULL, "%s: zeroZ violated ( b )", me );
airMopError( mop );
return 1;
}
if ( pts->fdim && orientTestLen ) {
if ( _pullConstrOrientFind( pctx, point, pts->fdim == 2,
polen, porin, porin, &constrFail ) ) {
biffAddf( PULL, "%s: at dir %u, step %u", me, dirIdx, step );
airMopError( mop );
return 1;
}
}
if ( trceArr[dirIdx]->len >= 2 ) {
/* see if we're moving too fast, by comparing with previous point */
/* actually, screw that */
}
/* else save new point on trace */
tidx = airArrayLenIncr( trceArr[dirIdx], 1 );
ELL_4V_COPY( trce[dirIdx] + 4*tidx, point->pos );
if ( recordStrength ) {
tidx = airArrayLenIncr( hstrnArr[dirIdx], 1 );
hstrn[dirIdx][tidx] = pullPointScalar( pctx, point, pullInfoStrength,
NULL, NULL );
}
if ( pts->fdim && orientTestLen ) {
tidx = airArrayLenIncr( horinArr[dirIdx], 1 );
ELL_3V_COPY( horin[dirIdx] + 3*tidx, porin );
}
step++;
}
}
/* transfer trace halves to pts->nvert */
vertNum = trceArr[0]->len + trceArr[1]->len;
if ( 0 == vertNum || 1 == vertNum || 2 == vertNum ) {
pts->whyNowhere = pullTraceStopStub;
airMopOkay( mop );
/* pctx->idtagNext -= 1; / * HACK * / */
return 0;
}
if ( nrrdMaybeAlloc_va( pts->nvert, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, vertNum ) )
|| nrrdMaybeAlloc_va( pts->nstab, nrrdTypeDouble, 2,
AIR_CAST( size_t, 2 ),
AIR_CAST( size_t, vertNum ) ) ) {
biffMovef( PULL, NRRD, "%s: allocating output", me );
airMopError( mop );
return 1;
}
if ( recordStrength ) {
/* doing slicing is a simple form of allocation */
if ( nrrdSlice( pts->nstrn, pts->nvert, 0 /* axis */, 0 /* pos */ ) ) {
biffMovef( PULL, NRRD, "%s: allocating strength output", me );
airMopError( mop );
return 1;
}
}
if ( pts->fdim && orientTestLen ) {
/* cropping just to allocate */
size_t cmin[2] = {0, 0}, cmax[2] = {2, pts->nvert->axis[1].size-1};
if ( nrrdCrop( pts->norin, pts->nvert, cmin, cmax ) ) {
biffMovef( PULL, NRRD, "%s: allocating orientation output", me );
airMopError( mop );
return 1;
}
}
vert = AIR_CAST( double *, pts->nvert->data );
if ( recordStrength ) {
strn = AIR_CAST( double *, pts->nstrn->data );
} else {
strn = NULL;
}
if ( pts->fdim && orientTestLen ) {
orin = AIR_CAST( double *, pts->norin->data );
} else {
orin = NULL;
}
stab = AIR_CAST( double *, pts->nstab->data );
lentmp = trceArr[0]->len;
oidx = 0;
for ( tidx=0; tidx<lentmp; tidx++ ) {
ELL_4V_COPY( vert + 4*oidx, trce[0] + 4*( lentmp - 1 - tidx ) );
if ( strn ) {
strn[oidx] = hstrn[0][lentmp - 1 - tidx];
}
if ( orin ) {
ELL_3V_COPY( orin + 3*oidx, horin[0] + 3*( lentmp - 1 - tidx ) );
}
oidx++;
}
/* the last index written to ( before oidx++ ) was the seed index */
pts->seedIdx = oidx-1;
lentmp = trceArr[1]->len;
for ( tidx=0; tidx<lentmp; tidx++ ) {
ELL_4V_COPY( vert + 4*oidx, trce[1] + 4*tidx );
if ( strn ) {
strn[oidx] = hstrn[1][tidx];
}
if ( orin ) {
ELL_3V_COPY( orin + 3*oidx, horin[1] + 3*tidx );
}
oidx++;
}
lentmp = pts->nstab->axis[1].size;
if ( 1 == lentmp ) {
stab[0 + 2*0] = 0.0;
stab[1 + 2*0] = 0.0;
} else {
for ( tidx=0; tidx<lentmp; tidx++ ) {
double *pA, *pB, *p0, *p1, *p2, *rA, *rB, *r0=NULL, *r1=NULL, *r2=NULL;
p0 = vert + 4*( tidx-1 );
p1 = vert + 4*tidx;
p2 = vert + 4*( tidx+1 );
if ( orin ) {
r0 = orin + 3*( tidx-1 );
r1 = orin + 3*tidx;
r2 = orin + 3*( tidx+1 );
}
if ( !tidx ) {
/* first */
pA = p1; rA = r1;
pB = p2; rB = r2;
} else if ( tidx < lentmp-1 ) {
/* middle */
pA = p0; rA = r0;
pB = p2; rB = r2;
} else {
/* last */
pA = p0; rA = r0;
pB = p1; rB = r1;
}
pullTraceStability( stab + 0 + 2*tidx, stab + 1 + 2*tidx,
pA, pB, rA, rB, 0.5 /* sigma0 */, pctx );
}
}
airMopOkay( mop );
/* pctx->idtagNext -= 1; / * HACK * / */
return 0;
}
typedef union {
pullTrace ***trace;
void **v;
} blahblahUnion;
652 pullTraceMulti *
pullTraceMultiNew( void ) {
/* static const char me[]="pullTraceMultiNew"; */
pullTraceMulti *ret;
blahblahUnion bbu;
ret = AIR_CALLOC( 1, pullTraceMulti );
if ( ret ) {
ret->trace = NULL;
ret->traceNum = 0;
ret->traceArr = airArrayNew( ( bbu.trace = &( ret->trace ), bbu.v ),
&( ret->traceNum ), sizeof( pullTrace* ),
_PULL_TRACE_MULTI_INCR );
airArrayPointerCB( ret->traceArr,
NULL, /* because we get handed pullTrace structs
that have already been allocated
( and then we own them ) */
( void *( * )( void * ) )pullTraceNix );
}
return ret;
}
674 int
pullTraceMultiAdd( pullTraceMulti *mtrc, pullTrace *trc, int *addedP ) {
static const char me[]="pullTraceMultiAdd";
unsigned int indx;
if ( !( mtrc && trc && addedP ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !( trc->nvert->data && trc->nvert->axis[1].size >= 3 ) ) {
/* for now getting a stub trace is not an error
biffAddf( PULL, "%s: got stub trace", me );
return 1; */
*addedP = AIR_FALSE;
return 0;
}
if ( !( trc->nstab->data
&& trc->nstab->axis[1].size == trc->nvert->axis[1].size ) ) {
biffAddf( PULL, "%s: stab data inconsistent", me );
return 1;
}
*addedP = AIR_TRUE;
indx = airArrayLenIncr( mtrc->traceArr, 1 );
if ( !mtrc->trace ) {
biffAddf( PULL, "%s: alloc error", me );
return 1;
}
mtrc->trace[indx] = trc;
return 0;
}
705 int
pullTraceMultiPlotAdd( Nrrd *nplot, const pullTraceMulti *mtrc,
const Nrrd *nfilt, int strengthUse,
int smooth, int flatWght,
unsigned int trcIdxMin, unsigned int trcNum,
Nrrd *nmaskedpos,
const Nrrd *nmask ) {
static const char me[]="pullTraceMultiPlotAdd";
double ssRange[2], vRange[2], *plot;
unsigned int sizeS, sizeT, trcIdx, trcIdxMax;
int *filt;
airArray *mop;
Nrrd *nsmst; /* smoothed stability */
airArray *mposArr;
double *mpos;
unsigned char *mask;
if ( !( nplot && mtrc ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nplot ) ) {
biffMovef( PULL, NRRD, "%s: trouble with nplot", me );
return 1;
}
if ( nfilt ) {
if ( nrrdCheck( nfilt ) ) {
biffMovef( PULL, NRRD, "%s: trouble with nfilt", me );
return 1;
}
if ( !( 1 == nfilt->dim && nrrdTypeInt == nfilt->type ) ) {
biffAddf( PULL, "%s: didn't get 1-D array of %s ( got %u-D of %s )", me,
airEnumStr( nrrdType, nrrdTypeInt ), nfilt->dim,
airEnumStr( nrrdType, nfilt->type ) );
return 1;
}
}
if ( !( 2 == nplot->dim && nrrdTypeDouble == nplot->type ) ) {
biffAddf( PULL, "%s: didn't get 2-D array of %s ( got %u-D of %s )", me,
airEnumStr( nrrdType, nrrdTypeDouble ), nplot->dim,
airEnumStr( nrrdType, nplot->type ) );
return 1;
}
if ( !( trcIdxMin < mtrc->traceNum ) ) {
biffAddf( PULL, "%s: trcIdxMin %u not < traceNum %u", me,
trcIdxMin, mtrc->traceNum );
return 1;
}
if ( trcNum ) {
trcIdxMax = trcIdxMin + trcNum-1;
if ( !( trcIdxMax < mtrc->traceNum ) ) {
biffAddf( PULL, "%s: trcIdxMax %u = %u+%u-1 not < traceNum %u", me,
trcIdxMax, trcIdxMin, trcNum, mtrc->traceNum );
return 1;
}
} else {
trcIdxMax = mtrc->traceNum-1;
}
if ( nmaskedpos || nmask ) {
if ( !( nmaskedpos && nmask ) ) {
biffAddf( PULL, "%s: need either both or neither of nmaskedpos ( %p )"
"and nmask ( %p )", me, nmaskedpos, nmask );
return 1;
}
if ( !( 2 == nmask->dim
&& nrrdTypeUChar == nmask->type
&& nplot->axis[0].size == nmask->axis[0].size
&& nplot->axis[1].size == nmask->axis[1].size ) ) {
biffAddf( PULL, "%s: got trace mask but wanted "
"2-D %s %u-by-%u ( not %u-D %s %u-by-%u )\n", me,
airEnumStr( nrrdType, nrrdTypeUChar ),
AIR_CAST( unsigned int, nplot->axis[0].size ),
AIR_CAST( unsigned int, nplot->axis[1].size ),
nmask->dim,
airEnumStr( nrrdType, nmask->type ),
AIR_CAST( unsigned int, nmask->axis[0].size ),
AIR_CAST( unsigned int, nmask->axis[1].size ) );
return 1;
}
mask = AIR_CAST( unsigned char *, nmask->data );
} else {
mask = NULL;
}
ssRange[0] = nplot->axis[0].min;
ssRange[1] = nplot->axis[0].max;
vRange[0] = nplot->axis[1].min;
vRange[1] = nplot->axis[1].max;
if ( !( AIR_EXISTS( ssRange[0] ) && AIR_EXISTS( ssRange[1] ) &&
AIR_EXISTS( vRange[0] ) && AIR_EXISTS( vRange[1] ) ) ) {
biffAddf( PULL, "%s: need both axis 0 ( %g, %g ) and 1 ( %g, %g ) min, max", me,
ssRange[0], ssRange[1], vRange[0], vRange[1] );
return 1;
}
if ( 1 != vRange[0] ) {
biffAddf( PULL, "%s: expected vRange[0] == 1 not %g", me, vRange[0] );
return 1;
}
mop = airMopNew( );
mpos = NULL;
if ( nmaskedpos && nmask ) {
nrrdEmpty( nmaskedpos );
mposArr = airArrayNew( ( void** )( &mpos ), NULL,
4*sizeof( double ), 512 /* HEY */ );
} else {
mposArr = NULL;
}
nsmst = nrrdNew( );
airMopAdd( mop, nsmst, ( airMopper )nrrdNuke, airMopAlways );
plot = AIR_CAST( double *, nplot->data );
filt = ( nfilt
? AIR_CAST( int *, nfilt->data )
: NULL );
sizeS = AIR_CAST( unsigned int, nplot->axis[0].size );
sizeT = AIR_CAST( unsigned int, nplot->axis[1].size );
for ( trcIdx=trcIdxMin; trcIdx<=trcIdxMax; trcIdx++ ) {
int pntIdx, pntNum;
const pullTrace *trc;
const double *vert, *stab, *strn;
unsigned int maskInCount;
double maskInPos[4];
if ( filt && !filt[trcIdx] ) {
continue;
}
trc = mtrc->trace[trcIdx];
if ( pullTraceStopStub == trc->whyNowhere ) {
continue;
}
if ( strengthUse && !( trc->nstrn && trc->nstrn->data ) ) {
biffAddf( PULL, "%s: requesting strength-based weighting, but don't have "
"strength info in trace %u", me, trcIdx );
airMopError( mop ); return 1;
}
pntNum = AIR_CAST( int, trc->nvert->axis[1].size );
vert = AIR_CAST( double *, trc->nvert->data );
stab = AIR_CAST( double *, trc->nstab->data );
if ( smooth > 0 ) {
double *smst;
if ( nrrdCopy( nsmst, trc->nstab ) ) {
biffMovef( PULL, NRRD, "%s: trouble w/ trace %u", me, trcIdx );
airMopError( mop ); return 1;
}
smst = AIR_CAST( double *, nsmst->data );
for ( pntIdx=0; pntIdx<pntNum; pntIdx++ ) {
int ii, jj;
double ss, ww, ws;
ss = ws = 0;
for ( jj=-smooth; jj<=smooth; jj++ ) {
ii = pntIdx+jj;
ii = AIR_CLAMP( 0, ii, pntNum-1 );
ww = nrrdKernelBSpline3->eval1_d( AIR_AFFINE( -smooth-1, jj,
smooth+1, -2, 2 ), NULL );
ws += ww;
ss += ww*stab[0 + 2*ii]*stab[1 + 2*ii];
}
smst[pntIdx] = ss/ws;
}
/* now redirect stab */
stab = smst;
}
strn = AIR_CAST( double *, ( strengthUse && trc->nstrn
? trc->nstrn->data : NULL ) );
/* would be nice to get some graphical indication of this */
fprintf( stderr, "!%s: trace %u in [%u, %u]: %u points; stops = %s( %s ) | %s( %s )\n",
me, trcIdx, trcIdxMin, trcIdxMax, pntNum,
airEnumStr( pullTraceStop, trc->whyStop[0] ),
( pullTraceStopConstrFail == trc->whyStop[0]
? airEnumStr( pullConstraintFail, trc->whyStopCFail[0] )
: "" ),
airEnumStr( pullTraceStop, trc->whyStop[1] ),
( pullTraceStopConstrFail == trc->whyStop[1]
? airEnumStr( pullConstraintFail, trc->whyStopCFail[1] )
: "" ) );
/* */
if ( mask ) {
maskInCount = 0;
ELL_4V_SET( maskInPos, 0, 0, 0, 0 );
}
for ( pntIdx=0; pntIdx<pntNum; pntIdx++ ) {
const double *pp;
double add, ww;
unsigned int sidx, vidx;
pp = vert + 4*pntIdx;
if ( !( AIR_IN_OP( ssRange[0], pp[3], ssRange[1] ) ) ) {
continue;
}
if ( flatWght > 0 ) {
if ( !pntIdx || pntIdx == pntNum-1 ) {
continue;
}
} else if ( flatWght < 0 ) {
/* HACK: only show the seed point */
if ( AIR_CAST( unsigned int, pntIdx ) != trc->seedIdx ) {
continue;
}
}
sidx = airIndex( ssRange[0], pp[3], ssRange[1], sizeS );
vidx = airIndexClamp( 1, stab[0 + 2*pntIdx]*stab[1 + 2*pntIdx], 0, sizeT );
add = strn ? strn[pntIdx] : 1;
if ( flatWght > 0 ) {
double dx = ( ( ( vert + 4*( pntIdx+1 ) )[3] - ( vert + 4*( pntIdx-1 ) )[3] )
/ ( ssRange[1] - ssRange[0] ) );
/*
double dx = ( ( ( vert + 4*( pntIdx+1 ) )[3] - pp[3] )
/ ( ssRange[1] - ssRange[0] ) );
*/
double dy = ( stab[0 + 2*( pntIdx+1 )]*stab[1 + 2*( pntIdx+1 )]
- stab[0 + 2*( pntIdx-1 )]*stab[1 + 2*( pntIdx-1 )] );
ww = dx/sqrt( dx*dx + dy*dy );
} else {
ww = 1;
}
plot[sidx + sizeS*vidx] += AIR_MAX( 0, ww*add );
if ( mask && mask[sidx + sizeS*vidx] > 200 ) {
ELL_4V_ADD2( maskInPos, maskInPos, pp );
maskInCount ++;
}
}
if ( mask && maskInCount ) {
unsigned int mpi = airArrayLenIncr( mposArr, 1 );
ELL_4V_SCALE( mpos + 4*mpi, 1.0/maskInCount, maskInPos );
}
}
if ( mask && mposArr->len ) {
if ( nrrdMaybeAlloc_va( nmaskedpos, nrrdTypeDouble, 2,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, mposArr->len ) ) ) {
biffAddf( PULL, "%s: couldn't allocate masked pos", me );
airMopError( mop ); return 1;
}
memcpy( nmaskedpos->data, mposArr->data,
4*( mposArr->len )*sizeof( double ) );
}
airMopOkay( mop );
return 0;
}
946 static size_t
nsizeof( const Nrrd *nrrd ) {
return ( nrrd
? nrrdElementSize( nrrd )*nrrdElementNumber( nrrd )
: 0 );
}
953 size_t
pullTraceMultiSizeof( const pullTraceMulti *mtrc ) {
size_t ret;
unsigned int ti;
if ( !mtrc ) {
return 0;
}
ret = 0;
for ( ti=0; ti<mtrc->traceNum; ti++ ) {
ret += sizeof( pullTrace );
ret += nsizeof( mtrc->trace[ti]->nvert );
ret += nsizeof( mtrc->trace[ti]->nstrn );
ret += nsizeof( mtrc->trace[ti]->nstab );
}
ret += sizeof( pullTrace* )*( mtrc->traceArr->size );
return ret;
}
972 pullTraceMulti *
pullTraceMultiNix( pullTraceMulti *mtrc ) {
if ( mtrc ) {
airArrayNuke( mtrc->traceArr );
free( mtrc );
}
return NULL;
}
#define PULL_MTRC_MAGIC "PULLMTRC0001"
#define DEMARK_STR "======"
986 static int
tracewrite( FILE *file, const pullTrace *trc, unsigned int ti ) {
static const char me[]="tracewrite";
/*
this was used to get ascii coordinates for a trace,
to help isolate ( via emacs ) one trace from a saved multi-trace
NrrdIoState *nio = nrrdIoStateNew( );
nio->encoding = nrrdEncodingAscii;
*/
fprintf( file, "%s %u\n", DEMARK_STR, ti );
ell_4v_print_d( file, trc->seedPos );
#define WRITE( FF ) \
if ( trc->FF && trc->FF->data ) { \
if ( nrrdWrite( file, trc->FF, NULL /* nio */ ) ) { \
biffMovef( PULL, NRRD, "%s: trouble with " #FF , me ); \
return 1; \
} \
} else { \
fprintf( file, "NULL" ); \
} \
fprintf( file, "\n" )
fprintf( file, "nrrds: vert strn stab = %d %d %d\n",
trc->nvert && trc->nvert->data,
trc->nstrn && trc->nstrn->data,
trc->nstab && trc->nstab->data );
WRITE( nvert );
WRITE( nstrn );
WRITE( nstab );
fprintf( file, "%u\n", trc->seedIdx );
fprintf( file, "%s %s %s\n",
airEnumStr( pullTraceStop, trc->whyStop[0] ),
airEnumStr( pullTraceStop, trc->whyStop[1] ),
airEnumStr( pullTraceStop, trc->whyNowhere ) );
#undef WRITE
return 0;
}
int
pullTraceMultiWrite( FILE *file, const pullTraceMulti *mtrc ) {
static const char me[]="pullTraceMultiWrite";
unsigned int ti;
if ( !( file && mtrc ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
fprintf( file, "%s\n", PULL_MTRC_MAGIC );
fprintf( file, "%u traces\n", mtrc->traceNum );
for ( ti=0; ti<mtrc->traceNum; ti++ ) {
if ( tracewrite( file, mtrc->trace[ti], ti ) ) {
biffAddf( PULL, "%s: trace %u/%u", me, ti, mtrc->traceNum );
return 1;
}
}
return 0;
}
static int
traceread( pullTrace *trc, FILE *file, unsigned int _ti ) {
static const char me[]="traceread";
char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
unsigned int ti, lineLen;
int stops[3], hackhack, vertHN, strnHN, stabHN; /* HN == have nrrd */
sprintf( name, "separator" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 1 != sscanf( line, DEMARK_STR " %u", &ti ) ) {
biffAddf( PULL, "%s: \"%s\" doesn't look like %s line", me, line, name );
return 1;
}
if ( ti != _ti ) {
biffAddf( PULL, "%s: read trace index %u but wanted %u", me, ti, _ti );
return 1;
}
sprintf( name, "seed pos" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 4 != sscanf( line, "%lg %lg %lg %lg", trc->seedPos + 0,
trc->seedPos + 1, trc->seedPos + 2, trc->seedPos + 3 ) ) {
biffAddf( PULL, "%s: couldn't parse %s line \"%s\" as 4 doubles",
me, name, line );
return 1;
}
sprintf( name, "have nrrds" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 3 != sscanf( line, "nrrds: vert strn stab = %d %d %d",
&vertHN, &strnHN, &stabHN ) ) {
biffAddf( PULL, "%s: couldn't parse %s line", me, name );
return 1;
}
#define READ( FF ) \
if ( FF##HN ) { \
if ( nrrdRead( trc->n##FF, file, NULL ) ) { \
biffMovef( PULL, NRRD, "%s: trouble with " #FF , me ); \
return 1; \
} \
fgetc( file ); \
} else { \
airOneLine( file, line, AIR_STRLEN_MED ); \
}
hackhack = nrrdStateVerboseIO; /* should be fixed in Teem v2 */
nrrdStateVerboseIO = 0;
READ( vert );
READ( strn );
READ( stab );
nrrdStateVerboseIO = hackhack;
sprintf( name, "seed idx" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 1 != sscanf( line, "%u", &( trc->seedIdx ) ) ) {
biffAddf( PULL, "%s: didn't parse uint from %s line \"%s\"",
me, name, line );
return 1;
}
sprintf( name, "stops" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 3 != airParseStrE( stops, line, " ", 3, pullTraceStop ) ) {
biffAddf( PULL, "%s: didn't see 3 %s on %s line \"%s\"", me,
pullTraceStop->name, name, line );
return 1;
}
return 0;
}
int
pullTraceMultiRead( pullTraceMulti *mtrc, FILE *file ) {
static const char me[]="pullTraceMultiRead";
char line[AIR_STRLEN_MED], name[AIR_STRLEN_MED];
unsigned int lineLen, ti, tnum;
pullTrace *trc;
if ( !( mtrc && file ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
airArrayLenSet( mtrc->traceArr, 0 );
sprintf( name, "magic" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( strcmp( line, PULL_MTRC_MAGIC ) ) {
biffAddf( PULL, "%s: %s line \"%s\" not expected \"%s\"",
me, name, line, PULL_MTRC_MAGIC );
return 1;
}
sprintf( name, "# of traces" );
lineLen = airOneLine( file, line, AIR_STRLEN_MED );
if ( !lineLen ) {
biffAddf( PULL, "%s: didn't get %s line", me, name );
return 1;
}
if ( 1 != sscanf( line, "%u traces", &tnum ) ) {
biffAddf( PULL, "%s: \"%s\" doesn't look like %s line", me, line, name );
return 1;
}
for ( ti=0; ti<tnum; ti++ ) {
int added;
trc = pullTraceNew( );
if ( traceread( trc, file, ti ) ) {
biffAddf( PULL, "%s: on trace %u/%u", me, ti, tnum );
return 1;
}
if ( pullTraceMultiAdd( mtrc, trc, &added ) ) {
biffAddf( PULL, "%s: adding trace %u/%u", me, ti, tnum );
return 1;
}
if ( !added ) {
trc = pullTraceNix( trc );
}
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "pull.h"
#include "privatePull.h"
pullVolume *
29 pullVolumeNew( ) {
pullVolume *vol;
vol = AIR_CAST( pullVolume *, calloc( 1, sizeof( pullVolume ) ) );
if ( vol ) {
vol->verbose = 0;
vol->name = NULL;
vol->kind = NULL;
vol->ninSingle = NULL;
vol->ninScale = NULL;
vol->scaleNum = 0;
vol->scalePos = NULL;
vol->scaleDerivNorm = AIR_FALSE;
vol->scaleDerivNormBias = 0.0;
vol->ksp00 = nrrdKernelSpecNew( );
vol->ksp11 = nrrdKernelSpecNew( );
vol->ksp22 = nrrdKernelSpecNew( );
vol->kspSS = nrrdKernelSpecNew( );
GAGE_QUERY_RESET( vol->pullValQuery );
vol->gctx = NULL;
vol->gpvl = NULL;
vol->gpvlSS = NULL;
/* this is turned OFF in volumes that have infos that aren't seedthresh,
see pullInfoSpecAdd( ) */
vol->seedOnly = AIR_TRUE;
vol->forSeedPreThresh = AIR_FALSE;
}
return vol;
}
pullVolume *
60 pullVolumeNix( pullVolume *vol ) {
if ( vol ) {
airFree( vol->name );
airFree( vol->scalePos );
vol->ksp00 = nrrdKernelSpecNix( vol->ksp00 );
vol->ksp11 = nrrdKernelSpecNix( vol->ksp11 );
vol->ksp22 = nrrdKernelSpecNix( vol->ksp22 );
vol->kspSS = nrrdKernelSpecNix( vol->kspSS );
if ( vol->gctx ) {
vol->gctx = gageContextNix( vol->gctx );
}
airFree( vol->gpvlSS );
airFree( vol );
}
return NULL;
}
/*
** used to set all the fields of pullVolume at once, including the
** gageContext inside the pullVolume
**
** OLD description ...
** used both for top-level volumes in the pullContext ( pctx->vol[i] )
** in which case pctx is non-NULL,
** and for the per-task volumes ( task->vol[i] ),
** in which case pctx is NULL
** ...................
*/
int
90 _pullVolumeSet( const pullContext *pctx, int taskCopy, pullVolume *vol,
const gageKind *kind,
int verbose, const char *name,
const Nrrd *ninSingle,
const Nrrd *const *ninScale,
double *scalePos,
unsigned int ninNum,
int scaleDerivNorm,
double scaleDerivNormBias,
const NrrdKernelSpec *ksp00,
const NrrdKernelSpec *ksp11,
const NrrdKernelSpec *ksp22,
const NrrdKernelSpec *kspSS ) {
static const char me[]="_pullVolumeSet";
int E;
unsigned int vi;
if ( !( vol && kind && airStrlen( name ) && ksp00 && ksp11 && ksp22 ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !ninSingle ) {
biffAddf( PULL, "%s: needed non-NULL ninSingle", me );
return 1;
}
if ( !taskCopy ) {
for ( vi=0; vi<pctx->volNum; vi++ ) {
if ( pctx->vol[vi] == vol ) {
biffAddf( PULL, "%s: already got vol %p as vol[%u]", me,
AIR_VOIDP( vol ), vi );
return 1;
}
}
}
if ( ninNum ) {
if ( !( ninNum >= 2 ) ) {
biffAddf( PULL, "%s: need at least 2 volumes ( not %u )", me, ninNum );
return 1;
}
if ( !scalePos ) {
biffAddf( PULL, "%s: need non-NULL scalePos with ninNum %u", me, ninNum );
return 1;
}
if ( !ninScale ) {
biffAddf( PULL, "%s: need non-NULL ninScale with ninNum %u", me, ninNum );
return 1;
}
}
vol->verbose = verbose;
vol->kind = kind;
vol->gctx = gageContextNew( );
gageParmSet( vol->gctx, gageParmVerbose,
vol->verbose > 0 ? vol->verbose - 1 : 0 );
gageParmSet( vol->gctx, gageParmRenormalize, AIR_FALSE );
/* because we're likely only using accurate kernels */
gageParmSet( vol->gctx, gageParmStackNormalizeRecon, AIR_FALSE );
vol->scaleDerivNorm = scaleDerivNorm;
gageParmSet( vol->gctx, gageParmStackNormalizeDeriv, scaleDerivNorm );
vol->scaleDerivNormBias = scaleDerivNormBias;
gageParmSet( vol->gctx, gageParmStackNormalizeDerivBias,
scaleDerivNormBias );
gageParmSet( vol->gctx, gageParmTwoDimZeroZ, pctx->flag.zeroZ );
gageParmSet( vol->gctx, gageParmCheckIntegrals, AIR_TRUE );
E = 0;
if ( !E ) E |= gageKernelSet( vol->gctx, gageKernel00,
ksp00->kernel, ksp00->parm );
if ( !E ) E |= gageKernelSet( vol->gctx, gageKernel11,
ksp11->kernel, ksp11->parm );
if ( !E ) E |= gageKernelSet( vol->gctx, gageKernel22,
ksp22->kernel, ksp22->parm );
if ( ninScale ) {
if ( !kspSS ) {
biffAddf( PULL, "%s: got NULL kspSS", me );
return 1;
}
gageParmSet( vol->gctx, gageParmStackUse, AIR_TRUE );
if ( !E ) E |= !( vol->gpvl = gagePerVolumeNew( vol->gctx, ninSingle, kind ) );
vol->gpvlSS = AIR_CAST( gagePerVolume **,
calloc( ninNum, sizeof( gagePerVolume * ) ) );
if ( !E ) E |= gageStackPerVolumeNew( vol->gctx, vol->gpvlSS,
ninScale, ninNum, kind );
if ( !E ) E |= gageStackPerVolumeAttach( vol->gctx, vol->gpvl, vol->gpvlSS,
scalePos, ninNum );
if ( !E ) E |= gageKernelSet( vol->gctx, gageKernelStack,
kspSS->kernel, kspSS->parm );
} else {
vol->gpvlSS = NULL;
if ( !E ) E |= !( vol->gpvl = gagePerVolumeNew( vol->gctx, ninSingle, kind ) );
if ( !E ) E |= gagePerVolumeAttach( vol->gctx, vol->gpvl );
}
if ( E ) {
biffMovef( PULL, GAGE, "%s: trouble ( %s %s )", me,
ninSingle ? "ninSingle" : "",
ninScale ? "ninScale" : "" );
return 1;
}
gageQueryReset( vol->gctx, vol->gpvl );
/* the query is the single thing remaining unset in the gageContext */
vol->name = airStrdup( name );
if ( !vol->name ) {
biffAddf( PULL, "%s: couldn't strdup name ( len %u )", me,
AIR_CAST( unsigned int, airStrlen( name ) ) );
return 1;
}
if ( vol->verbose ) {
printf( "%s: ---- vol=%p, name = %p = |%s|\n", me, AIR_VOIDP( vol ),
AIR_VOIDP( vol->name ), vol->name );
if ( 0 != vol->scaleDerivNormBias ) {
printf( "%s: ---- scale deriv norm bias = %g\n", me,
vol->scaleDerivNormBias );
}
}
nrrdKernelSpecSet( vol->ksp00, ksp00->kernel, ksp00->parm );
nrrdKernelSpecSet( vol->ksp11, ksp11->kernel, ksp11->parm );
nrrdKernelSpecSet( vol->ksp22, ksp22->kernel, ksp22->parm );
if ( ninScale ) {
vol->ninSingle = ninSingle;
vol->ninScale = ninScale;
vol->scaleNum = ninNum;
vol->scalePos = AIR_CAST( double *, calloc( ninNum, sizeof( double ) ) );
if ( !vol->scalePos ) {
biffAddf( PULL, "%s: couldn't calloc scalePos", me );
return 1;
}
for ( vi=0; vi<ninNum; vi++ ) {
vol->scalePos[vi] = scalePos[vi];
}
nrrdKernelSpecSet( vol->kspSS, kspSS->kernel, kspSS->parm );
} else {
vol->ninSingle = ninSingle;
vol->ninScale = NULL;
vol->scaleNum = 0;
/* leave kspSS as is ( unset ) */
}
return 0;
}
/*
** the effect is to give pctx ownership of the vol
*/
int
234 pullVolumeSingleAdd( pullContext *pctx,
const gageKind *kind,
char *name, const Nrrd *nin,
const NrrdKernelSpec *ksp00,
const NrrdKernelSpec *ksp11,
const NrrdKernelSpec *ksp22 ) {
static const char me[]="pullVolumeSingleSet";
pullVolume *vol;
vol = pullVolumeNew( );
if ( _pullVolumeSet( pctx, AIR_FALSE /* taskCopy */, vol, kind,
pctx->verbose, name,
nin,
NULL, NULL, 0, AIR_FALSE, 0.0,
ksp00, ksp11, ksp22, NULL ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
/* add this volume to context */
if ( pctx->verbose ) {
printf( "%s: adding pctx->vol[%u] = %p\n", me, pctx->volNum,
AIR_VOIDP( vol ) );
}
pctx->vol[pctx->volNum] = vol;
pctx->volNum++;
return 0;
}
/*
** the effect is to give pctx ownership of the vol
*/
int
267 pullVolumeStackAdd( pullContext *pctx,
const gageKind *kind,
char *name,
const Nrrd *nin,
const Nrrd *const *ninSS,
double *scalePos,
unsigned int ninNum,
int scaleDerivNorm,
double scaleDerivNormBias,
const NrrdKernelSpec *ksp00,
const NrrdKernelSpec *ksp11,
const NrrdKernelSpec *ksp22,
const NrrdKernelSpec *kspSS ) {
static const char me[]="pullVolumeStackAdd";
pullVolume *vol;
vol = pullVolumeNew( );
if ( _pullVolumeSet( pctx, AIR_FALSE /* taskCopy */, vol, kind,
pctx->verbose, name,
nin,
ninSS, scalePos, ninNum,
scaleDerivNorm, scaleDerivNormBias,
ksp00, ksp11, ksp22, kspSS ) ) {
biffAddf( PULL, "%s: trouble", me );
return 1;
}
/* add this volume to context */
pctx->vol[pctx->volNum++] = vol;
return 0;
}
/*
** this is only used to create pullVolumes for the pullTasks
**
** DOES use biff
*/
pullVolume *
305 _pullVolumeCopy( const pullContext *pctx, const pullVolume *volOrig ) {
static const char me[]="pullVolumeCopy";
pullVolume *volNew;
volNew = pullVolumeNew( );
if ( _pullVolumeSet( pctx, AIR_TRUE /* taskCopy */, volNew, volOrig->kind,
volOrig->verbose, volOrig->name,
volOrig->ninSingle,
volOrig->ninScale,
volOrig->scalePos,
volOrig->scaleNum,
volOrig->scaleDerivNorm,
volOrig->scaleDerivNormBias,
volOrig->ksp00, volOrig->ksp11,
volOrig->ksp22, volOrig->kspSS ) ) {
biffAddf( PULL, "%s: trouble creating new volume", me );
return NULL;
}
volNew->seedOnly = volOrig->seedOnly;
volNew->forSeedPreThresh = volOrig->forSeedPreThresh;
/* _pullVolumeSet just created a new ( per-task ) gageContext, and
it will not learn the items from the info specs, so we have to
add query here */
if ( gageQuerySet( volNew->gctx, volNew->gpvl, volOrig->gpvl->query )
|| gageUpdate( volNew->gctx ) ) {
biffMovef( PULL, GAGE, "%s: trouble with new volume gctx", me );
return NULL;
}
return volNew;
}
int
337 _pullInsideBBox( pullContext *pctx, double pos[4] ) {
return ( AIR_IN_CL( pctx->bboxMin[0], pos[0], pctx->bboxMax[0] ) &&
AIR_IN_CL( pctx->bboxMin[1], pos[1], pctx->bboxMax[1] ) &&
AIR_IN_CL( pctx->bboxMin[2], pos[2], pctx->bboxMax[2] ) &&
AIR_IN_CL( pctx->bboxMin[3], pos[3], pctx->bboxMax[3] ) );
}
/*
** sets:
** pctx->haveScale
** pctx->voxelSizeSpace, voxelSizeScale
** pctx->bboxMin ( [0] through [3], always )
** pctx->bboxMax ( same )
*/
int
353 _pullVolumeSetup( pullContext *pctx ) {
static const char me[]="_pullVolumeSetup";
unsigned int ii, numScale;
/* first see if there are any gage problems */
for ( ii=0; ii<pctx->volNum; ii++ ) {
if ( pctx->verbose ) {
printf( "%s: gageUpdate( vol[%u] )\n", me, ii );
}
if ( pctx->vol[ii]->gctx ) {
if ( gageUpdate( pctx->vol[ii]->gctx ) ) {
biffMovef( PULL, GAGE, "%s: trouble setting up gage on vol "
"%u/%u ( \"%s\" )", me, ii, pctx->volNum,
pctx->vol[ii]->name );
return 1;
}
} else {
biffAddf( PULL, "%s: vol[%u] has NULL gctx", me, ii );
}
}
pctx->voxelSizeSpace = 0.0;
for ( ii=0; ii<pctx->volNum; ii++ ) {
double min[3], max[3];
gageContext *gctx;
gctx = pctx->vol[ii]->gctx;
gageShapeBoundingBox( min, max, gctx->shape );
if ( !ii ) {
ELL_3V_COPY( pctx->bboxMin, min );
ELL_3V_COPY( pctx->bboxMax, max );
} else {
ELL_3V_MIN( pctx->bboxMin, pctx->bboxMin, min );
ELL_3V_MIN( pctx->bboxMax, pctx->bboxMax, max );
}
pctx->voxelSizeSpace += ELL_3V_LEN( gctx->shape->spacing )/sqrt( 3.0 );
if ( ii && !pctx->initParm.unequalShapesAllow ) {
if ( !gageShapeEqual( pctx->vol[0]->gctx->shape, pctx->vol[0]->name,
pctx->vol[ii]->gctx->shape, pctx->vol[ii]->name ) ) {
biffMovef( PULL, GAGE,
"%s: need equal shapes, but vol 0 and %u different",
me, ii );
return 1;
}
}
}
pctx->voxelSizeSpace /= pctx->volNum;
/* have now computed bbox{Min, Max}[0, 1, 2]; now do bbox{Min, Max}[3] */
pctx->bboxMin[3] = pctx->bboxMax[3] = 0.0;
pctx->haveScale = AIR_FALSE;
pctx->voxelSizeScale = 0.0;
numScale = 0;
for ( ii=0; ii<pctx->volNum; ii++ ) {
if ( pctx->vol[ii]->ninScale ) {
double sclMin, sclMax, sclStep;
unsigned int si;
numScale ++;
sclMin = pctx->vol[ii]->scalePos[0];
if ( pctx->flag.scaleIsTau ) {
sclMin = gageTauOfSig( sclMin );
}
sclMax = pctx->vol[ii]->scalePos[pctx->vol[ii]->scaleNum-1];
if ( pctx->flag.scaleIsTau ) {
sclMax = gageTauOfSig( sclMax );
}
sclStep = 0;
for ( si=0; si<pctx->vol[ii]->scaleNum-1; si++ ) {
double scl0, scl1;
scl1 = pctx->vol[ii]->scalePos[si+1];
scl0 = pctx->vol[ii]->scalePos[si];
if ( pctx->flag.scaleIsTau ) {
scl1 = gageTauOfSig( scl1 );
scl0 = gageTauOfSig( scl0 );
}
sclStep += ( scl1 - scl0 );
}
sclStep /= pctx->vol[ii]->scaleNum-1;
pctx->voxelSizeScale += sclStep;
if ( !pctx->haveScale ) {
pctx->bboxMin[3] = sclMin;
pctx->bboxMax[3] = sclMax;
pctx->haveScale = AIR_TRUE;
} else {
/* we already know haveScale; expand existing range */
pctx->bboxMin[3] = AIR_MIN( sclMin, pctx->bboxMin[3] );
pctx->bboxMax[3] = AIR_MAX( sclMax, pctx->bboxMax[3] );
}
}
}
if ( numScale ) {
pctx->voxelSizeScale /= numScale;
}
if ( pctx->verbose ) {
printf( "%s: bboxMin ( %g, %g, %g, %g ) max ( %g, %g, %g, %g )\n", me,
pctx->bboxMin[0], pctx->bboxMin[1],
pctx->bboxMin[2], pctx->bboxMin[3],
pctx->bboxMax[0], pctx->bboxMax[1],
pctx->bboxMax[2], pctx->bboxMax[3] );
printf( "%s: voxelSizeSpace %g Scale %g\n", me,
pctx->voxelSizeSpace, pctx->voxelSizeScale );
}
/* _energyInterParticle( ) depends on this error checking */
if ( pctx->haveScale ) {
if ( pullInterTypeJustR == pctx->interType ) {
biffAddf( PULL, "%s: need scale-aware intertype ( not %s ) with "
"a scale-space volume",
me, airEnumStr( pullInterType, pullInterTypeJustR ) );
return 1;
}
} else {
/* don't have scale */
if ( pullInterTypeJustR != pctx->interType ) {
biffAddf( PULL, "%s: can't use scale-aware intertype ( %s ) without "
"a scale-space volume",
me, airEnumStr( pullInterType, pctx->interType ) );
return 1;
}
}
if ( pctx->flag.energyFromStrength
&& !( pctx->ispec[pullInfoStrength] && pctx->haveScale ) ) {
biffAddf( PULL, "%s: sorry, can use energyFromStrength only with both "
"a scale-space volume, and a strength info", me );
return 1;
}
return 0;
}
/*
** basis of pullVolumeLookup
**
** uses biff, returns UINT_MAX in case of error
*/
unsigned int
487 _pullVolumeIndex( const pullContext *pctx,
const char *volName ) {
static const char me[]="_pullVolumeIndex";
unsigned int vi;
if ( !( pctx && volName ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return UINT_MAX;
}
if ( 0 == pctx->volNum ) {
biffAddf( PULL, "%s: given context has no volumes", me );
return UINT_MAX;
}
for ( vi=0; vi<pctx->volNum; vi++ ) {
if ( !strcmp( pctx->vol[vi]->name, volName ) ) {
break;
}
}
if ( vi == pctx->volNum ) {
biffAddf( PULL, "%s: no volume has name \"%s\"", me, volName );
return UINT_MAX;
}
return vi;
}
const pullVolume *
513 pullVolumeLookup( const pullContext *pctx,
const char *volName ) {
static const char me[]="pullVolumeLookup";
unsigned int vi;
vi = _pullVolumeIndex( pctx, volName );
if ( UINT_MAX == vi ) {
biffAddf( PULL, "%s: trouble looking up \"%s\"", me, volName );
return NULL;
}
return pctx->vol[vi];
}
/*
******** pullConstraintScaleRange
**
** returns scale range from a scale-space volume,
** either in terms of sigma, or ( if pctx->flag.scaleIsTau ), tau
*/
int
533 pullConstraintScaleRange( pullContext *pctx, double ssrange[2] ) {
static const char me[]="pullConstraintScaleRange";
pullVolume *cvol;
if ( !( pctx && ssrange ) ) {
biffAddf( PULL, "%s: got NULL pointer", me );
return 1;
}
if ( !( pctx->constraint ) ) {
biffAddf( PULL, "%s: given context doesn't have constraint set", me );
return 1;
}
if ( !( pctx->ispec[pctx->constraint] ) ) {
biffAddf( PULL, "%s: info %s not set for constriant", me,
airEnumStr( pullInfo, pctx->constraint ) );
return 1;
}
cvol = pctx->vol[pctx->ispec[pctx->constraint]->volIdx];
if ( !cvol->ninScale ) {
biffAddf( PULL, "%s: volume \"%s\" has constraint but no scale-space",
me, cvol->name );
return 1;
}
ssrange[0] = cvol->scalePos[0];
ssrange[1] = cvol->scalePos[cvol->scaleNum-1];
if ( pctx->flag.scaleIsTau ) {
ssrange[0] = gageTauOfSig( ssrange[0] );
ssrange[1] = gageTauOfSig( ssrange[1] );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
unsigned int
28 _pushPointTotal( pushContext *pctx ) {
unsigned int binIdx, pointNum;
pushBin *bin;
pointNum = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
pointNum += bin->pointNum;
}
return pointNum;
}
int
41 _pushProbe( pushTask *task, pushPoint *point ) {
static const char me[]="_pushProbe";
double posWorld[4], posIdx[4];
ELL_3V_COPY( posWorld, point->pos ); posWorld[3] = 1.0;
ELL_4MV_MUL( posIdx, task->gctx->shape->WtoI, posWorld );
ELL_4V_HOMOG( posIdx, posIdx );
posIdx[0] = AIR_CLAMP( -0.5, posIdx[0], task->gctx->shape->size[0]-0.5 );
posIdx[1] = AIR_CLAMP( -0.5, posIdx[1], task->gctx->shape->size[1]-0.5 );
posIdx[2] = AIR_CLAMP( -0.5, posIdx[2], task->gctx->shape->size[2]-0.5 );
if ( gageProbe( task->gctx, posIdx[0], posIdx[1], posIdx[2] ) ) {
biffAddf( PUSH, "%s: gageProbe failed:\n ( %d ) %s\n", me,
task->gctx->errNum, task->gctx->errStr );
return 1;
}
TEN_T_COPY( point->ten, task->tenAns );
TEN_T_COPY( point->inv, task->invAns );
ELL_3V_COPY( point->cnt, task->cntAns );
if ( tenGageUnknown != task->pctx->gravItem ) {
point->grav = task->gravAns[0];
ELL_3V_COPY( point->gravGrad, task->gravGradAns );
}
if ( tenGageUnknown != task->pctx->seedThreshItem ) {
point->seedThresh = task->seedThreshAns[0];
}
return 0;
}
int
72 pushOutputGet( Nrrd *nPosOut, Nrrd *nTenOut, Nrrd *nEnrOut,
pushContext *pctx ) {
static const char me[]="pushOutputGet";
unsigned int binIdx, pointRun, pointNum, pointIdx;
int E;
float *posOut, *tenOut, *enrOut;
pushBin *bin;
pushPoint *point;
pointNum = _pushPointTotal( pctx );
E = AIR_FALSE;
if ( nPosOut ) {
E |= nrrdMaybeAlloc_va( nPosOut, nrrdTypeFloat, 2,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, pointNum ) );
}
if ( nTenOut ) {
E |= nrrdMaybeAlloc_va( nTenOut, nrrdTypeFloat, 2,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, pointNum ) );
}
if ( nEnrOut ) {
E |= nrrdMaybeAlloc_va( nEnrOut, nrrdTypeFloat, 1,
AIR_CAST( size_t, pointNum ) );
}
if ( E ) {
biffMovef( PUSH, NRRD, "%s: trouble allocating outputs", me );
return 1;
}
posOut = nPosOut ? ( float* )( nPosOut->data ) : NULL;
tenOut = nTenOut ? ( float* )( nTenOut->data ) : NULL;
enrOut = nEnrOut ? ( float* )( nEnrOut->data ) : NULL;
pointRun = 0;
for ( binIdx=0; binIdx<pctx->binNum; binIdx++ ) {
bin = pctx->bin + binIdx;
for ( pointIdx=0; pointIdx<bin->pointNum; pointIdx++ ) {
point = bin->point[pointIdx];
if ( posOut ) {
ELL_3V_SET_TT( posOut + 3*pointRun, float,
point->pos[0], point->pos[1], point->pos[2] );
}
if ( tenOut ) {
TEN_T_COPY_TT( tenOut + 7*pointRun, float, point->ten );
}
if ( enrOut ) {
enrOut[pointRun] = AIR_CAST( float, point->enr );
}
pointRun++;
}
}
return 0;
}
int
128 _pushPairwiseEnergy( pushTask *task,
double *enrP,
double frc[3],
pushEnergySpec *ensp,
pushPoint *myPoint, pushPoint *herPoint,
double YY[3], double iscl ) {
static const char me[]="_pushPairwiseEnergy";
double inv[7], XX[3], nXX[3], rr, mag, WW[3];
if ( task->pctx->midPntSmp ) {
pushPoint _tmpPoint;
double det;
ELL_3V_SCALE_ADD2( _tmpPoint.pos,
0.5, myPoint->pos,
0.5, herPoint->pos );
if ( _pushProbe( task, &_tmpPoint ) ) {
biffAddf( PUSH, "%s: at midpoint of %u and %u", me,
myPoint->ttaagg, herPoint->ttaagg );
*enrP = AIR_NAN; return 1;
}
TEN_T_INV( inv, _tmpPoint.ten, det );
} else {
TEN_T_SCALE_ADD2( inv,
0.5, myPoint->inv,
0.5, herPoint->inv );
}
TEN_TV_MUL( XX, inv, YY );
ELL_3V_NORM( nXX, XX, rr );
ensp->energy->eval( enrP, &mag, rr*iscl, ensp->parm );
if ( mag ) {
mag *= iscl;
TEN_TV_MUL( WW, inv, nXX );
ELL_3V_SCALE( frc, mag, WW );
} else {
ELL_3V_SET( frc, 0, 0, 0 );
}
return 0;
}
#define EPS_PER_MAX_DIST 200
#define SEEK_MAX_ITER 30
int
173 pushBinProcess( pushTask *task, unsigned int myBinIdx ) {
static const char me[]="pushBinProcess";
pushBin *myBin, *herBin, **neighbor;
unsigned int myPointIdx, herPointIdx;
pushPoint *myPoint, *herPoint;
double enr, frc[3], delta[3], deltaLen, deltaNorm[3], warp[3], limit,
maxDiffLenSqrd, iscl, diff[3], diffLenSqrd;
if ( task->pctx->verbose > 2 ) {
fprintf( stderr, "%s( %u ): doing bin %u\n", me, task->threadIdx, myBinIdx );
}
maxDiffLenSqrd = ( task->pctx->maxDist )*( task->pctx->maxDist );
myBin = task->pctx->bin + myBinIdx;
iscl = 1.0/( 2*task->pctx->scale );
for ( myPointIdx=0; myPointIdx<myBin->pointNum; myPointIdx++ ) {
myPoint = myBin->point[myPointIdx];
myPoint->enr = 0;
ELL_3V_SET( myPoint->frc, 0, 0, 0 );
if ( 1.0 <= task->pctx->neighborTrueProb
|| airDrandMT_r( task->rng ) <= task->pctx->neighborTrueProb
|| !myPoint->neighArr->len ) {
neighbor = myBin->neighbor;
if ( 1.0 > task->pctx->neighborTrueProb ) {
airArrayLenSet( myPoint->neighArr, 0 );
}
while ( ( herBin = *neighbor ) ) {
for ( herPointIdx=0; herPointIdx<herBin->pointNum; herPointIdx++ ) {
herPoint = herBin->point[herPointIdx];
if ( myPoint == herPoint ) {
/* can't interact with myself */
continue;
}
ELL_3V_SUB( diff, herPoint->pos, myPoint->pos );
diffLenSqrd = ELL_3V_DOT( diff, diff );
if ( diffLenSqrd > maxDiffLenSqrd ) {
/* too far away to interact */
continue;
}
if ( _pushPairwiseEnergy( task, &enr, frc, task->pctx->ensp,
myPoint, herPoint, diff, iscl ) ) {
biffAddf( PUSH, "%s: between points %u and %u, A", me,
myPoint->ttaagg, herPoint->ttaagg );
return 1;
}
myPoint->enr += enr/2;
if ( ELL_3V_DOT( frc, frc ) ) {
ELL_3V_INCR( myPoint->frc, frc );
if ( 1.0 > task->pctx->neighborTrueProb ) {
unsigned int idx;
idx = airArrayLenIncr( myPoint->neighArr, 1 );
myPoint->neigh[idx] = herPoint;
}
}
if ( !ELL_3V_EXISTS( myPoint->frc ) ) {
biffAddf( PUSH, "%s: bad myPoint->frc ( %g, %g, %g ) @ bin %p end", me,
myPoint->frc[0], myPoint->frc[1], myPoint->frc[2],
AIR_VOIDP( herBin ) );
return 1;
}
}
neighbor++;
}
} else {
/* we are doing neighborhood list optimization, and this is an
iteration where we use the list. So the body of this loop
has to be the same as the meat of the above loop */
unsigned int neighIdx;
for ( neighIdx=0; neighIdx<myPoint->neighArr->len; neighIdx++ ) {
herPoint = myPoint->neigh[neighIdx];
ELL_3V_SUB( diff, herPoint->pos, myPoint->pos );
if ( _pushPairwiseEnergy( task, &enr, frc, task->pctx->ensp,
myPoint, herPoint, diff, iscl ) ) {
biffAddf( PUSH, "%s: between points %u and %u, B", me,
myPoint->ttaagg, herPoint->ttaagg );
return 1;
}
myPoint->enr += enr/2;
ELL_3V_INCR( myPoint->frc, frc );
}
}
if ( !ELL_3V_EXISTS( myPoint->frc ) ) {
biffAddf( PUSH, "%s: post-nei myPoint->frc ( %g, %g, %g ) doesn't exist", me,
myPoint->frc[0], myPoint->frc[1], myPoint->frc[2] );
return 1;
}
/* each point sees containment forces */
ELL_3V_SCALE( frc, task->pctx->cntScl, myPoint->cnt );
ELL_3V_INCR( myPoint->frc, frc );
myPoint->enr += task->pctx->cntScl*( 1 - myPoint->ten[0] );
/* each point also maybe experiences gravity */
if ( tenGageUnknown != task->pctx->gravItem ) {
ELL_3V_SCALE( frc, -task->pctx->gravScl, myPoint->gravGrad );
myPoint->enr +=
task->pctx->gravScl*( myPoint->grav - task->pctx->gravZero );
ELL_3V_INCR( myPoint->frc, frc );
}
if ( !ELL_3V_EXISTS( myPoint->frc ) ) {
biffAddf( PUSH, "%s: post-grav myPoint->frc ( %g, %g, %g ) doesn't exist", me,
myPoint->frc[0], myPoint->frc[1], myPoint->frc[2] );
return 1;
}
/* each point in this thing also maybe experiences wall forces */
if ( task->pctx->wall ) {
/* there's an effort here to get the forces and energies, which
are actually computed in index space, to be correctly scaled
into world space, but no promises that its right ... */
double enrIdx[4]={0, 0, 0, 0}, enrWorld[4];
unsigned int ci;
double posWorld[4], posIdx[4], len, frcIdx[4], frcWorld[4];
ELL_3V_COPY( posWorld, myPoint->pos ); posWorld[3] = 1.0;
ELL_4MV_MUL( posIdx, task->pctx->gctx->shape->WtoI, posWorld );
ELL_4V_HOMOG( posIdx, posIdx );
for ( ci=0; ci<3; ci++ ) {
if ( 1 == task->pctx->gctx->shape->size[ci] ) {
frcIdx[ci] = 0;
} else {
len = posIdx[ci] - -0.5;
if ( len < 0 ) {
len *= -1;
frcIdx[ci] = task->pctx->wall*len;
enrIdx[ci] = task->pctx->wall*len*len/2;
} else {
len = posIdx[ci] - ( task->pctx->gctx->shape->size[ci] - 0.5 );
if ( len > 0 ) {
frcIdx[ci] = -task->pctx->wall*len;
enrIdx[ci] = task->pctx->wall*len*len/2;
} else {
frcIdx[ci] = 0;
enrIdx[ci] = 0;
}
}
}
}
frcIdx[3] = 0.0;
enrIdx[3] = 0.0;
ELL_4MV_MUL( frcWorld, task->pctx->gctx->shape->ItoW, frcIdx );
ELL_4MV_MUL( enrWorld, task->pctx->gctx->shape->ItoW, enrIdx );
ELL_3V_INCR( myPoint->frc, frcWorld );
myPoint->enr += ELL_3V_LEN( enrWorld );
} /* wall */
if ( !ELL_3V_EXISTS( myPoint->frc ) ) {
biffAddf( PUSH, "%s: post-wall myPoint->frc ( %g, %g, %g ) doesn't exist", me,
myPoint->frc[0], myPoint->frc[1], myPoint->frc[2] );
return 1;
}
task->energySum += myPoint->enr;
/* -------------------------------------------- */
/* force calculation done, now update positions */
/* -------------------------------------------- */
ELL_3V_SCALE( delta, task->pctx->step, myPoint->frc );
ELL_3V_NORM( deltaNorm, delta, deltaLen );
if ( 0 == deltaLen ) {
/* an unforced point, but this isn't an error */
return 0;
}
if ( !( AIR_EXISTS( deltaLen ) && ELL_3V_EXISTS( deltaNorm ) ) ) {
biffAddf( PUSH, "%s: deltaLen %g or deltaNorm ( %g, %g, %g ) doesn't exist",
me, deltaLen, deltaNorm[0], deltaNorm[1], deltaNorm[2] );
return 1;
}
if ( deltaLen ) {
double newDelta;
TEN_TV_MUL( warp, myPoint->inv, delta );
/* limit is some fraction of glyph radius along direction of delta */
limit = ( task->pctx->deltaLimit
*task->pctx->scale*deltaLen/( FLT_MIN + ELL_3V_LEN( warp ) ) );
newDelta = limit*deltaLen/( limit + deltaLen );
/* by definition newDelta <= deltaLen */
task->deltaFracSum += newDelta/deltaLen;
ELL_3V_SCALE_INCR( myPoint->pos, newDelta, deltaNorm );
if ( !ELL_3V_EXISTS( myPoint->pos ) ) {
biffAddf( PUSH, "%s: myPoint->pos %g*( %g, %g, %g ) --> ( %g, %g, %g ) "
"doesn't exist", me,
newDelta, deltaNorm[0], deltaNorm[1], deltaNorm[2],
myPoint->pos[0], myPoint->pos[1], myPoint->pos[2] );
return 1;
}
}
if ( 2 == task->pctx->dimIn ) {
double posIdx[4], posWorld[4], posOrig[4];
ELL_3V_COPY( posOrig, myPoint->pos ); posOrig[3] = 1.0;
ELL_4MV_MUL( posIdx, task->pctx->gctx->shape->WtoI, posOrig );
ELL_4V_HOMOG( posIdx, posIdx );
posIdx[task->pctx->sliceAxis] = 0.0;
ELL_4MV_MUL( posWorld, task->pctx->gctx->shape->ItoW, posIdx );
ELL_34V_HOMOG( myPoint->pos, posWorld );
if ( !ELL_3V_EXISTS( myPoint->pos ) ) {
biffAddf( PUSH, "%s: myPoint->pos ( %g, %g, %g ) -> ( %g, %g, %g ) "
"doesn't exist", me,
posOrig[0], posOrig[1], posOrig[2],
myPoint->pos[0], myPoint->pos[1], myPoint->pos[2] );
return 1;
}
}
if ( 1.0 <= task->pctx->probeProb
|| airDrandMT_r( task->rng ) <= task->pctx->probeProb ) {
if ( _pushProbe( task, myPoint ) ) {
biffAddf( PUSH, "%s: probing at new field pos", me );
return 1;
}
}
/* the point lived, count it */
task->pointNum += 1;
} /* for myPointIdx */
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
/*
** because the pushContext keeps an array of bins ( not pointers to them )
** we have Init and Done functions ( not New and Nix )
*/
void
32 pushBinInit( pushBin *bin, unsigned int incr ) {
pushPtrPtrUnion pppu;
bin->pointNum = 0;
bin->point = NULL;
bin->pointArr = airArrayNew( ( pppu.point = &( bin->point ), pppu.v ),
&( bin->pointNum ),
sizeof( pushPoint * ), incr );
bin->neighbor = NULL;
return;
}
/*
** bins own the points they contain- so this frees them
*/
void
48 pushBinDone( pushBin *bin ) {
unsigned int idx;
for ( idx=0; idx<bin->pointNum; idx++ ) {
bin->point[idx] = pushPointNix( bin->point[idx] );
}
bin->pointArr = airArrayNuke( bin->pointArr );
bin->neighbor = ( pushBin ** )airFree( bin->neighbor );
return;
}
/*
** bins on boundary now extend to infinity; so the only time this
** returns NULL ( indicating error ) is for non-existent positions
*/
pushBin *
65 _pushBinLocate( pushContext *pctx, double *_posWorld ) {
static const char me[]="_pushBinLocate";
double posWorld[4], posIdx[4];
unsigned int axi, eidx[3], binIdx;
if ( !ELL_3V_EXISTS( _posWorld ) ) {
biffAddf( PUSH, "%s: non-existent position ( %g, %g, %g )", me,
_posWorld[0], _posWorld[1], _posWorld[2] );
return NULL;
}
if ( pctx->binSingle ) {
binIdx = 0;
} else {
ELL_3V_COPY( posWorld, _posWorld );
posWorld[3] = 1.0;
ELL_4MV_MUL( posIdx, pctx->gctx->shape->WtoI, posWorld );
ELL_34V_HOMOG( posIdx, posIdx );
for ( axi=0; axi<3; axi++ ) {
eidx[axi] = airIndexClamp( -0.5,
posIdx[axi],
pctx->gctx->shape->size[axi]-0.5,
pctx->binsEdge[axi] );
}
binIdx = ( eidx[0]
+ pctx->binsEdge[0]*( eidx[1]
+ pctx->binsEdge[1]*eidx[2] ) );
}
/*
fprintf( stderr, "!%s: bin( %g, %g, %g ) = %u\n", me,
_posWorld[0], _posWorld[1], _posWorld[2], binIdx );
*/
return pctx->bin + binIdx;
}
/*
** this makes the bin the owner of the point
*/
void
105 _pushBinPointAdd( pushContext *pctx, pushBin *bin, pushPoint *point ) {
int pntI;
AIR_UNUSED( pctx );
pntI = airArrayLenIncr( bin->pointArr, 1 );
bin->point[pntI] = point;
return;
}
/*
** the bin loses track of the point, caller responsible for ownership
*/
void
119 _pushBinPointRemove( pushContext *pctx, pushBin *bin, int loseIdx ) {
AIR_UNUSED( pctx );
bin->point[loseIdx] = bin->point[bin->pointNum-1];
airArrayLenIncr( bin->pointArr, -1 );
return;
}
void
129 _pushBinNeighborSet( pushBin *bin, pushBin **nei, unsigned int num ) {
unsigned int neiI;
bin->neighbor = ( pushBin ** )airFree( bin->neighbor );
bin->neighbor = ( pushBin ** )calloc( 1+num, sizeof( pushBin * ) );
for ( neiI=0; neiI<num; neiI++ ) {
bin->neighbor[neiI] = nei[neiI];
}
bin->neighbor[neiI] = NULL;
return;
}
void
142 pushBinAllNeighborSet( pushContext *pctx ) {
/* static const char me[]="pushBinAllNeighborSet"; */
pushBin *nei[3*3*3];
unsigned int neiNum, xi, yi, zi, xx, yy, zz, xmax, ymax, zmax, binIdx;
int xmin, ymin, zmin;
if ( pctx->binSingle ) {
neiNum = 0;
nei[neiNum++] = pctx->bin + 0;
_pushBinNeighborSet( pctx->bin + 0, nei, neiNum );
} else {
for ( zi=0; zi<pctx->binsEdge[2]; zi++ ) {
zmin = AIR_MAX( 0, ( int )zi-1 );
zmax = AIR_MIN( zi+1, pctx->binsEdge[2]-1 );
for ( yi=0; yi<pctx->binsEdge[1]; yi++ ) {
ymin = AIR_MAX( 0, ( int )yi-1 );
ymax = AIR_MIN( yi+1, pctx->binsEdge[1]-1 );
for ( xi=0; xi<pctx->binsEdge[0]; xi++ ) {
xmin = AIR_MAX( 0, ( int )xi-1 );
xmax = AIR_MIN( xi+1, pctx->binsEdge[0]-1 );
neiNum = 0;
for ( zz=zmin; zz<=zmax; zz++ ) {
for ( yy=ymin; yy<=ymax; yy++ ) {
for ( xx=xmin; xx<=xmax; xx++ ) {
binIdx = xx + pctx->binsEdge[0]*( yy + pctx->binsEdge[1]*zz );
/*
fprintf( stderr, "!%s: nei[%u]( %u, %u, %u ) = %u\n", me,
neiNum, xi, yi, zi, binIdx );
*/
nei[neiNum++] = pctx->bin + binIdx;
}
}
}
_pushBinNeighborSet( pctx->bin + xi + pctx->binsEdge[0]
*( yi + pctx->binsEdge[1]*zi ), nei, neiNum );
}
}
}
}
return;
}
int
185 pushBinPointAdd( pushContext *pctx, pushPoint *point ) {
static const char me[]="pushBinPointAdd";
pushBin *bin;
if ( !( bin = _pushBinLocate( pctx, point->pos ) ) ) {
biffAddf( PUSH, "%s: can't locate point %p %u",
me, AIR_CAST( void*, point ), point->ttaagg );
return 1;
}
_pushBinPointAdd( pctx, bin, point );
return 0;
}
/*
** This function is only called by the master thread, this
** does *not* have to be thread-safe in any way
*/
int
203 pushRebin( pushContext *pctx ) {
static const char me[]="pushRebin";
unsigned int oldBinIdx, pointIdx;
pushBin *oldBin, *newBin;
pushPoint *point;
if ( !pctx->binSingle ) {
for ( oldBinIdx=0; oldBinIdx<pctx->binNum; oldBinIdx++ ) {
oldBin = pctx->bin + oldBinIdx;
for ( pointIdx=0; pointIdx<oldBin->pointNum; /* nope! */ ) {
point = oldBin->point[pointIdx];
newBin = _pushBinLocate( pctx, point->pos );
if ( !newBin ) {
biffAddf( PUSH, "%s: can't locate point %p %u",
me, AIR_CAST( void*, point ), point->ttaagg );
return 1;
}
if ( oldBin != newBin ) {
_pushBinPointRemove( pctx, oldBin, pointIdx );
_pushBinPointAdd( pctx, newBin, point );
} else {
/* its in the right bin, move on */
pointIdx++;
}
} /* for pointIdx */
} /* for oldBinIdx */
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
/*
** this is the core of the worker threads: as long as there are bins
** left to process, get the next one, and process it
*/
int
32 _pushProcess( pushTask *task ) {
static const char me[]="_pushProcess";
unsigned int binIdx;
while ( task->pctx->binIdx < task->pctx->binNum ) {
/* get the index of the next bin to process */
if ( task->pctx->threadNum > 1 ) {
airThreadMutexLock( task->pctx->binMutex );
}
do {
binIdx = task->pctx->binIdx;
if ( task->pctx->binIdx < task->pctx->binNum ) {
task->pctx->binIdx++;
}
} while ( binIdx < task->pctx->binNum
&& 0 == task->pctx->bin[binIdx].pointNum );
if ( task->pctx->threadNum > 1 ) {
airThreadMutexUnlock( task->pctx->binMutex );
}
if ( binIdx == task->pctx->binNum ) {
/* no more bins to process! */
break;
}
if ( pushBinProcess( task, binIdx ) ) {
biffAddf( PUSH, "%s( %u ): had trouble on bin %u", me,
task->threadIdx, binIdx );
return 1;
}
}
return 0;
}
/* the main loop for each worker thread */
void *
68 _pushWorker( void *_task ) {
static const char me[]="_pushWorker";
pushTask *task;
task = ( pushTask * )_task;
while ( 1 ) {
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): waiting on barrier A\n",
me, task->threadIdx );
}
/* pushFinish sets finished prior to the barriers */
airThreadBarrierWait( task->pctx->iterBarrierA );
if ( task->pctx->finished ) {
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): done!\n", me, task->threadIdx );
}
break;
}
/* else there's work to do ... */
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): starting to process\n", me, task->threadIdx );
}
if ( _pushProcess( task ) ) {
/* HEY clearly not threadsafe ... */
biffAddf( PUSH, "%s: thread %u trouble", me, task->threadIdx );
task->pctx->finished = AIR_TRUE;
}
if ( task->pctx->verbose > 1 ) {
fprintf( stderr, "%s( %u ): waiting on barrier B\n",
me, task->threadIdx );
}
airThreadBarrierWait( task->pctx->iterBarrierB );
}
return _task;
}
int
107 _pushContextCheck( pushContext *pctx ) {
static const char me[]="_pushContextCheck";
unsigned int numSingle;
if ( !pctx ) {
biffAddf( PUSH, "%s: got NULL pointer", me );
return 1;
}
if ( !( pctx->pointNum >= 1 ) ) {
biffAddf( PUSH, "%s: pctx->pointNum ( %d ) not >= 1\n", me, pctx->pointNum );
return 1;
}
if ( !( AIR_IN_CL( 1, pctx->threadNum, PUSH_THREAD_MAXNUM ) ) ) {
biffAddf( PUSH, "%s: pctx->threadNum ( %d ) outside valid range [1, %d]", me,
pctx->threadNum, PUSH_THREAD_MAXNUM );
return 1;
}
if ( nrrdCheck( pctx->nin ) ) {
biffMovef( PUSH, NRRD, "%s: got a broken input nrrd", me );
return 1;
}
if ( !( ( 4 == pctx->nin->dim && 7 == pctx->nin->axis[0].size ) ) ) {
biffAddf( PUSH, "%s: input doesn't look like 3D masked tensor volume", me );
return 1;
}
numSingle = 0;
numSingle += ( 1 == pctx->nin->axis[1].size );
numSingle += ( 1 == pctx->nin->axis[2].size );
numSingle += ( 1 == pctx->nin->axis[3].size );
if ( numSingle > 1 ) {
biffAddf( PUSH, "%s: can have a single sample along at most one axis", me );
return 1;
}
if ( pctx->npos ) {
if ( nrrdCheck( pctx->npos ) ) {
biffMovef( PUSH, NRRD, "%s: got a broken position nrrd", me );
return 1;
}
if ( !( 2 == pctx->npos->dim
&& 3 == pctx->npos->axis[0].size ) ) {
biffAddf( PUSH, "%s: position nrrd not 2-D 3-by-N", me );
return 1;
}
}
if ( tenGageUnknown != pctx->gravItem ) {
if ( airEnumValCheck( tenGage, pctx->gravItem ) ) {
biffAddf( PUSH, "%s: gravity item %u invalid", me, pctx->gravItem );
return 1;
}
if ( 1 != tenGageKind->table[pctx->gravItem].answerLength ) {
biffAddf( PUSH, "%s: answer length of gravity item %s is %u, not 1", me,
airEnumStr( tenGage, pctx->gravItem ),
tenGageKind->table[pctx->gravItem].answerLength );
return 1;
}
if ( airEnumValCheck( tenGage, pctx->gravGradItem ) ) {
biffAddf( PUSH, "%s: gravity gradient item %u invalid",
me, pctx->gravGradItem );
return 1;
}
if ( 3 != tenGageKind->table[pctx->gravGradItem].answerLength ) {
biffAddf( PUSH, "%s: answer length of gravity grad item %s is %u, not 3",
me, airEnumStr( tenGage, pctx->gravGradItem ),
tenGageKind->table[pctx->gravGradItem].answerLength );
return 1;
}
if ( !AIR_EXISTS( pctx->gravScl ) ) {
biffAddf( PUSH, "%s: gravity scaling doesn't exist", me );
return 1;
}
if ( !AIR_EXISTS( pctx->gravZero ) ) {
biffAddf( PUSH, "%s: gravity zero doesn't exist", me );
return 1;
}
}
return 0;
}
int
188 pushStart( pushContext *pctx ) {
static const char me[]="pushStart";
unsigned int tidx;
if ( _pushContextCheck( pctx ) ) {
biffAddf( PUSH, "%s: trouble", me );
return 1;
}
airSrandMT( pctx->seedRNG );
/* the ordering of steps below is important: gage context
has to be set up before its copied by task setup */
pctx->step = pctx->stepInitial;
if ( _pushTensorFieldSetup( pctx )
|| _pushGageSetup( pctx )
|| _pushTaskSetup( pctx )
|| _pushBinSetup( pctx )
|| _pushPointSetup( pctx ) ) {
biffAddf( PUSH, "%s: trouble setting up context", me );
return 1;
}
fprintf( stderr, "!%s: setup done-ish\n", me );
if ( pctx->threadNum > 1 ) {
pctx->binMutex = airThreadMutexNew( );
pctx->iterBarrierA = airThreadBarrierNew( pctx->threadNum );
pctx->iterBarrierB = airThreadBarrierNew( pctx->threadNum );
/* start threads 1 and up running; they'll all hit iterBarrierA */
for ( tidx=1; tidx<pctx->threadNum; tidx++ ) {
if ( pctx->verbose > 1 ) {
fprintf( stderr, "%s: spawning thread %d\n", me, tidx );
}
airThreadStart( pctx->task[tidx]->thread, _pushWorker,
( void * )( pctx->task[tidx] ) );
}
} else {
pctx->binMutex = NULL;
pctx->iterBarrierA = NULL;
pctx->iterBarrierB = NULL;
}
pctx->iter = 0;
return 0;
}
/*
******** pushIterate
**
** ( documentation )
**
** NB: this implements the body of thread 0, the master thread
*/
int
242 pushIterate( pushContext *pctx ) {
static const char me[]="pushIterate";
unsigned int ti, pointNum;
double time0, time1;
int myError;
if ( !pctx ) {
biffAddf( PUSH, "%s: got NULL pointer", me );
return 1;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: starting iterations\n", me );
}
time0 = airTime( );
/* the _pushWorker checks finished after iterBarrierA */
pctx->finished = AIR_FALSE;
pctx->binIdx=0;
for ( ti=0; ti<pctx->threadNum; ti++ ) {
pctx->task[ti]->pointNum = 0;
pctx->task[ti]->energySum = 0;
pctx->task[ti]->deltaFracSum = 0;
}
if ( pctx->verbose ) {
fprintf( stderr, "%s: starting iter %d w/ %u threads\n",
me, pctx->iter, pctx->threadNum );
}
if ( pctx->threadNum > 1 ) {
airThreadBarrierWait( pctx->iterBarrierA );
}
myError = AIR_FALSE;
if ( _pushProcess( pctx->task[0] ) ) {
biffAddf( PUSH, "%s: master thread trouble w/ iter %u", me, pctx->iter );
pctx->finished = AIR_TRUE;
myError = AIR_TRUE;
}
if ( pctx->threadNum > 1 ) {
airThreadBarrierWait( pctx->iterBarrierB );
}
if ( pctx->finished ) {
if ( !myError ) {
/* we didn't set finished- one of the workers must have */
biffAddf( PUSH, "%s: worker error on iter %u", me, pctx->iter );
}
return 1;
}
pctx->energySum = 0;
pctx->deltaFrac = 0;
pointNum = 0;
for ( ti=0; ti<pctx->threadNum; ti++ ) {
pctx->energySum += pctx->task[ti]->energySum;
pctx->deltaFrac += pctx->task[ti]->deltaFracSum;
pointNum += pctx->task[ti]->pointNum;
}
pctx->deltaFrac /= pointNum;
if ( pushRebin( pctx ) ) {
biffAddf( PUSH, "%s: problem with new point locations", me );
return 1;
}
time1 = airTime( );
pctx->timeIteration = time1 - time0;
pctx->timeRun += time1 - time0;
pctx->iter += 1;
return 0;
}
int
315 pushRun( pushContext *pctx ) {
static const char me[]="pushRun";
char poutS[AIR_STRLEN_MED], toutS[AIR_STRLEN_MED];
Nrrd *npos, *nten;
double time0, time1, enrLast,
enrNew=AIR_NAN, enrImprov=AIR_NAN, enrImprovAvg=AIR_NAN;
if ( pushIterate( pctx ) ) {
biffAddf( PUSH, "%s: trouble on starting iteration", me );
return 1;
}
fprintf( stderr, "!%s: starting pctx->energySum = %g\n", me, pctx->energySum );
time0 = airTime( );
pctx->iter = 0;
do {
enrLast = pctx->energySum;
if ( pushIterate( pctx ) ) {
biffAddf( PUSH, "%s: trouble on iter %d", me, pctx->iter );
return 1;
}
if ( pctx->snap && !( pctx->iter % pctx->snap ) ) {
nten = nrrdNew( );
npos = nrrdNew( );
sprintf( poutS, "snap.%06d.pos.nrrd", pctx->iter );
sprintf( toutS, "snap.%06d.ten.nrrd", pctx->iter );
if ( pushOutputGet( npos, nten, NULL, pctx ) ) {
biffAddf( PUSH, "%s: couldn't get snapshot for iter %d",
me, pctx->iter );
return 1;
}
if ( nrrdSave( poutS, npos, NULL )
|| nrrdSave( toutS, nten, NULL ) ) {
biffMovef( PUSH, NRRD, "%s: couldn't save snapshot for iter %d",
me, pctx->iter );
return 1;
}
nten = nrrdNuke( nten );
npos = nrrdNuke( npos );
}
enrNew = pctx->energySum;
enrImprov = 2*( enrLast - enrNew )/( enrLast + enrNew );
fprintf( stderr, "!%s: %u, e=%g, de=%g, %g, df=%g\n",
me, pctx->iter, enrNew, enrImprov, enrImprovAvg, pctx->deltaFrac );
if ( enrImprov < 0 || pctx->deltaFrac < pctx->deltaFracMin ) {
/* either energy went up instead of down,
or particles were hitting their speed limit too much */
double tmp;
tmp = pctx->step;
if ( enrImprov < 0 ) {
pctx->step *= pctx->energyStepFrac;
fprintf( stderr, "%s: ***** iter %u e improv = %g; step = %g --> %g\n",
me, pctx->iter, enrImprov, tmp, pctx->step );
} else {
pctx->step *= pctx->deltaFracStepFrac;
fprintf( stderr, "%s: ##### iter %u deltaf = %g; step = %g --> %g\n",
me, pctx->iter, pctx->deltaFrac, tmp, pctx->step );
}
/* this forces another iteration */
enrImprovAvg = AIR_NAN;
} else {
/* there was some improvement; energy went down */
if ( !AIR_EXISTS( enrImprovAvg ) ) {
/* either enrImprovAvg has initial NaN setting, or was set to NaN
because we had to decrease step size; either way we now
re-initialize it to a large-ish value, to delay convergence */
enrImprovAvg = 3*enrImprov;
} else {
/* we had improvement this iteration and last, do weighted average
of the two, so that we are measuring the trend, rather than being
sensitive to two iterations that just happen to have the same
energy. Thus, when enrImprovAvg gets near user-defined threshold,
we really must have converged */
enrImprovAvg = ( enrImprovAvg + enrImprov )/2;
}
}
} while ( ( ( !AIR_EXISTS( enrImprovAvg )
|| enrImprovAvg > pctx->energyImprovMin )
&& ( 0 == pctx->maxIter
|| pctx->iter < pctx->maxIter ) ) );
fprintf( stderr, "%s: done after %u iters; enr = %g, enrImprov = %g, %g\n",
me, pctx->iter, enrNew, enrImprov, enrImprovAvg );
time1 = airTime( );
pctx->timeRun = time1 - time0;
return 0;
}
/*
** this is called *after* pushOutputGet
**
** should nix everything created by the many _push*Setup( ) functions
*/
int
410 pushFinish( pushContext *pctx ) {
static const char me[]="pushFinish";
unsigned int ii, tidx;
if ( !pctx ) {
biffAddf( PUSH, "%s: got NULL pointer", me );
return 1;
}
pctx->finished = AIR_TRUE;
if ( pctx->threadNum > 1 ) {
if ( pctx->verbose > 1 ) {
fprintf( stderr, "%s: finishing workers\n", me );
}
airThreadBarrierWait( pctx->iterBarrierA );
}
/* worker threads now pass barrierA and see that finished is AIR_TRUE,
and then bail, so now we collect them */
for ( tidx=pctx->threadNum; tidx>0; tidx-- ) {
if ( tidx-1 ) {
airThreadJoin( pctx->task[tidx-1]->thread,
&( pctx->task[tidx-1]->returnPtr ) );
}
pctx->task[tidx-1]->thread = airThreadNix( pctx->task[tidx-1]->thread );
pctx->task[tidx-1] = _pushTaskNix( pctx->task[tidx-1] );
}
pctx->task = ( pushTask ** )airFree( pctx->task );
pctx->nten = nrrdNuke( pctx->nten );
pctx->ninv = nrrdNuke( pctx->ninv );
pctx->nmask = nrrdNuke( pctx->nmask );
pctx->gctx = gageContextNix( pctx->gctx );
for ( ii=0; ii<pctx->binNum; ii++ ) {
pushBinDone( pctx->bin + ii );
}
pctx->bin = ( pushBin * )airFree( pctx->bin );
ELL_3V_SET( pctx->binsEdge, 0, 0, 0 );
pctx->binNum = 0;
if ( pctx->threadNum > 1 ) {
pctx->binMutex = airThreadMutexNix( pctx->binMutex );
pctx->iterBarrierA = airThreadBarrierNix( pctx->iterBarrierA );
pctx->iterBarrierB = airThreadBarrierNix( pctx->iterBarrierB );
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
const int
pushPresent = 42;
const char *
pushBiffKey = "push";
int
_pushVerbose = 0;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
#define SPRING "spring"
#define GAUSS "gauss"
#define COULOMB "coulomb"
#define COTAN "cotan"
#define ZERO "zero"
const char *
_pushEnergyTypeStr[PUSH_ENERGY_TYPE_MAX+1] = {
"( unknown_energy )",
SPRING,
GAUSS,
COULOMB,
COTAN,
ZERO
};
const char *
_pushEnergyTypeDesc[PUSH_ENERGY_TYPE_MAX+1] = {
"unknown_energy",
"Hooke's law-based potential, with a tunable region of attraction",
"Gaussian potential",
"Coulomb electrostatic potential, with tunable cut-off",
"Cotangent-based potential ( from Meyer et al. SMI '05 )",
"no energy"
};
const airEnum
_pushEnergyType = {
"energy",
PUSH_ENERGY_TYPE_MAX,
_pushEnergyTypeStr, NULL,
_pushEnergyTypeDesc,
NULL, NULL,
AIR_FALSE
};
62 const airEnum *const
pushEnergyType = &_pushEnergyType;
/* ----------------------------------------------------------------
** ------------------------------ UNKNOWN -------------------------
** ----------------------------------------------------------------
*/
void
70 _pushEnergyUnknownEval( double *enr, double *frc,
double dist, const double *parm ) {
static const char me[]="_pushEnergyUnknownEval";
AIR_UNUSED( dist );
AIR_UNUSED( parm );
*enr = AIR_NAN;
*frc = AIR_NAN;
fprintf( stderr, "%s: ERROR- using unknown energy.\n", me );
return;
}
double
83 _pushEnergyUnknownSupport( const double *parm ) {
static const char me[]="_pushEnergyUnknownSupport";
AIR_UNUSED( parm );
fprintf( stderr, "%s: ERROR- using unknown energy.\n", me );
return AIR_NAN;
}
pushEnergy
_pushEnergyUnknown = {
"unknown",
0,
_pushEnergyUnknownEval,
_pushEnergyUnknownSupport
};
98 const pushEnergy *const
pushEnergyUnknown = &_pushEnergyUnknown;
/* ----------------------------------------------------------------
** ------------------------------ SPRING --------------------------
** ----------------------------------------------------------------
** 1 parms:
** parm[0]: width of pull region ( beyond 1.0 )
**
** learned: "1/2" is not 0.5 !!!!!
*/
void
110 _pushEnergySpringEval( double *enr, double *frc,
double dist, const double *parm ) {
/* static const char me[]="_pushEnergySpringEval"; */
double xx, pull;
pull = parm[0];
xx = dist - 1.0;
if ( xx > pull ) {
*enr = 0;
*frc = 0;
} else if ( xx > 0 ) {
*enr = xx*xx*( xx*xx/( 4*pull*pull ) - 2*xx/( 3*pull ) + 1.0/2.0 );
*frc = xx*( xx*xx/( pull*pull ) - 2*xx/pull + 1 );
} else {
*enr = xx*xx/2;
*frc = xx;
}
/*
if ( !AIR_EXISTS( ret ) ) {
fprintf( stderr, "!%s: dist=%g, pull=%g, blah=%d --> ret=%g\n",
me, dist, pull, blah, ret );
}
*/
return;
}
double
137 _pushEnergySpringSupport( const double *parm ) {
return 1.0 + parm[0];
}
const pushEnergy
_pushEnergySpring = {
SPRING,
1,
_pushEnergySpringEval,
_pushEnergySpringSupport
};
149 const pushEnergy *const
pushEnergySpring = &_pushEnergySpring;
/* ----------------------------------------------------------------
** ------------------------------ GAUSS --------------------------
** ----------------------------------------------------------------
** 1 parms:
** ( distance to inflection point of force function is always 1.0 )
** parm[0]: cut-off ( as a multiple of standard dev ( which is 1.0 ) )
*/
/* HEY: copied from teem/src/nrrd/kernel.c */
#define _GAUSS( x, sig, cut ) ( \
x >= sig*cut ? 0 \
: exp( -x*x/( 2.0*sig*sig ) )/( sig*2.50662827463100050241 ) )
#define _DGAUSS( x, sig, cut ) ( \
165 x >= sig*cut ? 0 \
: -exp( -x*x/( 2.0*sig*sig ) )*x/( sig*sig*sig*2.50662827463100050241 ) )
void
_pushEnergyGaussEval( double *enr, double *frc,
double dist, const double *parm ) {
double cut;
cut = parm[0];
*enr = _GAUSS( dist, 1.0, cut );
*frc = _DGAUSS( dist, 1.0, cut );
176 return;
}
double
_pushEnergyGaussSupport( const double *parm ) {
return parm[0];
}
const pushEnergy
_pushEnergyGauss = {
GAUSS,
188 1,
_pushEnergyGaussEval,
_pushEnergyGaussSupport
};
const pushEnergy *const
pushEnergyGauss = &_pushEnergyGauss;
/* ----------------------------------------------------------------
** ------------------------------ CHARGE --------------------------
** ----------------------------------------------------------------
** 1 parms:
199 ** ( scale: distance to "1.0" in graph of x^( -2 ) )
** parm[0]: cut-off ( as multiple of "1.0" )
*/
void
_pushEnergyCoulombEval( double *enr, double *frc,
double dist, const double *parm ) {
*enr = ( dist > parm[0] ? 0 : 1.0/dist );
*frc = ( dist > parm[0] ? 0 : -1.0/( dist*dist ) );
208 return;
}
double
_pushEnergyCoulombSupport( const double *parm ) {
return parm[0];
}
const pushEnergy
_pushEnergyCoulomb = {
COULOMB,
220 1,
_pushEnergyCoulombEval,
_pushEnergyCoulombSupport
};
const pushEnergy *const
pushEnergyCoulomb = &_pushEnergyCoulomb;
/* ----------------------------------------------------------------
** ------------------------------ COTAN ---------------------------
229 ** ----------------------------------------------------------------
** 0 parms!
*/
void
_pushEnergyCotanEval( double *enr, double *frc,
double dist, const double *parm ) {
double pot, cc;
AIR_UNUSED( parm );
pot = AIR_PI/2.0;
cc = 1.0/( FLT_MIN + tan( dist*pot ) );
*enr = dist > 1 ? 0 : cc + dist*pot - pot;
*frc = dist > 1 ? 0 : -cc*cc*pot;
242 return;
}
double
_pushEnergyCotanSupport( const double *parm ) {
AIR_UNUSED( parm );
return 1;
}
const pushEnergy
_pushEnergyCotan = {
COTAN,
255 0,
_pushEnergyCotanEval,
_pushEnergyCotanSupport
};
const pushEnergy *const
pushEnergyCotan = &_pushEnergyCotan;
/* ----------------------------------------------------------------
** ------------------------------- ZERO ---------------------------
264 ** ----------------------------------------------------------------
** 0 parms:
*/
void
_pushEnergyZeroEval( double *enr, double *frc,
double dist, const double *parm ) {
AIR_UNUSED( dist );
AIR_UNUSED( parm );
*enr = 0;
*frc = 0;
275 return;
}
double
_pushEnergyZeroSupport( const double *parm ) {
AIR_UNUSED( parm );
return 1.0;
}
const pushEnergy
_pushEnergyZero = {
ZERO,
288 0,
_pushEnergyZeroEval,
_pushEnergyZeroSupport
};
const pushEnergy *const
pushEnergyZero = &_pushEnergyZero;
/* ----------------------------------------------------------------
296 ** ----------------------------------------------------------------
** ----------------------------------------------------------------
*/
const pushEnergy *const pushEnergyAll[PUSH_ENERGY_TYPE_MAX+1] = {
&_pushEnergyUnknown, /* 0 */
&_pushEnergySpring, /* 1 */
&_pushEnergyGauss, /* 2 */
&_pushEnergyCoulomb, /* 3 */
&_pushEnergyCotan, /* 4 */
&_pushEnergyZero /* 5 */
};
pushEnergySpec *
pushEnergySpecNew( ) {
pushEnergySpec *ensp;
int pi;
ensp = ( pushEnergySpec * )calloc( 1, sizeof( pushEnergySpec ) );
if ( ensp ) {
ensp->energy = pushEnergyUnknown;
for ( pi=0; pi<PUSH_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = AIR_NAN;
}
}
return ensp;
}
void
pushEnergySpecSet( pushEnergySpec *ensp, const pushEnergy *energy,
const double parm[PUSH_ENERGY_PARM_NUM] ) {
unsigned int pi;
if ( ensp && energy && parm ) {
ensp->energy = energy;
for ( pi=0; pi<PUSH_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = parm[pi];
}
}
return;
}
pushEnergySpec *
pushEnergySpecNix( pushEnergySpec *ensp ) {
airFree( ensp );
return NULL;
}
int
pushEnergySpecParse( pushEnergySpec *ensp, const char *_str ) {
static const char me[]="pushEnergySpecParse";
char *str, *col, *_pstr, *pstr;
int etype;
unsigned int pi, haveParm;
airArray *mop;
double pval;
if ( !( ensp && _str ) ) {
biffAddf( PUSH, "%s: got NULL pointer", me );
return 1;
}
/* see if its the name of something that needs no parameters */
etype = airEnumVal( pushEnergyType, _str );
if ( pushEnergyTypeUnknown != etype ) {
/* the string is the name of some energy */
ensp->energy = pushEnergyAll[etype];
if ( 0 != ensp->energy->parmNum ) {
biffAddf( PUSH, "%s: need %u parms for %s energy, but got none", me,
ensp->energy->parmNum, ensp->energy->name );
return 1;
}
/* the energy needs 0 parameters */
for ( pi=0; pi<PUSH_ENERGY_PARM_NUM; pi++ ) {
ensp->parm[pi] = AIR_NAN;
}
return 0;
}
/* start parsing parms after ':' */
mop = airMopNew( );
str = airStrdup( _str );
airMopAdd( mop, str, ( airMopper )airFree, airMopAlways );
col = strchr( str, ':' );
if ( !col ) {
biffAddf( PUSH, "%s: \"%s\" isn't a parameter-free energy, but it has no "
"\":\" separator to indicate parameters", me, str );
airMopError( mop ); return 1;
}
*col = '\0';
etype = airEnumVal( pushEnergyType, str );
if ( pushEnergyTypeUnknown == etype ) {
biffAddf( PUSH, "%s: didn't recognize \"%s\" as a %s", me,
str, pushEnergyType->name );
airMopError( mop ); return 1;
}
ensp->energy = pushEnergyAll[etype];
if ( 0 == ensp->energy->parmNum ) {
biffAddf( PUSH, "%s: \"%s\" energy has no parms, but got something", me,
ensp->energy->name );
return 1;
}
_pstr = pstr = col+1;
/* code lifted from teem/src/nrrd/kernel.c, should probably refactor... */
for ( haveParm=0; haveParm<ensp->energy->parmNum; haveParm++ ) {
if ( !pstr ) {
break;
}
if ( 1 != sscanf( pstr, "%lg", &pval ) ) {
biffAddf( PUSH, "%s: trouble parsing \"%s\" as double ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
}
ensp->parm[haveParm] = pval;
if ( ( pstr = strchr( pstr, ', ' ) ) ) {
pstr++;
if ( !*pstr ) {
biffAddf( PUSH, "%s: nothing after last comma in \"%s\" ( in \"%s\" )",
me, _pstr, _str );
airMopError( mop ); return 1;
}
}
}
/* haveParm is now the number of parameters that were parsed. */
if ( haveParm < ensp->energy->parmNum ) {
biffAddf( PUSH, "%s: parsed only %u of %u required parms ( for %s energy )"
"from \"%s\" ( in \"%s\" )",
me, haveParm, ensp->energy->parmNum,
ensp->energy->name, _pstr, _str );
airMopError( mop ); return 1;
} else {
if ( pstr ) {
biffAddf( PUSH, "%s: \"%s\" ( in \"%s\" ) has more than %u doubles",
me, _pstr, _str, ensp->energy->parmNum );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
int
_pushHestEnergyParse( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
pushEnergySpec **enspP;
static const char me[]="_pushHestForceParse";
char *perr;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
enspP = ( pushEnergySpec ** )ptr;
*enspP = pushEnergySpecNew( );
if ( pushEnergySpecParse( *enspP, str ) ) {
perr = biffGetDone( PUSH );
airStrcpy( err, AIR_STRLEN_HUGE, perr );
free( perr );
return 1;
}
return 0;
}
hestCB
_pushHestEnergySpec = {
sizeof( pushEnergySpec* ),
"energy specification",
_pushHestEnergyParse,
( airMopper )pushEnergySpecNix
};
hestCB *
pushHestEnergySpec = &_pushHestEnergySpec;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
/*
** the reason to take the pushContext *pctx argument is to allow
** doling out the ttaagg ID
*/
pushPoint *
33 pushPointNew( pushContext *pctx ) {
pushPoint *pnt;
pushPtrPtrUnion pppu;
if ( pctx ) {
pnt = AIR_CAST( pushPoint *, calloc( 1, sizeof( pushPoint ) ) );
if ( pnt ) {
pnt->ttaagg = pctx->ttaagg++;
ELL_3V_SET( pnt->pos, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pnt->frc, AIR_NAN, AIR_NAN, AIR_NAN );
TEN_T_SET( pnt->ten, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
TEN_T_SET( pnt->inv, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pnt->cnt, AIR_NAN, AIR_NAN, AIR_NAN );
pnt->grav = AIR_NAN;
ELL_3V_SET( pnt->gravGrad, AIR_NAN, AIR_NAN, AIR_NAN );
pnt->seedThresh = AIR_NAN;
pnt->enr = DBL_MAX; /* any finite quantity will be less than this */
pnt->neighArr = airArrayNew( ( pppu.point = &( pnt->neigh ), pppu.v ),
&( pnt->neighNum ),
sizeof( pushPoint * ), 10 );
}
} else {
pnt = NULL;
}
return pnt;
}
pushPoint *
64 pushPointNix( pushPoint *pnt ) {
airFree( pnt );
return NULL;
}
pushContext *
72 pushContextNew( void ) {
pushContext *pctx;
pctx = ( pushContext * )calloc( 1, sizeof( pushContext ) );
if ( pctx ) {
pctx->pointNum = 0;
pctx->nin = NULL;
pctx->npos = NULL;
pctx->stepInitial = 1;
pctx->scale = 0.2;
pctx->wall = 0.1;
pctx->cntScl = 0.0;
pctx->deltaLimit = 0.3;
pctx->deltaFracMin = 0.2;
pctx->energyStepFrac = 0.9;
pctx->deltaFracStepFrac = 0.5;
pctx->neighborTrueProb = 0.3;
pctx->probeProb = 0.5;
pctx->energyImprovMin = 0.01;
pctx->detReject = AIR_FALSE;
pctx->midPntSmp = AIR_FALSE;
pctx->verbose = 0;
pctx->seedRNG = 42;
pctx->threadNum = 1;
pctx->maxIter = 0;
pctx->snap = 0;
pctx->gravItem = tenGageUnknown;
pctx->gravGradItem = tenGageUnknown;
pctx->gravScl = AIR_NAN;
pctx->gravZero = AIR_NAN;
pctx->seedThreshItem = tenGageUnknown;
pctx->seedThreshSign = +1;
pctx->seedThresh = 0.0;
pctx->ensp = pushEnergySpecNew( );
pctx->binSingle = AIR_FALSE;
pctx->binIncr = 512;
pctx->ksp00 = nrrdKernelSpecNew( );
pctx->ksp11 = nrrdKernelSpecNew( );
pctx->ksp22 = nrrdKernelSpecNew( );
pctx->ttaagg = 0;
pctx->nten = NULL;
pctx->ninv = NULL;
pctx->nmask = NULL;
pctx->gctx = NULL;
pctx->tpvl = NULL;
pctx->ipvl = NULL;
pctx->finished = AIR_FALSE;
pctx->dimIn = 0;
pctx->sliceAxis = 42; /* an invalid value */
pctx->bin = NULL;
ELL_3V_SET( pctx->binsEdge, 0, 0, 0 );
pctx->binNum = 0;
pctx->binIdx = 0;
pctx->binMutex = NULL;
pctx->step = AIR_NAN;
pctx->maxDist = AIR_NAN;
pctx->maxEval = AIR_NAN;
pctx->meanEval = AIR_NAN;
pctx->maxDet = AIR_NAN;
pctx->energySum = 0;
pctx->task = NULL;
pctx->iterBarrierA = NULL;
pctx->iterBarrierB = NULL;
pctx->deltaFrac = AIR_NAN;
pctx->timeIteration = 0;
pctx->timeRun = 0;
pctx->iter = 0;
pctx->noutPos = nrrdNew( );
pctx->noutTen = nrrdNew( );
}
return pctx;
}
/*
** this should only nix things created by pushContextNew
*/
pushContext *
165 pushContextNix( pushContext *pctx ) {
if ( pctx ) {
pctx->ensp = pushEnergySpecNix( pctx->ensp );
pctx->ksp00 = nrrdKernelSpecNix( pctx->ksp00 );
pctx->ksp11 = nrrdKernelSpecNix( pctx->ksp11 );
pctx->ksp22 = nrrdKernelSpecNix( pctx->ksp22 );
pctx->noutPos = nrrdNuke( pctx->noutPos );
pctx->noutTen = nrrdNuke( pctx->noutTen );
airFree( pctx );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "push.h"
#include "privatePush.h"
/*
** _pushTensorFieldSetup sets:
**** pctx->dimIn
**** pctx->nten
**** pctx->ninv
**** pctx->nmask
** and checks mask range
*/
int
36 _pushTensorFieldSetup( pushContext *pctx ) {
static const char me[]="_pushTensorFieldSetup";
NrrdRange *nrange;
airArray *mop;
Nrrd *ntmp;
int E;
float *_ten, *_inv;
double ten[7], inv[7];
unsigned int numSingle;
size_t ii, NN;
mop = airMopNew( );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
pctx->nten = nrrdNew( );
pctx->ninv = nrrdNew( );
pctx->nmask = nrrdNew( );
numSingle = 0;
numSingle += ( 1 == pctx->nin->axis[1].size );
numSingle += ( 1 == pctx->nin->axis[2].size );
numSingle += ( 1 == pctx->nin->axis[3].size );
if ( 1 == numSingle ) {
pctx->dimIn = 2;
pctx->sliceAxis = ( 1 == pctx->nin->axis[1].size
? 0
: ( 1 == pctx->nin->axis[2].size
? 1
: 2 ) );
fprintf( stderr, "!%s: got 2-D input with sliceAxis %u\n",
me, pctx->sliceAxis );
} else {
pctx->dimIn = 3;
pctx->sliceAxis = 52; /* HEY: what the heck is 52 ? */
fprintf( stderr, "!%s: got 3-D input\n", me );
}
E = 0;
if ( !E ) E |= nrrdConvert( pctx->nten, pctx->nin, nrrdTypeFloat );
if ( !E ) E |= nrrdCopy( pctx->ninv, pctx->nten );
if ( E ) {
biffMovef( PUSH, NRRD, "%s: trouble creating 3D tensor input", me );
airMopError( mop ); return 1;
}
_ten = ( float* )pctx->nten->data;
_inv = ( float* )pctx->ninv->data;
NN = nrrdElementNumber( pctx->nten )/7;
for ( ii=0; ii<NN; ii++ ) {
double det;
TEN_T_COPY( ten, _ten );
TEN_T_INV( inv, ten, det );
if ( !det || !AIR_EXISTS( det ) ) {
fprintf( stderr, "!%s: tensor %u/%u has determinant %g\n", me,
AIR_CAST( unsigned int, ii ), AIR_CAST( unsigned int, NN ), det );
}
TEN_T_COPY_TT( _inv, float, inv );
_ten += 7;
_inv += 7;
}
if ( !E ) E |= nrrdSlice( pctx->nmask, pctx->nten, 0, 0 );
if ( E ) {
biffMovef( PUSH, NRRD, "%s: trouble creating mask", me );
airMopError( mop ); return 1;
}
nrange = nrrdRangeNewSet( pctx->nmask, nrrdBlind8BitRangeFalse );
airMopAdd( mop, nrange, ( airMopper )nrrdRangeNix, airMopAlways );
if ( AIR_ABS( 1.0 - nrange->max ) > 0.005 ) {
biffAddf( PUSH, "%s: tensor mask max %g not close 1.0", me, nrange->max );
airMopError( mop ); return 1;
}
pctx->nten->axis[1].center = nrrdCenterCell;
pctx->nten->axis[2].center = nrrdCenterCell;
pctx->nten->axis[3].center = nrrdCenterCell;
pctx->ninv->axis[1].center = nrrdCenterCell;
pctx->ninv->axis[2].center = nrrdCenterCell;
pctx->ninv->axis[3].center = nrrdCenterCell;
pctx->nmask->axis[0].center = nrrdCenterCell;
pctx->nmask->axis[1].center = nrrdCenterCell;
pctx->nmask->axis[2].center = nrrdCenterCell;
airMopOkay( mop );
return 0;
}
/*
** _pushGageSetup sets:
**** pctx->gctx
*/
int
125 _pushGageSetup( pushContext *pctx ) {
static const char me[]="_pushGageSetup";
gagePerVolume *mpvl;
int E;
pctx->gctx = gageContextNew( );
/* gageParmSet( pctx->gctx, gageParmRequireAllSpacings, AIR_TRUE ); */
E = AIR_FALSE;
/* set up tensor probing */
if ( !E ) E |= !( pctx->tpvl = gagePerVolumeNew( pctx->gctx,
pctx->nten, tenGageKind ) );
if ( !E ) E |= gagePerVolumeAttach( pctx->gctx, pctx->tpvl );
if ( !E ) E |= gageKernelSet( pctx->gctx, gageKernel00,
pctx->ksp00->kernel, pctx->ksp00->parm );
if ( !E ) E |= gageQueryItemOn( pctx->gctx, pctx->tpvl, tenGageTensor );
if ( tenGageUnknown != pctx->gravItem ) {
if ( !E ) E |= gageQueryItemOn( pctx->gctx, pctx->tpvl, pctx->gravItem );
if ( !E ) E |= gageQueryItemOn( pctx->gctx, pctx->tpvl, pctx->gravGradItem );
}
/* set up tensor inverse probing */
if ( !E ) E |= !( pctx->ipvl = gagePerVolumeNew( pctx->gctx,
pctx->ninv, tenGageKind ) );
if ( !E ) E |= gagePerVolumeAttach( pctx->gctx, pctx->ipvl );
if ( !E ) E |= gageQueryItemOn( pctx->gctx, pctx->ipvl, tenGageTensor );
/* set up mask gradient probing */
if ( !E ) E |= !( mpvl = gagePerVolumeNew( pctx->gctx,
pctx->nmask, gageKindScl ) );
if ( !E ) E |= gagePerVolumeAttach( pctx->gctx, mpvl );
if ( !E ) E |= gageQueryItemOn( pctx->gctx, mpvl, gageSclGradVec );
if ( !E ) E |= gageKernelSet( pctx->gctx, gageKernel11,
pctx->ksp11->kernel, pctx->ksp11->parm );
/* ( maybe ) turn on seed thresholding */
if ( tenGageUnknown != pctx->seedThreshItem ) {
if ( !E ) E |= gageQueryItemOn( pctx->gctx, pctx->tpvl, pctx->seedThreshItem );
}
/* HEY: seed threshold item should possibly be turned off later! */
if ( !E ) E |= gageUpdate( pctx->gctx );
if ( E ) {
biffMovef( PUSH, GAGE, "%s: trouble setting up gage", me );
return 1;
}
return 0;
}
pushTask *
172 _pushTaskNew( pushContext *pctx, int threadIdx ) {
static const char me[]="_pushTaskNew";
pushTask *task;
task = ( pushTask * )calloc( 1, sizeof( pushTask ) );
if ( task ) {
task->pctx = pctx;
if ( !( task->gctx = gageContextCopy( pctx->gctx ) ) ) {
biffMovef( PUSH, GAGE, "%s: trouble copying main gageContext", me );
return NULL;
}
/*
** HEY: its a limitation in gage that we have to know a priori
** the ordering of per-volumes in the context ...
*/
task->tenAns = gageAnswerPointer( task->gctx, task->gctx->pvl[0],
tenGageTensor );
task->invAns = gageAnswerPointer( task->gctx, task->gctx->pvl[1],
tenGageTensor );
task->cntAns = gageAnswerPointer( task->gctx, task->gctx->pvl[2],
gageSclGradVec );
if ( tenGageUnknown != task->pctx->gravItem ) {
task->gravAns = gageAnswerPointer( task->gctx, task->gctx->pvl[0],
task->pctx->gravItem );
task->gravGradAns = gageAnswerPointer( task->gctx, task->gctx->pvl[0],
task->pctx->gravGradItem );
} else {
task->gravAns = NULL;
task->gravGradAns = NULL;
}
if ( tenGageUnknown != task->pctx->seedThreshItem ) {
task->seedThreshAns = gageAnswerPointer( task->gctx, task->gctx->pvl[0],
task->pctx->seedThreshItem );
} else {
task->seedThreshAns = NULL;
}
if ( threadIdx ) {
task->thread = airThreadNew( );
}
task->rng = airRandMTStateNew( pctx->seedRNG + threadIdx );
task->threadIdx = threadIdx;
task->pointNum = 0;
task->energySum = 0;
task->deltaFracSum = 0;
task->returnPtr = NULL;
}
return task;
}
pushTask *
223 _pushTaskNix( pushTask *task ) {
if ( task ) {
task->gctx = gageContextNix( task->gctx );
if ( task->threadIdx ) {
task->thread = airThreadNix( task->thread );
}
task->rng = airRandMTStateNix( task->rng );
airFree( task );
}
return NULL;
}
/*
** _pushTaskSetup sets:
**** pctx->task
**** pctx->task[]
*/
int
242 _pushTaskSetup( pushContext *pctx ) {
static const char me[]="_pushTaskSetup";
unsigned int tidx;
pctx->task = ( pushTask ** )calloc( pctx->threadNum, sizeof( pushTask * ) );
if ( !( pctx->task ) ) {
biffAddf( PUSH, "%s: couldn't allocate array of tasks", me );
return 1;
}
for ( tidx=0; tidx<pctx->threadNum; tidx++ ) {
if ( pctx->verbose ) {
fprintf( stderr, "%s: creating task %u/%u\n", me, tidx, pctx->threadNum );
}
pctx->task[tidx] = _pushTaskNew( pctx, tidx );
if ( !( pctx->task[tidx] ) ) {
biffAddf( PUSH, "%s: couldn't allocate task %d", me, tidx );
return 1;
}
}
return 0;
}
/*
** _pushBinSetup sets:
**** pctx->maxDist, pctx->minEval, pctx->maxEval, pctx->maxDet
**** pctx->binsEdge[], pctx->binNum
**** pctx->bin
**** pctx->bin[]
*/
int
272 _pushBinSetup( pushContext *pctx ) {
static const char me[]="_pushBinSetup";
float eval[3], *tdata;
unsigned int ii, nn, count;
double col[3][4], volEdge[3];
/* ------------------------ find maxEval, maxDet, and set up binning */
nn = nrrdElementNumber( pctx->nten )/7;
pctx->maxEval = 0;
pctx->maxDet = 0;
pctx->meanEval = 0;
count = 0;
tdata = ( float* )pctx->nten->data;
for ( ii=0; ii<nn; ii++ ) {
tenEigensolve_f( eval, NULL, tdata );
if ( tdata[0] > 0.5 ) {
/* HEY: this limitation may be a bad idea */
count++;
pctx->meanEval += eval[0];
pctx->maxEval = AIR_MAX( pctx->maxEval, eval[0] );
if ( 2 == pctx->dimIn ) {
double det2d;
/* HEY! HEY! this assumes not only that the measurement frame
has been taken care of, but that the volume is axis-aligned */
det2d = ( 0 == pctx->sliceAxis
? TEN_T_DET_YZ( tdata )
: ( 1 == pctx->sliceAxis
? TEN_T_DET_XZ( tdata )
: TEN_T_DET_XY( tdata ) ) );
pctx->maxDet = AIR_MAX( pctx->maxDet, det2d );
} else {
pctx->maxDet = AIR_MAX( pctx->maxDet, eval[0]*eval[1]*eval[2] );
}
}
tdata += 7;
}
fprintf( stderr, "!%s: dimIn = %u( %u ) --> maxDet = %g\n", me,
pctx->dimIn, pctx->sliceAxis, pctx->maxDet );
pctx->meanEval /= count;
pctx->maxDist = ( 2*pctx->scale*pctx->maxEval
*pctx->ensp->energy->support( pctx->ensp->parm ) );
if ( pctx->binSingle ) {
pctx->binsEdge[0] = 1;
pctx->binsEdge[1] = 1;
pctx->binsEdge[2] = 1;
pctx->binNum = 1;
} else {
ELL_4MV_COL0_GET( col[0], pctx->gctx->shape->ItoW ); col[0][3] = 0.0;
ELL_4MV_COL1_GET( col[1], pctx->gctx->shape->ItoW ); col[1][3] = 0.0;
ELL_4MV_COL2_GET( col[2], pctx->gctx->shape->ItoW ); col[2][3] = 0.0;
volEdge[0] = ELL_3V_LEN( col[0] )*pctx->gctx->shape->size[0];
volEdge[1] = ELL_3V_LEN( col[1] )*pctx->gctx->shape->size[1];
volEdge[2] = ELL_3V_LEN( col[2] )*pctx->gctx->shape->size[2];
fprintf( stderr, "!%s: volEdge = %g %g %g\n", me,
volEdge[0], volEdge[1], volEdge[2] );
pctx->binsEdge[0] = AIR_CAST( unsigned int,
floor( volEdge[0]/pctx->maxDist ) );
pctx->binsEdge[0] = pctx->binsEdge[0] ? pctx->binsEdge[0] : 1;
pctx->binsEdge[1] = AIR_CAST( unsigned int,
floor( volEdge[1]/pctx->maxDist ) );
pctx->binsEdge[1] = pctx->binsEdge[1] ? pctx->binsEdge[1] : 1;
pctx->binsEdge[2] = AIR_CAST( unsigned int,
floor( volEdge[2]/pctx->maxDist ) );
pctx->binsEdge[2] = pctx->binsEdge[2] ? pctx->binsEdge[2] : 1;
if ( 2 == pctx->dimIn ) {
pctx->binsEdge[pctx->sliceAxis] = 1;
}
fprintf( stderr, "!%s: maxEval=%g -> maxDist=%g -> binsEdge=( %u, %u, %u )\n",
me, pctx->maxEval, pctx->maxDist,
pctx->binsEdge[0], pctx->binsEdge[1], pctx->binsEdge[2] );
pctx->binNum = pctx->binsEdge[0]*pctx->binsEdge[1]*pctx->binsEdge[2];
}
pctx->bin = ( pushBin * )calloc( pctx->binNum, sizeof( pushBin ) );
if ( !( pctx->bin ) ) {
biffAddf( PUSH, "%s: trouble allocating bin arrays", me );
return 1;
}
for ( ii=0; ii<pctx->binNum; ii++ ) {
pushBinInit( pctx->bin + ii, pctx->binIncr );
}
pushBinAllNeighborSet( pctx );
return 0;
}
/*
** _pushPointSetup sets:
**** pctx->pointNum ( in case pctx->npos )
**
** This is only called by the master thread
**
** this should set stuff to be like after an update stage and
** just before the rebinning
*/
int
368 _pushPointSetup( pushContext *pctx ) {
static const char me[]="_pushPointSetup";
double ( *lup )( const void *v, size_t I ), maxDet;
unsigned int pointIdx;
pushPoint *point;
/*
double posIdxHack[2][4] = {
{49.99999, 50, 0, 1},
{50, 50, 0, 1}};
*/
pctx->pointNum = ( pctx->npos
? pctx->npos->axis[1].size
: pctx->pointNum );
lup = pctx->npos ? nrrdDLookup[pctx->npos->type] : NULL;
fprintf( stderr, "!%s: initilizing/seeding ... \n", me );
/* HEY: we end up keeping a local copy of maxDet because convolution
can produce a tensor with higher determinant than that of any
original sample. However, if this is going into effect,
detReject should probably *not* be enabled... */
maxDet = pctx->maxDet;
for ( pointIdx=0; pointIdx<pctx->pointNum; pointIdx++ ) {
double detProbe;
/*
fprintf( stderr, "!%s: pointIdx = %u/%u\n", me, pointIdx, pctx->pointNum );
*/
point = pushPointNew( pctx );
if ( pctx->npos ) {
ELL_3V_SET( point->pos,
lup( pctx->npos->data, 0 + 3*pointIdx ),
lup( pctx->npos->data, 1 + 3*pointIdx ),
lup( pctx->npos->data, 2 + 3*pointIdx ) );
if ( _pushProbe( pctx->task[0], point ) ) {
biffAddf( PUSH, "%s: probing pointIdx %u of npos", me, pointIdx );
return 1;
}
} else {
/*
double posWorld[4];
ELL_4MV_MUL( posWorld, pctx->gctx->shape->ItoW, posIdxHack[pointIdx] );
ELL_34V_HOMOG( point->pos, posWorld );
_pushProbe( pctx->task[0], point );
*/
do {
double posIdx[4], posWorld[4];
posIdx[0] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0,
-0.5, pctx->gctx->shape->size[0]-0.5 );
posIdx[1] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0,
-0.5, pctx->gctx->shape->size[1]-0.5 );
posIdx[2] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0,
-0.5, pctx->gctx->shape->size[2]-0.5 );
posIdx[3] = 1.0;
if ( 2 == pctx->dimIn ) {
posIdx[pctx->sliceAxis] = 0.0;
}
ELL_4MV_MUL( posWorld, pctx->gctx->shape->ItoW, posIdx );
ELL_34V_HOMOG( point->pos, posWorld );
/*
fprintf( stderr, "%s: posIdx = %g %g %g --> posWorld = %g %g %g "
"--> %g %g %g\n", me,
posIdx[0], posIdx[1], posIdx[2],
posWorld[0], posWorld[1], posWorld[2],
point->pos[0], point->pos[1], point->pos[2] );
*/
if ( _pushProbe( pctx->task[0], point ) ) {
biffAddf( PUSH, "%s: probing pointIdx %u of world", me, pointIdx );
return 1;
}
detProbe = TEN_T_DET( point->ten );
if ( 2 == pctx->dimIn ) {
/* see above HEY! HEY! */
detProbe = ( 0 == pctx->sliceAxis
? TEN_T_DET_YZ( point->ten )
: ( 1 == pctx->sliceAxis
? TEN_T_DET_XZ( point->ten )
: TEN_T_DET_XY( point->ten ) ) );
} else {
detProbe = TEN_T_DET( point->ten );
}
maxDet = AIR_MAX( maxDet, detProbe );
/* assuming that we're not using some very blurring kernel,
this will eventually succeed, because we previously checked
the range of values in the mask */
/* HEY: can't ensure that this will eventually succeed with
seedThresh enabled! */
/*
fprintf( stderr, "!%s: ten[0] = %g\n", me, point->ten[0] );
*/
/* we OR together all the tests that would
make us REJECT this last sample */
} while ( point->ten[0] < 0.5
|| ( tenGageUnknown != pctx->seedThreshItem
&& ( ( pctx->seedThresh - point->seedThresh )
*pctx->seedThreshSign > 0 )
)
|| ( pctx->detReject
&& ( airDrandMT( ) < detProbe/maxDet ) )
);
}
if ( pushBinPointAdd( pctx, point ) ) {
biffAddf( PUSH, "%s: trouble binning point %u", me, point->ttaagg );
return 1;
}
}
fprintf( stderr, "!%s: ... seeding DONE\n", me );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../push.h"
char *info = ( "Tests parsing of energy, and its methods." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
pushEnergySpec *ensp;
unsigned int pi, xi, nn;
double xx, supp, del;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, NULL,
"specification of force function to use",
NULL, NULL, pushHestEnergySpec );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
fprintf( stderr, "%s: parsed energy \"%s\", with %u parms.\n", me,
ensp->energy->name, ensp->energy->parmNum );
for ( pi=0; pi<ensp->energy->parmNum; pi++ ) {
fprintf( stderr, "%u: %g\n", pi, ensp->parm[pi] );
}
fprintf( stderr, "\n" );
nn = 600;
supp = ensp->energy->support( ensp->parm );
del = AIR_DELTA( 0, 2, nn, 0, supp );
for ( xi=1; xi<nn; xi++ ) {
double x0, x1, ee, ff, e0, e1, dummy;
xx = AIR_AFFINE( 0, xi, nn, 0, supp );
x1 = AIR_AFFINE( 0, xi+1, nn, 0, supp );
x0 = AIR_AFFINE( 0, xi-1, nn, 0, supp );
ensp->energy->eval( &e1, &dummy, x1, ensp->parm );
ensp->energy->eval( &e0, &dummy, x0, ensp->parm );
ensp->energy->eval( &ee, &ff, xx, ensp->parm );
printf( "%g %g %g %g\n", xx, ee, ff, ( e1 - e0 )/del );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../push.h"
char *info = ( "Test program for push library." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
char *outS[3];
char *gravStr, *gravGradStr, *seedStr;
pushContext *pctx;
Nrrd *_nin, *nin, *nPosIn, *nPosOut, *nTenOut, *nEnrOut;
NrrdKernelSpec *ksp00, *ksp11, *ksp22;
pushEnergySpec *ensp;
int E;
me = argv[0];
mop = airMopNew( );
pctx = pushContextNew( );
airMopAdd( mop, pctx, ( airMopper )pushContextNix, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &_nin, NULL,
"input volume to filter", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "np", "# points", airTypeUInt, 1, 1,
&( pctx->pointNum ), "1000",
"number of points to use in simulation" );
hestOptAdd( &hopt, "pi", "npos", airTypeOther, 1, 1, &nPosIn, "",
"positions to start at ( overrides \"-np\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1,
&( pctx->stepInitial ), "1",
"step size for gradient descent" );
hestOptAdd( &hopt, "scl", "scale", airTypeDouble, 1, 1,
&( pctx->scale ), "1500",
"scaling from tensor size to glyph size" );
hestOptAdd( &hopt, "wall", "wall", airTypeDouble, 1, 1,
&( pctx->wall ), "0.0",
"spring constant of containing walls" );
hestOptAdd( &hopt, "cnts", "scale", airTypeDouble, 1, 1,
&( pctx->cntScl ), "0.0",
"scaling of containment force" );
hestOptAdd( &hopt, "limit", "frac", airTypeDouble, 1, 1,
&( pctx->deltaLimit ), "0.3",
"speed limit on particles' motion" );
hestOptAdd( &hopt, "dfmin", "frac", airTypeDouble, 1, 1,
&( pctx->deltaFracMin ), "0.2",
"decrease step size if deltaFrac goes below this" );
hestOptAdd( &hopt, "esf", "frac", airTypeDouble, 1, 1,
&( pctx->energyStepFrac ), "0.9",
"when energy goes up instead of down, fraction by "
"which to scale step size" );
hestOptAdd( &hopt, "dfsf", "frac", airTypeDouble, 1, 1,
&( pctx->deltaFracStepFrac ), "0.5",
"when deltaFrac goes below deltaFracMin, fraction by "
"which to scale step size" );
hestOptAdd( &hopt, "eimin", "frac", airTypeDouble, 1, 1,
&( pctx->energyImprovMin ), "0.01",
"convergence threshold: stop when fracional improvement "
"( decrease ) in energy dips below this" );
hestOptAdd( &hopt, "detr", NULL, airTypeBool, 0, 0,
&( pctx->detReject ), NULL,
"do determinant-based rejection of initial sample locations" );
hestOptAdd( &hopt, "mps", NULL, airTypeBool, 0, 0,
&( pctx->midPntSmp ), NULL,
"sampling midpoint in tensor field, and invert it, instead of "
"approximating it by averaging the inverses at the endpoints" );
hestOptAdd( &hopt, "rng", "seed", airTypeUInt, 1, 1,
&( pctx->seedRNG ), "42",
"seed value for RNG which determines initial point locations" );
hestOptAdd( &hopt, "nt", "# threads", airTypeUInt, 1, 1,
&( pctx->threadNum ), "1",
"number of threads to run" );
hestOptAdd( &hopt, "nprob", "# iters", airTypeDouble, 1, 1,
&( pctx->neighborTrueProb ), "1.0",
"do full neighbor traversal with this probability" );
hestOptAdd( &hopt, "pprob", "# iters", airTypeDouble, 1, 1,
&( pctx->probeProb ), "1.0",
"do field probing with this probability" );
hestOptAdd( &hopt, "maxi", "# iters", airTypeUInt, 1, 1,
&( pctx->maxIter ), "0",
"if non-zero, max # iterations to run" );
hestOptAdd( &hopt, "snap", "iters", airTypeUInt, 1, 1, &( pctx->snap ), "0",
"if non-zero, # iterations between which a snapshot "
"is saved" );
hestOptAdd( &hopt, "grv", "item", airTypeString, 1, 1, &gravStr, "none",
"item to act as gravity" );
hestOptAdd( &hopt, "grvgv", "item", airTypeString, 1, 1, &gravGradStr, "none",
"item to act as gravity gradient" );
hestOptAdd( &hopt, "grvs", "scale", airTypeDouble, 1, 1, &( pctx->gravScl ),
"nan", "magnitude and scaling of gravity vector" );
hestOptAdd( &hopt, "grvz", "scale", airTypeDouble, 1, 1, &( pctx->gravZero ),
"nan", "height ( WRT gravity ) of zero potential energy" );
hestOptAdd( &hopt, "seed", "item", airTypeString, 1, 1, &seedStr, "none",
"item to act as seed threshold" );
hestOptAdd( &hopt, "seedth", "thresh", airTypeDouble, 1, 1,
&( pctx->seedThresh ), "nan",
"seed threshold threshold" );
hestOptAdd( &hopt, "energy", "spec", airTypeOther, 1, 1, &ensp, "cotan",
"specification of energy function to use",
NULL, NULL, pushHestEnergySpec );
hestOptAdd( &hopt, "nobin", NULL, airTypeBool, 0, 0,
&( pctx->binSingle ), NULL,
"turn off spatial binning ( which prevents multi-threading "
"from being useful ), for debugging or speed-up measurement" );
hestOptAdd( &hopt, "k00", "kernel", airTypeOther, 1, 1, &ksp00,
"tent", "kernel for tensor field sampling",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k11", "kernel", airTypeOther, 1, 1, &ksp11,
"fordif", "kernel for finding containment gradient from mask",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "k22", "kernel", airTypeOther, 1, 1, &ksp22,
"cubicdd:1, 0", "kernel for 2nd derivatives",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "o", "nout", airTypeString, 3, 3, outS,
"p.nrrd t.nrrd e.nrrd",
"output files to save position and tensor info into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nPosOut = nrrdNew( );
airMopAdd( mop, nPosOut, ( airMopper )nrrdNuke, airMopAlways );
nTenOut = nrrdNew( );
airMopAdd( mop, nTenOut, ( airMopper )nrrdNuke, airMopAlways );
nEnrOut = nrrdNew( );
airMopAdd( mop, nEnrOut, ( airMopper )nrrdNuke, airMopAlways );
if ( 3 == _nin->spaceDim && AIR_EXISTS( _nin->measurementFrame[0][0] ) ) {
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( tenMeasurementFrameReduce( nin, _nin ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble undoing measurement frame:\n%s", me, err );
airMopError( mop );
exit( 1 );
}
} else {
nin = _nin;
}
pctx->nin = nin;
pctx->npos = nPosIn;
pctx->verbose = 0;
pctx->binIncr = 84; /* random small-ish value */
pushEnergySpecSet( pctx->ensp, ensp->energy, ensp->parm );
nrrdKernelSpecSet( pctx->ksp00, ksp00->kernel, ksp00->parm );
nrrdKernelSpecSet( pctx->ksp11, ksp11->kernel, ksp11->parm );
nrrdKernelSpecSet( pctx->ksp22, ksp22->kernel, ksp22->parm );
if ( strcmp( "none", gravStr ) ) {
pctx->gravItem = airEnumVal( tenGage, gravStr );
if ( tenGageUnknown == pctx->gravItem ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a %s ( gravity )\n", me,
gravStr, tenGage->name );
airMopError( mop );
return 1;
}
pctx->gravGradItem = airEnumVal( tenGage, gravGradStr );
if ( tenGageUnknown == pctx->gravGradItem ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a %s ( gravity grad )\n",
me, gravGradStr, tenGage->name );
airMopError( mop );
return 1;
}
} else {
pctx->gravItem = tenGageUnknown;
pctx->gravGradItem = tenGageUnknown;
pctx->gravZero = AIR_NAN;
pctx->gravScl = AIR_NAN;
}
if ( strcmp( "none", seedStr ) ) {
pctx->seedThreshItem = airEnumVal( tenGage, seedStr );
if ( tenGageUnknown == pctx->seedThreshItem ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a %s ( seedthresh )\n", me,
seedStr, tenGage->name );
airMopError( mop );
return 1;
}
} else {
pctx->seedThreshItem = 0;
pctx->seedThresh = AIR_NAN;
}
E = 0;
if ( !E ) E |= pushStart( pctx );
if ( !E ) E |= pushRun( pctx );
if ( !E ) E |= pushOutputGet( nPosOut, nTenOut, nEnrOut, pctx );
if ( !E ) E |= pushFinish( pctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( PUSH ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "%s: time for %d iterations= %g secs\n",
me, pctx->iter, pctx->timeRun );
if ( nrrdSave( outS[0], nPosOut, NULL )
|| nrrdSave( outS[1], nTenOut, NULL )
|| nrrdSave( outS[2], nEnrOut, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009, 2008 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* This file collects functions that implement various gradient
* descents required in the context of extracting crease surfaces */
#include "seek.h"
#include "privateSeek.h"
/* Tries to find a degenerate point on a bilinearly interpolated face
* of symmetric second-order tensors. Uses the discriminant constraint
* functions from Zheng/Parlett/Pang, TVCG 2005, and the
* Newton-Raphson method with Armijo stepsize control.
*
* coord are coordinates relative to the given face and will be
* updated in each iteration.
* botleft - topright are 9-vectors, representing the symmetric
* matrices at the corners of the face ( input only )
* maxiter is the maximum number of iterations allowed
* eps is the accuracy up to which the discriminant should be zero
* The discriminant scales with the Frobenius norm to the sixth power,
* so the exact constraint is disc/|T|^6<eps
* type can be 'l', 'p', or something else ( =both )
*
* Returns 0 if the point was found up to the given accuracy
* Returns 1 if we left the face
* Returns 2 if we hit maxiter
* Returns 3 if we could not invert a matrix to find next gradient dir
* Returns 4 if we found a point, but it does not have the desired type
* Returns 5 if Armijo rule failed to find a valid stepsize
* Returns 6 if we hit a zero tensor ( |T|<1e-300 )
*/
51 int seekDescendToDeg( double *coord, double *botleft, double *botright,
double *topleft, double *topright,
int maxiter, double eps, char type )
{
double discr; /* store discriminant value of previous iteration */
double hesstop[9]; /* used to interpolate Hessian */
double hessbot[9];
double hess[9];
double ten[6]; /* makes access more convenient */
double tsqr[6]; /* squared tensor values, are used repeatedly */
double cf[7]; /* constraint function vector */
double norm; /* Frobenius norm, for normalization */
int i, j, iter; /* counting variables for loops */
/* check initial point */
ELL_3M_LERP( hessbot, coord[0], botleft, botright );
ELL_3M_LERP( hesstop, coord[0], topleft, topright );
ELL_3M_LERP( hess, coord[1], hessbot, hesstop );
/* normalize for scale invariance & to avoid numerical problems */
norm = sqrt( hess[0]*hess[0]+hess[4]*hess[4]+hess[8]*hess[8]+
2*( hess[1]*hess[1]+hess[2]*hess[2]+hess[5]*hess[5] ) );
if ( norm<1e-300 ) return 6;
ten[0] = hess[0]/norm; ten[1] = hess[1]/norm; ten[2] = hess[2]/norm;
ten[3] = hess[4]/norm; ten[4] = hess[5]/norm; ten[5] = hess[8]/norm;
for ( i=0; i<6; i++ )
tsqr[i] = ten[i]*ten[i];
/* evaluate the constraint function vector */
cf[0] = ten[0]*( tsqr[3]-tsqr[5] )+ten[0]*( tsqr[1]-tsqr[2] )+
ten[3]*( tsqr[5]-tsqr[0] )+ten[3]*( tsqr[4]-tsqr[1] )+
ten[5]*( tsqr[0]-tsqr[3] )+ten[5]*( tsqr[2]-tsqr[4] ); /* fx */
cf[1] = ten[4]*( 2*( tsqr[4]-tsqr[0] )-( tsqr[2]+tsqr[1] )+
2*( ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5] ) )+
ten[1]*ten[2]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1 */
cf[2] = ten[2]*( 2*( tsqr[2]-tsqr[3] )-( tsqr[1]+tsqr[4] )+
2*( ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0] ) )+
ten[4]*ten[1]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2 */
cf[3] = ten[1]*( 2*( tsqr[1]-tsqr[5] )-( tsqr[4]+tsqr[2] )+
2*( ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3] ) )+
ten[2]*ten[4]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3 */
cf[4] = ten[4]*( tsqr[2]-tsqr[1] )+ten[1]*ten[2]*( ten[3]-ten[5] ); /* fz1 */
cf[5] = ten[2]*( tsqr[1]-tsqr[4] )+ten[4]*ten[1]*( ten[5]-ten[0] ); /* fz2 */
cf[6] = ten[1]*( tsqr[4]-tsqr[2] )+ten[2]*ten[4]*( ten[0]-ten[3] ); /* fz3 */
discr = cf[0]*cf[0]+cf[1]*cf[1]+cf[2]*cf[2]+cf[3]*cf[3]+
15*( cf[4]*cf[4]+cf[5]*cf[5]+cf[6]*cf[6] );
if ( discr<eps ) {
if ( type!='l' && type!='p' ) return 0;
else {
/* check if type is correct */
double dev[9], det;
double mean = ( ten[0]+ten[3]+ten[5] )/3;
dev[0] = ten[0]-mean; dev[1] = ten[1]; dev[2] = ten[2];
dev[3] = ten[1]; dev[4]=ten[3]-mean; dev[5] = ten[4];
dev[6] = ten[2]; dev[7]=ten[4]; dev[8]=ten[5]-mean;
det = ELL_3M_DET( dev );
if ( ( type=='l' && det>0 ) || ( type=='p' && det<0 ) ) return 0;
else return 4; /* sufficient accuracy, but wrong type */
}
}
for ( iter=0; iter<maxiter; iter++ ) {
/* find derivative of constraint function vector using the chain rule */
double cft[42]; /* derive relative to tensor values, 7x6 matrix */
double tx[12]; /* spatial derivative of tensor values, 6x2 matrix */
double cfx[14]; /* spatial derivative of constraint funct., 7x2 matrix */
double denom[3], det; /* symmetric 2x2 matrix that is to be inverted */
double inv[3]; /* inverse of that matrix */
double nom[2], dx[2]; /* more helpers to compute next step */
/* variables needed for Armijo stepsize rule */
double beta=1, _gamma=0.5, alpha=beta; /* parameters */
int accept=0, safetyct=0, maxct=30; /* counters */
double dxsqr; /* squared length of stepsize */
double hessleft[9], hessright[9]; /* used to compute Hessian derivative */
double hessder[9];
int row, col;
cft [0] = tsqr[3]-tsqr[5]+tsqr[1]-tsqr[2]-2*ten[0]*ten[3]+2*ten[0]*ten[5];
/* fx/T00 */
cft [1] = 2*ten[0]*ten[1]-2*ten[3]*ten[1]; /* fx/T01 */
cft [2] = -2*ten[0]*ten[2]+2*ten[5]*ten[2]; /* fx/T02 */
cft [3] = 2*ten[0]*ten[3]+tsqr[5]-tsqr[0]+tsqr[4]-tsqr[1]-2*ten[5]*ten[3];
/* fx/T11 */
cft [4] = 2*ten[3]*ten[4]-2*ten[5]*ten[4]; /* fx/T12 */
cft [5] = -2*ten[0]*ten[5]+2*ten[3]*ten[5]+tsqr[0]-tsqr[3]+tsqr[2]-tsqr[4];
/* fx/T22 */
cft [6] = -4*ten[0]*ten[4]+2*ten[4]*ten[3]+2*ten[4]*ten[5]+2*ten[1]*ten[2];
/* fy1/T00 */
cft [7] = -2*ten[4]*ten[1]+ten[2]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1/T01 */
cft [8] = -2*ten[4]*ten[2]+ten[1]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1/T02 */
cft [9] = 2*ten[4]*ten[0]-2*ten[4]*ten[5]-ten[1]*ten[2]; /* fy1/T11 */
cft[10] = 6*tsqr[4]-2*tsqr[0]-( tsqr[2]+tsqr[1] )+
2*( ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5] ); /* fy1/T12 */
cft[11] = 2*ten[4]*ten[0]-2*ten[4]*ten[3]-ten[1]*ten[2]; /* fy1/T22 */
cft[12] = 2*ten[2]*ten[3]-2*ten[2]*ten[5]-ten[4]*ten[1]; /* fy2/T00 */
cft[13] = -2*ten[2]*ten[1]+ten[4]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2/T01 */
cft[14] = 6*tsqr[2]-2*tsqr[3]-( tsqr[1]+tsqr[4] )+
2*( ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0] ); /* fy2/T02 */
cft[15] = -4*ten[2]*ten[3]+2*ten[2]*ten[5]+2*ten[2]*ten[0]+2*ten[4]*ten[1];
/* fy2/T11 */
cft[16] = -2*ten[2]*ten[4]+ten[1]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2/T12 */
cft[17] = 2*ten[2]*ten[3]-2*ten[2]*ten[0]-ten[4]*ten[1]; /* fy2/T22 */
cft[18] = 2*ten[1]*ten[5]-2*ten[1]*ten[3]-ten[2]*ten[4]; /* fy3/T00 */
cft[19] = 6*tsqr[1]-2*tsqr[5]-( tsqr[4]+tsqr[2] )+
2*( ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3] ); /* fy3/T01 */
cft[20] = -2*ten[1]*ten[2]+ten[4]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3/T02 */
cft[21] = 2*ten[1]*ten[5]-2*ten[1]*ten[0]-ten[2]*ten[4]; /* fy3/T11 */
cft[22] = -2*ten[1]*ten[4]+ten[2]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3/T12 */
cft[23] = -4*ten[1]*ten[5]+2*ten[0]*ten[1]+2*ten[1]*ten[3]+2*ten[2]*ten[4];
/* fy3/T22 */
cft[24] = 0; /* fz1/T00 */
cft[25] = -2*ten[4]*ten[1]+ten[2]*( ten[3]-ten[5] ); /* fz1/T01 */
cft[26] = 2*ten[4]*ten[2]+ten[1]*( ten[3]-ten[5] ); /* fz1/T02 */
cft[27] = ten[1]*ten[2]; /* fz1/T11 */
cft[28] = tsqr[2]-tsqr[1]; /* fz1/T12 */
cft[29] = -ten[1]*ten[2]; /* fz1/T22 */
cft[30] = -ten[4]*ten[1]; /* fz2/T00 */
cft[31] = 2*ten[2]*ten[1]+ten[4]*( ten[5]-ten[0] ); /* fz2/T01 */
cft[32] = tsqr[1]-tsqr[4]; /* fz2/T02 */
cft[33] = 0; /* fz2/T11 */
cft[34] = -2*ten[2]*ten[4]+ten[1]*( ten[5]-ten[0] ); /* fz2/T12 */
cft[35] = ten[4]*ten[1]; /* fz2/T22 */
cft[36] = ten[2]*ten[4]; /* fz3/T00 */
cft[37] = tsqr[4]-tsqr[2]; /* fz3/T01 */
cft[38] = -2*ten[1]*ten[2]+ten[4]*( ten[0]-ten[3] ); /* fz3/T02 */
cft[39] = -ten[2]*ten[4]; /* fz3/T11 */
cft[40] = 2*ten[1]*ten[4]+ten[2]*( ten[0]-ten[3] ); /* fz3/T12 */
cft[41] = 0; /* fz3/T22 */
/* approximate Hessian derivative in x dir */
ELL_3M_LERP( hessleft, coord[1], botleft, topleft );
ELL_3M_LERP( hessright, coord[1], botright, topright );
ELL_3M_SUB( hessder, hessright, hessleft );
ELL_3M_SCALE( hessder, 1.0/norm, hessder );
tx[0] = hessder[0]; /* T00 / x */
tx[2] = hessder[1]; /* T01 / x */
tx[4] = hessder[2]; /* T02 / x */
tx[6] = hessder[4]; /* T11 / x */
tx[8] = hessder[5]; /* T12 / x */
tx[10] = hessder[8]; /* T22 / x */
/* approximate Hessian derivative in z dir */
ELL_3M_SUB( hessder, hesstop, hessbot );
ELL_3M_SCALE( hessder, 1.0/norm, hessder );
tx[1] = hessder[0]; /* T00 / z */
tx[3] = hessder[1]; /* T01 / z */
tx[5] = hessder[2]; /* T02 / z */
tx[7] = hessder[4]; /* T11 / z */
tx[9] = hessder[5]; /* T12 / z */
tx[11] = hessder[8]; /* T22 / z */
/* matrix multiply cft*tx */
for ( row=0; row<7; row++ )
for ( col=0; col<2; col++ ) {
i = row*2+col;
cfx[i] = 0;
for ( j=0; j<6; j++ ) {
cfx[i] += cft[row*6+j]*tx[j*2+col];
}
}
for ( i=0; i<3; i++ )
denom[i] = 0;
for ( j=0; j<7; j++ ) {
denom[0] += cfx[j*2]*cfx[j*2];
denom[1] += cfx[j*2+1]*cfx[j*2];
denom[2] += cfx[j*2+1]*cfx[j*2+1];
}
det = denom[0]*denom[2]-denom[1]*denom[1];
if ( fabs( det )<DBL_EPSILON )
return 3;
inv[0] = denom[2] / det;
inv[1] = -denom[1] / det;
inv[2] = denom[0] / det;
/* multiply transpose( cfx )*cf */
nom[0]=0; nom[1]=0;
for ( j=0; j<7; j++ ) {
nom[0] += cfx[j*2] * cf[j];
nom[1] += cfx[j*2+1] * cf[j];
}
/* calculate the coordinate offset dx = inv*nom */
dx[0] = inv[0]*nom[0]+inv[1]*nom[1];
dx[1] = inv[1]*nom[0]+inv[2]*nom[1];
/* employ the Armijo stepsize rule for improved convergence */
dxsqr = dx[0]*dx[0]+dx[1]*dx[1];
while ( !accept && safetyct++<maxct ) {
/* test discriminant at new position */
double newcoord[2];
double newdiscr;
ELL_2V_SET( newcoord, coord[0]-alpha*dx[0], coord[1]-alpha*dx[1] );
if ( newcoord[0]<0 || newcoord[0]>1 ||
newcoord[1]<0 || newcoord[1]>1 ) {
if ( safetyct==maxct )
return 1; /* we left the cell */
alpha*=_gamma;
}
ELL_3M_LERP( hessbot, newcoord[0], botleft, botright );
ELL_3M_LERP( hesstop, newcoord[0], topleft, topright );
ELL_3M_LERP( hess, newcoord[1], hessbot, hesstop );
norm = sqrt( hess[0]*hess[0]+hess[4]*hess[4]+hess[8]*hess[8]+
2*( hess[1]*hess[1]+hess[2]*hess[2]+hess[5]*hess[5] ) );
if ( norm<1e-300 ) return 6;
/* copy over */
ten[0] = hess[0]/norm; ten[1] = hess[1]/norm; ten[2] = hess[2]/norm;
ten[3] = hess[4]/norm; ten[4] = hess[5]/norm; ten[5] = hess[8]/norm;
for ( i=0; i<6; i++ )
tsqr[i] = ten[i]*ten[i];
/* evaluate the constraint function vector */
cf[0] = ten[0]*( tsqr[3]-tsqr[5] )+ten[0]*( tsqr[1]-tsqr[2] )+
ten[3]*( tsqr[5]-tsqr[0] )+ten[3]*( tsqr[4]-tsqr[1] )+
ten[5]*( tsqr[0]-tsqr[3] )+ten[5]*( tsqr[2]-tsqr[4] ); /* fx */
cf[1] = ten[4]*( 2*( tsqr[4]-tsqr[0] )-( tsqr[2]+tsqr[1] )+
2*( ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5] ) )+
ten[1]*ten[2]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1 */
cf[2] = ten[2]*( 2*( tsqr[2]-tsqr[3] )-( tsqr[1]+tsqr[4] )+
2*( ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0] ) )+
ten[4]*ten[1]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2 */
cf[3] = ten[1]*( 2*( tsqr[1]-tsqr[5] )-( tsqr[4]+tsqr[2] )+
2*( ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3] ) )+
ten[2]*ten[4]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3 */
cf[4] = ten[4]*( tsqr[2]-tsqr[1] )+ten[1]*ten[2]*( ten[3]-ten[5] ); /* fz1 */
cf[5] = ten[2]*( tsqr[1]-tsqr[4] )+ten[4]*ten[1]*( ten[5]-ten[0] ); /* fz2 */
cf[6] = ten[1]*( tsqr[4]-tsqr[2] )+ten[2]*ten[4]*( ten[0]-ten[3] ); /* fz3 */
newdiscr = cf[0]*cf[0]+cf[1]*cf[1]+cf[2]*cf[2]+cf[3]*cf[3]+
15*( cf[4]*cf[4]+cf[5]*cf[5]+cf[6]*cf[6] );
if ( newdiscr<eps ) {
coord[0]=newcoord[0]; coord[1]=newcoord[1]; /* update coord! */
if ( type!='l' && type!='p' ) return 0;
else {
/* check if type is correct */
double dev[9];
double mean = ( ten[0]+ten[3]+ten[5] )/3;
dev[0] = ten[0]-mean; dev[1] = ten[1]; dev[2] = ten[2];
dev[3] = ten[1]; dev[4]=ten[3]-mean; dev[5] = ten[4];
dev[6] = ten[2]; dev[7]=ten[4]; dev[8]=ten[5]-mean;
det = ELL_3M_DET( dev );
if ( ( type=='l' && det>0 ) || ( type=='p' && det<0 ) ) return 0;
else return 4; /* sufficient accuracy, but wrong type */
}
}
if ( newdiscr<=discr-0.5*alpha*dxsqr ) {
accept=1;
discr = newdiscr;
} else {
alpha*=_gamma;
}
}
if ( !accept )
return 5;
coord[0] -= alpha*dx[0];
coord[1] -= alpha*dx[1];
}
return 2; /* hit maxiter */
}
/* Descends to the degenerate line in a trilinearly interpolated cell
* using the discriminant constraint functions and the Newton-Raphson
* method with Armijo stepsize control.
*
* This function is NOT part of the crease extraction, but has been
* used for debugging it.
*
* coord are coordinates relative to the given cell and will be
* updated in each iteration.
* Hbfl - Htbr are 9-vectors, representing the symmetric matrices at the
* corners of the face ( input only )
* maxiter is the maximum number of iterations allowed
* eps is the accuracy up to which the discriminant should be zero
* The discriminant scales with the Frobenius norm to the sixth power,
* so the exact constraint is disc/|T|^6<eps
* type can be 'l', 'p' or something else ( =both )
*
* Returns 0 if the point was found up to the given accuracy
* Returns 1 if we left the cell
* Returns 2 if we hit maxiter
* Returns 3 if we could not invert a matrix to find next gradient dir
* Returns 4 if we found a point, but it does not have the desired type
* Returns 5 if Armijo rule failed to find a valid stepsize
* Returns 6 if we hit a zero tensor ( |T|<1e-300 )
*/
352 int seekDescendToDegCell( double *coord, double *Hbfl, double *Hbfr,
double *Hbbl, double *Hbbr,
double *Htfl, double *Htfr, double *Htbl, double *Htbr,
int maxiter, double eps, char type )
{
double discr=0; /* store discriminant value for previous point */
double Hfrontleft[9]={0, 0, 0, 0, 0, 0, 0, 0, 0}, Hbackleft[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
double Hfrontright[9]={0, 0, 0, 0, 0, 0, 0, 0, 0}, Hbackright[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
double Hleft[9]={0, 0, 0, 0, 0, 0, 0, 0, 0}, Hright[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
double H[9]={0, 0, 0, 0, 0, 0, 0, 0, 0}; /* init takes care of compiler warnings */
double optgrad[3]={0.0, 0.0, 0.0}; /* gradient for descent */
int iter=0;
do {
/* on the first run, initialize discr; later, employ the Armijo
* stepsize rule to guarantee convergence */
double beta=1.0;
double _gamma=0.5;
double alpha=beta;
int accept=0;
double optgradsqr = ELL_3V_DOT( optgrad, optgrad );
unsigned int safetyct=0;
const unsigned int maxct=30;
double tsqr[6]={0, 0, 0, 0, 0, 0}, /* only initialize to silence warning */
ten[6]={0, 0, 0, 0, 0, 0}, norm=0;
double cf[7]={0, 0, 0, 0, 0, 0, 0}, /* only initialize to silence warning */
cft[42]; /* derive relative to tensor values, 7x6 matrix */
double cfx[21]; /* spatial derivative of constraint functions, 7x3 matrix */
double tx[18]; /* spatial derivative of tensor values, 6x3 matrix */
double Hder[9], Hfront[9], Hback[9]; /* used to approximate Hessian der. */
double Htopleft[9], Htopright[9], Hbotleft[9], Hbotright[9],
Htop[9], Hbot[9];
double denom[9]; /* 3x3 matrix that is to be inverted */
double inv[9]; /* inverse of that matrix */
double nom[3]={0, 0, 0};
int i, j, row, col; /* counters, used later on */
while ( !accept && safetyct++<maxct ) {
/* compute distance at new position */
double newcoord[3];
double newdiscr;
ELL_3V_SET( newcoord, coord[0]-alpha*optgrad[0],
coord[1]-alpha*optgrad[1], coord[2]-alpha*optgrad[2] );
if ( newcoord[0]<0 || newcoord[0]>1 ||
newcoord[1]<0 || newcoord[1]>1 ||
newcoord[2]<0 || newcoord[2]>1 ) {
if ( safetyct==maxct ) {
ELL_3V_COPY( coord, newcoord ); /* such that caller knows which
dir was the culprit */
return 1; /* we left the cell */
}
alpha*=_gamma;
continue;
}
ELL_3M_LERP( Hfrontleft, newcoord[2], Hbfl, Htfl );
ELL_3M_LERP( Hbackleft, newcoord[2], Hbbl, Htbl );
ELL_3M_LERP( Hleft, newcoord[1], Hfrontleft, Hbackleft );
ELL_3M_LERP( Hfrontright, newcoord[2], Hbfr, Htfr );
ELL_3M_LERP( Hbackright, newcoord[2], Hbbr, Htbr );
ELL_3M_LERP( Hright, newcoord[1], Hfrontright, Hbackright );
ELL_3M_LERP( H, newcoord[0], Hleft, Hright );
norm = sqrt( H[0]*H[0]+H[4]*H[4]+H[8]*H[8]+
2*( H[1]*H[1]+H[2]*H[2]+H[5]*H[5] ) );
if ( norm<1e-300 ) return 6;
ten[0]=H[0]/norm; ten[1]=H[1]/norm; ten[2]=H[2]/norm;
ten[3]=H[4]/norm; ten[4]=H[5]/norm; ten[5]=H[7]/norm;
for ( i=0; i<6; i++ )
tsqr[i] = ten[i]*ten[i];
/* evaluate the constraint function vector */
cf[0] = ten[0]*( tsqr[3]-tsqr[5] )+ten[0]*( tsqr[1]-tsqr[2] )+
ten[3]*( tsqr[5]-tsqr[0] )+
ten[3]*( tsqr[4]-tsqr[1] )+ten[5]*( tsqr[0]-tsqr[3] )+
ten[5]*( tsqr[2]-tsqr[4] ); /* fx */
cf[1] = ten[4]*( 2*( tsqr[4]-tsqr[0] )-( tsqr[2]+tsqr[1] )+
2*( ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5] ) )+
ten[1]*ten[2]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1 */
cf[2] = ten[2]*( 2*( tsqr[2]-tsqr[3] )-( tsqr[1]+tsqr[4] )+
2*( ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0] ) )+
ten[4]*ten[1]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2 */
cf[3] = ten[1]*( 2*( tsqr[1]-tsqr[5] )-( tsqr[4]+tsqr[2] )+
2*( ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3] ) )+
ten[2]*ten[4]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3 */
cf[4] = ten[4]*( tsqr[2]-tsqr[1] )+ten[1]*ten[2]*( ten[3]-ten[5] ); /* fz1 */
cf[5] = ten[2]*( tsqr[1]-tsqr[4] )+ten[4]*ten[1]*( ten[5]-ten[0] ); /* fz2 */
cf[6] = ten[1]*( tsqr[4]-tsqr[2] )+ten[2]*ten[4]*( ten[0]-ten[3] ); /* fz3 */
newdiscr = cf[0]*cf[0]+cf[1]*cf[1]+cf[2]*cf[2]+cf[3]*cf[3]+
15*( cf[4]*cf[4]+cf[5]*cf[5]+cf[6]*cf[6] );
if ( newdiscr<eps ) {
ELL_3V_COPY( coord, newcoord ); /* update coord for output */
if ( type!='l' && type!='p' ) return 0;
else {
/* check if type is correct */
double dev[9], det;
double mean = ( ten[0]+ten[3]+ten[5] )/3;
dev[0] = ten[0]-mean; dev[1] = ten[1]; dev[2] = ten[2];
dev[3] = ten[1]; dev[4]=ten[3]-mean; dev[5] = ten[4];
dev[6] = ten[2]; dev[7]=ten[4]; dev[8]=ten[5]-mean;
det = ELL_3M_DET( dev );
if ( ( type=='l' && det>0 ) || ( type=='p' && det<0 ) ) return 0;
else return 4; /* sufficient accuracy, but wrong type */
}
}
if ( iter==0 || newdiscr<=discr-0.5*alpha*optgradsqr ) {
accept=1;
discr = newdiscr;
} else {
alpha*=_gamma;
}
}
if ( !accept )
return 5; /* could not find a valid stepsize */
coord[0] -= alpha*optgrad[0];
coord[1] -= alpha*optgrad[1];
coord[2] -= alpha*optgrad[2];
if ( iter==maxiter-1 )
break;
/* find derivative of constraint function vector using the chain rule */
cft [0] = tsqr[3]-tsqr[5]+tsqr[1]-tsqr[2]-
2*ten[0]*ten[3]+2*ten[0]*ten[5]; /* fx/T00 */
cft [1] = 2*ten[0]*ten[1]-2*ten[3]*ten[1]; /* fx/T01 */
cft [2] = -2*ten[0]*ten[2]+2*ten[5]*ten[2]; /* fx/T02 */
cft [3] = 2*ten[0]*ten[3]+tsqr[5]-tsqr[0]+tsqr[4]-tsqr[1]-
2*ten[5]*ten[3]; /* fx/T11 */
cft [4] = 2*ten[3]*ten[4]-2*ten[5]*ten[4]; /* fx/T12 */
cft [5] = -2*ten[0]*ten[5]+2*ten[3]*ten[5]+tsqr[0]-tsqr[3]+
tsqr[2]-tsqr[4]; /* fx/T22 */
cft [6] = -4*ten[0]*ten[4]+2*ten[4]*ten[3]+2*ten[4]*ten[5]+
2*ten[1]*ten[2]; /* fy1/T00 */
cft [7] = -2*ten[4]*ten[1]+ten[2]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1/T01 */
cft [8] = -2*ten[4]*ten[2]+ten[1]*( 2*ten[0]-ten[5]-ten[3] ); /* fy1/T02 */
cft [9] = 2*ten[4]*ten[0]-2*ten[4]*ten[5]-ten[1]*ten[2]; /* fy1/T11 */
cft[10] = 6*tsqr[4]-2*tsqr[0]-( tsqr[2]+tsqr[1] )+
2*( ten[3]*ten[0]+ten[5]*ten[0]-ten[3]*ten[5] ); /* fy1/T12 */
cft[11] = 2*ten[4]*ten[0]-2*ten[4]*ten[3]-ten[1]*ten[2]; /* fy1/T22 */
cft[12] = 2*ten[2]*ten[3]-2*ten[2]*ten[5]-ten[4]*ten[1]; /* fy2/T00 */
cft[13] = -2*ten[2]*ten[1]+ten[4]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2/T01 */
cft[14] = 6*tsqr[2]-2*tsqr[3]-( tsqr[1]+tsqr[4] )+
2*( ten[5]*ten[3]+ten[0]*ten[3]-ten[5]*ten[0] ); /* fy2/T02 */
cft[15] = -4*ten[2]*ten[3]+2*ten[2]*ten[5]+2*ten[2]*ten[0]+
2*ten[4]*ten[1]; /* fy2/T11 */
cft[16] = -2*ten[2]*ten[4]+ten[1]*( 2*ten[3]-ten[0]-ten[5] ); /* fy2/T12 */
cft[17] = 2*ten[2]*ten[3]-2*ten[2]*ten[0]-ten[4]*ten[1]; /* fy2/T22 */
cft[18] = 2*ten[1]*ten[5]-2*ten[1]*ten[3]-ten[2]*ten[4]; /* fy3/T00 */
cft[19] = 6*tsqr[1]-2*tsqr[5]-( tsqr[4]+tsqr[2] )+
2*( ten[0]*ten[5]+ten[3]*ten[5]-ten[0]*ten[3] ); /* fy3/T01 */
cft[20] = -2*ten[1]*ten[2]+ten[4]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3/T02 */
cft[21] = 2*ten[1]*ten[5]-2*ten[1]*ten[0]-ten[2]*ten[4]; /* fy3/T11 */
cft[22] = -2*ten[1]*ten[4]+ten[2]*( 2*ten[5]-ten[3]-ten[0] ); /* fy3/T12 */
cft[23] = -4*ten[1]*ten[5]+2*ten[0]*ten[1]+2*ten[1]*ten[3]+
2*ten[2]*ten[4]; /* fy3/T22 */
cft[24] = 0; /* fz1/T00 */
cft[25] = -2*ten[4]*ten[1]+ten[2]*( ten[3]-ten[5] ); /* fz1/T01 */
cft[26] = 2*ten[4]*ten[2]+ten[1]*( ten[3]-ten[5] ); /* fz1/T02 */
cft[27] = ten[1]*ten[2]; /* fz1/T11 */
cft[28] = tsqr[2]-tsqr[1]; /* fz1/T12 */
cft[29] = -ten[1]*ten[2]; /* fz1/T22 */
cft[30] = -ten[4]*ten[1]; /* fz2/T00 */
cft[31] = 2*ten[2]*ten[1]+ten[4]*( ten[5]-ten[0] ); /* fz2/T01 */
cft[32] = tsqr[1]-tsqr[4]; /* fz2/T02 */
cft[33] = 0; /* fz2/T11 */
cft[34] = -2*ten[2]*ten[4]+ten[1]*( ten[5]-ten[0] ); /* fz2/T12 */
cft[35] = ten[4]*ten[1]; /* fz2/T22 */
cft[36] = ten[2]*ten[4]; /* fz3/T00 */
cft[37] = tsqr[4]-tsqr[2]; /* fz3/T01 */
cft[38] = -2*ten[1]*ten[2]+ten[4]*( ten[0]-ten[3] ); /* fz3/T02 */
cft[39] = -ten[2]*ten[4]; /* fz3/T11 */
cft[40] = 2*ten[1]*ten[4]+ten[2]*( ten[0]-ten[3] ); /* fz3/T12 */
cft[41] = 0; /* fz3/T22 */
/* approximate Hessian derivative in x dir */
ELL_3M_SUB( Hder, Hright, Hleft );
ELL_3M_SCALE( Hder, 1.0/norm, Hder );
tx[0] = Hder[0]; /* T00 / x */
tx[3] = Hder[1]; /* T01 / x */
tx[6] = Hder[2]; /* T02 / x */
tx[9] = Hder[4]; /* T11 / x */
tx[12] = Hder[5]; /* T12 / x */
tx[15] = Hder[8]; /* T22 / x */
ELL_3M_LERP( Hfront, coord[0], Hfrontleft, Hfrontright );
ELL_3M_LERP( Hback, coord[0], Hbackleft, Hbackright );
ELL_3M_SUB( Hder, Hback, Hfront ); /* y dir */
ELL_3M_SCALE( Hder, 1.0/norm, Hder );
tx[1] = Hder[0]; /* T00 / y */
tx[4] = Hder[1]; /* T01 / y */
tx[7] = Hder[2]; /* T02 / y */
tx[10] = Hder[4]; /* T11 / y */
tx[13] = Hder[5]; /* T12 / y */
tx[16] = Hder[8]; /* T22 / y */
/* approximate Hessian derivative in z dir */
ELL_3M_LERP( Htopleft, coord[1], Htfl, Htbl );
ELL_3M_LERP( Htopright, coord[1], Htfr, Htbr );
ELL_3M_LERP( Hbotleft, coord[1], Hbfl, Hbbl );
ELL_3M_LERP( Hbotright, coord[1], Hbfr, Hbbr );
ELL_3M_LERP( Htop, coord[0], Htopleft, Htopright );
ELL_3M_LERP( Hbot, coord[0], Hbotleft, Hbotright );
ELL_3M_SUB( Hder, Htop, Hbot ); /* z dir */
ELL_3M_SCALE( Hder, 1.0/norm, Hder );
tx[2] = Hder[0]; /* T00 / z */
tx[5] = Hder[1]; /* T01 / z */
tx[8] = Hder[2]; /* T02 / z */
tx[11] = Hder[4]; /* T11 / z */
tx[14] = Hder[5]; /* T12 / z */
tx[17] = Hder[8]; /* T22 / z */
/* matrix multiply cft*tx */
for ( row=0; row<7; row++ )
for ( col=0; col<3; col++ ) {
i = row*3+col;
cfx[i] = 0;
for ( j=0; j<6; j++ ) {
cfx[i] += cft[row*6+j]*tx[j*3+col];
}
}
for ( row=0; row<3; row++ )
for ( col=0; col<3; col++ ) {
i = row*3+col;
denom[i] = 0;
for ( j=0; j<7; j++ ) {
denom[i] += cfx[j*3+row]*cfx[j*3+col];
}
}
ell_3m_inv_d( inv, denom );
/* multiply transpose( cfx )*cf */
for ( j=0; j<7; j++ ) {
nom[0] += cfx[j*3] * cf[j];
nom[1] += cfx[j*3+1]* cf[j];
nom[2] += cfx[j*3+2]* cf[j];
}
/* compute new optgrad = inv*nom */
ELL_3MV_MUL( optgrad, inv, nom );
} while ( iter++<maxiter );
return 2; /* hit maxiter */
}
/* Gradient descent to a point on a crease surface
*
* NOT used as part of the crease extraction, only for debugging
*
* coord are coordinates relative to the given cell and will be
* updated in each iteration.
* Hbfl - Htbr are 9-vectors, representing the Hessian matrices at the
* corners of the face ( input only )
* gbfl - gbbr are 3-vectors, representing the gradient directions at the
* corners of the face ( input only )
* maxiter is the maximum number of iterations allowed
* eps is the accuracy up to which |h| ( |Tg-g|, cf. paper ) must be zero
* ridge is non-zero if we are looking for a ridge ( zero for valley )
*
* Returns 0 if the point was found up to the given accuracy
* Returns 1 if we left the cell
* Returns 2 if we hit maxiter
* Returns 3 if Armijo rule failed to find a valid stepsize
*/
635 int seekDescendToRidge( double *coord,
double *Hbfl, double *gbfl, double *Hbfr, double *gbfr,
double *Hbbl, double *gbbl, double *Hbbr, double *gbbr,
double *Htfl, double *gtfl, double *Htfr, double *gtfr,
double *Htbl, double *gtbl, double *Htbr, double *gtbr,
int maxiter, double eps, char ridge,
const double evalDiffThresh ) {
double dist=0; /* store distance value of previous iteration */
double Hfrontleft[9], Hbackleft[9];
double Hfrontright[9], Hbackright[9];
double Hleft[9], Hright[9];
double H[9], evals[3], evecs[9], T[9];
double gfrontleft[3], gbackleft[3];
double gfrontright[3], gbackright[3];
double gleft[3], gright[3];
double g[3];
double optgrad[3]={0.0, 0.0, 0.0}; /* gradient for descent */
int iter=0;
do {
double Tg[3];
/* on the first run, initialize dist; later, employ the Armijo
* stepsize rule to guarantee convergence */
double beta=0.1;
double _gamma=0.5;
double alpha=beta;
int accept=0;
double optgradsqr = ELL_3V_DOT( optgrad, optgrad );
int safetyct=0;
int maxct=30; /* avoid infinite loops when finding stepsize */
/* variables used to compute the next step */
double Hder[9], gder[3], Tder[9];
double Tpg[3], Tgp[3];
double Hfront[9], Hback[9], gfront[3], gback[3];
double Htopleft[9], Htopright[9], Hbotleft[9], Hbotright[9],
gtopleft[3], gtopright[3], gbotleft[3], gbotright[3],
Htop[9], Hbot[9], gtop[3], gbot[3];
while ( !accept && safetyct++<maxct ) {
/* compute distance at new position */
double newcoord[3];
double diff[3], newdist;
ELL_3V_SET( newcoord, coord[0]-alpha*optgrad[0],
coord[1]-alpha*optgrad[1], coord[2]-alpha*optgrad[2] );
if ( newcoord[0]<0 || newcoord[0]>1 ||
newcoord[1]<0 || newcoord[1]>1 ||
newcoord[2]<0 || newcoord[2]>1 ) {
if ( safetyct==maxct )
return 1; /* we left the cell */
alpha*=_gamma;
}
ELL_3M_LERP( Hfrontleft, newcoord[2], Hbfl, Htfl );
ELL_3M_LERP( Hbackleft, newcoord[2], Hbbl, Htbl );
ELL_3M_LERP( Hleft, newcoord[1], Hfrontleft, Hbackleft );
ELL_3M_LERP( Hfrontright, newcoord[2], Hbfr, Htfr );
ELL_3M_LERP( Hbackright, newcoord[2], Hbbr, Htbr );
ELL_3M_LERP( Hright, newcoord[1], Hfrontright, Hbackright );
ELL_3M_LERP( H, newcoord[0], Hleft, Hright );
ell_3m_eigensolve_d( evals, evecs, H, AIR_TRUE );
_seekHess2T( T, evals, evecs, evalDiffThresh, ridge );
ELL_3V_LERP( gfrontleft, newcoord[2], gbfl, gtfl );
ELL_3V_LERP( gbackleft, newcoord[2], gbbl, gtbl );
ELL_3V_LERP( gleft, newcoord[1], gfrontleft, gbackleft );
ELL_3V_LERP( gfrontright, newcoord[2], gbfr, gtfr );
ELL_3V_LERP( gbackright, newcoord[2], gbbr, gtbr );
ELL_3V_LERP( gright, newcoord[1], gfrontright, gbackright );
ELL_3V_LERP( g, newcoord[0], gleft, gright );
ell_3mv_mul_d( Tg, T, g );
ELL_3V_SUB( diff, Tg, g );
newdist = ELL_3V_DOT( diff, diff );
if ( newdist<eps ) {
ELL_3V_COPY( coord, newcoord ); /* update for output */
return 0; /* we are on the surface */
}
if ( iter==0 || newdist<=dist-0.5*alpha*optgradsqr ) {
accept=1;
dist = newdist;
} else {
alpha*=_gamma;
}
}
if ( !accept )
return 3; /* could not find a valid stepsize */
coord[0] -= alpha*optgrad[0];
coord[1] -= alpha*optgrad[1];
coord[2] -= alpha*optgrad[2];
if ( iter==maxiter-1 )
break;
/* compute a new optgrad from derivatives of T and g */
ELL_3V_SUB( gder, gright, gleft ); /* x dir */
ELL_3M_SUB( Hder, Hright, Hleft );
_seekHessder2Tder( Tder, Hder, evals, evecs, evalDiffThresh, ridge );
ell_3mv_mul_d( Tpg, Tder, g );
ell_3mv_mul_d( Tgp, T, gder );
optgrad[0]=ELL_3V_DOT( Tpg, Tg )+ELL_3V_DOT( Tgp, Tg )-
ELL_3V_DOT( Tpg, g )-ELL_3V_DOT( Tgp, g )-
ELL_3V_DOT( Tg, gder )+ELL_3V_DOT( g, gder );
ELL_3M_LERP( Hfront, coord[0], Hfrontleft, Hfrontright );
ELL_3M_LERP( Hback, coord[0], Hbackleft, Hbackright );
ELL_3M_SUB( Hder, Hback, Hfront ); /* y dir */
_seekHessder2Tder( Tder, Hder, evals, evecs, evalDiffThresh, ridge );
ELL_3V_LERP( gfront, coord[0], gfrontleft, gfrontright );
ELL_3V_LERP( gback, coord[0], gbackleft, gbackright );
ELL_3V_SUB( gder, gback, gfront );
ell_3mv_mul_d( Tpg, Tder, g );
ell_3mv_mul_d( Tgp, T, gder );
optgrad[1]=ELL_3V_DOT( Tpg, Tg )+ELL_3V_DOT( Tgp, Tg )-
ELL_3V_DOT( Tpg, g )-ELL_3V_DOT( Tgp, g )-
ELL_3V_DOT( Tg, gder )+ELL_3V_DOT( g, gder );
ELL_3M_LERP( Htopleft, coord[1], Htfl, Htbl );
ELL_3M_LERP( Htopright, coord[1], Htfr, Htbr );
ELL_3M_LERP( Hbotleft, coord[1], Hbfl, Hbbl );
ELL_3M_LERP( Hbotright, coord[1], Hbfr, Hbbr );
ELL_3M_LERP( Htop, coord[0], Htopleft, Htopright );
ELL_3M_LERP( Hbot, coord[0], Hbotleft, Hbotright );
ELL_3M_SUB( Hder, Htop, Hbot ); /* z dir */
_seekHessder2Tder( Tder, Hder, evals, evecs, evalDiffThresh, ridge );
ELL_3V_LERP( gtopleft, coord[1], gtfl, gtbl );
ELL_3V_LERP( gtopright, coord[1], gtfr, gtbr );
ELL_3V_LERP( gbotleft, coord[1], gbfl, gbbl );
ELL_3V_LERP( gbotright, coord[1], gbfr, gbbr );
ELL_3V_LERP( gtop, coord[0], gtopleft, gtopright );
ELL_3V_LERP( gbot, coord[0], gbotleft, gbotright );
ELL_3V_SUB( gder, gtop, gbot );
ell_3mv_mul_d( Tpg, Tder, g );
ell_3mv_mul_d( Tgp, T, gder );
optgrad[2]=ELL_3V_DOT( Tpg, Tg )+ELL_3V_DOT( Tgp, Tg )-
ELL_3V_DOT( Tpg, g )-ELL_3V_DOT( Tgp, g )-
ELL_3V_DOT( Tg, gder )+ELL_3V_DOT( g, gder );
} while ( iter++<maxiter );
return 2; /* hit maxiter */
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
const char *
seekBiffKey = "seek";
const char *
_seekTypeStr[SEEK_TYPE_MAX+1] = {
"( unknown_feature )",
"isocontour",
"ridge surface",
"valley surface",
"ridge line",
"valley line",
"minimal surface",
"maximal surface",
"OP ridge surface",
"T ridge surface",
"OP valley surface",
"T valley surface"
};
const char *
_seekTypeDesc[SEEK_TYPE_MAX+1] = {
"unknown_feature",
"standard marching cubes surface",
"ridge surface",
"valley surface",
"ridge line",
"valley line",
"minimal surface",
"maximal surface",
"ridge surface using outer product rule",
"ridge surface using tensor T",
"valley surface using outer product rule",
"valley surface using tensor T"
};
const char *
_seekTypeStrEqv[] = {
"isocontour",
"ridge surface", "ridgesurface", "rs",
"valley surface", "valleysurface", "vs",
"ridge line", "ridgeline", "rl",
"valley line", "valleyline", "vl",
"minimal surface", "mins",
"maximal surface", "maxs",
"OP ridge surface", "ridgesurfaceop", "rsop",
"T ridge surface", "ridgesurfacet", "rst",
"OP valley surface", "valleysurfaceop", "vsop",
"T valley surface", "valleysurfacet", "vst",
""
};
const int
_seekTypeValEqv[] = {
seekTypeIsocontour,
seekTypeRidgeSurface, seekTypeRidgeSurface, seekTypeRidgeSurface,
seekTypeValleySurface, seekTypeValleySurface, seekTypeValleySurface,
seekTypeRidgeLine, seekTypeRidgeLine, seekTypeRidgeLine,
seekTypeValleyLine, seekTypeValleyLine, seekTypeValleyLine,
seekTypeMinimalSurface, seekTypeMinimalSurface,
seekTypeMaximalSurface, seekTypeMaximalSurface,
seekTypeRidgeSurfaceOP, seekTypeRidgeSurfaceOP, seekTypeRidgeSurfaceOP,
seekTypeRidgeSurfaceT, seekTypeRidgeSurfaceT, seekTypeRidgeSurfaceT,
seekTypeValleySurfaceOP, seekTypeValleySurfaceOP, seekTypeValleySurfaceOP,
seekTypeValleySurfaceT, seekTypeValleySurfaceT, seekTypeValleySurfaceT
};
const airEnum
_seekType = {
"format",
SEEK_TYPE_MAX,
_seekTypeStr, NULL,
_seekTypeDesc,
_seekTypeStrEqv, _seekTypeValEqv,
AIR_FALSE
};
101 const airEnum *const
seekType = &_seekType;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
#include "privateSeek.h"
static baggage *
28 baggageNew( seekContext *sctx ) {
baggage *bag;
unsigned int sx;
bag = AIR_CALLOC( 1, baggage );
/* this is basically the mapping from the 12 edges on each voxel to
the 5 unique edges for each sample on the slab, based on the lay-out
defined in the beginning of tables.c */
sx = AIR_CAST( unsigned int, sctx->sx );
/* X Y */
bag->evti[ 0] = 0 + 5*( 0 + sx*0 );
bag->evti[ 1] = 1 + 5*( 0 + sx*0 );
bag->evti[ 2] = 1 + 5*( 1 + sx*0 );
bag->evti[ 3] = 0 + 5*( 0 + sx*1 );
bag->evti[ 4] = 2 + 5*( 0 + sx*0 );
bag->evti[ 5] = 2 + 5*( 1 + sx*0 );
bag->evti[ 6] = 2 + 5*( 0 + sx*1 );
bag->evti[ 7] = 2 + 5*( 1 + sx*1 );
bag->evti[ 8] = 3 + 5*( 0 + sx*0 );
bag->evti[ 9] = 4 + 5*( 0 + sx*0 );
bag->evti[10] = 4 + 5*( 1 + sx*0 );
bag->evti[11] = 3 + 5*( 0 + sx*1 );
switch ( sctx->type ) {
case seekTypeRidgeSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeRidgeSurfaceT:
bag->esIdx = 2;
bag->modeSign = -1;
break;
case seekTypeValleySurface:
case seekTypeValleySurfaceOP:
case seekTypeValleySurfaceT:
bag->esIdx = 0;
bag->modeSign = +1;
break;
case seekTypeMaximalSurface:
bag->esIdx = 0;
bag->modeSign = -1;
break;
case seekTypeMinimalSurface:
bag->esIdx = 2;
bag->modeSign = +1;
break;
default:
/* biffAddf( SEEK, "%s: feature type %s not handled", me,
airEnumStr( seekType, sctx->type ) );
return 1;
*/
/* without biff, we get as nasty as possible */
bag->esIdx = UINT_MAX;
bag->modeSign = 0;
break;
}
if ( seekTypeIsocontour == sctx->type ) {
if ( sctx->ninscl ) {
bag->scllup = nrrdDLookup[sctx->ninscl->type];
bag->scldata = sctx->ninscl->data;
} else {
bag->scllup = nrrdDLookup[sctx->nsclDerived->type];
bag->scldata = sctx->nsclDerived->data;
}
} else {
bag->scllup = NULL;
bag->scldata = NULL;
}
bag->xyzwArr = NULL;
bag->normArr = NULL;
bag->indxArr = NULL;
return bag;
}
static baggage *
104 baggageNix( baggage *bag ) {
if ( bag ) {
airArrayNix( bag->normArr );
airArrayNix( bag->xyzwArr );
airArrayNix( bag->indxArr );
airFree( bag );
}
return NULL;
}
static int
117 outputInit( seekContext *sctx, baggage *bag, limnPolyData *lpld ) {
static const char me[]="outputInit";
unsigned int estVertNum, estFaceNum, minI, maxI, valI, *spanHist;
airPtrPtrUnion appu;
int E;
if ( seekTypeIsocontour == sctx->type
&& AIR_IN_OP( sctx->range->min, sctx->isovalue, sctx->range->max ) ) {
unsigned int estVoxNum=0;
/* estimate number of voxels, faces, and vertices involved */
spanHist = AIR_CAST( unsigned int*, sctx->nspanHist->data );
valI = airIndex( sctx->range->min, sctx->isovalue, sctx->range->max,
sctx->spanSize );
for ( minI=0; minI<=valI; minI++ ) {
for ( maxI=valI; maxI<sctx->spanSize; maxI++ ) {
estVoxNum += spanHist[minI + sctx->spanSize*maxI];
}
}
estVertNum = AIR_CAST( unsigned int, estVoxNum*( sctx->vertsPerVoxel ) );
estFaceNum = AIR_CAST( unsigned int, estVoxNum*( sctx->facesPerVoxel ) );
if ( sctx->verbose ) {
fprintf( stderr, "%s: estimated vox --> vert, face: %u --> %u, %u\n", me,
estVoxNum, estVertNum, estFaceNum );
}
} else {
estVertNum = 0;
estFaceNum = 0;
}
/* need something non-zero so that pre-allocations below aren't no-ops */
estVertNum = AIR_MAX( 1, estVertNum );
estFaceNum = AIR_MAX( 1, estFaceNum );
/* initialize limnPolyData with estimated # faces and vertices */
/* we will manage the innards of the limnPolyData entirely ourselves */
if ( limnPolyDataAlloc( lpld, 0, 0, 0, 0 ) ) {
biffAddf( SEEK, "%s: trouble emptying given polydata", me );
return 1;
}
bag->xyzwArr = airArrayNew( ( appu.f = &( lpld->xyzw ), appu.v ),
&( lpld->xyzwNum ),
4*sizeof( float ), sctx->pldArrIncr );
if ( sctx->normalsFind ) {
bag->normArr = airArrayNew( ( appu.f = &( lpld->norm ), appu.v ),
&( lpld->normNum ),
3*sizeof( float ), sctx->pldArrIncr );
} else {
bag->normArr = NULL;
}
bag->indxArr = airArrayNew( ( appu.ui = &( lpld->indx ), appu.v ),
&( lpld->indxNum ),
sizeof( unsigned int ), sctx->pldArrIncr );
lpld->primNum = 1; /* for now, its just triangle soup */
lpld->type = AIR_CALLOC( lpld->primNum, unsigned char );
lpld->icnt = AIR_CALLOC( lpld->primNum, unsigned int );
lpld->type[0] = limnPrimitiveTriangles;
lpld->icnt[0] = 0; /* incremented below */
E = 0;
airArrayLenPreSet( bag->xyzwArr, estVertNum );
E |= !( bag->xyzwArr->data );
if ( sctx->normalsFind ) {
airArrayLenPreSet( bag->normArr, estVertNum );
E |= !( bag->normArr->data );
}
airArrayLenPreSet( bag->indxArr, 3*estFaceNum );
E |= !( bag->indxArr->data );
if ( E ) {
biffAddf( SEEK, "%s: couldn't pre-allocate contour geometry ( %p %p %p )", me,
bag->xyzwArr->data,
( sctx->normalsFind ? bag->normArr->data : NULL ),
bag->indxArr->data );
return 1;
}
/* initialize output summary info */
sctx->voxNum = 0;
sctx->vertNum = 0;
sctx->faceNum = 0;
return 0;
}
static double
200 sclGet( seekContext *sctx, baggage *bag,
unsigned int xi, unsigned int yi, unsigned int zi ) {
zi = AIR_MIN( sctx->sz-1, zi );
return bag->scllup( bag->scldata, xi + sctx->sx*( yi + sctx->sy*zi ) );
}
void
208 _seekIdxProbe( seekContext *sctx, baggage *bag,
double xi, double yi, double zi ) {
double idxOut[4], idxIn[4];
AIR_UNUSED( bag );
ELL_4V_SET( idxOut, xi, yi, zi, 1 );
ELL_4MV_MUL( idxIn, sctx->txfIdx, idxOut );
ELL_4V_HOMOG( idxIn, idxIn );
gageProbe( sctx->gctx, idxIn[0], idxIn[1], idxIn[2] );
return;
}
/*
** this is one of the few things that has to operate on more than one
** zi plane at once, and it is honestly probably the primary motivation
** for putting zi into the baggage.
**
** NOTE: this is doing some bounds ( on the positive x, y, z edges of the
** volume ) that probably should be done closer to the caller
*/
static int
229 evecFlipProbe( seekContext *sctx, baggage *bag,
signed char *flip, /* OUTPUT HERE */
unsigned int xi, unsigned int yi, unsigned int ziOff,
unsigned int dx, unsigned int dy, unsigned int dz ) {
static const char me[]="evecFlipProbe";
unsigned int sx, sy, sz;
double u, du, dot, wantDot, minDu, mode;
double current[3], next[3], posNext[3], posA[3], posB[3], evecA[3], evecB[3];
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
sz = AIR_CAST( unsigned int, sctx->sz );
if ( !( xi + dx < sx
&& yi + dy < sy
&& bag->zi + ziOff + dz < sz ) ) {
/* the edge we're being asked about is outside the volume */
*flip = 0;
return 0;
}
/* Note: Strength checking is no longer performed here.
* TS 2009-08-18 */
/* this edge is in bounds */
ELL_3V_SET( posA, xi, yi, bag->zi+ziOff );
ELL_3V_SET( posB, xi+dx, yi+dy, bag->zi+ziOff+dz );
ELL_3V_COPY( evecA, sctx->evec
+ 3*( bag->esIdx + 3*( ziOff + 2*( xi + sx*yi ) ) ) );
ELL_3V_COPY( evecB, sctx->evec
+ 3*( bag->esIdx + 3*( ziOff+dz + 2*( xi+dx + sx*( yi+dy ) ) ) ) );
#define SETNEXT( uu ) \
ELL_3V_SCALE_ADD2( posNext, 1.0-( uu ), posA, ( uu ), posB ); \
_seekIdxProbe( sctx, bag, posNext[0], posNext[1], posNext[2] ); \
ELL_3V_COPY( next, sctx->evecAns + 3*bag->esIdx ); \
if ( ELL_3V_DOT( current, next ) < 0 ) { \
ELL_3V_SCALE( next, -1, next ); \
} \
dot = ELL_3V_DOT( current, next ); \
mode = bag->modeSign*airMode3_d( sctx->evalAns );
ELL_3V_COPY( current, evecA );
u = 0;
du = 0.49999;
wantDot = 0.9; /* between cos( pi/6 ) and cos( pi/8 ) */
minDu = 0.0002;
while ( u + du < 1.0 ) {
SETNEXT( u+du );
/* Note: This was set to -0.8 in the original code. Again, I found
* that increasing it could eliminate spurious holes in the
* mesh. TS 2009-08-18 */
if ( mode < -0.99 ) {
/* sorry, eigenvalue mode got too close to 2nd order isotropy */
*flip = 0;
return 0;
}
while ( dot < wantDot ) {
/* angle between current and next is too big; reduce step */
du /= 2;
if ( du < minDu ) {
fprintf( stderr, "%s: evector wild @ u=%g: du=%g < minDu=%g; "
"dot=%g; mode = %g; "
"( xi, yi, zi )=( %u, %u, %u+%u ); ( dx, dy, dz )=( %u, %u, %u ) ",
me, u, du, minDu,
dot, mode,
xi, yi, bag->zi, ziOff, dx, dy, dz );
*flip = 0;
return 0;
}
SETNEXT( u+du );
if ( mode < -0.99 ) {
/* sorry, eigenvalue mode got too close to 2nd order isotropy */
*flip = 0;
return 0;
}
}
/* current and next have a small angle between them */
ELL_3V_COPY( current, next );
u += du;
}
/* last fake iteration, to check endpoint explicitly */
u = 1.0;
SETNEXT( u );
if ( dot < wantDot ) {
biffAddf( SEEK, "%s: confused at end of edge", me );
return 1;
}
ELL_3V_COPY( current, next );
#undef SETNEXT
/* we have now tracked the eigenvector along the edge */
dot = ELL_3V_DOT( current, evecB );
*flip = ( dot > 0
? +1
: -1 );
return 0;
}
/*
331 ** !!! this has to be done as a separate second pass because of how
** !!! the flip quantities correspond to the edges between voxels
**
** For efficiency, evecFlipProbe is only executed on request ( by
** setting sctx->treated: 0x01 requests all edges of this voxel, 0x02
** states that unique edge 3 was treated, 0x04 for unique edge 4 ) TS
*/
static int
evecFlipShuffleProbe( seekContext *sctx, baggage *bag ) {
static const char me[]="evecFlipShuffleProbe";
unsigned int xi, yi, sx, sy, si;
signed char flipA, flipB, flipC;
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
/* NOTE: these have to go all the way to sy-1 and sx-1, instead of
sy-2 and sx-2 ( like shuffleProbe( ) below ) because of the need to
set the flips on the edges at the positive X and Y volume
boundary. The necessary bounds checking happens in
evecFlipProbe( ): messy, I know */
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
/* ================================================= */
if ( sctx->treated[si]&0x02 ) { /* has been treated, just copy result */
sctx->flip[0 + 5*si] = sctx->flip[3 + 5*si];
} else if ( sctx->treated[si]&0x01 ||
( yi!=0 && sctx->treated[xi+sx*( yi-1 )]&0x01 ) ) {
/* need to treat this */
if ( evecFlipProbe( sctx, bag, &flipA, xi, yi, 0, 1, 0, 0 ) ) {
biffAddf( SEEK, "%s: problem at ( xi, yi ) = ( %u, %u ), zi=0", me, xi, yi );
return 1;
}
sctx->flip[0 + 5*si] = flipA;
}
if ( sctx->treated[si]&0x04 ) { /* has been treated, just copy */
sctx->flip[1 + 5*si] = sctx->flip[4 + 5*si];
} else if ( sctx->treated[si]&0x01 ||
( xi!=0 && sctx->treated[xi-1+sx*yi]&0x01 ) ) {
if ( evecFlipProbe( sctx, bag, &flipB, xi, yi, 0, 0, 1, 0 ) ) {
biffAddf( SEEK, "%s: problem at ( xi, yi ) = ( %u, %u ), zi=0", me, xi, yi );
return 1;
}
sctx->flip[1 + 5*si] = flipB;
}
if ( sctx->treated[si]&0x01 || ( xi!=0 && sctx->treated[xi-1+sx*yi]&0x01 ) ||
( yi!=0 && sctx->treated[xi+sx*( yi-1 )]&0x01 ) ||
( xi!=0 && yi!=0 && sctx->treated[xi-1+sx*( yi-1 )]&0x01 ) ) {
if ( evecFlipProbe( sctx, bag, &flipA, xi, yi, 0, 0, 0, 1 ) ) {
biffAddf( SEEK, "%s: problem at ( xi, yi, zi ) = ( %u, %u, %u )", me,
xi, yi, bag->zi );
return 1;
}
sctx->flip[2 + 5*si] = flipA;
}
if ( sctx->treated[si]&0x01 ||
( yi!=0 && sctx->treated[xi+sx*( yi-1 )]&0x01 ) ) {
if ( evecFlipProbe( sctx, bag, &flipB, xi, yi, 1, 1, 0, 0 ) ) {
biffAddf( SEEK, "%s: problem at ( xi, yi, zi ) = ( %u, %u, %u )", me,
xi, yi, bag->zi );
return 1;
}
sctx->flip[3 + 5*si] = flipB;
sctx->treated[si]|=0x02;
} else
sctx->treated[si]&=0xFD;
if ( sctx->treated[si]&0x01 ||
( xi!=0 && sctx->treated[xi-1+sx*yi]&0x01 ) ) {
if ( evecFlipProbe( sctx, bag, &flipC, xi, yi, 1, 0, 1, 0 ) ) {
biffAddf( SEEK, "%s: problem at ( xi, yi, zi ) = ( %u, %u, %u )", me,
xi, yi, bag->zi );
return 1;
}
sctx->flip[4 + 5*si] = flipC;
sctx->treated[si]|=0x04;
} else
sctx->treated[si]&=0xFB;
409 /* ================================================= */
}
}
return 0;
}
static int
shuffleProbe( seekContext *sctx, baggage *bag ) {
static const char me[]="shuffleProbe";
unsigned int xi, yi, sx, sy, si, spi;
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
if ( !sctx->strengthUse ) { /* just request all edges */
memset( sctx->treated, 0x01, sizeof( char )*sctx->sx*sctx->sy );
} else {
if ( !bag->zi ) {
/* clear full treated array */
memset( sctx->treated, 0, sizeof( char )*sctx->sx*sctx->sy );
} else {
/* only clear requests for edge orientation */
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
sctx->treated[xi+sx*yi] &= 0xFE;
}
}
}
}
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
spi = ( xi+1 ) + ( sx+2 )*( yi+1 );
/* ================================================= */
if ( !bag->zi ) {
/* ----------------- set/probe bottom of initial slab */
sctx->vidx[0 + 5*si] = -1;
sctx->vidx[1 + 5*si] = -1;
if ( sctx->gctx ) { /* HEY: need this check, what's the right way? */
_seekIdxProbe( sctx, bag, xi, yi, 0 );
}
if ( sctx->strengthUse ) {
sctx->stng[0 + 2*si] = sctx->strengthSign*sctx->stngAns[0];
if ( !si ) {
sctx->strengthSeenMax = sctx->stng[0 + 2*si];
}
sctx->strengthSeenMax = AIR_MAX( sctx->strengthSeenMax,
sctx->stng[0 + 2*si] );
}
switch ( sctx->type ) {
case seekTypeIsocontour:
sctx->sclv[0 + 4*spi] = ( sclGet( sctx, bag, xi, yi, 0 )
- sctx->isovalue );
sctx->sclv[1 + 4*spi] = ( sclGet( sctx, bag, xi, yi, 0 )
- sctx->isovalue );
sctx->sclv[2 + 4*spi] = ( sclGet( sctx, bag, xi, yi, 1 )
- sctx->isovalue );
break;
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMaximalSurface:
case seekTypeMinimalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeValleySurfaceOP:
ELL_3V_COPY( sctx->grad + 3*( 0 + 2*si ), sctx->gradAns );
ELL_3V_COPY( sctx->eval + 3*( 0 + 2*si ), sctx->evalAns );
ELL_3M_COPY( sctx->evec + 9*( 0 + 2*si ), sctx->evecAns );
break;
}
} else {
/* ------------------- shuffle to bottom from top of slab */
sctx->vidx[0 + 5*si] = sctx->vidx[3 + 5*si];
sctx->vidx[1 + 5*si] = sctx->vidx[4 + 5*si];
if ( sctx->strengthUse ) {
sctx->stng[0 + 2*si] = sctx->stng[1 + 2*si];
}
switch ( sctx->type ) {
case seekTypeIsocontour:
sctx->sclv[0 + 4*spi] = sctx->sclv[1 + 4*spi];
sctx->sclv[1 + 4*spi] = sctx->sclv[2 + 4*spi];
sctx->sclv[2 + 4*spi] = sctx->sclv[3 + 4*spi];
break;
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMaximalSurface:
case seekTypeMinimalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeValleySurfaceOP:
ELL_3V_COPY( sctx->grad + 3*( 0 + 2*si ), sctx->grad + 3*( 1 + 2*si ) );
ELL_3V_COPY( sctx->eval + 3*( 0 + 2*si ), sctx->eval + 3*( 1 + 2*si ) );
ELL_3M_COPY( sctx->evec + 9*( 0 + 2*si ), sctx->evec + 9*( 1 + 2*si ) );
break;
}
}
/* ----------------------- set/probe top of slab */
sctx->vidx[2 + 5*si] = -1;
sctx->vidx[3 + 5*si] = -1;
sctx->vidx[4 + 5*si] = -1;
if ( sctx->gctx ) { /* HEY: need this check, what's the right way? */
_seekIdxProbe( sctx, bag, xi, yi, bag->zi+1 );
}
if ( sctx->strengthUse ) {
sctx->stng[1 + 2*si] = sctx->strengthSign*sctx->stngAns[0];
sctx->strengthSeenMax = AIR_MAX( sctx->strengthSeenMax,
sctx->stng[1 + 2*si] );
if ( sctx->stng[0+2*si]>sctx->strength ||
sctx->stng[1+2*si]>sctx->strength ) {
/* mark up to four voxels as needed */
sctx->treated[si] |= 0x01;
if ( xi!=0 ) sctx->treated[xi-1+sx*yi] |= 0x01;
if ( yi!=0 ) sctx->treated[xi+sx*( yi-1 )] |= 0x01;
if ( xi!=0 && yi!=0 ) sctx->treated[xi-1+sx*( yi-1 )] |= 0x01;
}
}
switch ( sctx->type ) {
case seekTypeIsocontour:
sctx->sclv[3 + 4*spi] = ( sclGet( sctx, bag, xi, yi, bag->zi+2 )
- sctx->isovalue );
break;
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMaximalSurface:
case seekTypeMinimalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeValleySurfaceOP:
ELL_3V_COPY( sctx->grad + 3*( 1 + 2*si ), sctx->gradAns );
ELL_3V_COPY( sctx->eval + 3*( 1 + 2*si ), sctx->evalAns );
ELL_3M_COPY( sctx->evec + 9*( 1 + 2*si ), sctx->evecAns );
break;
}
/* ================================================= */
}
/* copy ends of this scanline left/right to margin */
if ( seekTypeIsocontour == sctx->type ) {
ELL_4V_COPY( sctx->sclv + 4*( 0 + ( sx+2 )*( yi+1 ) ),
sctx->sclv + 4*( 1 + ( sx+2 )*( yi+1 ) ) );
ELL_4V_COPY( sctx->sclv + 4*( sx+1 + ( sx+2 )*( yi+1 ) ),
sctx->sclv + 4*( sx + ( sx+2 )*( yi+1 ) ) );
}
}
/* copy top and bottom scanline up/down to margin */
if ( seekTypeIsocontour == sctx->type ) {
for ( xi=0; xi<sx+2; xi++ ) {
ELL_4V_COPY( sctx->sclv + 4*( xi + ( sx+2 )*0 ),
sctx->sclv + 4*( xi + ( sx+2 )*1 ) );
ELL_4V_COPY( sctx->sclv + 4*( xi + ( sx+2 )*( sy+1 ) ),
sctx->sclv + 4*( xi + ( sx+2 )*sy ) );
}
}
/* this is done as a separate pass because it looks at values between
voxels ( so its indexing is not trivial to fold into loops above ) */
if ( seekTypeRidgeSurface == sctx->type
|| seekTypeValleySurface == sctx->type
|| seekTypeMaximalSurface == sctx->type
|| seekTypeMinimalSurface == sctx->type ) {
if ( evecFlipShuffleProbe( sctx, bag ) ) {
biffAddf( SEEK, "%s: trouble at zi=%u\n", me, bag->zi );
return 1;
570 }
}
return 0;
}
#define VAL( xx, yy, zz ) ( val[4*( ( xx ) + ( yy )*( sx+2 ) + spi ) + ( zz+1 )] )
static void
voxelGrads( double vgrad[8][3], double *val, int sx, int spi ) {
ELL_3V_SET( vgrad[0],
VAL( 1, 0, 0 ) - VAL( -1, 0, 0 ),
VAL( 0, 1, 0 ) - VAL( 0, -1, 0 ),
VAL( 0, 0, 1 ) - VAL( 0, 0, -1 ) );
ELL_3V_SET( vgrad[1],
VAL( 2, 0, 0 ) - VAL( 0, 0, 0 ),
VAL( 1, 1, 0 ) - VAL( 1, -1, 0 ),
VAL( 1, 0, 1 ) - VAL( 1, 0, -1 ) );
ELL_3V_SET( vgrad[2],
VAL( 1, 1, 0 ) - VAL( -1, 1, 0 ),
VAL( 0, 2, 0 ) - VAL( 0, 0, 0 ),
VAL( 0, 1, 1 ) - VAL( 0, 1, -1 ) );
ELL_3V_SET( vgrad[3],
VAL( 2, 1, 0 ) - VAL( 0, 1, 0 ),
VAL( 1, 2, 0 ) - VAL( 1, 0, 0 ),
VAL( 1, 1, 1 ) - VAL( 1, 1, -1 ) );
ELL_3V_SET( vgrad[4],
VAL( 1, 0, 1 ) - VAL( -1, 0, 1 ),
VAL( 0, 1, 1 ) - VAL( 0, -1, 1 ),
VAL( 0, 0, 2 ) - VAL( 0, 0, 0 ) );
ELL_3V_SET( vgrad[5],
VAL( 2, 0, 1 ) - VAL( 0, 0, 1 ),
VAL( 1, 1, 1 ) - VAL( 1, -1, 1 ),
VAL( 1, 0, 2 ) - VAL( 1, 0, 0 ) );
ELL_3V_SET( vgrad[6],
VAL( 1, 1, 1 ) - VAL( -1, 1, 1 ),
VAL( 0, 2, 1 ) - VAL( 0, 0, 1 ),
VAL( 0, 1, 2 ) - VAL( 0, 1, 0 ) );
607 ELL_3V_SET( vgrad[7],
VAL( 2, 1, 1 ) - VAL( 0, 1, 1 ),
VAL( 1, 2, 1 ) - VAL( 1, 0, 1 ),
VAL( 1, 1, 2 ) - VAL( 1, 1, 0 ) );
}
#undef VAL
static void
vvalIsoSet( seekContext *sctx, baggage *bag, double vval[8],
unsigned int xi, unsigned int yi ) {
unsigned int sx, si, spi, vi;
AIR_UNUSED( bag );
sx = AIR_CAST( unsigned int, sctx->sx );
si = xi + sx*yi;
spi = ( xi+1 ) + ( sx+2 )*( yi+1 );
/* learn voxel values */
/* X Y Z */
vval[0] = sctx->sclv[4*( 0 + 0*( sx+2 ) + spi ) + 1];
vval[1] = sctx->sclv[4*( 1 + 0*( sx+2 ) + spi ) + 1];
vval[2] = sctx->sclv[4*( 0 + 1*( sx+2 ) + spi ) + 1];
vval[3] = sctx->sclv[4*( 1 + 1*( sx+2 ) + spi ) + 1];
vval[4] = sctx->sclv[4*( 0 + 0*( sx+2 ) + spi ) + 2];
vval[5] = sctx->sclv[4*( 1 + 0*( sx+2 ) + spi ) + 2];
vval[6] = sctx->sclv[4*( 0 + 1*( sx+2 ) + spi ) + 2];
vval[7] = sctx->sclv[4*( 1 + 1*( sx+2 ) + spi ) + 2];
if ( sctx->strengthUse ) {
double s, w, ssum, wsum;
/* Z X Y */
ssum = wsum = 0;
#define ACCUM( vi ) w = AIR_ABS( 1.0/vval[vi] ); ssum += w*s; wsum += w
s = sctx->stng[0 + 2*( 0 + 0*sx + si )]; ACCUM( 0 );
s = sctx->stng[0 + 2*( 1 + 0*sx + si )]; ACCUM( 1 );
s = sctx->stng[0 + 2*( 0 + 1*sx + si )]; ACCUM( 2 );
s = sctx->stng[0 + 2*( 1 + 1*sx + si )]; ACCUM( 3 );
s = sctx->stng[1 + 2*( 0 + 0*sx + si )]; ACCUM( 4 );
s = sctx->stng[1 + 2*( 1 + 0*sx + si )]; ACCUM( 5 );
s = sctx->stng[1 + 2*( 0 + 1*sx + si )]; ACCUM( 6 );
s = sctx->stng[1 + 2*( 1 + 1*sx + si )]; ACCUM( 7 );
#undef ACCUM
if ( ssum/wsum < sctx->strength ) {
for ( vi=0; vi<8; vi++ ) {
vval[vi] = 0;
651 }
}
}
return;
}
static void
vvalSurfSet( seekContext *sctx, baggage *bag, double vval[8],
unsigned int xi, unsigned int yi ) {
/* static const char me[]="vvalSurfSet"; */
double evec[8][3], grad[8][3], stng[8], maxStrength=0;
signed char flip[12]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, flipProd;
unsigned int sx, si, vi, ei, vrti[8];
sx = AIR_CAST( unsigned int, sctx->sx );
si = xi + sx*yi;
vrti[0] = 0 + 2*( xi + 0 + sx*( yi + 0 ) );
vrti[1] = 0 + 2*( xi + 1 + sx*( yi + 0 ) );
vrti[2] = 0 + 2*( xi + 0 + sx*( yi + 1 ) );
vrti[3] = 0 + 2*( xi + 1 + sx*( yi + 1 ) );
vrti[4] = 1 + 2*( xi + 0 + sx*( yi + 0 ) );
vrti[5] = 1 + 2*( xi + 1 + sx*( yi + 0 ) );
vrti[6] = 1 + 2*( xi + 0 + sx*( yi + 1 ) );
vrti[7] = 1 + 2*( xi + 1 + sx*( yi + 1 ) );
/* Our strategy is to create all triangles of which at least some
* part meets the strength criterion, and to trim them in a
* post-process. This avoids ragged boundaries */
for ( vi=0; vi<8; vi++ ) {
ELL_3V_COPY( grad[vi], sctx->grad + 3*vrti[vi] );
ELL_3V_COPY( evec[vi], sctx->evec + 3*( bag->esIdx + 3*vrti[vi] ) );
if ( sctx->strengthUse ) {
stng[vi] = sctx->stng[vrti[vi]];
if ( !vi ) {
maxStrength = stng[vi];
} else {
maxStrength = AIR_MAX( maxStrength, stng[vi] );
}
}
}
flipProd = 1;
if ( sctx->type!=seekTypeRidgeSurfaceOP &&
sctx->type!=seekTypeValleySurfaceOP ) {
for ( ei=0; ei<12; ei++ ) {
flip[ei] = sctx->flip[bag->evti[ei] + 5*si];
flipProd *= flip[ei];
}
}
if ( ( sctx->strengthUse && maxStrength < sctx->strength )
|| !flipProd ) {
/* either the corners this voxel don't meet strength,
or something else is funky */
for ( vi=0; vi<8; vi++ ) {
vval[vi] = 0;
}
} else {
if ( sctx->type==seekTypeRidgeSurfaceOP ||
sctx->type==seekTypeValleySurfaceOP ) {
/* find orientation based on outer product rule */
double outer[9];
double outerevals[3], outerevecs[9];
ELL_3MV_OUTER( outer, evec[0], evec[0] );
for ( vi=1; vi<8; ++vi ) {
ELL_3MV_OUTER_INCR( outer, evec[vi], evec[vi] );
}
ell_3m_eigensolve_d( outerevals, outerevecs, outer, AIR_TRUE );
for ( vi=0; vi<8; ++vi ) {
if ( ELL_3V_DOT( evec[vi], outerevecs )<0 )
ELL_3V_SCALE( evec[vi], -1.0, evec[vi] );
}
} else {
ELL_3V_SCALE( evec[1], flip[0], evec[1] );
ELL_3V_SCALE( evec[2], flip[1], evec[2] );
ELL_3V_SCALE( evec[3], flip[0]*flip[2], evec[3] );
ELL_3V_SCALE( evec[4], flip[4], evec[4] );
ELL_3V_SCALE( evec[5], flip[4]*flip[8], evec[5] );
ELL_3V_SCALE( evec[6], flip[4]*flip[9], evec[6] );
ELL_3V_SCALE( evec[7], flip[4]*flip[8]*flip[10], evec[7] );
}
732 for ( vi=0; vi<8; vi++ ) {
vval[vi] = ELL_3V_DOT( grad[vi], evec[vi] );
}
}
return;
}
static int
triangulate( seekContext *sctx, baggage *bag, limnPolyData *lpld ) {
/* static const char me[]="triangulate"; */
unsigned xi, yi, sx, sy, si, spi;
/* ========================================================== */
/* NOTE: these things must agree with information in tables.c */
int e2v[12][2] = { /* maps edge index to corner vertex indices */
{0, 1}, /* 0 */
{0, 2}, /* 1 */
{1, 3}, /* 2 */
{2, 3}, /* 3 */
{0, 4}, /* 4 */
{1, 5}, /* 5 */
{2, 6}, /* 6 */
{3, 7}, /* 7 */
{4, 5}, /* 8 */
{4, 6}, /* 9 */
{5, 7}, /* 10 */
{6, 7} /* 11 */
};
double vccoord[8][3] = { /* vertex corner coordinates */
{0, 0, 0}, /* 0 */
{1, 0, 0}, /* 1 */
{0, 1, 0}, /* 2 */
{1, 1, 0}, /* 3 */
{0, 0, 1}, /* 4 */
{1, 0, 1}, /* 5 */
{0, 1, 1}, /* 6 */
{1, 1, 1} /* 7 */
};
/* ========================================================== */
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
for ( yi=0; yi<sy-1; yi++ ) {
double vval[8], vgrad[8][3], vert[3], tvertA[4], tvertB[4], ww;
unsigned char vcase;
int ti, vi, ei, vi0, vi1, ecase;
const int *tcase;
unsigned int vii[3];
for ( xi=0; xi<sx-1; xi++ ) {
si = xi + sx*yi;
spi = ( xi+1 ) + ( sx+2 )*( yi+1 );
switch ( sctx->type ) {
case seekTypeIsocontour:
vvalIsoSet( sctx, bag, vval, xi, yi );
break;
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMaximalSurface:
case seekTypeMinimalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeValleySurfaceOP:
vvalSurfSet( sctx, bag, vval, xi, yi );
break;
}
/* determine voxel and edge case */
vcase = 0;
for ( vi=0; vi<8; vi++ ) {
vcase |= ( vval[vi] > 0 ) << vi;
}
if ( 0 == vcase || 255 == vcase ) {
/* no triangles added here */
continue;
}
/* set voxel corner gradients */
if ( seekTypeIsocontour == sctx->type
&& sctx->normalsFind
&& !sctx->normAns ) {
voxelGrads( vgrad, sctx->sclv, sx, spi );
}
sctx->voxNum++;
ecase = seekContour3DTopoHackEdge[vcase];
/* create new vertices as needed */
for ( ei=0; ei<12; ei++ ) {
if ( ( ecase & ( 1 << ei ) )
&& -1 == sctx->vidx[bag->evti[ei] + 5*si] ) {
int ovi;
double tvec[3], grad[3], tlen;
/* this edge is needed for triangulation,
and, we haven't already created a vertex for it */
vi0 = e2v[ei][0];
vi1 = e2v[ei][1];
ww = vval[vi0]/( vval[vi0] - vval[vi1] );
ELL_3V_LERP( vert, ww, vccoord[vi0], vccoord[vi1] );
ELL_4V_SET( tvertA, vert[0] + xi, vert[1] + yi, vert[2] + bag->zi, 1 );
ELL_4MV_MUL( tvertB, sctx->txfIdx, tvertA );
/* tvertB is now in input index space */
ELL_4MV_MUL( tvertA, sctx->shape->ItoW, tvertB );
/* tvertA is now in input world space */
ELL_4V_HOMOG( tvertA, tvertA );
ELL_4V_HOMOG( tvertB, tvertB );
ovi = sctx->vidx[bag->evti[ei] + 5*si] =
airArrayLenIncr( bag->xyzwArr, 1 );
ELL_4V_SET_TT( lpld->xyzw + 4*ovi, float,
tvertA[0], tvertA[1], tvertA[2], 1.0 );
/*
fprintf( stderr, "!%s: vert %u: %g %g %g\n", me, ovi,
tvertA[0], tvertA[1], tvertA[2] );
*/
if ( sctx->normalsFind ) {
airArrayLenIncr( bag->normArr, 1 );
if ( sctx->normAns ) {
gageProbe( sctx->gctx, tvertB[0], tvertB[1], tvertB[2] );
ELL_3V_SCALE_TT( lpld->norm + 3*ovi, float, -1, sctx->normAns );
if ( sctx->reverse ) {
ELL_3V_SCALE( lpld->norm + 3*ovi, -1, lpld->norm + 3*ovi );
}
} else {
ELL_3V_LERP( grad, ww, vgrad[vi0], vgrad[vi1] );
ELL_3MV_MUL( tvec, sctx->txfNormal, grad );
ELL_3V_NORM_TT( lpld->norm + 3*ovi, float, tvec, tlen );
}
}
sctx->vertNum++;
/*
fprintf( stderr, "%s: vert %d ( edge %d ) of ( %d, %d, %d ) "
"at %g %g %g\n",
me, sctx->vidx[bag->evti[ei] + 5*si], ei, xi, yi, zi,
vert[0] + xi, vert[1] + yi, vert[2] + bag->zi );
*/
}
}
/* add triangles */
ti = 0;
tcase = seekContour3DTopoHackTriangle[vcase];
while ( -1 != tcase[0 + 3*ti] ) {
unsigned iii;
ELL_3V_SET( vii,
sctx->vidx[bag->evti[tcase[0 + 3*ti]] + 5*si],
sctx->vidx[bag->evti[tcase[1 + 3*ti]] + 5*si],
sctx->vidx[bag->evti[tcase[2 + 3*ti]] + 5*si] );
if ( sctx->reverse ) {
int tmpi;
tmpi = vii[1]; vii[1] = vii[2]; vii[2] = tmpi;
}
iii = airArrayLenIncr( bag->indxArr, 3 );
ELL_3V_COPY( lpld->indx + iii, vii );
/*
fprintf( stderr, "!%s: tri %u: %u %u %u\n",
me, iii/3, vii[0], vii[1], vii[2] );
*/
lpld->icnt[0] += 3;
sctx->faceNum++;
884 ti++;
}
}
}
return 0;
}
static int
surfaceExtract( seekContext *sctx, limnPolyData *lpld ) {
static const char me[]="surfaceExtract";
char done[AIR_STRLEN_SMALL];
unsigned int zi, sz;
baggage *bag;
bag = baggageNew( sctx );
sz = AIR_CAST( unsigned int, sctx->sz );
/* this creates the airArrays in bag */
if ( outputInit( sctx, bag, lpld ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->verbose > 2 ) {
fprintf( stderr, "%s: extracting ... ", me );
}
for ( zi=0; zi<sz-1; zi++ ) {
char trouble=0;
if ( sctx->verbose > 2 ) {
fprintf( stderr, "%s", airDoneStr( 0, zi, sz-2, done ) );
fflush( stderr );
}
bag->zi = zi;
if ( sctx->type==seekTypeRidgeSurfaceT ||
sctx->type==seekTypeValleySurfaceT ) {
if ( _seekShuffleProbeT( sctx, bag ) ||
_seekTriangulateT( sctx, bag, lpld ) )
trouble = 1;
} else {
if ( shuffleProbe( sctx, bag ) ||
triangulate( sctx, bag, lpld ) )
trouble = 1;
}
if ( trouble ) {
biffAddf( SEEK, "%s: trouble on zi = %u", me, zi );
return 1;
}
}
if ( sctx->verbose > 2 ) {
fprintf( stderr, "%s\n", airDoneStr( 0, zi, sz-2, done ) );
}
935
/* this cleans up the airArrays in bag */
baggageNix( bag );
return 0;
}
int
seekExtract( seekContext *sctx, limnPolyData *lpld ) {
static const char me[]="seekExtract";
double time0;
int E;
if ( !( sctx && lpld ) ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( seekTypeIsocontour == sctx->type ) {
if ( !AIR_EXISTS( sctx->isovalue ) ) {
biffAddf( SEEK, "%s: didn't seem to ever set isovalue ( now %g )", me,
sctx->isovalue );
return 1;
}
}
if ( sctx->verbose ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagResult = %d\n", me,
sctx->flag[flagResult] );
}
/* reset max strength seen */
sctx->strengthSeenMax = AIR_NAN;
/* start time */
time0 = airTime( );
switch( sctx->type ) {
case seekTypeIsocontour:
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMinimalSurface:
case seekTypeMaximalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeRidgeSurfaceT:
case seekTypeValleySurfaceOP:
case seekTypeValleySurfaceT:
E = surfaceExtract( sctx, lpld );
break;
default:
biffAddf( SEEK, "%s: sorry, %s extraction not implemented", me,
airEnumStr( seekType, sctx->type ) );
return 1;
break;
}
if ( E ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
/* end time */
sctx->time = airTime( ) - time0;
sctx->flag[flagResult] = AIR_FALSE;
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
#include "privateSeek.h"
const int
seekPresent = 42;
seekContext *
31 seekContextNew( void ) {
seekContext *sctx;
unsigned int fi;
sctx = ( seekContext * )calloc( 1, sizeof( seekContext ) );
if ( sctx ) {
sctx->verbose = 0;
sctx->ninscl = NULL;
sctx->gctx = NULL;
sctx->pvl = NULL;
sctx->type = seekTypeUnknown;
sctx->sclvItem = -1;
sctx->normItem = -1;
sctx->gradItem = -1;
sctx->evalItem = -1;
sctx->evecItem = -1;
sctx->stngItem = -1;
sctx->hessItem = -1;
sctx->lowerInside = AIR_FALSE;
sctx->normalsFind = AIR_FALSE;
sctx->strengthUse = AIR_FALSE;
sctx->strengthSign = 1;
sctx->isovalue = AIR_NAN;
sctx->evalDiffThresh = 1.0; /* roughly reasonable for uchar data;
* really should depend on dynamic range */
sctx->strength = 0.0;
ELL_3V_SET( sctx->samples, 0, 0, 0 );
/* these two magic values assume a certain level of surface smoothness,
which certainly does not apply to all cases */
sctx->facesPerVoxel = 2.15;
sctx->vertsPerVoxel = 1.15;
/* this should be larger so that we don't waste time from airArrayLenIncr
constantly re-allocating, when the isosurface is rough; better
solution is to use multiplicatively scaled dynamic array. But caller
can also change this value on a per-context basis. */
sctx->pldArrIncr = 2048;
sctx->nin = NULL;
sctx->flag = AIR_CAST( int *, calloc( flagLast, sizeof( int ) ) );
for ( fi=flagUnknown+1; fi<flagLast; fi++ ) {
sctx->flag[fi] = AIR_FALSE;
}
sctx->baseDim = 0;
sctx->_shape = gageShapeNew( );
sctx->shape = NULL;
sctx->nsclDerived = nrrdNew( );
sctx->sclvAns = NULL;
sctx->normAns = NULL;
sctx->gradAns = NULL;
sctx->evalAns = NULL;
sctx->evecAns = NULL;
sctx->stngAns = NULL;
sctx->hessAns = NULL;
sctx->reverse = AIR_FALSE;
ELL_3M_IDENTITY_SET( sctx->txfNormal );
sctx->spanSize = 300;
sctx->nspanHist = nrrdNew( );
sctx->range = nrrdRangeNew( AIR_NAN, AIR_NAN );
sctx->sx = 0;
sctx->sy = 0;
sctx->sz = 0;
ELL_4M_IDENTITY_SET( sctx->txfIdx );
sctx->nvidx = nrrdNew( );
sctx->nsclv = nrrdNew( );
sctx->ngrad = nrrdNew( );
sctx->neval = nrrdNew( );
sctx->nevec = nrrdNew( );
sctx->nflip = nrrdNew( );
sctx->nstng = nrrdNew( );
sctx->nhess = nrrdNew( );
sctx->nt = nrrdNew( );
sctx->nfacevidx = nrrdNew( );
sctx->nedgealpha = nrrdNew( );
sctx->nedgenorm = nrrdNew( );
sctx->nedgeicoord = nrrdNew( );
sctx->nfacecoord = nrrdNew( );
sctx->nfacenorm = nrrdNew( );
sctx->nfaceicoord = nrrdNew( );
sctx->npairs = nrrdNew( );
sctx->ngradcontext = nrrdNew( );
sctx->nhesscontext = nrrdNew( );
sctx->ntcontext = nrrdNew( );
sctx->nstngcontext = nrrdNew( );
sctx->ntreated = nrrdNew( );
sctx->vidx = NULL;
sctx->sclv = NULL;
sctx->grad = NULL;
sctx->eval = NULL;
sctx->evec = NULL;
sctx->flip = NULL;
sctx->stng = NULL;
sctx->voxNum = 0;
sctx->vertNum = 0;
sctx->faceNum = 0;
sctx->strengthSeenMax = AIR_NAN;
sctx->time = AIR_NAN;
}
return sctx;
}
seekContext *
133 seekContextNix( seekContext *sctx ) {
if ( sctx ) {
airFree( sctx->flag );
sctx->flag = NULL;
sctx->_shape = gageShapeNix( sctx->_shape );
sctx->nsclDerived = nrrdNuke( sctx->nsclDerived );
sctx->nspanHist = nrrdNuke( sctx->nspanHist );
sctx->range = nrrdRangeNix( sctx->range );
sctx->nvidx = nrrdNuke( sctx->nvidx );
sctx->nsclv = nrrdNuke( sctx->nsclv );
sctx->ngrad = nrrdNuke( sctx->ngrad );
sctx->neval = nrrdNuke( sctx->neval );
sctx->nevec = nrrdNuke( sctx->nevec );
sctx->nflip = nrrdNuke( sctx->nflip );
sctx->nstng = nrrdNuke( sctx->nstng );
sctx->nhess = nrrdNuke( sctx->nhess );
sctx->nt = nrrdNuke( sctx->nt );
sctx->nfacevidx = nrrdNuke( sctx->nfacevidx );
sctx->nedgealpha = nrrdNuke( sctx->nedgealpha );
sctx->nedgenorm = nrrdNuke( sctx->nedgenorm );
sctx->nedgeicoord = nrrdNuke( sctx->nedgeicoord );
sctx->nfacecoord = nrrdNuke( sctx->nfacecoord );
sctx->nfacenorm = nrrdNuke( sctx->nfacenorm );
sctx->nfaceicoord = nrrdNuke( sctx->nfaceicoord );
sctx->npairs = nrrdNuke( sctx->npairs );
sctx->ngradcontext = nrrdNuke( sctx->ngradcontext );
sctx->nhesscontext = nrrdNuke( sctx->nhesscontext );
sctx->ntcontext = nrrdNuke( sctx->ntcontext );
sctx->nstngcontext = nrrdNuke( sctx->nstngcontext );
sctx->ntreated = nrrdNuke( sctx->ntreated );
airFree( sctx );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
#include "privateSeek.h"
/*
******** seekVerboseSet
**
*/
void
32 seekVerboseSet( seekContext *sctx, int verbose ) {
if ( sctx ) {
sctx->verbose = verbose;
}
return;
}
/*
******** seekDataSet
**
** user sets EITHER: ninscl, or, gctx and pvlIdx
**
** if ninscl: this is a vanilla scalar volume, and we can do seekTypeIsocontour
** if gctx: this is a scalar or non-scalar volume, and we can do any seekType
**
** sets from input:
** ninscl, gctx, pvl
**
** So the rest of seek can use "if ( sctx->ninscl )" to see if we're working
** with a vanilla scalar volume or not
**
** invalidates:
** valItem, normItem, gradItem, evalItem, evecItem
*/
int
58 seekDataSet( seekContext *sctx, const Nrrd *ninscl,
gageContext *gctx, unsigned int pvlIdx ) {
static const char me[]="seekDataSet";
if ( !( sctx && ( ninscl || gctx ) ) ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( ninscl && gctx ) {
biffAddf( SEEK, "%s: must give ninscl or gctx, but not both", me );
return 1;
}
if ( ninscl ) {
if ( nrrdCheck( ninscl ) ) {
biffMovef( SEEK, NRRD, "%s: problem with volume", me );
return 1;
}
if ( 3 != ninscl->dim ) {
biffAddf( SEEK, "%s: vanilla scalar volume must be 3-D ( not %d-D )",
me, ninscl->dim );
return 1;
}
if ( nrrdTypeBlock == ninscl->type ) {
biffAddf( SEEK, "%s: can't work with %s type values", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
sctx->ninscl = ninscl;
sctx->gctx = NULL;
sctx->pvl = NULL;
} else {
if ( !( pvlIdx < gctx->pvlNum ) ) {
biffAddf( SEEK, "%s: pvlIdx %u not < pvlNum %u",
me, pvlIdx, gctx->pvlNum );
return 1;
}
/* we assume that caller has done a gageUpdate( ), so no other error
checking is required ( or really possible ) here */
sctx->ninscl = NULL;
sctx->gctx = gctx;
sctx->pvl = gctx->pvl[pvlIdx];
}
sctx->flag[flagData] = AIR_TRUE;
sctx->sclvItem = -1;
sctx->normItem = -1;
sctx->gradItem = -1;
sctx->evalItem = -1;
sctx->evecItem = -1;
sctx->hessItem = -1;
return 0;
}
/*
******** seekSamplesSet
**
** sets: samples[3]
*/
int
119 seekSamplesSet( seekContext *sctx, size_t samples[3] ) {
static const char me[]="seekSamplesSet";
unsigned int numZero;
if ( !( sctx && samples ) ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
numZero = 0;
numZero += 0 == samples[0];
numZero += 0 == samples[1];
numZero += 0 == samples[2];
if ( !( 0 == numZero || 3 == numZero ) ) {
biffAddf( SEEK, "%s: samples ( %u, %u, %u ) must all be 0 or !=0 together", me,
AIR_CAST( unsigned int, samples[0] ),
AIR_CAST( unsigned int, samples[1] ),
AIR_CAST( unsigned int, samples[2] ) );
return 1;
}
if ( sctx->samples[0] != samples[0]
|| sctx->samples[1] != samples[1]
|| sctx->samples[2] != samples[2] ) {
sctx->samples[0] = samples[0];
sctx->samples[1] = samples[1];
sctx->samples[2] = samples[2];
sctx->flag[flagSamples] = AIR_TRUE;
}
return 0;
}
/*
******** seekTypeSet
**
** sets: featureType
*/
int
155 seekTypeSet( seekContext *sctx, int type ) {
static const char me[]="seekTypeSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( seekType, type ) ) {
biffAddf( SEEK, "%s: %d not a valid %s", me, type, seekType->name );
return 1;
}
if ( sctx->type != type ) {
sctx->type = type;
sctx->flag[flagType] = AIR_TRUE;
}
return 0;
}
/*
********* seekLowerInsideSet
**
** sets: lowerInside
*/
int
179 seekLowerInsideSet( seekContext *sctx, int lowerInside ) {
static const char me[]="seekLowerInsideSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( sctx->lowerInside != lowerInside ) {
sctx->lowerInside = lowerInside;
sctx->flag[flagLowerInside] = AIR_TRUE;
}
return 0;
}
/*
********* seekNormalsFindSet
**
** sets: normalsFind
*/
int
199 seekNormalsFindSet( seekContext *sctx, int normalsFind ) {
static const char me[]="seekNormalsFindSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( sctx->normalsFind != normalsFind ) {
sctx->normalsFind = normalsFind;
sctx->flag[flagNormalsFind] = AIR_TRUE;
}
return 0;
}
int
214 seekStrengthUseSet( seekContext *sctx, int doit ) {
static const char me[]="seekStrengthUseSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( sctx->strengthUse != doit ) {
sctx->strengthUse = doit;
sctx->flag[flagStrengthUse] = AIR_TRUE;
}
return 0;
}
int
229 seekStrengthSet( seekContext *sctx, int strengthSign,
double strength ) {
static const char me[]="seekStrengthSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( !( 1 == strengthSign || -1 == strengthSign ) ) {
biffAddf( SEEK, "%s: strengthSign ( %d ) not +1 or -1", me, strengthSign );
return 1;
}
if ( !AIR_EXISTS( strength ) ) {
biffAddf( SEEK, "%s: strength %g doesn't exist", me, strength );
return 1;
}
if ( sctx->strengthSign != strengthSign ) {
sctx->strengthSign = strengthSign;
sctx->flag[flagStrength] = AIR_TRUE;
}
if ( sctx->strength != strength ) {
sctx->strength = strength;
sctx->flag[flagStrength] = AIR_TRUE;
}
return 0;
}
static int
257 itemCheck( seekContext *sctx, int item, unsigned int wantLen ) {
static const char me[]="itemCheck";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( !( sctx->gctx && sctx->pvl ) ) {
biffAddf( SEEK, "%s: don't have a gage context", me );
return 1;
}
if ( airEnumValCheck( sctx->pvl->kind->enm, item ) ) {
biffAddf( SEEK, "%s: %d not valid %s item", me, item,
sctx->pvl->kind->enm->name );
return 1;
}
if ( !GAGE_QUERY_ITEM_TEST( sctx->pvl->query, item ) ) {
biffAddf( SEEK, "%s: item \"%s\" ( %d ) not set in query", me,
airEnumStr( sctx->pvl->kind->enm, item ), item );
return 1;
}
if ( sctx->pvl->kind->table[item].answerLength != wantLen ) {
biffAddf( SEEK, "%s: item %s has length %u, not wanted %u", me,
airEnumStr( sctx->pvl->kind->enm, item ),
sctx->pvl->kind->table[item].answerLength, wantLen );
return 1;
}
return 0;
}
/*
******** seekItemScalarSet
**
** sets: sclvItem
*/
int
293 seekItemScalarSet( seekContext *sctx, int item ) {
static const char me[]="seekItemScalarSet";
if ( itemCheck( sctx, item, 1 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->sclvItem != item ) {
sctx->sclvItem = item;
sctx->flag[flagItemValue] = AIR_TRUE;
}
return 0;
}
/*
******** seekItemStrengthSet
**
*/
int
312 seekItemStrengthSet( seekContext *sctx, int item ) {
static const char me[]="seekItemStrengthSet";
if ( itemCheck( sctx, item, 1 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->stngItem != item ) {
sctx->stngItem = item;
sctx->flag[flagItemStrength] = AIR_TRUE;
}
return 0;
}
/*
******** seekItemHessSet
**
*/
int
331 seekItemHessSet( seekContext *sctx, int item ) {
char me[]="seekItemHessSet";
if ( itemCheck( sctx, item, 9 ) ) {
biffAddf( SEEK, "%s: trouble", me ); return 1;
}
if ( sctx->hessItem != item ) {
sctx->hessItem = item;
sctx->flag[flagItemHess] = AIR_TRUE;
}
return 0;
}
/*
******** seekItemGradientSet
**
** sets: gradItem
*/
int
350 seekItemGradientSet( seekContext *sctx, int item ) {
static const char me[]="seekItemGradientSet";
if ( itemCheck( sctx, item, 3 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->gradItem != item ) {
sctx->gradItem = item;
sctx->flag[flagItemGradient] = AIR_TRUE;
}
/* sctx->gradAns = gageAnswerPointer( sctx->gctx, sctx->pvl, item ); */
return 0;
}
/*
******** seekItemNormalSet
**
** sets: normItem
*/
int
371 seekItemNormalSet( seekContext *sctx, int item ) {
static const char me[]="seekItemNormalSet";
if ( itemCheck( sctx, item, 3 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->normItem != item ) {
sctx->normItem = item;
sctx->flag[flagItemNormal] = AIR_TRUE;
}
/* sctx->normAns = gageAnswerPointer( sctx->gctx, sctx->pvl, item ); */
return 0;
}
/*
******** seekItemEigensystemSet
**
** sets: evalItem, evecItem
*/
int
392 seekItemEigensystemSet( seekContext *sctx, int evalItem, int evecItem ) {
static const char me[]="seekItemEigenvectorSet";
if ( itemCheck( sctx, evalItem, 3 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( itemCheck( sctx, evecItem, 9 ) ) {
biffAddf( SEEK, "%s: trouble", me );
return 1;
}
if ( sctx->evalItem != evalItem
|| sctx->evecItem != evecItem ) {
sctx->evalItem = evalItem;
sctx->evecItem = evecItem;
sctx->flag[flagItemEigensystem] = AIR_TRUE;
}
/*
sctx->evalAns = gageAnswerPointer( sctx->gctx, sctx->pvl, sctx->evalItem );
sctx->evecAns = gageAnswerPointer( sctx->gctx, sctx->pvl, sctx->evecItem );
*/
return 0;
}
/*
******** seekIsovalueSet
**
** sets: isovalue
*/
int
422 seekIsovalueSet( seekContext *sctx, double isovalue ) {
static const char me[]="seekIsovalueSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_EXISTS( isovalue ) ) {
biffAddf( SEEK, "%s: given isovalue %g doesn't exit", me, isovalue );
return 1;
}
if ( sctx->isovalue != isovalue ) {
sctx->isovalue = isovalue;
sctx->flag[flagIsovalue] = AIR_TRUE;
}
return 0;
}
/*
******** seekEvalDiffThreshSet
**
** sets: difference threshold from which two eigenvalues are
** considered "similar" ( cf. Eq. ( 4 ) in TVCG paper by
** Schultz/Theisel/Seidel )
*/
int
448 seekEvalDiffThreshSet( seekContext *sctx, double evalDiffThresh ) {
char me[]="seekEvalDiffThreshSet";
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me ); return 1;
}
if ( !AIR_EXISTS( evalDiffThresh ) ) {
biffAddf( SEEK, "%s: given eigenvalue difference threshold %g doesn't exit",
me, evalDiffThresh ); return 1;
}
if ( sctx->evalDiffThresh != evalDiffThresh ) {
sctx->evalDiffThresh = evalDiffThresh;
sctx->flag[flagEvalDiffThresh] = AIR_TRUE;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2010, 2009 Thomas Schultz
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
/*
** vertex, edge, and face numbering, and canonical edge arrangement
**
** Z
** ^ Y
** | ^
** | /
** | /
** |/
** O--------> X
**
** ( 6 )---11---( 7 ) +----------+ +----------+
** /| /| /| /| /| \ /|
** 9 | 10 | / | /5/ / | / | \/ |
** / 6 / 7 / | |4|/ | / \| /\ |
** ( 4 )----8---( 5 ) | +----------+ | +----------+ |
** | | | | ||2|| ||3|| | | \ | |
** | ( 2 )---3-|--( 3 ) | +------|---+ | +----\-|---+
** 4 / 5 / | / |1| | / |\ / |\ /
** | 1 | 2 | / /0/ | / | /\ | /
** |/ |/ |/ |/ |/ \ |/
** ( 0 )----0---( 1 ) +----------+ +----------+
** canonical edge arrangement
** creates 2 triangular and
** 1 hexagonal surface
*/
/* According to this layout, in the code comments, "right" denotes
* positive x, "back" denotes positive y, "top" denotes positive z */
/*
** the seekContext's vidx cache uses this numbering
** ( . )--------( . )
** /| /|
** 4 | / |
** / | / |
** ( . )----3---( . ) |
** | | | |
** | ( . )-----|--( . )
** 2 / | /
** | 1 | /
** |/ |/
** ( X )----0---( . )
*/
/* We now only need a numbering of faces. It is:
* 0: xy plane, z=0
* 1: xz plane, y=0
* 2: yz plane, x=1
* 3: xz plane, y=1
* 4: yz plane, x=0
* 5: xy plane, z=1
*
* There are four unique faces to each voxel ( e.g., in facevidx ):
* 0: xy plane, z=0
* 1: xz plane, y=0
* 2: yz plane, x=0
* 3: xy plane, z=1
*/
const int
seekContour3DTopoHackEdge[256] = {
0x000, 0x013, 0x025, 0x036, 0x04A, 0x059, 0x06F, 0x07C,
0x08C, 0x09F, 0x0A9, 0x0BA, 0x0C6, 0x0D5, 0x0E3, 0x0F0,
0x310, 0x303, 0x335, 0x326, 0x35A, 0x349, 0x37F, 0x36C,
0x39C, 0x38F, 0x3B9, 0x3AA, 0x3D6, 0x3C5, 0x3F3, 0x3E0,
0x520, 0x533, 0x505, 0x516, 0x56A, 0x579, 0x54F, 0x55C,
0x5AC, 0x5BF, 0x589, 0x59A, 0x5E6, 0x5F5, 0x5C3, 0x5D0,
0x630, 0x623, 0x615, 0x606, 0x67A, 0x669, 0x65F, 0x64C,
0x6BC, 0x6AF, 0x699, 0x68A, 0x6F6, 0x6E5, 0x6D3, 0x6C0,
0xA40, 0xA53, 0xA65, 0xA76, 0xA0A, 0xA19, 0xA2F, 0xA3C,
0xACC, 0xADF, 0xAE9, 0xAFA, 0xA86, 0xA95, 0xAA3, 0xAB0,
0x950, 0x943, 0x975, 0x966, 0x91A, 0x909, 0x93F, 0x92C,
0x9DC, 0x9CF, 0x9F9, 0x9EA, 0x996, 0x985, 0x9B3, 0x9A0,
0xF60, 0xF73, 0xF45, 0xF56, 0xF2A, 0xF39, 0xF0F, 0xF1C,
0xFEC, 0xFFF, 0xFC9, 0xFDA, 0xFA6, 0xFB5, 0xF83, 0xF90,
0xC70, 0xC63, 0xC55, 0xC46, 0xC3A, 0xC29, 0xC1F, 0xC0C,
0xCFC, 0xCEF, 0xCD9, 0xCCA, 0xCB6, 0xCA5, 0xC93, 0xC80,
0xC80, 0xC93, 0xCA5, 0xCB6, 0xCCA, 0xCD9, 0xCEF, 0xCFC,
0xC0C, 0xC1F, 0xC29, 0xC3A, 0xC46, 0xC55, 0xC63, 0xC70,
0xF90, 0xF83, 0xFB5, 0xFA6, 0xFDA, 0xFC9, 0xFFF, 0xFEC,
0xF1C, 0xF0F, 0xF39, 0xF2A, 0xF56, 0xF45, 0xF73, 0xF60,
0x9A0, 0x9B3, 0x985, 0x996, 0x9EA, 0x9F9, 0x9CF, 0x9DC,
0x92C, 0x93F, 0x909, 0x91A, 0x966, 0x975, 0x943, 0x950,
0xAB0, 0xAA3, 0xA95, 0xA86, 0xAFA, 0xAE9, 0xADF, 0xACC,
0xA3C, 0xA2F, 0xA19, 0xA0A, 0xA76, 0xA65, 0xA53, 0xA40,
0x6C0, 0x6D3, 0x6E5, 0x6F6, 0x68A, 0x699, 0x6AF, 0x6BC,
0x64C, 0x65F, 0x669, 0x67A, 0x606, 0x615, 0x623, 0x630,
0x5D0, 0x5C3, 0x5F5, 0x5E6, 0x59A, 0x589, 0x5BF, 0x5AC,
0x55C, 0x54F, 0x579, 0x56A, 0x516, 0x505, 0x533, 0x520,
0x3E0, 0x3F3, 0x3C5, 0x3D6, 0x3AA, 0x3B9, 0x38F, 0x39C,
0x36C, 0x37F, 0x349, 0x35A, 0x326, 0x335, 0x303, 0x310,
0x0F0, 0x0E3, 0x0D5, 0x0C6, 0x0BA, 0x0A9, 0x09F, 0x08C,
0x07C, 0x06F, 0x059, 0x04A, 0x036, 0x025, 0x013, 0x000
};
/*
This case table implements the ideas from
Dietrich et al. Edge Groups: an approach to understanding the mesh
quality of marching methods. IEEE Trans. Vis. Comp. Graph. 2008
and has been generated from the case table distributed with macet,
written by Carlos Dietrich.
Re-used with kind permission of the authors.
*/
const int
seekContour3DTopoHackTriangle[256][16] = {
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 2, 4, 4, 2, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 3, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 0, 6, 6, 0, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 3, 6, 1, 5, 2, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 4, 3, 4, 2, 3, 4, 5, 2, -1, -1, -1, -1, -1, -1, -1},
{ 2, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 0, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 5, 1, 5, 3, 1, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1},
{ 6, 1, 7, 7, 1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 7, 6, 2, 6, 0, 2, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1},
{ 5, 7, 0, 7, 1, 0, 7, 6, 1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 6, 4, 5, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 9, 8, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 8, 8, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 9, 8, 2, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 1, 5, 1, 8, 5, 1, 9, 8, -1, -1, -1, -1, -1, -1, -1},
{ 9, 8, 4, 3, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 8, 0, 9, 0, 6, 9, 0, 3, 6, -1, -1, -1, -1, -1, -1, -1},
{ 8, 4, 9, 3, 6, 1, 2, 0, 5, -1, -1, -1, -1, -1, -1, -1},
{ 2, 3, 6, 2, 6, 9, 5, 2, 9, 8, 5, 9, -1, -1, -1, -1},
{ 9, 8, 4, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 9, 8, 1, 8, 0, 1, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1},
{ 7, 3, 5, 3, 0, 5, 9, 8, 4, -1, -1, -1, -1, -1, -1, -1},
{ 5, 7, 8, 8, 7, 3, 1, 9, 8, 1, 8, 3, -1, -1, -1, -1},
{ 2, 7, 1, 7, 6, 1, 8, 4, 9, -1, -1, -1, -1, -1, -1, -1},
{ 7, 6, 9, 9, 0, 2, 8, 0, 9, 7, 9, 2, -1, -1, -1, -1},
{ 4, 9, 8, 7, 6, 1, 0, 7, 1, 5, 7, 0, -1, -1, -1, -1},
{ 7, 6, 9, 8, 7, 9, 5, 7, 8, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, 10, 5, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 8, 10, 0, 0, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 2, 8, 2, 4, 8, 2, 1, 4, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 8, 3, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 3, 6, 0, 6, 4, 0, 10, 5, 8, -1, -1, -1, -1, -1, -1, -1},
{ 8, 10, 0, 10, 2, 0, 6, 1, 3, -1, -1, -1, -1, -1, -1, -1},
{10, 2, 8, 8, 2, 3, 6, 4, 8, 6, 8, 3, -1, -1, -1, -1},
{ 5, 8, 10, 3, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 8, 1, 4, 0, 3, 2, 7, -1, -1, -1, -1, -1, -1, -1},
{ 3, 0, 7, 0, 10, 7, 0, 8, 10, -1, -1, -1, -1, -1, -1, -1},
{ 7, 3, 10, 10, 3, 8, 3, 1, 8, 1, 4, 8, -1, -1, -1, -1},
{ 6, 1, 7, 1, 2, 7, 8, 10, 5, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 8, 6, 4, 0, 2, 6, 0, 7, 6, 2, -1, -1, -1, -1},
{ 0, 8, 1, 1, 8, 10, 7, 6, 1, 7, 1, 10, -1, -1, -1, -1},
{ 6, 4, 8, 10, 6, 8, 7, 6, 10, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 9, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 9, 0, 9, 5, 0, 9, 10, 5, -1, -1, -1, -1, -1, -1, -1},
{ 9, 10, 4, 10, 0, 4, 10, 2, 0, -1, -1, -1, -1, -1, -1, -1},
{10, 1, 9, 2, 1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 9, 5, 4, 9, 3, 6, 1, -1, -1, -1, -1, -1, -1, -1},
{ 9, 10, 5, 5, 3, 6, 0, 3, 5, 9, 5, 6, -1, -1, -1, -1},
{ 1, 3, 6, 10, 2, 0, 4, 10, 0, 9, 10, 4, -1, -1, -1, -1},
{10, 2, 3, 6, 10, 3, 9, 10, 6, -1, -1, -1, -1, -1, -1, -1},
{ 4, 9, 5, 9, 10, 5, 3, 2, 7, -1, -1, -1, -1, -1, -1, -1},
{ 3, 2, 7, 9, 10, 5, 0, 9, 5, 1, 9, 0, -1, -1, -1, -1},
{ 3, 0, 4, 4, 10, 7, 9, 10, 4, 3, 4, 7, -1, -1, -1, -1},
{ 9, 10, 7, 3, 9, 7, 1, 9, 3, -1, -1, -1, -1, -1, -1, -1},
{ 1, 2, 6, 2, 7, 6, 9, 5, 4, 9, 10, 5, -1, -1, -1, -1},
{ 9, 10, 5, 0, 9, 5, 0, 6, 9, 2, 6, 0, 7, 6, 2, -1},
{ 7, 6, 1, 0, 7, 1, 0, 10, 7, 4, 10, 0, 9, 10, 4, -1},
{ 6, 9, 10, 6, 10, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 9, 0, 1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 9, 2, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 2, 4, 2, 1, 4, 11, 9, 6, -1, -1, -1, -1, -1, -1, -1},
{ 1, 3, 9, 9, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 3, 4, 3, 9, 4, 3, 11, 9, -1, -1, -1, -1, -1, -1, -1},
{11, 9, 3, 9, 1, 3, 5, 2, 0, -1, -1, -1, -1, -1, -1, -1},
{ 3, 11, 9, 9, 5, 2, 4, 5, 9, 3, 9, 2, -1, -1, -1, -1},
{11, 9, 6, 2, 7, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, 11, 9, 6, 7, 3, 2, -1, -1, -1, -1, -1, -1, -1},
{ 0, 5, 3, 5, 7, 3, 9, 6, 11, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 9, 5, 7, 3, 1, 5, 3, 4, 5, 1, -1, -1, -1, -1},
{ 9, 1, 11, 1, 7, 11, 1, 2, 7, -1, -1, -1, -1, -1, -1, -1},
{ 2, 7, 0, 0, 7, 4, 7, 11, 4, 11, 9, 4, -1, -1, -1, -1},
{ 9, 1, 11, 11, 1, 0, 5, 7, 11, 5, 11, 0, -1, -1, -1, -1},
{ 5, 7, 11, 9, 5, 11, 4, 5, 9, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 4, 4, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{11, 8, 6, 8, 1, 6, 8, 0, 1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 11, 4, 11, 8, 4, 2, 0, 5, -1, -1, -1, -1, -1, -1, -1},
{11, 8, 5, 5, 1, 6, 2, 1, 5, 11, 5, 6, -1, -1, -1, -1},
{ 3, 11, 1, 11, 4, 1, 11, 8, 4, -1, -1, -1, -1, -1, -1, -1},
{ 0, 11, 8, 3, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 0, 5, 11, 8, 4, 1, 11, 4, 3, 11, 1, -1, -1, -1, -1},
{11, 8, 5, 2, 11, 5, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1},
{ 8, 4, 11, 4, 6, 11, 2, 7, 3, -1, -1, -1, -1, -1, -1, -1},
{ 7, 3, 2, 8, 0, 1, 6, 8, 1, 11, 8, 6, -1, -1, -1, -1},
{ 5, 7, 0, 7, 3, 0, 4, 11, 8, 4, 6, 11, -1, -1, -1, -1},
{ 3, 5, 7, 1, 5, 3, 1, 8, 5, 6, 8, 1, 11, 8, 6, -1},
{ 1, 2, 4, 4, 2, 7, 11, 8, 4, 11, 4, 7, -1, -1, -1, -1},
{ 8, 0, 2, 7, 8, 2, 11, 8, 7, -1, -1, -1, -1, -1, -1, -1},
{11, 8, 4, 1, 11, 4, 1, 7, 11, 0, 7, 1, 5, 7, 0, -1},
{ 7, 11, 8, 7, 8, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 5, 8, 6, 11, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 0, 10, 5, 8, 11, 9, 6, -1, -1, -1, -1, -1, -1, -1},
{ 2, 0, 10, 0, 8, 10, 6, 11, 9, -1, -1, -1, -1, -1, -1, -1},
{11, 9, 6, 2, 1, 4, 8, 2, 4, 10, 2, 8, -1, -1, -1, -1},
{ 1, 3, 9, 3, 11, 9, 5, 8, 10, -1, -1, -1, -1, -1, -1, -1},
{ 5, 8, 10, 3, 11, 9, 4, 3, 9, 0, 3, 4, -1, -1, -1, -1},
{ 1, 11, 9, 3, 11, 1, 0, 8, 10, 2, 0, 10, -1, -1, -1, -1},
{ 3, 11, 9, 4, 3, 9, 4, 2, 3, 8, 2, 4, 10, 2, 8, -1},
{ 6, 11, 9, 5, 8, 10, 2, 7, 3, -1, -1, -1, -1, -1, -1, -1},
{ 3, 2, 7, 10, 5, 8, 0, 1, 4, 11, 9, 6, -1, -1, -1, -1},
{ 6, 11, 9, 0, 8, 10, 7, 0, 10, 3, 0, 7, -1, -1, -1, -1},
{10, 7, 8, 7, 3, 8, 3, 4, 8, 4, 3, 1, 6, 11, 9, -1},
{10, 5, 8, 1, 2, 7, 11, 1, 7, 9, 1, 11, -1, -1, -1, -1},
{ 0, 2, 4, 2, 7, 4, 7, 9, 4, 9, 7, 11, 10, 5, 8, -1},
{10, 0, 8, 7, 0, 10, 7, 1, 0, 11, 1, 7, 9, 1, 11, -1},
{ 4, 8, 10, 7, 4, 10, 4, 11, 9, 4, 7, 11, -1, -1, -1, -1},
{ 5, 4, 10, 4, 11, 10, 4, 6, 11, -1, -1, -1, -1, -1, -1, -1},
{11, 10, 5, 11, 5, 0, 6, 11, 0, 1, 6, 0, -1, -1, -1, -1},
{ 4, 6, 11, 11, 2, 0, 10, 2, 11, 4, 11, 0, -1, -1, -1, -1},
{ 2, 1, 6, 11, 2, 6, 10, 2, 11, -1, -1, -1, -1, -1, -1, -1},
{ 5, 4, 1, 1, 11, 10, 3, 11, 1, 5, 1, 10, -1, -1, -1, -1},
{ 3, 11, 10, 5, 3, 10, 0, 3, 5, -1, -1, -1, -1, -1, -1, -1},
{10, 2, 0, 4, 10, 0, 4, 11, 10, 1, 11, 4, 3, 11, 1, -1},
{11, 10, 2, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 7, 3, 4, 6, 11, 10, 4, 11, 5, 4, 10, -1, -1, -1, -1},
{ 1, 6, 0, 6, 11, 0, 11, 5, 0, 5, 11, 10, 7, 3, 2, -1},
{11, 4, 6, 10, 4, 11, 10, 0, 4, 7, 0, 10, 3, 0, 7, -1},
{10, 7, 3, 1, 10, 3, 10, 6, 11, 10, 1, 6, -1, -1, -1, -1},
{ 1, 2, 7, 11, 1, 7, 11, 4, 1, 10, 4, 11, 5, 4, 10, -1},
{ 0, 2, 7, 11, 0, 7, 0, 10, 5, 0, 11, 10, -1, -1, -1, -1},
{ 1, 0, 4, 10, 7, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 7, 11, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{11, 7, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, 7, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 0, 5, 11, 7, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 2, 4, 5, 2, 11, 7, 10, -1, -1, -1, -1, -1, -1, -1},
{ 7, 10, 11, 1, 3, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 0, 6, 0, 3, 6, 10, 11, 7, -1, -1, -1, -1, -1, -1, -1},
{ 5, 2, 0, 6, 1, 3, 11, 7, 10, -1, -1, -1, -1, -1, -1, -1},
{ 7, 10, 11, 4, 5, 2, 3, 4, 2, 6, 4, 3, -1, -1, -1, -1},
{10, 11, 2, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 11, 2, 11, 3, 2, 4, 0, 1, -1, -1, -1, -1, -1, -1, -1},
{11, 3, 10, 3, 5, 10, 3, 0, 5, -1, -1, -1, -1, -1, -1, -1},
{ 4, 5, 1, 1, 5, 10, 11, 3, 1, 11, 1, 10, -1, -1, -1, -1},
{ 1, 2, 6, 2, 11, 6, 2, 10, 11, -1, -1, -1, -1, -1, -1, -1},
{ 6, 4, 11, 11, 4, 0, 2, 10, 11, 2, 11, 0, -1, -1, -1, -1},
{10, 11, 5, 5, 11, 0, 11, 6, 0, 6, 1, 0, -1, -1, -1, -1},
{ 4, 5, 10, 11, 4, 10, 6, 4, 11, -1, -1, -1, -1, -1, -1, -1},
{ 8, 4, 9, 7, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 8, 1, 9, 8, 7, 10, 11, -1, -1, -1, -1, -1, -1, -1},
{ 9, 8, 4, 2, 0, 5, 7, 10, 11, -1, -1, -1, -1, -1, -1, -1},
{ 7, 10, 11, 1, 9, 8, 5, 1, 8, 2, 1, 5, -1, -1, -1, -1},
{ 4, 9, 8, 7, 10, 11, 3, 6, 1, -1, -1, -1, -1, -1, -1, -1},
{11, 7, 10, 0, 3, 6, 9, 0, 6, 8, 0, 9, -1, -1, -1, -1},
{ 9, 8, 4, 1, 3, 6, 2, 0, 5, 7, 10, 11, -1, -1, -1, -1},
{ 8, 5, 9, 5, 2, 9, 2, 6, 9, 6, 2, 3, 7, 10, 11, -1},
{ 3, 2, 11, 2, 10, 11, 4, 9, 8, -1, -1, -1, -1, -1, -1, -1},
{11, 3, 10, 3, 2, 10, 8, 1, 9, 8, 0, 1, -1, -1, -1, -1},
{ 8, 4, 9, 3, 0, 5, 10, 3, 5, 11, 3, 10, -1, -1, -1, -1},
{ 1, 9, 8, 5, 1, 8, 5, 3, 1, 10, 3, 5, 11, 3, 10, -1},
{ 9, 8, 4, 2, 10, 11, 6, 2, 11, 1, 2, 6, -1, -1, -1, -1},
{ 2, 10, 11, 6, 2, 11, 6, 0, 2, 9, 0, 6, 8, 0, 9, -1},
{ 5, 10, 0, 10, 11, 0, 11, 1, 0, 1, 11, 6, 9, 8, 4, -1},
{ 5, 10, 11, 6, 5, 11, 5, 9, 8, 5, 6, 9, -1, -1, -1, -1},
{11, 7, 8, 8, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{11, 7, 8, 7, 5, 8, 1, 4, 0, -1, -1, -1, -1, -1, -1, -1},
{ 0, 8, 2, 8, 7, 2, 8, 11, 7, -1, -1, -1, -1, -1, -1, -1},
{ 2, 1, 4, 4, 11, 7, 8, 11, 4, 2, 4, 7, -1, -1, -1, -1},
{ 5, 8, 7, 8, 11, 7, 1, 3, 6, -1, -1, -1, -1, -1, -1, -1},
{11, 5, 8, 7, 5, 11, 6, 4, 0, 3, 6, 0, -1, -1, -1, -1},
{ 3, 6, 1, 8, 11, 7, 2, 8, 7, 0, 8, 2, -1, -1, -1, -1},
{ 8, 11, 7, 2, 8, 7, 2, 4, 8, 3, 4, 2, 6, 4, 3, -1},
{ 8, 11, 5, 11, 2, 5, 11, 3, 2, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, 11, 3, 2, 5, 11, 2, 8, 11, 5, -1, -1, -1, -1},
{11, 0, 8, 11, 3, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{11, 3, 1, 4, 11, 1, 8, 11, 4, -1, -1, -1, -1, -1, -1, -1},
{ 8, 11, 5, 5, 11, 6, 1, 2, 5, 1, 5, 6, -1, -1, -1, -1},
{ 6, 4, 0, 2, 6, 0, 2, 11, 6, 5, 11, 2, 8, 11, 5, -1},
{ 8, 11, 6, 1, 8, 6, 0, 8, 1, -1, -1, -1, -1, -1, -1, -1},
{11, 6, 4, 11, 4, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 7, 5, 11, 5, 9, 11, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1},
{ 1, 9, 11, 11, 5, 0, 7, 5, 11, 1, 11, 0, -1, -1, -1, -1},
{ 7, 2, 0, 7, 0, 4, 11, 7, 4, 9, 11, 4, -1, -1, -1, -1},
{ 1, 9, 11, 7, 1, 11, 2, 1, 7, -1, -1, -1, -1, -1, -1, -1},
{ 3, 6, 1, 5, 4, 9, 11, 5, 9, 7, 5, 11, -1, -1, -1, -1},
{ 0, 3, 6, 9, 0, 6, 9, 5, 0, 11, 5, 9, 7, 5, 11, -1},
{ 9, 11, 4, 11, 7, 4, 7, 0, 4, 0, 7, 2, 3, 6, 1, -1},
{ 9, 11, 7, 2, 9, 7, 9, 3, 6, 9, 2, 3, -1, -1, -1, -1},
{11, 3, 9, 9, 3, 2, 5, 4, 9, 5, 9, 2, -1, -1, -1, -1},
{11, 3, 2, 5, 11, 2, 5, 9, 11, 0, 9, 5, 1, 9, 0, -1},
{ 3, 0, 4, 9, 3, 4, 11, 3, 9, -1, -1, -1, -1, -1, -1, -1},
{ 3, 1, 9, 3, 9, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 4, 9, 11, 5, 9, 11, 2, 5, 6, 2, 11, 1, 2, 6, -1},
{11, 6, 9, 0, 2, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{11, 6, 1, 0, 11, 1, 11, 4, 9, 11, 0, 4, -1, -1, -1, -1},
{11, 6, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 9, 6, 10, 10, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 7, 10, 6, 10, 9, 6, 0, 1, 4, -1, -1, -1, -1, -1, -1, -1},
{ 9, 6, 10, 6, 7, 10, 0, 5, 2, -1, -1, -1, -1, -1, -1, -1},
{ 5, 1, 4, 2, 1, 5, 10, 9, 6, 7, 10, 6, -1, -1, -1, -1},
{10, 9, 7, 9, 3, 7, 9, 1, 3, -1, -1, -1, -1, -1, -1, -1},
{ 0, 3, 4, 4, 3, 7, 10, 9, 4, 10, 4, 7, -1, -1, -1, -1},
{ 2, 0, 5, 9, 1, 3, 7, 9, 3, 10, 9, 7, -1, -1, -1, -1},
{ 4, 5, 2, 3, 4, 2, 3, 9, 4, 7, 9, 3, 10, 9, 7, -1},
{ 2, 10, 3, 10, 6, 3, 10, 9, 6, -1, -1, -1, -1, -1, -1, -1},
{ 0, 1, 4, 10, 9, 6, 3, 10, 6, 2, 10, 3, -1, -1, -1, -1},
{10, 9, 5, 6, 3, 5, 3, 0, 5, 5, 9, 6, -1, -1, -1, -1},
{10, 9, 6, 3, 10, 6, 3, 5, 10, 1, 5, 3, 4, 5, 1, -1},
{ 1, 10, 9, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{10, 9, 4, 0, 10, 4, 2, 10, 0, -1, -1, -1, -1, -1, -1, -1},
{ 9, 1, 0, 5, 9, 0, 10, 9, 5, -1, -1, -1, -1, -1, -1, -1},
{ 5, 10, 9, 5, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 6, 8, 6, 10, 8, 6, 7, 10, -1, -1, -1, -1, -1, -1, -1},
{ 8, 0, 1, 1, 7, 10, 6, 7, 1, 8, 1, 10, -1, -1, -1, -1},
{ 5, 2, 0, 6, 7, 10, 8, 6, 10, 4, 6, 8, -1, -1, -1, -1},
{ 6, 7, 10, 8, 6, 10, 8, 1, 6, 5, 1, 8, 2, 1, 5, -1},
{ 3, 7, 10, 3, 10, 8, 1, 3, 8, 4, 1, 8, -1, -1, -1, -1},
{ 0, 3, 7, 10, 0, 7, 8, 0, 10, -1, -1, -1, -1, -1, -1, -1},
{ 4, 1, 8, 1, 3, 8, 3, 10, 8, 10, 3, 7, 2, 0, 5, -1},
{ 8, 5, 2, 3, 8, 2, 8, 7, 10, 8, 3, 7, -1, -1, -1, -1},
{ 2, 10, 8, 3, 2, 8, 6, 3, 8, 4, 6, 8, -1, -1, -1, -1},
{ 8, 0, 1, 6, 8, 1, 6, 10, 8, 3, 10, 6, 2, 10, 3, -1},
{ 3, 0, 5, 10, 3, 5, 10, 6, 3, 8, 6, 10, 4, 6, 8, -1},
{ 5, 10, 8, 6, 3, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 10, 8, 4, 2, 8, 1, 2, 4, -1, -1, -1, -1, -1, -1, -1},
{10, 8, 0, 10, 0, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 0, 5, 10, 1, 5, 1, 8, 4, 1, 10, 8, -1, -1, -1, -1},
{ 5, 10, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 7, 9, 7, 8, 9, 7, 5, 8, -1, -1, -1, -1, -1, -1, -1},
{ 1, 4, 0, 7, 5, 8, 9, 7, 8, 6, 7, 9, -1, -1, -1, -1},
{ 6, 7, 9, 9, 7, 2, 0, 8, 9, 0, 9, 2, -1, -1, -1, -1},
{ 2, 1, 4, 8, 2, 4, 8, 7, 2, 9, 7, 8, 6, 7, 9, -1},
{ 7, 5, 8, 8, 1, 3, 9, 1, 8, 7, 8, 3, -1, -1, -1, -1},
{ 7, 5, 8, 9, 7, 8, 9, 3, 7, 4, 3, 9, 0, 3, 4, -1},
{ 9, 1, 3, 7, 9, 3, 7, 8, 9, 2, 8, 7, 0, 8, 2, -1},
{ 8, 9, 4, 3, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 3, 2, 6, 6, 2, 9, 2, 5, 9, 5, 8, 9, -1, -1, -1, -1},
{ 6, 3, 9, 3, 2, 9, 2, 8, 9, 8, 2, 5, 0, 1, 4, -1},
{ 0, 8, 9, 6, 0, 9, 3, 0, 6, -1, -1, -1, -1, -1, -1, -1},
{ 8, 9, 6, 3, 8, 6, 8, 1, 4, 8, 3, 1, -1, -1, -1, -1},
{ 1, 2, 5, 8, 1, 5, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1},
{ 9, 4, 0, 2, 9, 0, 9, 5, 8, 9, 2, 5, -1, -1, -1, -1},
{ 1, 0, 8, 1, 8, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 8, 9, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 5, 4, 7, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 7, 5, 0, 1, 7, 0, 6, 7, 1, -1, -1, -1, -1, -1, -1, -1},
{ 6, 7, 2, 0, 6, 2, 4, 6, 0, -1, -1, -1, -1, -1, -1, -1},
{ 1, 6, 7, 1, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 5, 4, 1, 3, 5, 1, 7, 5, 3, -1, -1, -1, -1, -1, -1, -1},
{ 5, 0, 3, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 1, 3, 7, 4, 3, 4, 2, 0, 4, 7, 2, -1, -1, -1, -1},
{ 7, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 4, 6, 3, 2, 4, 3, 5, 4, 2, -1, -1, -1, -1, -1, -1, -1},
{ 6, 3, 2, 5, 6, 2, 6, 0, 1, 6, 5, 0, -1, -1, -1, -1},
{ 0, 4, 6, 0, 6, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 3, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 5, 4, 2, 4, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 2, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{ 1, 0, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../seek.h"
char *info = ( "test stupid cubes isosurfaces." );
int
28 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
limnPolyData *pld;
gageContext *gctx=NULL;
gagePerVolume *pvl;
Nrrd *nin, *nmeas;
double isoval, kparm[3];
seekContext *sctx;
FILE *file;
int usegage, E, hack;
size_t samples[3];
me = argv[0];
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume to surface",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "v", "iso", airTypeDouble, 1, 1, &isoval, NULL,
"isovalue" );
hestOptAdd( &hopt, "g", NULL, airTypeInt, 0, 0, &usegage, NULL,
"use gage too" );
hestOptAdd( &hopt, "hack", NULL, airTypeInt, 0, 0, &hack, NULL, "hack" );
hestOptAdd( &hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd",
"output file to save LMPD into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
pld = limnPolyDataNew( );
airMopAdd( mop, pld, ( airMopper )limnPolyDataNix, airMopAlways );
file = airFopen( outS, stdout, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
sctx = seekContextNew( );
airMopAdd( mop, sctx, ( airMopper )seekContextNix, airMopAlways );
if ( usegage ) {
gctx = gageContextNew( );
airMopAdd( mop, gctx, ( airMopper )gageContextNix, airMopAlways );
ELL_3V_SET( kparm, 2, 1.0, 0.0 );
if ( !( pvl = gagePerVolumeNew( gctx, nin, gageKindScl ) )
|| gagePerVolumeAttach( gctx, pvl )
|| gageKernelSet( gctx, gageKernel00, nrrdKernelBCCubic, kparm )
|| gageKernelSet( gctx, gageKernel11, nrrdKernelBCCubicD, kparm )
|| gageKernelSet( gctx, gageKernel22, nrrdKernelBCCubicDD, kparm )
|| gageQueryItemOn( gctx, pvl, gageSclValue )
|| gageQueryItemOn( gctx, pvl, gageSclNormal )
|| ( usegage
&& ( gageQueryItemOn( gctx, pvl, gageSclGradVec )
|| gageQueryItemOn( gctx, pvl, gageSclHessEval )
|| gageQueryItemOn( gctx, pvl, gageSclHessEvec )
|| gageQueryItemOn( gctx, pvl, gageSclHessEvec2 ) ) )
|| gageUpdate( gctx ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
seekVerboseSet( sctx, 10 );
E = 0;
if ( usegage ) {
if ( !E ) E |= seekDataSet( sctx, NULL, gctx, 0 );
if ( hack ) {
ELL_3V_SET( samples, 5, 5, 5 );
if ( !E ) E |= seekSamplesSet( sctx, samples );
if ( !E ) E |= seekItemGradientSet( sctx, gageSclGradVec );
if ( !E ) E |= seekItemEigensystemSet( sctx, gageSclHessEval,
gageSclHessEvec );
if ( !E ) E |= seekItemNormalSet( sctx, gageSclHessEvec2 );
} else {
if ( !E ) E |= seekItemScalarSet( sctx, gageSclValue );
if ( !E ) E |= seekItemNormalSet( sctx, gageSclNormal );
}
} else {
if ( !E ) E |= seekDataSet( sctx, nin, NULL, 0 );
}
if ( !E ) E |= seekNormalsFindSet( sctx, AIR_TRUE );
if ( hack ) {
if ( !E ) E |= seekTypeSet( sctx, seekTypeRidgeSurface );
} else {
if ( !E ) E |= seekTypeSet( sctx, seekTypeIsocontour );
if ( !E ) E |= seekIsovalueSet( sctx, isoval );
}
if ( !E ) E |= seekUpdate( sctx );
if ( !E ) E |= seekExtract( sctx, pld );
if ( E ) {
airMopAdd( mop, err = biffGetDone( SEEK ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: extraction time = %g\n", me, sctx->time );
nmeas = nrrdNew( );
airMopAdd( mop, nmeas, ( airMopper )nrrdNuke, airMopAlways );
if ( limnPolyDataCCFind( pld )
|| limnPolyDataPrimitiveArea( nmeas, pld )
|| limnPolyDataPrimitiveSort( pld, nmeas ) ) {
err = biffGetDone( LIMN );
fprintf( stderr, "%s: trouble sorting:\n%s", me, err );
free( err );
}
if ( limnPolyDataWriteLMPD( file, pld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../seek.h"
char *info = ( "test crease surface extraction." );
int
28 probeParseKind( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[] = "probeParseKind";
gageKind **kindP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
kindP = ( gageKind ** )ptr;
airToLower( str );
if ( !strcmp( gageKindScl->name, str ) ) {
*kindP = gageKindScl;
} else if ( !strcmp( gageKindVec->name, str ) ) {
*kindP = gageKindVec;
} else {
sprintf( err, "%s: not \"%s\" or \"%s\"", me,
gageKindScl->name, gageKindVec->name );
return 1;
}
return 0;
}
void *
52 probeParseKindDestroy( void *ptr ) {
gageKind *kind;
if ( ptr ) {
kind = AIR_CAST( gageKind *, ptr );
}
return NULL;
}
hestCB probeKindHestCB = {
sizeof( gageKind * ),
"kind",
probeParseKind,
probeParseKindDestroy
};
int
69 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
limnPolyData *pld, *pldSub;
gageContext *gctx=NULL;
gagePerVolume *pvl;
Nrrd *nin, *nmeas;
double kparm[3], strength, scaling[3];
seekContext *sctx;
FILE *file;
unsigned int ncc;
size_t samples[3];
gageKind *kind;
char *itemGradS; /* , *itemEvalS[2], *itemEvecS[2]; */
int itemGrad; /* , itemEval[2], itemEvec[2]; */
int E;
me = argv[0];
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input volume to analyze",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "k", "kind", airTypeOther, 1, 1, &kind, NULL,
"\"kind\" of volume ( \"scalar\", \"vector\", \"tensor\" )",
NULL, NULL, &probeKindHestCB );
hestOptAdd( &hopt, "s", "strength", airTypeDouble, 1, 1, &strength, "0.01",
"strength" );
hestOptAdd( &hopt, "gi", "grad item", airTypeString, 1, 1, &itemGradS, NULL,
"item for gradient vector" );
hestOptAdd( &hopt, "c", "scaling", airTypeDouble, 3, 3, scaling, "1 1 1",
"amount by which to up/down-sample on each spatial axis" );
hestOptAdd( &hopt, "n", "# CC", airTypeUInt, 1, 1, &ncc, "0",
"if non-zero, number of CC to save" );
hestOptAdd( &hopt, "o", "output LMPD", airTypeString, 1, 1, &outS, "out.lmpd",
"output file to save LMPD into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
itemGrad = airEnumVal( kind->enm, itemGradS );
pld = limnPolyDataNew( );
airMopAdd( mop, pld, ( airMopper )limnPolyDataNix, airMopAlways );
pldSub = limnPolyDataNew( );
airMopAdd( mop, pldSub, ( airMopper )limnPolyDataNix, airMopAlways );
file = airFopen( outS, stdout, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
sctx = seekContextNew( );
airMopAdd( mop, sctx, ( airMopper )seekContextNix, airMopAlways );
gctx = gageContextNew( );
airMopAdd( mop, gctx, ( airMopper )gageContextNix, airMopAlways );
ELL_3V_SET( kparm, 1, 1.0, 0.0 );
if ( !( pvl = gagePerVolumeNew( gctx, nin, kind ) )
|| gagePerVolumeAttach( gctx, pvl )
|| gageKernelSet( gctx, gageKernel00, nrrdKernelBCCubic, kparm )
|| gageKernelSet( gctx, gageKernel11, nrrdKernelBCCubicD, kparm )
|| gageKernelSet( gctx, gageKernel22, nrrdKernelBCCubicDD, kparm )
|| gageQueryItemOn( gctx, pvl, itemGrad )
|| gageQueryItemOn( gctx, pvl, gageSclHessEval )
|| gageQueryItemOn( gctx, pvl, gageSclHessEval2 )
|| gageQueryItemOn( gctx, pvl, gageSclHessEvec )
|| gageQueryItemOn( gctx, pvl, gageSclHessEvec2 )
|| gageUpdate( gctx ) ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
seekVerboseSet( sctx, 10 );
E = 0;
if ( !E ) E |= seekDataSet( sctx, NULL, gctx, 0 );
ELL_3V_SET( samples,
scaling[0]*nin->axis[kind->baseDim + 0].size,
scaling[1]*nin->axis[kind->baseDim + 1].size,
scaling[2]*nin->axis[kind->baseDim + 2].size );
if ( !E ) E |= seekSamplesSet( sctx, samples );
if ( !E ) E |= seekItemGradientSet( sctx, itemGrad );
if ( !E ) E |= seekItemEigensystemSet( sctx, gageSclHessEval,
gageSclHessEvec );
if ( !E ) E |= seekItemNormalSet( sctx, gageSclHessEvec2 );
if ( !E ) E |= seekStrengthUseSet( sctx, AIR_TRUE );
if ( !E ) E |= seekStrengthSet( sctx, -1, strength );
if ( !E ) E |= seekItemStrengthSet( sctx, gageSclHessEval2 );
if ( !E ) E |= seekNormalsFindSet( sctx, AIR_TRUE );
if ( !E ) E |= seekTypeSet( sctx, seekTypeRidgeSurface );
if ( !E ) E |= seekUpdate( sctx );
if ( !E ) E |= seekExtract( sctx, pld );
if ( E ) {
airMopAdd( mop, err = biffGetDone( SEEK ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: extraction time = %g\n", me, sctx->time );
nmeas = nrrdNew( );
airMopAdd( mop, nmeas, ( airMopper )nrrdNuke, airMopAlways );
if ( limnPolyDataVertexWindingFix( pld, AIR_TRUE )
|| limnPolyDataVertexWindingFlip( pld )
|| limnPolyDataVertexNormals( pld )
|| limnPolyDataCCFind( pld )
|| limnPolyDataPrimitiveArea( nmeas, pld )
|| limnPolyDataPrimitiveSort( pld, nmeas ) ) {
err = biffGetDone( LIMN );
fprintf( stderr, "%s: trouble sorting:\n%s", me, err );
free( err );
}
if ( ncc > 1 ) {
double *meas;
unsigned int ccIdx;
nrrdSave( "meas.nrrd", nmeas, NULL );
ncc = AIR_MIN( ncc, nmeas->axis[0].size );
meas = AIR_CAST( double *, nmeas->data );
for ( ccIdx=ncc; ccIdx<nmeas->axis[0].size; ccIdx++ ) {
meas[ccIdx] = 0.0;
}
if ( !E ) E |= limnPolyDataPrimitiveSelect( pldSub, pld, nmeas );
if ( !E ) E |= limnPolyDataWriteLMPD( file, pldSub );
} else {
if ( !E ) E |= limnPolyDataWriteLMPD( file, pld );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009, 2008 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* This file collects functions that implement extraction of crease
* surfaces as proposed in Schultz / Theisel / Seidel, "Crease
* Surfaces: From Theory to Extraction and Application to Diffusion
* Tensor MRI", IEEE TVCG 16( 1 ):109-119, 2010 */
#include "seek.h"
#include "privateSeek.h"
/* private helper routines for the T-based extraction */
/* Converts a Hessian into the transformed tensor T ( cf. paper )
*
* T is a 9-vector representing the output
* evals is a 3-vector ( eigenvalues of the Hessian )
* evecs is a 9-vector ( eigenvectors of the Hessian )
* evalDiffThresh is the threshold parameter theta ( cf. Eq. ( 4 ) in paper )
* ridge is non-zero if we are looking for a ridge ( zero for valley )
*/
void
41 _seekHess2T( double *T, const double *evals, const double *evecs,
const double evalDiffThresh, const char ridge ) {
double lambdas[3]={0.0, 0.0, 0.0};
double tmpMat[9], diag[9], evecsT[9];
if ( ridge ) {
double diff = evals[1]-evals[2];
lambdas[0]=lambdas[1]=1.0;
if ( diff<evalDiffThresh )
lambdas[2]=( 1.0-diff/evalDiffThresh )*( 1.0-diff/evalDiffThresh );
} else {
double diff = evals[0]-evals[1];
lambdas[1]=lambdas[2]=1.0;
if ( diff<evalDiffThresh )
lambdas[0]=( 1.0-diff/evalDiffThresh )*( 1.0-diff/evalDiffThresh );
}
ELL_3M_ZERO_SET( diag );
ELL_3M_DIAG_SET( diag, lambdas[0], lambdas[1], lambdas[2] );
ELL_3M_TRANSPOSE( evecsT, evecs );
ELL_3M_MUL( tmpMat, diag, evecs );
ELL_3M_MUL( T, evecsT, tmpMat );
}
/* Converts a Hessian derivative into a derivative of the transformed
* tensor field T
*
* Tder is a 9-vector representing the output
* hessder is a 9-vector ( Hessian derivative )
* evals is a 3-vector ( eigenvalues of the Hessian value )
* evecs is a 9-vector ( eigenvectors of the Hessian value )
* evalDiffThresh is the threshold parameter theta ( cf. Eq. ( 4 ) in the paper )
* ridge is non-zero if we are looking for a ridge ( zero for valley )
*/
void
74 _seekHessder2Tder( double *Tder, const double *hessder, const double *evals,
const double *evecs, const double evalDiffThresh,
const char ridge ) {
double evecTrans[9];
double hessderE[9]; /* Hessian derivative in eigenframe */
double tmp[9];
ELL_3M_TRANSPOSE( evecTrans, evecs );
ell_3m_mul_d( tmp, hessder, evecTrans );
ell_3m_mul_d( hessderE, evecs, tmp );
if ( ridge ) {
double diff=evals[1]-evals[2];
double l3;
double l3der;
if ( diff<evalDiffThresh )
l3=( 1.0-diff/evalDiffThresh )*( 1.0-diff/evalDiffThresh );
else l3=0.0;
hessderE[2]*=( 1.0-l3 )/( evals[0]-evals[2] );
hessderE[6]=hessderE[2];
hessderE[5]*=( 1.0-l3 )/( evals[1]-evals[2] );
hessderE[7]=hessderE[5];
if ( diff<evalDiffThresh )
l3der = 2/evalDiffThresh*( 1-diff/evalDiffThresh )*
( hessderE[8]-hessderE[4] );
else
l3der=0;
hessderE[8]=l3der;
hessderE[0]=hessderE[1]=hessderE[3]=hessderE[4]=0.0;
} else {
double diff=evals[0]-evals[1];
double l1;
double l1der;
if ( diff<evalDiffThresh )
l1=( 1.0-diff/evalDiffThresh )*( 1.0-diff/evalDiffThresh );
else l1=0.0;
hessderE[1]*=( l1-1.0 )/( evals[0]-evals[1] );
hessderE[3]=hessderE[1];
hessderE[2]*=( l1-1.0 )/( evals[0]-evals[2] );
hessderE[6]=hessderE[2];
if ( diff<evalDiffThresh )
l1der = 2/evalDiffThresh*( 1-diff/evalDiffThresh )*
( hessderE[4]-hessderE[0] );
else
l1der = 0;
hessderE[0]=l1der;
hessderE[4]=hessderE[5]=hessderE[7]=hessderE[8]=0.0;
}
ell_3m_mul_d( tmp, hessderE, evecs );
ell_3m_mul_d( Tder, evecTrans, tmp );
}
static int
129 findFeatureIntersection( double *results, double *Tleft,
double *hessleft, double *gleft,
double *Tright, double *hessright,
double *gright, double idxleft,
double idxright, char ridge,
const double evalDiffThresh,
const double dotThresh ) {
double Tdp = ELL_3V_DOT( Tleft, Tright ) + ELL_3V_DOT( Tleft+3, Tright+3 ) +
ELL_3V_DOT( Tleft+6, Tright+6 );
double denom_l = sqrt( ELL_3V_DOT( Tleft, Tleft ) + ELL_3V_DOT( Tleft+3, Tleft+3 )
+ ELL_3V_DOT( Tleft+6, Tleft+6 ) ),
denom_r = sqrt( ELL_3V_DOT( Tright, Tright ) + ELL_3V_DOT( Tright+3, Tright+3 )
+ ELL_3V_DOT( Tright+6, Tright+6 ) );
if ( Tdp/( denom_l*denom_r )<dotThresh &&
idxright-idxleft>0.24 ) { /* do a recursive step */
double idxcenter = 0.5*( idxleft+idxright );
/* simply interpolate Hessian linearly */
double hessnew[9];
double evals[3], evecs[9];
double Tnew[9], gradnew[3];
int retval;
ELL_3M_LERP( hessnew, 0.5, hessleft, hessright );
ell_3m_eigensolve_d( evals, evecs, hessnew, AIR_TRUE );
_seekHess2T( Tnew, evals, evecs, evalDiffThresh, ridge );
ELL_3V_LERP( gradnew, 0.5, gleft, gright );
retval = findFeatureIntersection( results, Tleft, hessleft,
gleft, Tnew, hessnew, gradnew,
idxleft, idxcenter,
ridge, evalDiffThresh, dotThresh );
retval += findFeatureIntersection( results+retval, Tnew, hessnew,
gradnew, Tright, hessright, gright,
idxcenter, idxright,
ridge, evalDiffThresh, dotThresh );
return retval;
} else {
double d1[3], d4[3];
ell_3mv_mul_d( d1, Tleft, gleft );
ELL_3V_SUB( d1, d1, gleft );
ell_3mv_mul_d( d4, Tright, gright );
ELL_3V_SUB( d4, d4, gright );
if ( ELL_3V_DOT( d1, d4 )<0 ) { /* mark edge as crossed */
/* find assumed intersection point */
double diff[3], dlen, alpha;
ELL_3V_SUB( diff, d4, d1 );
dlen=ELL_3V_LEN( diff );
if ( dlen>1e-5 ) {
double ap=-ELL_3V_DOT( d1, diff )/dlen;
alpha = ap/dlen;
} else
alpha = 0.5;
*results = ( 1-alpha )*idxleft+alpha*idxright;
return 1;
}
}
return 0;
}
/* Assuming ( simplistic ) linearly interpolated Hessians and gradients,
* computes the analytical normal of the crease surface.
* The result is _not_ normalized. */
static void
196 computeGradientLin( double *result, double *T, double *g,
double *Txm, double *gxm, double *Txp, double *gxp,
double *Tym, double *gym, double *Typ, double *gyp,
double *Tzm, double *gzm, double *Tzp, double *gzp ) {
double Tder[9];
double gder[3];
double tmp[3], tmp1[3], tmp2[3];
double derxv[3], deryv[3], derzv[3];
ELL_3M_SUB( Tder, Txp, Txm );
ELL_3V_SUB( gder, gxp, gxm );
ell_3mv_mul_d( tmp, T, gder );
ELL_3V_SUB( tmp, tmp, gder );
ell_3mv_mul_d( derxv, Tder, g );
ELL_3V_ADD2( derxv, derxv, tmp );
ELL_3M_SUB( Tder, Typ, Tym );
ELL_3V_SUB( gder, gyp, gym );
ell_3mv_mul_d( tmp, T, gder );
ELL_3V_SUB( tmp, tmp, gder );
ell_3mv_mul_d( deryv, Tder, g );
ELL_3V_ADD2( deryv, deryv, tmp );
ELL_3M_SUB( Tder, Tzp, Tzm );
ELL_3V_SUB( gder, gzp, gzm );
ell_3mv_mul_d( tmp, T, gder );
ELL_3V_SUB( tmp, tmp, gder );
ell_3mv_mul_d( derzv, Tder, g );
ELL_3V_ADD2( derzv, derzv, tmp );
/* accumulate a gradient */
tmp1[0]=derxv[0]; tmp1[1]=deryv[0]; tmp1[2]=derzv[0];
tmp2[0]=derxv[1]; tmp2[1]=deryv[1]; tmp2[2]=derzv[1];
if ( ELL_3V_DOT( tmp1, tmp2 )<0 )
ELL_3V_SCALE( tmp2, -1.0, tmp2 );
ELL_3V_ADD2( tmp1, tmp1, tmp2 );
tmp2[0]=derxv[2]; tmp2[1]=deryv[2]; tmp2[2]=derzv[2];
if ( ELL_3V_DOT( tmp1, tmp2 )<0 )
ELL_3V_SCALE( tmp2, -1.0, tmp2 );
ELL_3V_ADD2( result, tmp1, tmp2 );
}
/* Given a unique edge ID and an intersection point given by some value
* alpha \in [0, 1], compute the crease surface normal at that point */
static void
240 computeEdgeGradient( seekContext *sctx, baggage *bag, double *res,
unsigned int xi, unsigned int yi, char edgeid, double alpha )
{
double Txm[9], Txp[9], Tym[9], Typ[9], Tzm[9], Tzp[9], T[9],
gxm[3], gxp[3], gym[3], gyp[3], gzm[3], gzp[3], g[3];
unsigned int sx = AIR_CAST( unsigned int, sctx->sx );
unsigned int sy = AIR_CAST( unsigned int, sctx->sy );
unsigned int sz = AIR_CAST( unsigned int, sctx->sz );
unsigned int si = xi + sx*yi;
unsigned int six = xi + 1 + sx*yi, siX = xi - 1 + sx*yi;
unsigned int siy = xi + sx*( yi+1 ), siY = xi + sx*( yi-1 );
unsigned int sixy = xi + 1 + sx*( yi+1 ),
sixY = xi + 1 + sx*( yi-1 ),
siXy = xi - 1 + sx*( yi+1 );
/* many special cases needed to fill Txm, gxm, etc. :-( */
switch ( edgeid ) {
case 0:
ELL_3M_LERP( T, alpha, sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*six ) );
ELL_3V_LERP( g, alpha, sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 0+2*six ) );
ELL_3M_LERP( Tzp, alpha, sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gzp, alpha, sctx->grad + 3*( 1+2*si ), sctx->grad + 3*( 1+2*six ) );
if ( bag->zi==0 ) {
ELL_3M_COPY( Tzm, T ); ELL_3V_COPY( gzm, g );
} else {
ELL_3M_LERP( Tzm, alpha, sctx->tcontext + 9*( 0+2*si ),
sctx->tcontext + 9*( 0+2*six ) );
ELL_3V_LERP( gzm, alpha, sctx->gradcontext + 3*( 0+2*si ),
sctx->gradcontext + 3*( 0+2*six ) );
ELL_3M_SCALE( Tzm, 0.5, Tzm ); ELL_3M_SCALE( Tzp, 0.5, Tzp );
ELL_3V_SCALE( gzm, 0.5, gzm ); ELL_3V_SCALE( gzp, 0.5, gzp );
}
if ( yi==0 ) {
ELL_3M_COPY( Tym, T ); ELL_3V_COPY( gym, g );
} else {
ELL_3M_LERP( Tym, alpha, sctx->t + 9*( 0+2*siY ), sctx->t + 9*( 0+2*sixY ) );
ELL_3V_LERP( gym, alpha, sctx->grad + 3*( 0+2*siY ),
sctx->grad + 3*( 0+2*sixY ) );
}
if ( yi==sy-1 ) {
ELL_3M_COPY( Typ, T ); ELL_3V_COPY( gyp, g );
} else {
ELL_3M_LERP( Typ, alpha, sctx->t + 9*( 0+2*siy ), sctx->t + 9*( 0+2*sixy ) );
ELL_3V_LERP( gyp, alpha, sctx->grad + 3*( 0+2*siy ),
sctx->grad + 3*( 0+2*sixy ) );
}
if ( yi!=0 && yi!=sy-1 ) {
ELL_3M_SCALE( Tym, 0.5, Tym ); ELL_3M_SCALE( Typ, 0.5, Typ );
ELL_3V_SCALE( gym, 0.5, gym ); ELL_3V_SCALE( gyp, 0.5, gyp );
}
computeGradientLin( res, T, g,
sctx->t + 9*( 0+2*si ), sctx->grad + 3*( 0+2*si ),
sctx->t + 9*( 0+2*six ), sctx->grad + 3*( 0+2*six ),
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
break;
case 1:
ELL_3M_LERP( T, alpha, sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*siy ) );
ELL_3V_LERP( g, alpha, sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 0+2*siy ) );
ELL_3M_LERP( Tzp, alpha, sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gzp, alpha, sctx->grad + 3*( 1+2*si ), sctx->grad + 3*( 1+2*siy ) );
if ( bag->zi==0 ) {
ELL_3M_COPY( Tzm, T ); ELL_3V_COPY( gzm, g );
} else {
ELL_3M_LERP( Tzm, alpha, sctx->tcontext + 9*( 0+2*si ),
sctx->tcontext + 9*( 0+2*siy ) );
ELL_3V_LERP( gzm, alpha, sctx->gradcontext + 3*( 0+2*si ),
sctx->gradcontext + 3*( 0+2*siy ) );
ELL_3M_SCALE( Tzm, 0.5, Tzm ); ELL_3M_SCALE( Tzp, 0.5, Tzp );
ELL_3V_SCALE( gzm, 0.5, gzm ); ELL_3V_SCALE( gzp, 0.5, gzp );
}
if ( xi==0 ) {
ELL_3M_COPY( Txm, T ); ELL_3V_COPY( gxm, g );
} else {
ELL_3M_LERP( Txm, alpha, sctx->t + 9*( 0+2*siX ), sctx->t + 9*( 0+2*siXy ) );
ELL_3V_LERP( gxm, alpha, sctx->grad + 3*( 0+2*siX ),
sctx->grad + 3*( 0+2*siXy ) );
}
if ( xi==sx-1 ) {
ELL_3M_COPY( Txp, T ); ELL_3V_COPY( gxm, g );
} else {
ELL_3M_LERP( Txp, alpha, sctx->t + 9*( 0+2*six ), sctx->t + 9*( 0+2*sixy ) );
ELL_3V_LERP( gxp, alpha, sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 0+2*sixy ) );
}
if ( xi!=0 && xi!=sx-1 ) {
ELL_3M_SCALE( Txm, 0.5, Txm ); ELL_3M_SCALE( Txp, 0.5, Txp );
ELL_3V_SCALE( gxm, 0.5, gxm ); ELL_3V_SCALE( gxp, 0.5, gxp );
}
computeGradientLin( res, T, g,
Txm, gxm, Txp, gxp,
sctx->t + 9*( 0+2*si ), sctx->grad + 3*( 0+2*si ),
sctx->t + 9*( 0+2*siy ), sctx->grad + 3*( 0+2*siy ),
T, g, Tzp, gzp );
break;
case 2:
ELL_3M_LERP( T, alpha, sctx->t + 9*( 0+2*si ), sctx->t + 9*( 1+2*si ) );
ELL_3V_LERP( g, alpha, sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 1+2*si ) );
if ( xi==0 ) {
ELL_3M_COPY( Txm, T ); ELL_3V_COPY( gxm, g );
} else {
ELL_3M_LERP( Txm, alpha, sctx->t + 9*( 0+2*siX ), sctx->t + 9*( 1+2*siX ) );
ELL_3V_LERP( gxm, alpha, sctx->grad + 3*( 0+2*siX ),
sctx->grad + 3*( 1+2*siX ) );
}
if ( xi==sx-1 ) {
ELL_3M_COPY( Txp, T ); ELL_3V_COPY( gxp, g );
} else {
ELL_3M_LERP( Txp, alpha, sctx->t + 9*( 0+2*six ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gxp, alpha, sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 1+2*six ) );
}
if ( xi!=0 && xi!=sx-1 ) {
ELL_3M_SCALE( Txm, 0.5, Txm ); ELL_3M_SCALE( Txp, 0.5, Txp );
ELL_3V_SCALE( gxm, 0.5, gxm ); ELL_3V_SCALE( gxp, 0.5, gxp );
}
if ( yi==0 ) {
ELL_3M_COPY( Tym, T ); ELL_3V_COPY( gym, g );
} else {
ELL_3M_LERP( Tym, alpha, sctx->t + 9*( 0+2*siY ), sctx->t + 9*( 1+2*siY ) );
ELL_3V_LERP( gym, alpha, sctx->grad + 3*( 0+2*siY ),
sctx->grad + 3*( 1+2*siY ) );
}
if ( yi==sy-1 ) {
ELL_3M_COPY( Typ, T ); ELL_3V_COPY( gyp, g );
} else {
ELL_3M_LERP( Typ, alpha, sctx->t + 9*( 0+2*siy ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gyp, alpha, sctx->grad + 3*( 0+2*siy ),
sctx->grad + 3*( 1+2*siy ) );
}
if ( yi!=0 && yi!=sy-1 ) {
ELL_3M_SCALE( Tym, 0.5, Tym ); ELL_3M_SCALE( Typ, 0.5, Typ );
ELL_3V_SCALE( gym, 0.5, gym ); ELL_3V_SCALE( gyp, 0.5, gyp );
}
if ( bag->zi>0 && bag->zi<sz-2 ) {
ELL_3M_LERP( Tzm, alpha, sctx->tcontext + 9*( 0+2*si ),
sctx->t + 9*( 0+2*si ) );
ELL_3V_LERP( gzm, alpha, sctx->gradcontext + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*si ) );
ELL_3M_LERP( Tzp, alpha, sctx->t + 9*( 1+2*si ),
sctx->tcontext + 9*( 1+2*si ) );
ELL_3V_LERP( gzp, alpha, sctx->grad + 3*( 1+2*si ),
sctx->gradcontext + 3*( 1+2*si ) );
ELL_3M_SCALE( Tzm, 0.5, Tzm ); ELL_3M_SCALE( Tzp, 0.5, Tzp );
ELL_3V_SCALE( gzm, 0.5, gzm ); ELL_3V_SCALE( gzp, 0.5, gzp );
} else {
ELL_3M_COPY( Tzm, sctx->t + 9*( 0+2*si ) );
ELL_3V_COPY( gzm, sctx->grad + 3*( 0+2*si ) );
ELL_3M_COPY( Tzp, sctx->t + 9*( 1+2*si ) );
ELL_3V_COPY( gzp, sctx->grad + 3*( 1+2*si ) );
}
computeGradientLin( res, T, g,
Txm, gxm, Txp, gxp,
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
break;
case 3:
ELL_3M_LERP( T, alpha, sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( g, alpha, sctx->grad + 3*( 1+2*si ), sctx->grad + 3*( 1+2*six ) );
ELL_3M_LERP( Tzm, alpha, sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*six ) );
ELL_3V_LERP( gzm, alpha, sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 0+2*six ) );
if ( bag->zi==sz-2 ) {
ELL_3M_COPY( Tzp, T ); ELL_3V_COPY( gzp, g );
} else {
ELL_3M_LERP( Tzp, alpha, sctx->tcontext + 9*( 1+2*si ),
sctx->tcontext + 9*( 1+2*six ) );
ELL_3V_LERP( gzp, alpha, sctx->gradcontext + 3*( 1+2*si ),
sctx->gradcontext + 3*( 1+2*six ) );
ELL_3M_SCALE( Tzm, 0.5, Tzm ); ELL_3M_SCALE( Tzp, 0.5, Tzp );
ELL_3V_SCALE( gzm, 0.5, gzm ); ELL_3V_SCALE( gzp, 0.5, gzp );
}
if ( xi>0 && xi<sx-2 ) {
unsigned int sixx = xi + 2 + sx*yi;
ELL_3M_LERP( Txm, alpha, sctx->t + 9*( 1+2*siX ), sctx->t + 9*( 1+2*si ) );
ELL_3V_LERP( gxm, alpha, sctx->grad + 3*( 1+2*siX ),
sctx->grad + 3*( 1+2*si ) );
ELL_3M_LERP( Txp, alpha, sctx->t + 9*( 1+2*six ), sctx->t + 9*( 1+2*sixx ) );
ELL_3V_LERP( gxp, alpha, sctx->grad + 3*( 1+2*six ),
sctx->grad + 3*( 1+2*sixx ) );
ELL_3M_SCALE( Txm, 0.5, Txm ); ELL_3M_SCALE( Txp, 0.5, Txp );
ELL_3V_SCALE( gxm, 0.5, gxm ); ELL_3V_SCALE( gxp, 0.5, gxp );
} else {
ELL_3M_COPY( Txm, sctx->t + 9*( 1+2*si ) );
ELL_3V_COPY( gxm, sctx->grad + 3*( 1+2*si ) );
ELL_3M_COPY( Txp, sctx->t + 9*( 1+2*six ) );
ELL_3V_COPY( gxp, sctx->grad + 3*( 1+2*six ) );
}
if ( yi==0 ) {
ELL_3M_COPY( Tym, T ); ELL_3V_COPY( gym, g );
} else {
ELL_3M_LERP( Tym, alpha, sctx->t + 9*( 1+2*siY ), sctx->t + 9*( 1+2*sixY ) );
ELL_3V_LERP( gym, alpha, sctx->grad + 3*( 1+2*siY ),
sctx->grad + 3*( 1+2*sixY ) );
}
if ( yi==sy-1 ) {
ELL_3M_COPY( Typ, T ); ELL_3V_COPY( gyp, g );
} else {
ELL_3M_LERP( Typ, alpha, sctx->t + 9*( 1+2*siy ), sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gyp, alpha, sctx->grad + 3*( 1+2*siy ),
sctx->grad + 3*( 1+2*sixy ) );
}
if ( yi!=0 && yi!=sy-1 ) {
ELL_3M_SCALE( Tym, 0.5, Tym ); ELL_3M_SCALE( Typ, 0.5, Typ );
ELL_3V_SCALE( gym, 0.5, gym ); ELL_3V_SCALE( gyp, 0.5, gyp );
}
computeGradientLin( res, T, g,
Txm, gxm, Txp, gxp,
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
break;
case 4:
ELL_3M_LERP( T, alpha, sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( g, alpha, sctx->grad + 3*( 1+2*si ), sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( Tzm, alpha, sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*siy ) );
ELL_3V_LERP( gzm, alpha, sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 0+2*siy ) );
if ( bag->zi==sz-2 ) {
ELL_3M_COPY( Tzp, T ); ELL_3V_COPY( gzp, g );
} else {
ELL_3M_LERP( Tzp, alpha, sctx->tcontext + 9*( 1+2*si ),
sctx->tcontext + 9*( 1+2*siy ) );
ELL_3V_LERP( gzp, alpha, sctx->gradcontext + 3*( 1+2*si ),
sctx->gradcontext + 3*( 1+2*siy ) );
ELL_3M_SCALE( Tzm, 0.5, Tzm ); ELL_3M_SCALE( Tzp, 0.5, Tzp );
ELL_3V_SCALE( gzm, 0.5, gzm ); ELL_3V_SCALE( gzp, 0.5, gzp );
}
if ( xi==0 ) {
ELL_3M_COPY( Txm, T ); ELL_3V_COPY( gxm, g );
} else {
ELL_3M_LERP( Txm, alpha, sctx->t + 9*( 1+2*siX ), sctx->t + 9*( 1+2*siXy ) );
ELL_3V_LERP( gxm, alpha, sctx->grad + 3*( 1+2*siX ),
sctx->grad + 3*( 1+2*siXy ) );
}
if ( xi==sx-1 ) {
ELL_3M_COPY( Txp, T ); ELL_3V_COPY( gxp, g );
} else {
ELL_3M_LERP( Txp, alpha, sctx->t + 9*( 1+2*six ), sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gxp, alpha, sctx->grad + 3*( 1+2*six ),
sctx->grad + 3*( 1+2*sixy ) );
}
if ( xi!=0 && xi!=sx-1 ) {
ELL_3M_SCALE( Txm, 0.5, Txm ); ELL_3M_SCALE( Txp, 0.5, Txp );
ELL_3V_SCALE( gxm, 0.5, gxm ); ELL_3V_SCALE( gxp, 0.5, gxp );
}
if ( yi>0 && yi<sy-2 ) {
unsigned int siyy = xi + sx*( yi+2 );
ELL_3M_LERP( Tym, alpha, sctx->t + 9*( 1+2*siY ), sctx->t + 9*( 1+2*si ) );
ELL_3V_LERP( gym, alpha, sctx->grad + 3*( 1+2*siY ),
sctx->grad + 3*( 1+2*si ) );
ELL_3M_LERP( Typ, alpha, sctx->t + 9*( 1+2*siy ), sctx->t + 9*( 1+2*siyy ) );
ELL_3V_LERP( gyp, alpha, sctx->grad + 3*( 1+2*siy ),
sctx->grad + 3*( 1+2*siyy ) );
ELL_3M_SCALE( Tym, 0.5, Tym ); ELL_3M_SCALE( Typ, 0.5, Typ );
ELL_3V_SCALE( gym, 0.5, gym ); ELL_3V_SCALE( gyp, 0.5, gyp );
} else {
ELL_3M_COPY( Tym, sctx->t + 9*( 1+2*si ) );
ELL_3V_COPY( gym, sctx->grad + 3*( 1+2*si ) );
ELL_3M_COPY( Typ, sctx->t + 9*( 1+2*six ) );
ELL_3V_COPY( gyp, sctx->grad + 3*( 1+2*six ) );
}
computeGradientLin( res, T, g,
Txm, gxm, Txp, gxp,
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
break;
}
}
/* Given a unique face ID and coordinates, compute the crease surface
* normal at the specified degenerate point */
static void
510 computeFaceGradient( seekContext *sctx, double *res,
unsigned int xi, unsigned int yi,
char faceid, double *coords ) {
double T[9], Txm[9], Txp[9], Tym[9], Typ[9], Tzm[9], Tzp[9],
g[3], gxm[3], gxp[3], gym[3], gyp[3], gzm[3], gzp[3];
unsigned int sx = AIR_CAST( unsigned int, sctx->sx );
unsigned int sy = AIR_CAST( unsigned int, sctx->sy );
unsigned int si = xi + sx*yi;
unsigned int six = xi + 1 + sx*yi, siX = xi - 1 + sx*yi;
unsigned int siy = xi + sx*( yi+1 ), siY = xi + sx*( yi-1 );
unsigned int sixy = xi + 1 + sx*( yi+1 ), sixY = xi + 1 + sx*( yi-1 ),
siXy = xi - 1 + sx*( yi+1 );
/* Again, lots of special cases to fill Txm, gxm, etc. */
switch ( faceid ) {
case 0:
/* bilinearly interpolate Tzp/gzp first */
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 1+2*si ),
sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 1+2*six ), sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 1+2*six ),
sctx->grad + 3*( 1+2*sixy ) );
ELL_3M_LERP( Tzp, coords[0], Txm, Txp );
ELL_3V_LERP( gzp, coords[0], gxm, gxp );
/* now, compute all required points on the bottom face */
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*siy ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*siy ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 0+2*six ), sctx->t + 9*( 0+2*sixy ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 0+2*sixy ) );
ELL_3M_LERP( Tym, coords[0], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*six ) );
ELL_3V_LERP( gym, coords[0], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*six ) );
ELL_3M_LERP( Typ, coords[0], sctx->t + 9*( 0+2*siy ), sctx->t + 9*( 0+2*sixy ) );
ELL_3V_LERP( gyp, coords[0], sctx->grad + 3*( 0+2*siy ),
sctx->grad + 3*( 0+2*sixy ) );
ELL_3M_LERP( T, coords[0], Txm, Txp );
ELL_3V_LERP( g, coords[0], gxm, gxp );
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
Txm, gxm, Txp, gxp,
Tym, gym, Typ, gyp,
T, g, Tzp, gzp );
break;
case 1:
/* bilinearly interpolate Typ/gyp first */
if ( yi!=sy-1 ) {
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 0+2*siy ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 0+2*siy ),
sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 0+2*sixy ),
sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 0+2*sixy ),
sctx->grad + 3*( 1+2*sixy ) );
ELL_3M_LERP( Typ, coords[0], Txm, Txp );
ELL_3V_LERP( gyp, coords[0], gxm, gxp );
} else {
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 0+2*siY ), sctx->t + 9*( 1+2*siY ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 0+2*siY ),
sctx->grad + 3*( 1+2*siY ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 0+2*sixY ),
sctx->t + 9*( 1+2*sixY ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 0+2*sixY ),
sctx->grad + 3*( 1+2*sixY ) );
ELL_3M_LERP( Tym, coords[0], Txm, Txp );
ELL_3V_LERP( gym, coords[0], gxm, gxp );
}
/* now, compute remaining points */
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 1+2*si ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 1+2*si ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 0+2*six ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 1+2*six ) );
ELL_3M_LERP( Tzm, coords[0], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*six ) );
ELL_3V_LERP( gzm, coords[0], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*six ) );
ELL_3M_LERP( Tzp, coords[0], sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gzp, coords[0], sctx->grad + 3*( 1+2*si ),
sctx->grad + 3*( 1+2*six ) );
ELL_3M_LERP( T, coords[0], Txm, Txp );
ELL_3V_LERP( g, coords[0], gxm, gxp );
if ( yi!=sy-1 ) {
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
Txm, gxm, Txp, gxp,
T, g, Typ, gyp,
Tzm, gzm, Tzp, gzp );
} else {
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
Txm, gxm, Txp, gxp,
Tym, gym, T, g,
Tzm, gzm, Tzp, gzp );
}
break;
case 2:
/* bilinearly interpolate Txp/gxp first */
if ( xi!=sx-1 ) {
ELL_3M_LERP( Tym, coords[1], sctx->t + 9*( 0+2*six ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gym, coords[1], sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 1+2*six ) );
ELL_3M_LERP( Typ, coords[1], sctx->t + 9*( 0+2*sixy ),
sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gyp, coords[1], sctx->grad + 3*( 0+2*sixy ),
sctx->grad + 3*( 1+2*sixy ) );
ELL_3M_LERP( Txp, coords[0], Tym, Typ );
ELL_3V_LERP( gxp, coords[0], gym, gyp );
} else {
ELL_3M_LERP( Tym, coords[1], sctx->t + 9*( 0+2*siX ), sctx->t + 9*( 1+2*siX ) );
ELL_3V_LERP( gym, coords[1], sctx->grad + 3*( 0+2*siX ),
sctx->grad + 3*( 1+2*siX ) );
ELL_3M_LERP( Typ, coords[1], sctx->t + 9*( 0+2*siXy ),
sctx->t + 9*( 1+2*siXy ) );
ELL_3V_LERP( gyp, coords[1], sctx->grad + 3*( 0+2*siXy ),
sctx->grad + 3*( 1+2*siXy ) );
ELL_3M_LERP( Txm, coords[0], Tym, Typ );
ELL_3V_LERP( gxm, coords[0], gym, gyp );
}
/* now, compute remaining points */
ELL_3M_LERP( Tym, coords[1], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 1+2*si ) );
ELL_3V_LERP( gym, coords[1], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 1+2*si ) );
ELL_3M_LERP( Typ, coords[1], sctx->t + 9*( 0+2*siy ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gyp, coords[1], sctx->grad + 3*( 0+2*siy ),
sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( Tzm, coords[0], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*siy ) );
ELL_3V_LERP( gzm, coords[0], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*siy ) );
ELL_3M_LERP( Tzp, coords[0], sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gzp, coords[0], sctx->grad + 3*( 1+2*si ),
sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( T, coords[0], Tym, Typ );
ELL_3V_LERP( g, coords[0], gym, gyp );
if ( xi!=sx-1 ) {
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
T, g, Txp, gxp,
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
} else {
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
Txm, gxm, T, g,
Tym, gym, Typ, gyp,
Tzm, gzm, Tzp, gzp );
}
break;
case 3:
/* bilinearly interpolate Tzm/gzm first */
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 0+2*si ), sctx->t + 9*( 0+2*siy ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 0+2*si ),
sctx->grad + 3*( 0+2*siy ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 0+2*six ), sctx->t + 9*( 0+2*sixy ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 0+2*six ),
sctx->grad + 3*( 0+2*sixy ) );
ELL_3M_LERP( Tzm, coords[0], Txm, Txp );
ELL_3V_LERP( gzm, coords[0], gxm, gxp );
/* now, compute all required points on the top face */
ELL_3M_LERP( Txm, coords[1], sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*siy ) );
ELL_3V_LERP( gxm, coords[1], sctx->grad + 3*( 1+2*si ),
sctx->grad + 3*( 1+2*siy ) );
ELL_3M_LERP( Txp, coords[1], sctx->t + 9*( 1+2*six ), sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gxp, coords[1], sctx->grad + 3*( 1+2*six ),
sctx->grad + 3*( 1+2*sixy ) );
ELL_3M_LERP( Tym, coords[0], sctx->t + 9*( 1+2*si ), sctx->t + 9*( 1+2*six ) );
ELL_3V_LERP( gym, coords[0], sctx->grad + 3*( 1+2*si ),
sctx->grad + 3*( 1+2*six ) );
ELL_3M_LERP( Typ, coords[0], sctx->t + 9*( 1+2*siy ), sctx->t + 9*( 1+2*sixy ) );
ELL_3V_LERP( gyp, coords[0], sctx->grad + 3*( 1+2*siy ),
sctx->grad + 3*( 1+2*sixy ) );
ELL_3M_LERP( T, coords[0], Txm, Txp );
ELL_3V_LERP( g, coords[0], gxm, gxp );
computeGradientLin( sctx->facenorm+3*( faceid+4*si ), T, g,
Txm, gxm, Txp, gxp,
Tym, gym, Typ, gyp,
Tzm, gzm, T, g );
break;
}
ELL_3V_COPY( res, sctx->facenorm+3*( faceid+4*si ) );
}
/* small helper routines: intersection tests */
/* check if a given 2D triangle is oriented clockwise ( -1 )
* or counter-clockwise ( 1 ).
* returns 0 if given points are collinear */
static int
707 checkTriOrientation ( double *p1, double *p2, double *p3 ) {
double test = ( ( ( p2[0]-p1[0] )*( p3[1]-p1[1] ) ) - ( ( p3[0]-p1[0] )*( p2[1]-p1[1] ) ) );
if ( test > 0 ) return 1;
else if ( test < 0 ) return -1;
else return 0;
}
/* check if two given 2D lines intersect */
static int
716 lineIntersectionTest ( double *l1p1, double *l1p2, double *l2p1, double *l2p2 ) {
int or1 = checkTriOrientation( l1p1, l1p2, l2p1 );
int or2 = checkTriOrientation( l1p1, l1p2, l2p2 );
if ( or1 != or2 ) {
or1 = checkTriOrientation( l2p1, l2p2, l1p1 );
or2 = checkTriOrientation( l2p1, l2p2, l1p2 );
if ( or1 != or2 )
return 1;
}
return 0;
}
/* check if two given 3D triangles intersect */
static int
730 triIntersectionTest ( double *t1v1, double *t1v2, double *t1v3,
double *t2v1, double *t2v2, double *t2v3 ) {
double n1[3], n2[3], d1, d2;
double diff1[3], diff2[3];
double t2sd1, t2sd2, t2sd3;
ELL_3V_SUB( diff1, t1v2, t1v1 );
ELL_3V_SUB( diff2, t1v3, t1v1 );
ELL_3V_CROSS( n1, diff1, diff2 );
d1=-ELL_3V_DOT( n1, t1v1 );
/* compute scaled signed distances of t2 to plane of t1 */
t2sd1 = ELL_3V_DOT( n1, t2v1 )+d1;
t2sd2 = ELL_3V_DOT( n1, t2v2 )+d1;
t2sd3 = ELL_3V_DOT( n1, t2v3 )+d1;
if ( t2sd1==0 && t2sd2==0 && t2sd3==0 ) {
/* coplanar case: handle in 2D */
double t1v12d[2], t1v22d[2], t1v32d[2], t2v12d[2], t2v22d[2], t2v32d[2];
if ( fabs( n1[0] )>=fabs( n1[1] ) && fabs( n1[0] )>=fabs( n1[2] ) ) {
t1v12d[0]=t1v1[1]; t1v12d[1]=t1v1[2];
t1v22d[0]=t1v2[1]; t1v22d[1]=t1v2[2];
t1v32d[0]=t1v3[1]; t1v32d[1]=t1v3[2];
t2v12d[0]=t2v1[1]; t2v12d[1]=t2v1[2];
t2v22d[0]=t2v2[1]; t2v22d[1]=t2v2[2];
t2v32d[0]=t2v3[1]; t2v32d[1]=t2v3[2];
} else if ( fabs( n1[1] )>=fabs( n1[0] ) && fabs( n1[1] )>=fabs( n1[2] ) ) {
t1v12d[0]=t1v1[0]; t1v12d[1]=t1v1[2];
t1v22d[0]=t1v2[0]; t1v22d[1]=t1v2[2];
t1v32d[0]=t1v3[0]; t1v32d[1]=t1v3[2];
t2v12d[0]=t2v1[0]; t2v12d[1]=t2v1[2];
t2v22d[0]=t2v2[0]; t2v22d[1]=t2v2[2];
t2v32d[0]=t2v3[0]; t2v32d[1]=t2v3[2];
} else {
t1v12d[0]=t1v1[0]; t1v12d[1]=t1v1[1];
t1v22d[0]=t1v2[0]; t1v22d[1]=t1v2[1];
t1v32d[0]=t1v3[0]; t1v32d[1]=t1v3[1];
t2v12d[0]=t2v1[0]; t2v12d[1]=t2v1[1];
t2v22d[0]=t2v2[0]; t2v22d[1]=t2v2[1];
t2v32d[0]=t2v3[0]; t2v32d[1]=t2v3[1];
}
/* we may assume that none of the triangles is fully contained
* within the other. Thus, it suffices to do a lot of 2D line-line
* intersections */
if ( lineIntersectionTest( t1v12d, t1v22d, t2v12d, t2v22d ) ||
lineIntersectionTest( t1v22d, t1v32d, t2v12d, t2v22d ) ||
lineIntersectionTest( t1v32d, t1v12d, t2v12d, t2v22d ) ||
lineIntersectionTest( t1v12d, t1v22d, t2v22d, t2v32d ) ||
lineIntersectionTest( t1v22d, t1v32d, t2v22d, t2v32d ) ||
lineIntersectionTest( t1v32d, t1v12d, t2v22d, t2v32d ) ||
lineIntersectionTest( t1v12d, t1v22d, t2v32d, t2v12d ) ||
lineIntersectionTest( t1v22d, t1v32d, t2v32d, t2v12d ) ||
lineIntersectionTest( t1v32d, t1v12d, t2v32d, t2v12d ) )
return 1;
return 0;
} else {
/* pointers to the vertices on the same side / opposite side of plane */
double *t2s11, *t2s12, *t2s2, t2s11sd, t2s12sd, t2s2sd;
double t1sd1, t1sd2, t1sd3;
double *t1s11, *t1s12, *t1s2, t1s11sd, t1s12sd, t1s2sd;
double t1p11, t1p12, t1p2, t2p11, t2p12, t2p2;
double D[3]; /* direction vector of line */
double t1t1, t1t2, t2t1, t2t2;
if ( t2sd1*t2sd2>=0 && t2sd1*t2sd3<=0 ) {
t2s11=t2v1; t2s12=t2v2; t2s2=t2v3; t2s11sd=t2sd1;
t2s12sd=t2sd2; t2s2sd=t2sd3;
} else if ( t2sd1*t2sd3>=0 && t2sd1*t2sd2<=0 ) {
t2s11=t2v1; t2s12=t2v3; t2s2=t2v2; t2s11sd=t2sd1;
t2s12sd=t2sd3; t2s2sd=t2sd2;
} else if ( t2sd2*t2sd3>=0 && t2sd1*t2sd2<=0 ) {
t2s11=t2v2; t2s12=t2v3; t2s2=t2v1; t2s11sd=t2sd2;
t2s12sd=t2sd3; t2s2sd=t2sd1;
} else
return 0; /* all on the same side; no intersection */
/* same game for triangle 2 */
ELL_3V_SUB( diff1, t2v2, t2v1 );
ELL_3V_SUB( diff2, t2v3, t2v1 );
ELL_3V_CROSS( n2, diff1, diff2 );
d2=-ELL_3V_DOT( n2, t2v1 );
t1sd1 = ELL_3V_DOT( n2, t1v1 )+d2;
t1sd2 = ELL_3V_DOT( n2, t1v2 )+d2;
t1sd3 = ELL_3V_DOT( n2, t1v3 )+d2;
if ( t1sd1*t1sd2>=0 && t1sd1*t1sd3<=0 ) {
t1s11=t1v1; t1s12=t1v2; t1s2=t1v3; t1s11sd=t1sd1;
t1s12sd=t1sd2; t1s2sd=t1sd3;
} else if ( t1sd1*t1sd3>=0 && t1sd1*t1sd2<=0 ) {
t1s11=t1v1; t1s12=t1v3; t1s2=t1v2; t1s11sd=t1sd1;
t1s12sd=t1sd3; t1s2sd=t1sd2;
} else if ( t1sd2*t1sd3>=0 && t1sd1*t1sd2<=0 ) {
t1s11=t1v2; t1s12=t1v3; t1s2=t1v1; t1s11sd=t1sd2;
t1s12sd=t1sd3; t1s2sd=t1sd1;
} else
return 0; /* all on the same side; no intersection */
/* both planes intersect in a line; check if the intervals on that
* line intersect */
ELL_3V_CROSS( D, n1, n2 );
/* we are only interested in component magnitudes */
D[0]=fabs( D[0] ); D[1]=fabs( D[1] ); D[2]=fabs( D[2] );
if ( D[0]>=D[1] && D[0]>=D[2] ) {
t1p11=t1s11[0]; t1p12=t1s12[0]; t1p2=t1s2[0];
t2p11=t2s11[0]; t2p12=t2s12[0]; t2p2=t2s2[0];
} else if ( D[1]>=D[0] && D[1]>=D[2] ) {
t1p11=t1s11[1]; t1p12=t1s12[1]; t1p2=t1s2[1];
t2p11=t2s11[1]; t2p12=t2s12[1]; t2p2=t2s2[1];
} else {
t1p11=t1s11[2]; t1p12=t1s12[2]; t1p2=t1s2[2];
t2p11=t2s11[2]; t2p12=t2s12[2]; t2p2=t2s2[2];
}
/* compute interval boundaries */
t1t1=t1p11+( t1p2-t1p11 )*t1s11sd/( t1s11sd-t1s2sd );
t1t2=t1p12+( t1p2-t1p12 )*t1s12sd/( t1s12sd-t1s2sd );
if ( t1t1>t1t2 ) {
double help=t1t1;
t1t1=t1t2;
t1t2=help;
}
t2t1=t2p11+( t2p2-t2p11 )*t2s11sd/( t2s11sd-t2s2sd );
t2t2=t2p12+( t2p2-t2p12 )*t2s12sd/( t2s12sd-t2s2sd );
if ( t2t1>t2t2 ) {
double help=t2t1;
t2t1=t2t2;
t2t2=help;
}
/* test for interval intersection */
if ( t2t1>t1t2 || t1t1>t2t2 ) return 0;
return 1;
}
}
/* Score possible local topologies based on the agreement of
* connecting lines with normal directions. Lower scores are
* better. */
/* Connections between degenerate points on cell faces; if only four
* degenerate points are present, set p31 to NULL */
static double
867 evaluateDegConnection( double *p11, double *p12, double *p13, double *p14,
double *p21, double *p22, double *p23, double *p24,
double *p31, double *p32, double *p33, double *p34,
double *n12, double *n13, double *n22, double *n23,
double *n32, double *n33 ) {
double diff1[3], diff2[3], diff3[3], ret;
/* first, perform intersection testing */
if ( triIntersectionTest( p11, p12, p13, p21, p22, p23 ) ||
triIntersectionTest( p13, p14, p11, p21, p22, p23 ) ||
triIntersectionTest( p11, p12, p13, p23, p24, p21 ) ||
triIntersectionTest( p13, p14, p11, p23, p24, p21 ) )
return 1e20;
if ( p31 != NULL ) { /* three pairs - some more to do */
if ( triIntersectionTest( p11, p12, p13, p31, p32, p33 ) ||
triIntersectionTest( p11, p12, p13, p33, p34, p31 ) ||
triIntersectionTest( p13, p14, p11, p31, p32, p33 ) ||
triIntersectionTest( p13, p14, p11, p33, p34, p31 ) ||
triIntersectionTest( p21, p22, p23, p31, p32, p33 ) ||
triIntersectionTest( p21, p22, p23, p33, p34, p31 ) ||
triIntersectionTest( p23, p24, p21, p31, p32, p33 ) ||
triIntersectionTest( p23, p24, p21, p33, p34, p31 ) )
return 1e20;
}
ELL_3V_SUB( diff1, p13, p12 );
ELL_3V_SUB( diff2, p23, p22 );
ret=fabs( ELL_3V_DOT( diff1, n12 ) )+fabs( ELL_3V_DOT( diff1, n13 ) )+
fabs( ELL_3V_DOT( diff2, n22 ) )+fabs( ELL_3V_DOT( diff2, n23 ) );
if ( p31 != NULL ) {
ELL_3V_SUB( diff3, p33, p32 );
ret+=fabs( ELL_3V_DOT( diff3, n32 ) )+fabs( ELL_3V_DOT( diff3, n33 ) );
}
return ret;
}
/* suggests a connectivity for a non-trivial combination of
* intersection points in the plane.
* pairs is output ( permutation of idcs )
* bestval is input/output ( best value so far, start with something big )
* ct is the number of points ( currently assumed even )
* idcs is input ( idcs that still need to be permuted )
* fixedct is input ( number of idcs that are already fixed at this depth )
* coords is an array of 2D coordinates
* norms is an array of 2D vectors */
static void
911 findConnectivity( signed char *pairs, double *bestval, int ct, char *idcs,
int fixedct, double *coords, double *norms ) {
int i, j;
if ( fixedct==ct ) {
double weight=0;
for ( i=0; i<ct-1; i+=2 ) {
double diff[2];
ELL_2V_SUB( diff, coords+2*idcs[i], coords+2*idcs[i+1] );
weight+=fabs( ELL_2V_DOT( diff, norms+2*idcs[i] ) )+
fabs( ELL_2V_DOT( diff, norms+2*idcs[i+1] ) );
}
if ( weight<*bestval ) {
*bestval=weight;
memcpy( pairs, idcs, sizeof( char )*ct );
}
return;
}
/* else: we need a recursion */
for ( i=fixedct+1; i<ct; i++ ) {
int intersect=0;
char *idxnew;
if ( NULL == ( idxnew = ( char* ) malloc ( sizeof( char )*ct ) ) )
return;
memcpy( idxnew, idcs, sizeof( char )*ct );
/* try any of the remaining indices as a pair */
idxnew[fixedct+1]=idcs[i];
idxnew[i]=idcs[fixedct+1];
/* check if the resulting line causes an intersection */
for ( j=0;j<fixedct;j+=2 ) {
if ( lineIntersectionTest( coords+2*idxnew[fixedct],
coords+2*idxnew[fixedct+1],
coords+2*idxnew[j], coords+2*idxnew[j+1] ) ) {
intersect=1;
break;
}
}
if ( !intersect ) {
findConnectivity( pairs, bestval, ct, idxnew, fixedct+2, coords, norms );
}
free( idxnew );
}
}
#define _SEEK_TREATED_REQUEST 0x01 /* this voxel has to be treated */
#define _SEEK_TREATED_EDGE0 0x02 /* unique edge 0 has been treated */
#define _SEEK_TREATED_EDGE1 0x04 /* unique edge 1 has been treated */
#define _SEEK_TREATED_EDGE2 0x08 /* unique edge 2 has been treated */
#define _SEEK_TREATED_EDGE3 0x10 /* unique edge 3 has been treated */
#define _SEEK_TREATED_EDGE4 0x20 /* unique edge 4 has been treated */
#define _SEEK_TREATED_FACE3 0x40 /* unique face 3 has been treated */
/* find deg. points, normals, and connectivity on a given ( unique ) face
* now refines the search if it couldn't find a degenerate point */
static void
965 connectFace( seekContext *sctx, baggage *bag,
unsigned int xi, unsigned int yi, unsigned char faceid ) {
int edgeid[4][4]={{0, 2, 3, 1}, /* which edges belong to which unique face? */
{0, 5, 8, 4},
{1, 6, 9, 4},
{8, 10, 11, 9}};
unsigned int sx = AIR_CAST( unsigned int, sctx->sx );
unsigned int si = xi + sx*yi;
unsigned int six = xi + 1 + sx*yi;
unsigned int siy = xi + sx*( yi+1 );
unsigned int sixy = xi + 1 + sx*( yi+1 );
char inter[12]; /* indices of intersections */
int pass; /* allow multiple refined passes */
int i;
/* voxel in which treated information is stored for local edge i */
/* which vertices form which unique face? */
int verti[4][4];
int voxel[4][4];
/* mask for treated information in the above voxel */
char mask[4][4]={{_SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE1,
_SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE1},
{_SEEK_TREATED_EDGE0, _SEEK_TREATED_EDGE2,
_SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE2},
{_SEEK_TREATED_EDGE1, _SEEK_TREATED_EDGE2,
_SEEK_TREATED_EDGE4, _SEEK_TREATED_EDGE2},
{_SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE4,
_SEEK_TREATED_EDGE3, _SEEK_TREATED_EDGE4}};
/* start- and endpoints of the edges */
int verts[4][4], verte[4][4];
char treat[4]={0, 0, 0, 0};
double dpthresh[3]={0.7, 0.8, 0.9};
/* candidates for degenerate points */
double candidates[18]={0.5, 0.5, 0.25, 0.25, 0.25, 0.75,
0.75, 0.25, 0.75, 0.75, 0.5, 0.25,
0.25, 0.5, 0.75, 0.5, 0.6, 0.75};
int cand_idx[4]={0, 1, 5, 9};
int interct;
/* apparently, some C compilers cannot make these initializations in-place */
ELL_4V_SET( verti[0], 0+2*si, 0+2*six, 0+2*siy, 0+2*sixy );
ELL_4V_SET( verti[1], 0+2*si, 0+2*six, 1+2*si, 1+2*six );
ELL_4V_SET( verti[2], 0+2*si, 0+2*siy, 1+2*si, 1+2*siy );
ELL_4V_SET( verti[3], 1+2*si, 1+2*six, 1+2*siy, 1+2*sixy );
ELL_4V_SET( voxel[0], si, six, siy, si );
ELL_4V_SET( voxel[1], si, six, si, si );
ELL_4V_SET( voxel[2], si, siy, si, si );
ELL_4V_SET( voxel[3], si, six, siy, si );
ELL_4V_SET( verts[0], 0+2*si, 0+2*six, 0+2*siy, 0+2*si );
ELL_4V_SET( verts[1], 0+2*si, 0+2*six, 1+2*si, 0+2*si );
ELL_4V_SET( verts[2], 0+2*si, 0+2*siy, 1+2*si, 0+2*si );
ELL_4V_SET( verts[3], 1+2*si, 1+2*six, 1+2*siy, 1+2*si );
ELL_4V_SET( verte[0], 0+2*six, 0+2*sixy, 0+2*sixy, 0+2*siy );
ELL_4V_SET( verte[1], 0+2*six, 1+2*six, 1+2*six, 1+2*si );
ELL_4V_SET( verte[2], 0+2*siy, 1+2*siy, 1+2*siy, 1+2*si );
ELL_4V_SET( verte[3], 1+2*six, 1+2*sixy, 1+2*sixy, 1+2*siy );
/* find out which edges have not yet been treated */
for ( i=0; i<4; i++ ) {
if ( !( sctx->treated[voxel[faceid][i]]&mask[faceid][i] ) ) {
treat[i]=1; /* we need to treat this */
sctx->treated[voxel[faceid][i]] |= mask[faceid][i];
}
}
for ( pass=0; pass<3; pass++ ) {
/* first, find intersections for edges that need treatment */
int j;
for ( j=0; j<4; j++ ) {
double interpos[8];
if ( !treat[j] ) continue;
interct=findFeatureIntersection( interpos,
sctx->t + 9*verts[faceid][j],
sctx->hess + 9*verts[faceid][j],
sctx->grad + 3*verts[faceid][j],
sctx->t + 9*verte[faceid][j],
sctx->hess + 9*verte[faceid][j],
sctx->grad + 3*verte[faceid][j],
0.0, 1.0, bag->esIdx==2,
sctx->evalDiffThresh,
dpthresh[pass] );
if ( interct>3 ) interct=3;
for ( i=0; i<interct; i++ ) {
double x=0, y=0, z=0; unsigned int xb=0, yb=0, idb=0;
sctx->edgealpha[3*( bag->evti[edgeid[faceid][j]]+5*si )+i] = interpos[i];
switch ( edgeid[faceid][j] ) {
case 0: x=xi+interpos[i]; y=yi; z=bag->zi;
xb=xi; yb=yi; idb=0; break;
case 1: x=xi; y=yi+interpos[i]; z=bag->zi;
xb=xi; yb=yi; idb=1; break;
case 2: x=xi+1; y=yi+interpos[i]; z=bag->zi;
xb=xi+1; yb=yi; idb=1; break;
case 3: x=xi+interpos[i]; y=yi+1; z=bag->zi;
xb=xi; yb=yi+1; idb=0; break;
case 4: x=xi; y=yi; z=bag->zi+interpos[i];
xb=xi; yb=yi; idb=2; break;
case 5: x=xi+1; y=yi; z=bag->zi+interpos[i];
xb=xi+1; yb=yi; idb=2; break;
case 6: x=xi; y=yi+1; z=bag->zi+interpos[i];
xb=xi; yb=yi+1; idb=2; break;
case 7: x=xi+1; y=yi+1; z=bag->zi+interpos[i];
xb=xi+1; yb=yi+1; idb=2; break;
case 8: x=xi+interpos[i]; y=yi; z=bag->zi+1;
xb=xi; yb=yi; idb=3; break;
case 9: x=xi; y=yi+interpos[i]; z=bag->zi+1;
xb=xi; yb=yi; idb=4; break;
case 10: x=xi+1; y=yi+interpos[i]; z=bag->zi+1;
xb=xi+1; yb=yi; idb=4; break;
case 11: x=xi+interpos[i]; y=yi+1; z=bag->zi+1;
xb=xi; yb=yi+1; idb=3; break;
}
ELL_3V_SET( sctx->edgeicoord+9*( bag->evti[edgeid[faceid][j]]+5*si )+3*i,
x, y, z );
computeEdgeGradient( sctx, bag, sctx->edgenorm+
9*( bag->evti[edgeid[faceid][j]]+5*si )+3*i,
xb, yb, idb, interpos[i] );
}
}
interct=0; /* number of feature intersections */
for ( i=0; i<3; i++ ) {
if ( sctx->edgealpha[3*( bag->evti[edgeid[faceid][0]]+5*si )+i]>=0 )
inter[interct++]=i; /* numbering is local w.r.t. face */
if ( sctx->edgealpha[3*( bag->evti[edgeid[faceid][1]]+5*si )+i]>=0 )
inter[interct++]=3+i;
if ( sctx->edgealpha[3*( bag->evti[edgeid[faceid][2]]+5*si )+i]>=0 )
inter[interct++]=6+i;
if ( sctx->edgealpha[3*( bag->evti[edgeid[faceid][3]]+5*si )+i]>=0 )
inter[interct++]=9+i;
}
if ( interct%2==1 ) { /* we need to look for a degeneracy */
int k;
for ( k=cand_idx[pass]; k<cand_idx[pass+1]; k++ ) {
ELL_2V_SET( sctx->facecoord+2*( faceid+4*si ),
candidates[2*k], candidates[2*k+1] );
if ( !seekDescendToDeg( sctx->facecoord+2*( faceid+4*si ),
sctx->hess + 9*verti[faceid][0],
sctx->hess + 9*verti[faceid][1],
sctx->hess + 9*verti[faceid][2],
sctx->hess + 9*verti[faceid][3],
50, 1e-9, ( bag->esIdx==2 )?'l':'p' ) ) {
inter[interct++]=12; /* 12 means "deg. point on this face */
break;
}
}
if ( ( pass==2 ) && ( inter[interct-1]!=12 ) ) {
/* nothing helped, so insert a dummy vertex */
ELL_2V_SET( sctx->facecoord+2*( faceid+4*si ), 0.5, 0.5 );
inter[interct++]=12;
}
if ( inter[interct-1]==12 ) {
computeFaceGradient( sctx, sctx->facenorm+3*( faceid+4*si ),
xi, yi, faceid, sctx->facecoord+2*( faceid+4*si ) );
switch ( faceid ) {
case 0: ELL_3V_SET( sctx->faceicoord+3*( faceid+4*si ),
xi+sctx->facecoord[2*( faceid+4*si )],
yi+sctx->facecoord[2*( faceid+4*si )+1], bag->zi );
break;
case 1: ELL_3V_SET( sctx->faceicoord+3*( faceid+4*si ),
xi+sctx->facecoord[2*( faceid+4*si )], yi,
bag->zi+sctx->facecoord[2*( faceid+4*si )+1] );
break;
case 2: ELL_3V_SET( sctx->faceicoord+3*( faceid+4*si ), xi,
yi+sctx->facecoord[2*( faceid+4*si )],
bag->zi+sctx->facecoord[2*( faceid+4*si )+1] );
break;
case 3: ELL_3V_SET( sctx->faceicoord+3*( faceid+4*si ),
xi+sctx->facecoord[2*( faceid+4*si )],
yi+sctx->facecoord[2*( faceid+4*si )+1], bag->zi+1 );
break;
}
}
}
if ( interct%2==0 ) { /* we can break out */
break;
}
}
if ( interct<=1 ) { /* there is no connectivity on this face */
ELL_4V_SET( sctx->pairs+12*( faceid+4*si ), -1, -1, -1, -1 );
} else if ( interct==2 ) { /* connectivity is straightforward */
ELL_4V_SET( sctx->pairs+12*( faceid+4*si ), inter[0], inter[1], -1, -1 );
} else { /* we need gradients and coordinates to make a decision */
double interc[24]; /* 2D coordinates of intersection points */
double intern[24]; /* 2D normals at intersection points */
/* used to find the best pairing without self-intersection */
double bestscore=1e20;
char idcs[12]; /* consider if we need to restrict this */
for ( i=0; i<interct; i++ ) {
if ( inter[i]<12 ) { /* edge feature */
int resolved=edgeid[faceid][inter[i]/3];
int offset=inter[i]%3;
switch ( faceid ) {
case 0: case 3:
ELL_2V_SET( interc+2*i,
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset],
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset+1] );
ELL_2V_SET( intern+2*i,
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset],
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset+1] );
break;
case 1:
ELL_2V_SET( interc+2*i,
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset],
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset+2] );
ELL_2V_SET( intern+2*i,
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset],
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset+2] );
break;
case 2:
ELL_2V_SET( interc+2*i,
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset+1],
sctx->edgeicoord[9*( bag->evti[resolved]+5*si )+3*offset+2] );
ELL_2V_SET( intern+2*i,
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset+1],
sctx->edgenorm[9*( bag->evti[resolved]+5*si )+3*offset+2] );
break;
}
} else { /* face feature */
switch ( faceid ) {
case 0: case 3:
ELL_2V_SET( interc+2*i, sctx->faceicoord[3*( faceid+4*si )],
sctx->faceicoord[3*( faceid+4*si )+1] );
ELL_2V_SET( intern+2*i, sctx->facenorm[3*( faceid+4*si )],
sctx->facenorm[3*( faceid+4*si )+1] );
break;
case 1:
ELL_2V_SET( interc+2*i, sctx->faceicoord[3*( faceid+4*si )],
sctx->faceicoord[3*( faceid+4*si )+2] );
ELL_2V_SET( intern+2*i, sctx->facenorm[3*( faceid+4*si )],
sctx->facenorm[3*( faceid+4*si )+2] );
break;
case 2:
ELL_2V_SET( interc+2*i, sctx->faceicoord[3*( faceid+4*si )+1],
sctx->faceicoord[3*( faceid+4*si )+2] );
ELL_2V_SET( intern+2*i, sctx->facenorm[3*( faceid+4*si )+1],
sctx->facenorm[3*( faceid+4*si )+2] );
break;
}
}
}
for ( i=0; i<interct; i++ ) {
sctx->pairs[12*( faceid+4*si )+i]=i;
idcs[i]=i;
}
findConnectivity( sctx->pairs+12*( faceid+4*si ), &bestscore, interct,
idcs, 0, interc, intern );
for ( i=0; i<interct; i++ )
sctx->pairs[12*( faceid+4*si )+i]=inter[sctx->pairs[12*( faceid+4*si )+i]];
for ( i=interct; i<12; i++ )
sctx->pairs[12*( faceid+4*si )+i]=-1;
}
}
static void
1219 intersectionShuffleProbe( seekContext *sctx, baggage *bag ) {
unsigned int xi, yi, sx, sy, si;
int i;
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
/* take care of facevidx array */
if ( !bag->zi ) { /* initialize, else copy over */
sctx->facevidx[0 + 4*si] = -1;
} else {
sctx->facevidx[0 + 4*si] = sctx->facevidx[3 + 4*si];
}
sctx->facevidx[1 + 4*si] = sctx->facevidx[2 + 4*si] =
sctx->facevidx[3 + 4*si] = -1;
/* copy or reset data on the 5 unique edges */
if ( sctx->treated[si]&_SEEK_TREATED_EDGE3 ) {
/* has been treated, just copy results */
ELL_3V_COPY( sctx->edgealpha+3*( 0+5*si ), sctx->edgealpha+3*( 3+5*si ) );
ELL_3M_COPY( sctx->edgenorm+9*( 0+5*si ), sctx->edgenorm+9*( 3+5*si ) );
ELL_3M_COPY( sctx->edgeicoord+9*( 0+5*si ), sctx->edgeicoord+9*( 3+5*si ) );
sctx->treated[si]|=_SEEK_TREATED_EDGE0;
} else if ( xi!=sx-1 ) {
ELL_3V_SET( sctx->edgealpha+3*( 0+5*si ), -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE0;
}
if ( sctx->treated[si]&_SEEK_TREATED_EDGE4 ) {
/* has been treated, just copy results */
ELL_3V_COPY( sctx->edgealpha+3*( 1+5*si ), sctx->edgealpha+3*( 4+5*si ) );
ELL_3M_COPY( sctx->edgenorm+9*( 1+5*si ), sctx->edgenorm+9*( 4+5*si ) );
ELL_3M_COPY( sctx->edgeicoord+9*( 1+5*si ), sctx->edgeicoord+9*( 4+5*si ) );
sctx->treated[si]|=_SEEK_TREATED_EDGE1;
} else if ( yi!=sy-1 ) {
ELL_3V_SET( sctx->edgealpha+3*( 1+5*si ), -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE1;
}
/* edges within and at top of the slab are new */
ELL_3V_SET( sctx->edgealpha+3*( 2+5*si ), -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE2;
ELL_3V_SET( sctx->edgealpha+3*( 3+5*si ), -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE3;
ELL_3V_SET( sctx->edgealpha+3*( 4+5*si ), -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_EDGE4;
}
}
/* find missing deg. points, edge intersections, normals, and
* connectivity on the four unique faces
* this is done in a separate pass to make sure that all edge information
* has been updated */
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
if ( sctx->treated[si]&_SEEK_TREATED_FACE3 ) {
/* we can copy previous results */
ELL_2V_COPY( sctx->facecoord+2*( 0+4*si ), sctx->facecoord+2*( 3+4*si ) );
ELL_3V_COPY( sctx->faceicoord+3*( 0+4*si ), sctx->faceicoord+3*( 3+4*si ) );
ELL_3V_COPY( sctx->facenorm+3*( 0+4*si ), sctx->facenorm+3*( 3+4*si ) );
for ( i=0; i<3; i++ )
ELL_4V_COPY( sctx->pairs+12*( 0+4*si )+4*i, sctx->pairs+12*( 3+4*si )+4*i );
} else if ( xi!=sx-1 && yi!=sy-1 ) {
if ( sctx->treated[si]&_SEEK_TREATED_REQUEST )
connectFace( sctx, bag, xi, yi, 0 );
else
ELL_4V_SET( sctx->pairs+12*( 0+4*si ), -1, -1, -1, -1 );
}
if ( xi!=sx-1 ) {
if ( sctx->treated[si]&_SEEK_TREATED_REQUEST ||
( yi!=0 && sctx->treated[xi+sx*( yi-1 )]&_SEEK_TREATED_REQUEST ) )
connectFace( sctx, bag, xi, yi, 1 );
else ELL_4V_SET( sctx->pairs+12*( 1+4*si ), -1, -1, -1, -1 );
}
if ( yi!=sy-1 ) {
if ( sctx->treated[si]&_SEEK_TREATED_REQUEST ||
( xi!=0 && sctx->treated[xi-1+sx*yi]&_SEEK_TREATED_REQUEST ) )
connectFace( sctx, bag, xi, yi, 2 );
else ELL_4V_SET( sctx->pairs+12*( 2+4*si ), -1, -1, -1, -1 );
if ( xi!=sx-1 ) {
if ( sctx->treated[si]&_SEEK_TREATED_REQUEST ) {
connectFace( sctx, bag, xi, yi, 3 );
sctx->treated[si]|=_SEEK_TREATED_FACE3;
} else {
ELL_4V_SET( sctx->pairs+12*( 3+4*si ), -1, -1, -1, -1 );
sctx->treated[si]&=0xFF^_SEEK_TREATED_FACE3;
}
}
}
}
}
}
/* special triangulation routine for use with T-based extraction */
int
1321 _seekTriangulateT( seekContext *sctx, baggage *bag, limnPolyData *lpld ) {
unsigned xi, yi, sx, sy, si, i;
/* map edge indices w.r.t. faces ( as used in sctx->pairs ) back to
* edge indices w.r.t. voxel */
char edges[6][5]={{0, 2, 3, 1, 12},
{0, 5, 8, 4, 13},
{2, 7, 10, 5, 14},
{3, 7, 11, 6, 15},
{1, 6, 9, 4, 16},
{8, 10, 11, 9, 17}};
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
for ( yi=0; yi<sy-1; yi++ ) {
for ( xi=0; xi<sx-1; xi++ ) {
int fvti[6]; /* indices into unique face array */
char connections[84];/* ( 12 edges * 3 possible intersections+6 faces )*2 */
char degeneracies[6];
int degct=0;
unsigned int face;
if ( sctx->strengthUse && sctx->stng[0+2*( xi+sx*yi )] < sctx->strength &&
sctx->stng[1+2*( xi+sx*yi )] < sctx->strength &&
sctx->stng[0+2*( xi+1+sx*yi )] < sctx->strength &&
sctx->stng[1+2*( xi+1+sx*yi )] < sctx->strength &&
sctx->stng[0+2*( xi+sx*( yi+1 ) )] < sctx->strength &&
sctx->stng[1+2*( xi+sx*( yi+1 ) )] < sctx->strength &&
sctx->stng[0+2*( xi+1+sx*( yi+1 ) )] < sctx->strength &&
sctx->stng[1+2*( xi+1+sx*( yi+1 ) )] < sctx->strength )
continue;/* all vertices below strength limit, do not create geometry */
si = xi + sx*yi;
ELL_3V_SET( fvti, 0 + 4*si, 1 + 4*si, 2 + 4*( xi+1 + sx*yi ) );
ELL_3V_SET( fvti+3, 1 + 4*( xi + sx*( yi+1 ) ), 2 + 4*si, 3 + 4*si );
/* collect all intersection + connectivity info for this voxel */
memset( connections, -1, sizeof( connections ) );
for ( face=0; face<6; face++ ) {
for ( i=0; i<6; i++ ) {
int idx1, offset1, idx2, offset2, idxmap1, idxmap2;
if ( sctx->pairs[12*fvti[face]+2*i]==-1 ) break;
idx1=edges[face][sctx->pairs[12*fvti[face]+2*i]/3];
offset1=sctx->pairs[12*fvti[face]+2*i]%3;
idx2=edges[face][sctx->pairs[12*fvti[face]+2*i+1]/3];
offset2=sctx->pairs[12*fvti[face]+2*i+1]%3;
idxmap1=3*idx1+offset1;
idxmap2=3*idx2+offset2;
if ( idx1>11 ) {
idxmap1=idx1+24; /* +36-12 */
degeneracies[degct++] = idxmap1;
}
if ( idx2>11 ) {
idxmap2=idx2+24;
degeneracies[degct++] = idxmap2;
}
if ( connections[2*idxmap1]==-1 )
connections[2*idxmap1]=idxmap2;
else
connections[2*idxmap1+1]=idxmap2;
if ( connections[2*idxmap2]==-1 )
connections[2*idxmap2]=idxmap1;
else
connections[2*idxmap2+1]=idxmap1;
}
}
/* connect the degenerate points */
if ( degct==2 ) {
connections[2*degeneracies[0]+1]=degeneracies[1];
connections[2*degeneracies[1]+1]=degeneracies[0];
} else if ( degct==4 ) {
int bestchoice=0;
int eidcs[4], fidcs[4];
int k;
double bestscore, score;
for ( k=0; k<4; ++k ) {
eidcs[k]=3*( bag->evti[connections[2*degeneracies[k]]/3]+5*si )+
connections[2*degeneracies[k]]%3;
fidcs[k]=fvti[degeneracies[k]-36];
}
bestscore=evaluateDegConnection( sctx->edgeicoord+3*eidcs[0],
sctx->faceicoord+3*fidcs[0],
sctx->faceicoord+3*fidcs[1],
sctx->edgeicoord+3*eidcs[1],
sctx->edgeicoord+3*eidcs[2],
sctx->faceicoord+3*fidcs[2],
sctx->faceicoord+3*fidcs[3],
sctx->edgeicoord+3*eidcs[3],
NULL, NULL, NULL, NULL,
sctx->facenorm+3*fidcs[0],
sctx->facenorm+3*fidcs[1],
sctx->facenorm+3*fidcs[2],
sctx->facenorm+3*fidcs[3],
NULL, NULL );
score=evaluateDegConnection( sctx->edgeicoord+3*eidcs[0],
sctx->faceicoord+3*fidcs[0],
sctx->faceicoord+3*fidcs[2],
sctx->edgeicoord+3*eidcs[2],
sctx->edgeicoord+3*eidcs[1],
sctx->faceicoord+3*fidcs[1],
sctx->faceicoord+3*fidcs[3],
sctx->edgeicoord+3*eidcs[3],
NULL, NULL, NULL, NULL,
sctx->facenorm+3*fidcs[0],
sctx->facenorm+3*fidcs[2],
sctx->facenorm+3*fidcs[1],
sctx->facenorm+3*fidcs[3],
NULL, NULL );
if ( score<bestscore ) {
bestscore=score;
bestchoice=1;
}
score=evaluateDegConnection( sctx->edgeicoord+3*eidcs[0],
sctx->faceicoord+3*fidcs[0],
sctx->faceicoord+3*fidcs[3],
sctx->edgeicoord+3*eidcs[3],
sctx->edgeicoord+3*eidcs[1],
sctx->faceicoord+3*fidcs[1],
sctx->faceicoord+3*fidcs[2],
sctx->edgeicoord+3*eidcs[2],
NULL, NULL, NULL, NULL,
sctx->facenorm+3*fidcs[0],
sctx->facenorm+3*fidcs[3],
sctx->facenorm+3*fidcs[1],
sctx->facenorm+3*fidcs[2],
NULL, NULL );
if ( score<bestscore ) {
bestscore=score;
bestchoice=2;
}
switch ( bestchoice ) {
case 0: connections[2*degeneracies[0]+1]=degeneracies[1];
connections[2*degeneracies[1]+1]=degeneracies[0];
connections[2*degeneracies[2]+1]=degeneracies[3];
connections[2*degeneracies[3]+1]=degeneracies[2];
break;
case 1: connections[2*degeneracies[0]+1]=degeneracies[2];
connections[2*degeneracies[2]+1]=degeneracies[0];
connections[2*degeneracies[1]+1]=degeneracies[3];
connections[2*degeneracies[3]+1]=degeneracies[1];
break;
case 2: connections[2*degeneracies[0]+1]=degeneracies[3];
connections[2*degeneracies[3]+1]=degeneracies[0];
connections[2*degeneracies[1]+1]=degeneracies[2];
connections[2*degeneracies[2]+1]=degeneracies[1];
break;
}
} else if ( degct==6 ) {
int bestchoice=0;
int eidcs[6], fidcs[6];
int k;
double bestscore;
int pairings[15][6]={{0, 1, 2, 3, 4, 5}, {0, 1, 2, 4, 3, 5}, {0, 1, 2, 5, 3, 4},
{0, 2, 1, 3, 4, 5}, {0, 2, 1, 4, 3, 5}, {0, 2, 1, 5, 3, 4},
{0, 3, 1, 2, 4, 5}, {0, 3, 1, 4, 2, 5}, {0, 3, 1, 5, 2, 4},
{0, 4, 1, 2, 3, 5}, {0, 4, 1, 3, 2, 5}, {0, 4, 1, 5, 2, 3},
{0, 5, 1, 2, 3, 4}, {0, 5, 1, 3, 2, 4}, {0, 5, 1, 4, 2, 3}};
for ( k=0; k<6; ++k ) {
eidcs[k]=3*( bag->evti[connections[2*degeneracies[k]]/3]+5*si )+
connections[2*degeneracies[k]]%3;
fidcs[k]=fvti[degeneracies[k]-36];
}
bestscore=evaluateDegConnection( sctx->edgeicoord+3*eidcs[0],
sctx->faceicoord+3*fidcs[0],
sctx->faceicoord+3*fidcs[1],
sctx->edgeicoord+3*eidcs[1],
sctx->edgeicoord+3*eidcs[2],
sctx->faceicoord+3*fidcs[2],
sctx->faceicoord+3*fidcs[3],
sctx->edgeicoord+3*eidcs[3],
sctx->edgeicoord+3*eidcs[4],
sctx->faceicoord+3*fidcs[4],
sctx->faceicoord+3*fidcs[5],
sctx->edgeicoord+3*eidcs[5],
sctx->facenorm+3*fidcs[0],
sctx->facenorm+3*fidcs[1],
sctx->facenorm+3*fidcs[2],
sctx->facenorm+3*fidcs[3],
sctx->facenorm+3*fidcs[4],
sctx->facenorm+3*fidcs[5] );
for ( k=1; k<15; ++k ) {
double score=evaluateDegConnection
( sctx->edgeicoord+3*eidcs[pairings[k][0]],
sctx->faceicoord+3*fidcs[pairings[k][0]],
sctx->faceicoord+3*fidcs[pairings[k][1]],
sctx->edgeicoord+3*eidcs[pairings[k][1]],
sctx->edgeicoord+3*eidcs[pairings[k][2]],
sctx->faceicoord+3*fidcs[pairings[k][2]],
sctx->faceicoord+3*fidcs[pairings[k][3]],
sctx->edgeicoord+3*eidcs[pairings[k][3]],
sctx->edgeicoord+3*eidcs[pairings[k][4]],
sctx->faceicoord+3*fidcs[pairings[k][4]],
sctx->faceicoord+3*fidcs[pairings[k][5]],
sctx->edgeicoord+3*eidcs[pairings[k][5]],
sctx->facenorm+3*fidcs[pairings[k][0]],
sctx->facenorm+3*fidcs[pairings[k][1]],
sctx->facenorm+3*fidcs[pairings[k][2]],
sctx->facenorm+3*fidcs[pairings[k][3]],
sctx->facenorm+3*fidcs[pairings[k][4]],
sctx->facenorm+3*fidcs[pairings[k][5]] );
if ( score<bestscore ) {
bestscore=score; bestchoice=k;
}
}
connections[2*degeneracies[pairings[bestchoice][0]]+1]=
degeneracies[pairings[bestchoice][1]];
connections[2*degeneracies[pairings[bestchoice][1]]+1]=
degeneracies[pairings[bestchoice][0]];
connections[2*degeneracies[pairings[bestchoice][2]]+1]=
degeneracies[pairings[bestchoice][3]];
connections[2*degeneracies[pairings[bestchoice][3]]+1]=
degeneracies[pairings[bestchoice][2]];
connections[2*degeneracies[pairings[bestchoice][4]]+1]=
degeneracies[pairings[bestchoice][5]];
connections[2*degeneracies[pairings[bestchoice][5]]+1]=
degeneracies[pairings[bestchoice][4]];
}
/* sufficient to run to 36: each polygon will contain at least
* one edge vertex */
for ( i=0; i<36; i++ ) {
if ( connections[2*i]!=-1 ) {
/* extract polygon from connections array */
signed char polygon[42];
unsigned char polyct=0;
char thiz=i;
char next=connections[2*i];
polygon[polyct++]=i;
connections[2*i]=-1;
while ( next!=-1 ) {
char helpnext;
polygon[polyct++]=next;
if ( connections[2*next]==thiz ) {
helpnext=connections[2*next+1];
} else {
helpnext=connections[2*next];
}
connections[2*next]=connections[2*next+1]=-1;
thiz = next; next = helpnext;
if ( next==polygon[0] )
break; /* polygon is closed */
}
if ( next!=-1 ) { /* else: discard unclosed polygon */
/* make sure all required vertices are there */
int j;
for ( j=0; j<polyct; ++j ) {
double tvertA[4], tvertB[4];
if ( polygon[j]<36 ) { /* we may need to insert an edge vertex */
int eidx=3*( bag->evti[polygon[j]/3] + 5*si )+polygon[j]%3;
if ( -1 == sctx->vidx[eidx] ) {
int ovi;
ELL_3V_COPY( tvertA, sctx->edgeicoord+3*eidx );
tvertA[3]=1.0;
/* tvertB in input index space */
ELL_4MV_MUL( tvertB, sctx->txfIdx, tvertA );
/* tvertA in world space */
ELL_4MV_MUL( tvertA, sctx->shape->ItoW, tvertB );
ELL_4V_HOMOG( tvertA, tvertA );
ovi = sctx->vidx[eidx] =
airArrayLenIncr( bag->xyzwArr, 1 );
ELL_4V_SET_TT( lpld->xyzw + 4*ovi, float,
tvertA[0], tvertA[1], tvertA[2], 1.0 );
if ( sctx->normalsFind ) {
double len=ELL_3V_LEN( sctx->edgenorm+3*eidx );
airArrayLenIncr( bag->normArr, 1 );
ELL_3V_SCALE_TT( lpld->norm + 3*ovi, float, 1.0/len,
sctx->edgenorm+3*eidx );
}
sctx->vertNum++;
}
} else { /* we may need to insert a face vertex */
int fidx=fvti[polygon[j]-36];
if ( -1 == sctx->facevidx[fidx] ) {
int ovi;
ELL_3V_COPY( tvertA, sctx->faceicoord+3*fidx );
tvertA[3]=1.0;
/* tvertB in input index space */
ELL_4MV_MUL( tvertB, sctx->txfIdx, tvertA );
/* tvertA in world space */
ELL_4MV_MUL( tvertA, sctx->shape->ItoW, tvertB );
ELL_4V_HOMOG( tvertA, tvertA );
ovi = sctx->facevidx[fidx] =
airArrayLenIncr( bag->xyzwArr, 1 );
ELL_4V_SET_TT( lpld->xyzw + 4*ovi, float,
tvertA[0], tvertA[1], tvertA[2], 1.0 );
if ( sctx->normalsFind ) {
double len=ELL_3V_LEN( sctx->facenorm+3*fidx );
airArrayLenIncr( bag->normArr, 1 );
ELL_3V_SCALE_TT( lpld->norm + 3*ovi, float, 1.0/len,
sctx->facenorm+3*fidx );
}
sctx->vertNum++;
}
}
}
if ( polyct>4 ) { /* we need to insert a helper vertex */
double tvertA[4], tvertB[4], tvertAsum[4]={0, 0, 0, 0},
normsum[3]={0, 0, 0};
int ovi;
unsigned int vii[3];
for ( j=0; j<polyct; j++ ) {
if ( polygon[j]<36 ) {
int eidx=3*( bag->evti[polygon[j]/3] + 5*si )+polygon[j]%3;
ELL_3V_COPY( tvertA, sctx->edgeicoord+3*eidx );
tvertA[3]=1.0;
ELL_4V_INCR( tvertAsum, tvertA );
if ( ELL_3V_DOT( normsum, sctx->edgenorm+3*eidx )<0 )
ELL_3V_SUB( normsum, normsum, sctx->edgenorm+3*eidx );
else
ELL_3V_INCR( normsum, sctx->edgenorm+3*eidx );
} else {
int fidx=fvti[polygon[j]-36];
ELL_3V_COPY( tvertA, sctx->faceicoord+3*fidx );
tvertA[3]=1.0;
ELL_4V_INCR( tvertAsum, tvertA );
if ( ELL_3V_DOT( normsum, sctx->facenorm+3*fidx )<0 )
ELL_3V_SUB( normsum, normsum, sctx->facenorm+3*fidx );
else
ELL_3V_INCR( normsum, sctx->facenorm+3*fidx );
}
}
/* tvertB in input index space */
ELL_4MV_MUL( tvertB, sctx->txfIdx, tvertAsum );
/* tvertA in world space */
ELL_4MV_MUL( tvertA, sctx->shape->ItoW, tvertB );
ELL_4V_HOMOG( tvertA, tvertA );
ovi = airArrayLenIncr( bag->xyzwArr, 1 );
ELL_4V_SET_TT( lpld->xyzw + 4*ovi, float,
tvertA[0], tvertA[1], tvertA[2], 1.0 );
if ( sctx->normalsFind ) {
double len=ELL_3V_LEN( normsum );
airArrayLenIncr( bag->normArr, 1 );
ELL_3V_SCALE_TT( lpld->norm + 3*ovi, float, 1.0/len, normsum );
}
sctx->vertNum++;
vii[0]=ovi;
vii[1]=sctx->vidx[3*( bag->evti[polygon[0]/3]+5*si )+polygon[0]%3];
for ( j=0; j<polyct; ++j ) {
double edgeA[3], edgeB[3];
double norm[3];
vii[2]=vii[1];
if ( j==polyct-1 ) {
if ( polygon[0]<36 )
vii[1]=sctx->vidx[3*( bag->evti[polygon[0]/3] + 5*si )+
polygon[0]%3];
else vii[1]=sctx->facevidx[fvti[polygon[0]-36]];
} else {
if ( polygon[j+1]<36 )
vii[1]=sctx->vidx[3*( bag->evti[polygon[j+1]/3] + 5*si )+
polygon[j+1]%3];
else vii[1]=sctx->facevidx[fvti[polygon[j+1]-36]];
}
/* check for degenerate tris */
ELL_3V_SUB( edgeA, lpld->xyzw+4*vii[1], lpld->xyzw+4*vii[0] );
ELL_3V_SUB( edgeB, lpld->xyzw+4*vii[2], lpld->xyzw+4*vii[0] );
ELL_3V_CROSS( norm, edgeA, edgeB );
if ( ELL_3V_DOT( norm, norm )!=0 ) {
unsigned iii = airArrayLenIncr( bag->indxArr, 3 );
ELL_3V_COPY( lpld->indx + iii, vii );
lpld->icnt[0] += 3;
sctx->faceNum++;
}
/* else: degeneracies are caused by intersections that
* more or less coincide with a grid vertex. They
* should be harmless, so just don't create
* deg. triangles in this case */
}
} else if ( polyct>2 ) {
/* insert the actual triangles */
unsigned int vii[3], iii;
if ( polygon[0]<36 )
vii[0]=sctx->vidx[3*( bag->evti[polygon[0]/3] + 5*si )+
polygon[0]%3];
else vii[0]=sctx->facevidx[fvti[polygon[0]-36]];
if ( polygon[1]<36 )
vii[1]=sctx->vidx[3*( bag->evti[polygon[1]/3] + 5*si )+
polygon[1]%3];
else vii[1]=sctx->facevidx[fvti[polygon[1]-36]];
if ( polygon[2]<36 )
vii[2]=sctx->vidx[3*( bag->evti[polygon[2]/3] + 5*si )+
polygon[2]%3];
else vii[2]=sctx->facevidx[fvti[polygon[2]-36]];
iii = airArrayLenIncr( bag->indxArr, 3 );
ELL_3V_COPY( lpld->indx + iii, vii );
lpld->icnt[0] += 3;
sctx->faceNum++;
if ( polyct==4 ) {
vii[1]=vii[2];
if ( polygon[3]<36 )
vii[2]=sctx->vidx[3*( bag->evti[polygon[3]/3] + 5*si )+
polygon[3]%3];
else vii[2]=sctx->facevidx[fvti[polygon[3]-36]];
iii = airArrayLenIncr( bag->indxArr, 3 );
ELL_3V_COPY( lpld->indx + iii, vii );
lpld->icnt[0] += 3;
sctx->faceNum++;
}
}
}
}
}
}
}
return 0;
}
static void
1744 shuffleT( seekContext *sctx, baggage *bag ) {
unsigned int xi, yi, sx, sy, si;
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
if ( sctx->strengthUse ) { /* requests need to be cleared initially */
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
sctx->treated[xi+sx*yi] &= 0xFF^_SEEK_TREATED_REQUEST;
}
}
} /* else, the request bits are always on */
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
/* vidx neither needs past nor future context */
if ( !bag->zi ) {
ELL_3V_SET( sctx->vidx+3*( 0+5*si ), -1, -1, -1 );
ELL_3V_SET( sctx->vidx+3*( 1+5*si ), -1, -1, -1 );
} else {
ELL_3V_COPY( sctx->vidx+3*( 0+5*si ), sctx->vidx+3*( 3+5*si ) );
ELL_3V_COPY( sctx->vidx+3*( 1+5*si ), sctx->vidx+3*( 4+5*si ) );
}
ELL_3V_SET( sctx->vidx+3*( 2+5*si ), -1, -1, -1 );
ELL_3V_SET( sctx->vidx+3*( 3+5*si ), -1, -1, -1 );
ELL_3V_SET( sctx->vidx+3*( 4+5*si ), -1, -1, -1 );
/* strength only has future context */
if ( sctx->strengthUse ) {
sctx->stng[0 + 2*si] = sctx->stng[1 + 2*si];
sctx->stng[1 + 2*si] = sctx->stngcontext[si];
if ( sctx->stng[0+2*si]>sctx->strength ||
sctx->stng[1+2*si]>sctx->strength ) {
/* set up to four request bits */
sctx->treated[si] |= _SEEK_TREATED_REQUEST;
if ( xi!=0 ) sctx->treated[xi-1+sx*yi] |= _SEEK_TREATED_REQUEST;
if ( yi!=0 ) sctx->treated[xi+sx*( yi-1 )] |= _SEEK_TREATED_REQUEST;
if ( xi!=0 && yi!=0 )
sctx->treated[xi-1+sx*( yi-1 )] |= _SEEK_TREATED_REQUEST;
}
}
/* shuffle grad, hess and t in three steps: move to past context,
* shuffle in slab itself, move from future context */
ELL_3V_COPY( sctx->gradcontext + 3*( 0+2*si ), sctx->grad + 3*( 0+2*si ) );
ELL_3V_COPY( sctx->grad + 3*( 0+2*si ), sctx->grad + 3*( 1+2*si ) );
ELL_3V_COPY( sctx->grad + 3*( 1+2*si ), sctx->gradcontext + 3*( 1+2*si ) );
ELL_3M_COPY( sctx->hesscontext + 9*( 0+2*si ), sctx->hess + 9*( 0+2*si ) );
ELL_3M_COPY( sctx->hess + 9*( 0+2*si ), sctx->hess + 9*( 1+2*si ) );
ELL_3M_COPY( sctx->hess + 9*( 1+2*si ), sctx->hesscontext + 9*( 1+2*si ) );
ELL_3M_COPY( sctx->tcontext + 9*( 0+2*si ), sctx->t + 9*( 0+2*si ) );
ELL_3M_COPY( sctx->t + 9*( 0+2*si ), sctx->t + 9*( 1+2*si ) );
ELL_3M_COPY( sctx->t + 9*( 1+2*si ), sctx->tcontext + 9*( 1+2*si ) );
}
}
}
static void
1808 probeT( seekContext *sctx, baggage *bag, double zi ) {
unsigned int xi, yi, sx, sy, si;
sx = AIR_CAST( unsigned int, sctx->sx );
sy = AIR_CAST( unsigned int, sctx->sy );
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
si = xi + sx*yi;
if ( sctx->gctx ) { /* HEY: need this check, what's the right way? */
_seekIdxProbe( sctx, bag, xi, yi, zi );
}
if ( sctx->strengthUse ) {
sctx->stngcontext[si] = sctx->strengthSign*sctx->stngAns[0];
if ( sctx->strengthSeenMax==AIR_NAN ) {
sctx->strengthSeenMax = sctx->stngcontext[si];
}
sctx->strengthSeenMax = AIR_MAX( sctx->strengthSeenMax,
sctx->stngcontext[si] );
}
ELL_3V_COPY( sctx->gradcontext + 3*( 1 + 2*si ), sctx->gradAns );
ELL_3M_COPY( sctx->hesscontext + 9*( 1 + 2*si ), sctx->hessAns );
_seekHess2T( sctx->tcontext + 9*( 1 + 2*si ), sctx->evalAns, sctx->evecAns,
sctx->evalDiffThresh, ( sctx->type==seekTypeRidgeSurfaceT ) );
}
}
}
/* it has now become much easier to make this its own routine
* ( vs. adding many more case distinctions to shuffleProbe )
* this only duplicates little ( and trivial ) code */
int
1841 _seekShuffleProbeT( seekContext *sctx, baggage *bag ) {
/* for high-quality normal estimation, we need two slices of data
* context; to keep the code simple, separate shuffle and probe
* operations - let's hope this doesn't destroy cache performance */
if ( !bag->zi ) {
if ( sctx->strengthUse )
/* before the first round, initialize treated to zero */
memset( sctx->treated, 0, sizeof( char )*sctx->sx*sctx->sy );
else /* request all edges */
memset( sctx->treated, _SEEK_TREATED_REQUEST,
sizeof( char )*sctx->sx*sctx->sy );
probeT( sctx, bag, 0 );
shuffleT( sctx, bag );
probeT( sctx, bag, 1 );
}
shuffleT( sctx, bag );
if ( bag->zi!=sctx->sz-2 )
probeT( sctx, bag, bag->zi+2 );
intersectionShuffleProbe( sctx, bag );
return 0;
}
/* For all vertices in pld, use sctx to probe the strength measure,
* and return the answer ( times strengthSign ) in nval. The intended
* use is postfiltering ( with limnPolyDataClip ), which is obligatory
* when using seekType*SurfaceT
*
* Returns 1 and adds a message to biff upon error
* Returns 0 on success, -n when probing n vertices failed ( strength
* is set to AIR_NAN for those ); note that positions outside the field
* are clamped to lie on its boundary.
*
* This routine assumes that a strength has been set in sctx and
* seekUpdate( ) has been run.
* This routine does not modify sctx->strengthSeenMax.
*/
int
1881 seekVertexStrength( Nrrd *nval, seekContext *sctx, limnPolyData *pld ) {
static const char me[]="seekVertexStrength";
unsigned int i;
double *data;
int E=0;
if ( !( nval && sctx && pld ) ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
if ( !( sctx->gctx && sctx->pvl ) ) {
biffAddf( SEEK, "%s: need sctx with attached gageContext", me );
return 1;
}
if ( !sctx->stngAns ) {
biffAddf( SEEK, "%s: no strength item found. Did you enable strengthUse?",
me );
return 1;
}
if ( nrrdAlloc_va( nval, nrrdTypeDouble, 1, ( size_t ) pld->xyzwNum ) ) {
biffAddf( SEEK, "%s: could not allocate output", me );
return 1;
}
data = ( double* ) nval->data;
for ( i=0; i<pld->xyzwNum; i++ ) {
float homog[4];
ELL_4V_HOMOG( homog, pld->xyzw+4*i );
if ( !gageProbeSpace( sctx->gctx, homog[0], homog[1], homog[2], 0, 1 ) ) {
*( data++ )=*( sctx->stngAns )*sctx->strengthSign;
} else {
*( data++ )=AIR_NAN;
E--;
}
}
return E;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2009 Thomas Schultz
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "seek.h"
#include "privateSeek.h"
static int
28 updateNinEtAl( seekContext *sctx ) {
static const char me[]="updateNinEtAl";
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagData = %d\n", me, sctx->flag[flagData] );
}
if ( !( sctx->ninscl || sctx->pvl ) ) {
biffAddf( SEEK, "%s: data never set", me );
return 1;
}
if ( sctx->flag[flagData] ) {
if ( sctx->ninscl ) {
sctx->nin = sctx->ninscl;
sctx->baseDim = 0;
if ( gageShapeSet( sctx->_shape, sctx->ninscl, 0 ) ) {
biffMovef( SEEK, GAGE, "%s: trouble with scalar volume", me );
return 1;
}
sctx->shape = sctx->_shape;
} else {
sctx->nin = sctx->pvl->nin;
sctx->baseDim = sctx->pvl->kind->baseDim;
sctx->shape = sctx->gctx->shape;
}
sctx->flag[flagData] = AIR_FALSE;
sctx->flag[flagNinEtAl] = AIR_TRUE;
}
return 0;
}
static int
62 updateAnswerPointers( seekContext *sctx ) {
static const char me[]="updateAnswerPointers";
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagItemValue = %d\n", me,
sctx->flag[flagItemValue] );
fprintf( stderr, "%s: flagItemStrength = %d\n", me,
sctx->flag[flagItemStrength] );
fprintf( stderr, "%s: flagItemNormal = %d\n", me,
sctx->flag[flagItemNormal] );
fprintf( stderr, "%s: flagItemGradient = %d\n", me,
sctx->flag[flagItemGradient] );
fprintf( stderr, "%s: flagItemEigensystem = %d\n", me,
sctx->flag[flagItemEigensystem] );
fprintf( stderr, "%s: flagItemHess = %d\n", me,
sctx->flag[flagItemHess] );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
fprintf( stderr, "%s: flagNormalsFind = %d\n", me,
sctx->flag[flagNormalsFind] );
fprintf( stderr, "%s: flagStrengthUse = %d\n", me,
sctx->flag[flagStrengthUse] );
fprintf( stderr, "%s: flagType = %d\n", me,
sctx->flag[flagType] );
fprintf( stderr, "%s: flagData = %d\n", me,
sctx->flag[flagData] );
}
if ( seekTypeUnknown == sctx->type ) {
biffAddf( SEEK, "%s: feature type never set", me );
return 1;
}
if ( sctx->flag[flagItemValue]
|| sctx->flag[flagItemStrength]
|| sctx->flag[flagItemNormal]
|| sctx->flag[flagItemGradient]
|| sctx->flag[flagItemEigensystem]
|| sctx->flag[flagItemHess]
|| sctx->flag[flagNinEtAl]
|| sctx->flag[flagNormalsFind]
|| sctx->flag[flagStrengthUse]
|| sctx->flag[flagType] ) {
/* this is apt regardless of feature type */
if ( sctx->strengthUse ) {
if ( -1 == sctx->stngItem ) {
biffAddf( SEEK, "%s: need to set strength item to use strength", me );
return 1;
}
sctx->stngAns = ( gageAnswerPointer( sctx->gctx, sctx->pvl,
sctx->stngItem ) );
} else {
sctx->stngAns = NULL;
}
switch ( sctx->type ) {
case seekTypeIsocontour:
if ( !( sctx->ninscl || -1 != sctx->sclvItem ) ) {
biffAddf( SEEK,
"%s: need either scalar volume or value item set for %s",
me, airEnumStr( seekType, seekTypeIsocontour ) );
return 1;
}
if ( sctx->normalsFind ) {
/* NOTE simplifying assumption described in seek.h */
if ( !( sctx->ninscl || -1 != sctx->normItem ) ) {
biffAddf( SEEK, "%s: need either scalar volume or "
"normal item set for normals for %s",
me, airEnumStr( seekType, seekTypeIsocontour ) );
return 1;
}
}
if ( sctx->ninscl ) {
sctx->sclvAns = NULL;
sctx->normAns = NULL;
} else {
sctx->sclvAns = gageAnswerPointer( sctx->gctx, sctx->pvl,
sctx->sclvItem );
sctx->normAns = ( sctx->normalsFind
? gageAnswerPointer( sctx->gctx, sctx->pvl,
sctx->normItem )
: NULL );
}
if ( sctx->flag[flagItemGradient]
|| sctx->flag[flagItemEigensystem]
|| sctx->flag[flagItemHess] ) {
biffAddf( SEEK,
"%s: can't set gradient, Hessian, or eigensystem for %s",
me, airEnumStr( seekType, seekTypeIsocontour ) );
return 1;
}
sctx->gradAns = NULL;
sctx->evalAns = NULL;
sctx->evecAns = NULL;
sctx->hessAns = NULL;
break;
case seekTypeRidgeSurface:
case seekTypeValleySurface:
case seekTypeMaximalSurface:
case seekTypeMinimalSurface:
case seekTypeRidgeSurfaceOP:
case seekTypeRidgeSurfaceT:
case seekTypeValleySurfaceOP:
case seekTypeValleySurfaceT:
if ( !sctx->pvl ) {
biffAddf( SEEK, "%s: can't find %s without a gage context",
me, airEnumStr( seekType, sctx->type ) );
return 1;
}
if ( !( -1 != sctx->gradItem
&& -1 != sctx->evalItem
&& -1 != sctx->evecItem ) ) {
biffAddf( SEEK, "%s: grad, eval, evec items not all set", me );
return 1;
}
if ( ( sctx->type==seekTypeRidgeSurfaceT ||
sctx->type==seekTypeValleySurfaceT ) &&
-1 == sctx->hessItem ) {
biffAddf( SEEK, "%s: hess item not set", me );
return 1;
}
if ( sctx->normalsFind ) {
/* NOTE simplifying assumption described in seek.h */
if ( -1 == sctx->normItem ) {
biffAddf( SEEK, "%s: need normal item set for normals for %s",
me, airEnumStr( seekType, sctx->type ) );
return 1;
}
sctx->normAns = ( gageAnswerPointer( sctx->gctx, sctx->pvl,
sctx->normItem ) );
} else {
sctx->normAns = NULL;
}
sctx->sclvAns = NULL;
sctx->gradAns = gageAnswerPointer( sctx->gctx, sctx->pvl, sctx->gradItem );
sctx->evalAns = gageAnswerPointer( sctx->gctx, sctx->pvl, sctx->evalItem );
sctx->evecAns = gageAnswerPointer( sctx->gctx, sctx->pvl, sctx->evecItem );
if ( sctx->type==seekTypeRidgeSurfaceT ||
sctx->type==seekTypeValleySurfaceT )
sctx->hessAns = gageAnswerPointer( sctx->gctx, sctx->pvl,
sctx->hessItem );
else
sctx->hessAns = NULL;
break;
default:
biffAddf( SEEK, "%s: sorry, %s extraction not implemented", me,
airEnumStr( seekType, sctx->type ) );
return 1;
}
sctx->flag[flagItemValue] = AIR_FALSE;
sctx->flag[flagItemStrength] = AIR_FALSE;
sctx->flag[flagItemNormal] = AIR_FALSE;
sctx->flag[flagItemGradient] = AIR_FALSE;
sctx->flag[flagItemEigensystem] = AIR_FALSE;
sctx->flag[flagItemHess] = AIR_FALSE;
sctx->flag[flagNormalsFind] = AIR_FALSE;
sctx->flag[flagAnswerPointers] = AIR_TRUE;
}
return 0;
}
static int
228 updateSxSySz( seekContext *sctx ) {
static const char me[]="updateSxSySz";
size_t sizeIn[3], sizeOut[3];
double min, max, scl[3], off[3];
unsigned int axi;
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagSamples = %d\n", me, sctx->flag[flagSamples] );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me, sctx->flag[flagNinEtAl] );
}
sizeIn[0] = sctx->nin->axis[sctx->baseDim+0].size;
sizeIn[1] = sctx->nin->axis[sctx->baseDim+1].size;
sizeIn[2] = sctx->nin->axis[sctx->baseDim+2].size;
if ( sctx->flag[flagSamples]
|| sctx->flag[flagNinEtAl] ) {
if ( 0 == sctx->samples[0]
|| 0 == sctx->samples[1]
|| 0 == sctx->samples[2] ) {
ELL_3V_COPY( sizeOut, sizeIn );
} else {
if ( !sctx->pvl ) {
biffAddf( SEEK,
"%s: can't specify # samples ( %u, %u, %u ) independent of "
"volume dimensions ( %u, %u, %u ) without a gage context", me,
AIR_CAST( unsigned int, sctx->samples[0] ),
AIR_CAST( unsigned int, sctx->samples[1] ),
AIR_CAST( unsigned int, sctx->samples[2] ),
AIR_CAST( unsigned int, sizeIn[0] ),
AIR_CAST( unsigned int, sizeIn[1] ),
AIR_CAST( unsigned int, sizeIn[2] ) );
return 1;
}
ELL_3V_COPY( sizeOut, sctx->samples );
}
/* want to make absolutely sure txfIdx was being set ...
if ( sctx->sx != sizeOut[0]
|| sctx->sy != sizeOut[1]
|| sctx->sz != sizeOut[2] ) { */
sctx->sx = sizeOut[0];
sctx->sy = sizeOut[1];
sctx->sz = sizeOut[2];
/* there has got to be a better way of doing this... */
/* perhaps refer to the way origin is calculated in new nrrd resampler? */
for ( axi=0; axi<3; axi++ ) {
min = ( nrrdCenterCell == sctx->shape->center
? -0.5
: 0.0 );
max = ( nrrdCenterCell == sctx->shape->center
? sizeIn[axi] - 0.5
: sizeIn[axi] - 1.0 );
off[axi] = NRRD_POS( sctx->shape->center, min, max, sizeOut[axi], 0 );
scl[axi] = ( NRRD_POS( sctx->shape->center, min, max, sizeOut[axi], 1 )
- off[axi] );
}
ELL_4V_SET( sctx->txfIdx + 0*4, scl[0], 0.0, 0.0, off[0] );
ELL_4V_SET( sctx->txfIdx + 1*4, 0.0, scl[1], 0.0, off[1] );
ELL_4V_SET( sctx->txfIdx + 2*4, 0.0, 0.0, scl[2], off[2] );
ELL_4V_SET( sctx->txfIdx + 3*4, 0.0, 0.0, 0.0, 1.0 );
sctx->flag[flagSxSySz] = AIR_TRUE;
sctx->flag[flagSamples] = AIR_FALSE;
}
return 0;
}
static int
296 updateReverse( seekContext *sctx ) {
static const char me[]="updateReverse";
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
fprintf( stderr, "%s: flagLowerInside = %d\n", me,
sctx->flag[flagLowerInside] );
}
if ( sctx->flag[flagNinEtAl]
|| sctx->flag[flagLowerInside] ) {
double mat[9];
int reverse;
ELL_34M_EXTRACT( mat, sctx->shape->ItoW );
reverse = ( !!sctx->lowerInside ) ^ ( ELL_3M_DET( mat ) < 0 );
if ( sctx->reverse != reverse ) {
sctx->reverse = reverse;
sctx->flag[flagReverse] = AIR_TRUE;
}
}
return 0;
}
static int
323 updateTxfNormal( seekContext *sctx ) {
static const char me[]="updateTxfNormal";
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
fprintf( stderr, "%s: flagLowerInside = %d\n", me,
sctx->flag[flagLowerInside] );
}
if ( sctx->flag[flagNinEtAl]
|| sctx->flag[flagLowerInside] ) {
double matA[9], matB[9];
ELL_34M_EXTRACT( matA, sctx->shape->ItoW );
ell_3m_inv_d( matB, matA );
ELL_3M_TRANSPOSE( sctx->txfNormal, matB );
if ( !sctx->lowerInside ) {
ELL_3M_SCALE( sctx->txfNormal, -1, sctx->txfNormal );
}
sctx->flag[flagTxfNormal] = AIR_TRUE;
}
sctx->flag[flagLowerInside] = AIR_FALSE;
return 0;
}
static int
351 updateSlabCacheAlloc( seekContext *sctx ) {
static const char me[]="updateSlabCacheAlloc";
int E;
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagType = %d ( type = %s )\n", me,
sctx->flag[flagType], airEnumStr( seekType, sctx->type ) );
fprintf( stderr, "%s: flagStrengthUse = %d\n", me,
sctx->flag[flagStrengthUse] );
fprintf( stderr, "%s: flagSxSySz = %d\n", me,
sctx->flag[flagSxSySz] );
}
E = 0;
if ( sctx->flag[flagType]
|| sctx->flag[flagStrengthUse] /* kind of sloppy/overkill */
|| sctx->flag[flagSxSySz] ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nvidx, nrrdTypeInt, 3,
AIR_CAST( size_t, 15 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->vidx = AIR_CAST( int*, sctx->nvidx->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->ntreated, nrrdTypeChar, 2,
sctx->sx,
sctx->sy );
if ( !E ) sctx->treated = AIR_CAST( signed char*, sctx->ntreated->data );
if ( sctx->strengthUse ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nstng, nrrdTypeDouble, 3,
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->stng = AIR_CAST( double*, sctx->nstng->data );
}
if ( seekTypeIsocontour == sctx->type ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nsclv, nrrdTypeDouble, 3,
AIR_CAST( size_t, 4 ),
sctx->sx + 2,
sctx->sy + 2 );
if ( !E ) sctx->sclv = AIR_CAST( double*, sctx->nsclv->data );
}
if ( seekTypeRidgeSurface == sctx->type
|| seekTypeValleySurface == sctx->type
|| seekTypeMaximalSurface == sctx->type
|| seekTypeMinimalSurface == sctx->type
|| seekTypeRidgeSurfaceOP == sctx->type
|| seekTypeRidgeSurfaceT == sctx->type
|| seekTypeValleySurfaceOP == sctx->type
|| seekTypeValleySurfaceT == sctx->type ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->ngrad, nrrdTypeDouble, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->grad = AIR_CAST( double*, sctx->ngrad->data );
} else {
sctx->grad = NULL;
}
if ( seekTypeRidgeSurface == sctx->type
|| seekTypeValleySurface == sctx->type
|| seekTypeMaximalSurface == sctx->type
|| seekTypeMinimalSurface == sctx->type
|| seekTypeRidgeSurfaceOP == sctx->type
|| seekTypeValleySurfaceOP == sctx->type ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->neval, nrrdTypeDouble, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->eval = AIR_CAST( double*, sctx->neval->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nevec, nrrdTypeDouble, 4,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->evec = AIR_CAST( double*, sctx->nevec->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nflip, nrrdTypeChar, 3,
AIR_CAST( size_t, 5 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->flip = AIR_CAST( signed char*, sctx->nflip->data );
} else {
sctx->eval = NULL;
sctx->evec = NULL;
sctx->flip = NULL;
}
if ( seekTypeRidgeSurfaceT == sctx->type ||
seekTypeValleySurfaceT == sctx->type ) {
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nfacevidx, nrrdTypeInt, 3,
AIR_CAST( size_t, 4 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->facevidx = AIR_CAST( int*, sctx->nfacevidx->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nhess, nrrdTypeDouble, 4,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->hess = AIR_CAST( double*, sctx->nhess->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nt, nrrdTypeDouble, 4,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->t = AIR_CAST( double*, sctx->nt->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nedgealpha, nrrdTypeDouble, 4,
AIR_CAST( size_t, 5 ),
AIR_CAST( size_t, 3 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->edgealpha = AIR_CAST( double*, sctx->nedgealpha->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nedgenorm, nrrdTypeDouble, 4,
AIR_CAST( size_t, 5 ),
AIR_CAST( size_t, 9 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->edgenorm = AIR_CAST( double*, sctx->nedgenorm->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nedgeicoord, nrrdTypeDouble, 4,
AIR_CAST( size_t, 5 ),
AIR_CAST( size_t, 9 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->edgeicoord = AIR_CAST( double*, sctx->nedgeicoord->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nfacecoord, nrrdTypeDouble, 4,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->facecoord = AIR_CAST( double*, sctx->nfacecoord->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nfacenorm, nrrdTypeDouble, 4,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, 3 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->facenorm = AIR_CAST( double*, sctx->nfacenorm->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nfaceicoord, nrrdTypeDouble, 4,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, 3 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->faceicoord = AIR_CAST( double*, sctx->nfaceicoord->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->npairs, nrrdTypeChar, 4,
AIR_CAST( size_t, 4 ),
AIR_CAST( size_t, 12 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->pairs = AIR_CAST( signed char*, sctx->npairs->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->ngradcontext, nrrdTypeDouble, 4,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->gradcontext = AIR_CAST( double*, sctx->ngradcontext->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nhesscontext, nrrdTypeDouble, 4,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->hesscontext = AIR_CAST( double*, sctx->nhesscontext->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->ntcontext, nrrdTypeDouble, 4,
AIR_CAST( size_t, 9 ),
AIR_CAST( size_t, 2 ),
sctx->sx,
sctx->sy );
if ( !E ) sctx->tcontext = AIR_CAST( double*, sctx->ntcontext->data );
if ( !E ) E |= nrrdMaybeAlloc_va( sctx->nstngcontext, nrrdTypeDouble, 2,
sctx->sx,
sctx->sy );
if ( !E ) sctx->stngcontext = AIR_CAST( double*, sctx->nstngcontext->data );
} else {
sctx->facevidx = NULL;
sctx->hess = NULL;
sctx->t = NULL;
sctx->edgealpha = NULL;
sctx->edgenorm = NULL;
sctx->facecoord = NULL;
sctx->facenorm = NULL;
sctx->pairs = NULL;
sctx->gradcontext = NULL;
sctx->hesscontext = NULL;
sctx->tcontext = NULL;
sctx->stngcontext = NULL;
}
sctx->flag[flagSlabCacheAlloc] = AIR_TRUE;
}
if ( E ) {
biffMovef( SEEK, NRRD, "%s: couldn't allocate all slab caches", me );
return 1;
}
sctx->flag[flagStrengthUse] = AIR_FALSE;
sctx->flag[flagSxSySz] = AIR_FALSE;
return 0;
}
static int
548 updateSclDerived( seekContext *sctx ) {
static const char me[]="updateSclDerived";
char doneStr[AIR_STRLEN_SMALL];
double *scl, idxIn[4], idxOut[4], val;
unsigned int xi, yi, zi;
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagType = %d\n", me,
sctx->flag[flagType] );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
}
if ( sctx->flag[flagType]
|| sctx->flag[flagNinEtAl] ) {
if ( !( seekTypeIsocontour == sctx->type
&& sctx->pvl ) ) {
nrrdEmpty( sctx->nsclDerived );
} else {
if ( nrrdMaybeAlloc_va( sctx->nsclDerived, nrrdTypeDouble, 3,
sctx->sx, sctx->sy, sctx->sz ) ) {
biffMovef( SEEK, NRRD,
"%s: couldn't allocated derived scalar volume", me );
return 1;
}
scl = AIR_CAST( double*, sctx->nsclDerived->data );
if ( sctx->verbose ) {
fprintf( stderr, "%s: pre-computing scalar volume ... ", me );
}
for ( zi=0; zi<sctx->sz; zi++ ) {
if ( sctx->verbose ) {
fprintf( stderr, "%s", airDoneStr( 0, zi, sctx->sz-1, doneStr ) );
fflush( stderr );
}
for ( yi=0; yi<sctx->sy; yi++ ) {
for ( xi=0; xi<sctx->sx; xi++ ) {
ELL_4V_SET( idxOut, xi, yi, zi, 1.0 );
ELL_4MV_MUL( idxIn, sctx->txfIdx, idxOut );
ELL_34V_HOMOG( idxIn, idxIn );
gageProbe( sctx->gctx, idxIn[0], idxIn[1], idxIn[2] );
val = sctx->sclvAns[0];
if ( !AIR_EXISTS( val ) ) {
biffAddf( SEEK, "%s: probed scalar[%u, %u, %u] %g doesn't exist",
me, xi, yi, zi, val );
return 1;
}
scl[xi + sctx->sx*( yi + sctx->sy*zi )] = val;
}
}
}
if ( sctx->verbose ) {
fprintf( stderr, "%s\n", airDoneStr( 0, zi, sctx->sz-1, doneStr ) );
}
}
sctx->flag[flagSclDerived] = AIR_TRUE;
}
return 0;
}
static int
610 updateSpanSpaceHist( seekContext *sctx ) {
static const char me[]="updateSpanSpaceHist";
unsigned int sx, sy, sz, ss, xi, yi, zi, vi, si, minI, maxI, *spanHist;
double min, max, val;
const void *data;
double ( *lup )( const void *, size_t );
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagType = %d\n", me,
sctx->flag[flagType] );
fprintf( stderr, "%s: flagSclDerived = %d\n", me,
sctx->flag[flagSclDerived] );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
}
if ( sctx->flag[flagType]
|| sctx->flag[flagSclDerived]
|| sctx->flag[flagNinEtAl] ) {
if ( seekTypeIsocontour != sctx->type ) {
nrrdEmpty( sctx->nspanHist );
sctx->range->min = AIR_NAN;
sctx->range->max = AIR_NAN;
} else {
nrrdRangeSet( sctx->range,
( sctx->ninscl ? sctx->ninscl : sctx->nsclDerived ),
nrrdBlind8BitRangeFalse );
if ( sctx->range->hasNonExist ) {
biffAddf( SEEK, "%s: scalar volume has non-existent values", me );
return 1;
}
sctx->nspanHist->axis[0].min = sctx->range->min;
sctx->nspanHist->axis[1].min = sctx->range->min;
sctx->nspanHist->axis[0].max = sctx->range->max;
sctx->nspanHist->axis[1].max = sctx->range->max;
if ( sctx->ninscl ) {
lup = nrrdDLookup[sctx->ninscl->type];
data = sctx->ninscl->data;
} else {
lup = nrrdDLookup[sctx->nsclDerived->type];
data = sctx->nsclDerived->data;
}
/* calculate the span space histogram */
if ( nrrdMaybeAlloc_va( sctx->nspanHist, nrrdTypeUInt, 2,
AIR_CAST( size_t, sctx->spanSize ),
AIR_CAST( size_t, sctx->spanSize ) ) ) {
biffMovef( SEEK, NRRD,
"%s: couldn't allocate space space histogram", me );
return 1;
}
spanHist = AIR_CAST( unsigned int*, sctx->nspanHist->data );
sx = sctx->sx;
sy = sctx->sy;
sz = sctx->sz;
ss = sctx->spanSize;
for ( si=0; si<ss*ss; si++ ) {
spanHist[si] = 0;
}
for ( zi=0; zi<sz-1; zi++ ) {
for ( yi=0; yi<sy-1; yi++ ) {
for ( xi=0; xi<sx-1; xi++ ) {
vi = xi + sx*( yi + sy*zi );
val = lup( data, vi + 0 + 0*sx + 0*sx*sy );
min = max = val;
val = lup( data, vi + 1 + 0*sx + 0*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 0 + 1*sx + 0*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 1 + 1*sx + 0*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 0 + 0*sx + 1*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 1 + 0*sx + 1*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 0 + 1*sx + 1*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
val = lup( data, vi + 1 + 1*sx + 1*sx*sy );
min = AIR_MIN( min, val );
max = AIR_MAX( max, val );
minI = airIndex( sctx->range->min, min, sctx->range->max, ss );
maxI = airIndex( sctx->range->min, max, sctx->range->max, ss );
spanHist[minI + ss*maxI]++;
}
}
}
}
sctx->flag[flagSclDerived] = AIR_FALSE;
sctx->flag[flagSpanSpaceHist] = AIR_TRUE;
}
return 0;
}
static int
712 updateResult( seekContext *sctx ) {
static const char me[]="updateResult";
if ( sctx->verbose > 5 ) {
fprintf( stderr, "%s: --------------------\n", me );
fprintf( stderr, "%s: flagIsovalue = %d\n", me,
sctx->flag[flagIsovalue] );
fprintf( stderr, "%s: flagAnswerPointers = %d\n", me,
sctx->flag[flagAnswerPointers] );
fprintf( stderr, "%s: flagType = %d\n", me,
sctx->flag[flagType] );
fprintf( stderr, "%s: flagSlabCacheAlloc = %d\n", me,
sctx->flag[flagSlabCacheAlloc] );
fprintf( stderr, "%s: flagSpanSpaceHist = %d\n", me,
sctx->flag[flagSpanSpaceHist] );
fprintf( stderr, "%s: flagNinEtAl = %d\n", me,
sctx->flag[flagNinEtAl] );
fprintf( stderr, "%s: flagReverse = %d\n", me,
sctx->flag[flagReverse] );
fprintf( stderr, "%s: flagTxfNormal = %d\n", me,
sctx->flag[flagTxfNormal] );
}
if ( seekTypeIsocontour != sctx->type
&& sctx->flag[flagIsovalue] ) {
biffAddf( SEEK, "%s: can't set isovalue for %s ( only %s )", me,
airEnumStr( seekType, sctx->type ),
airEnumStr( seekType, seekTypeIsocontour ) );
return 1;
}
if ( sctx->strengthUse && !sctx->stngAns ) {
biffAddf( SEEK, "%s: can't use feature strength without a strength item",
me );
return 1;
}
/* this seems to be a very pointless exercise */
if ( sctx->flag[flagIsovalue]
|| sctx->flag[flagEvalDiffThresh]
|| sctx->flag[flagAnswerPointers]
|| sctx->flag[flagStrengthUse]
|| sctx->flag[flagStrength]
|| sctx->flag[flagType]
|| sctx->flag[flagSlabCacheAlloc]
|| sctx->flag[flagSpanSpaceHist]
|| sctx->flag[flagNinEtAl]
|| sctx->flag[flagReverse]
|| sctx->flag[flagTxfNormal] ) {
sctx->flag[flagResult] = AIR_TRUE;
sctx->flag[flagIsovalue] = AIR_FALSE;
sctx->flag[flagEvalDiffThresh] = AIR_FALSE;
sctx->flag[flagAnswerPointers] = AIR_FALSE;
sctx->flag[flagStrengthUse] = AIR_FALSE;
sctx->flag[flagStrength] = AIR_FALSE;
sctx->flag[flagType] = AIR_FALSE;
sctx->flag[flagSlabCacheAlloc] = AIR_FALSE;
sctx->flag[flagSpanSpaceHist] = AIR_FALSE;
sctx->flag[flagNinEtAl] = AIR_FALSE;
sctx->flag[flagReverse] = AIR_FALSE;
sctx->flag[flagTxfNormal] = AIR_FALSE;
}
return 0;
}
/*
******** seekUpdate
**
** traverses dependency graph for all of seek stuff, which necessarily
** includes nodes specific to, say, isosurfacing, even when isosurfacing
** is not being done. The idea is to use the same graph for all feature
** types, for error checking if nothing else, leavings steps as no-ops
** as needed.
*/
int
790 seekUpdate( seekContext *sctx ) {
static const char me[]="seekUpdate";
int E;
if ( !sctx ) {
biffAddf( SEEK, "%s: got NULL pointer", me );
return 1;
}
E = 0;
if ( !E ) E |= updateNinEtAl( sctx );
if ( !E ) E |= updateAnswerPointers( sctx );
if ( !E ) E |= updateSxSySz( sctx );
if ( !E ) E |= updateReverse( sctx );
if ( !E ) E |= updateTxfNormal( sctx );
if ( !E ) E |= updateSlabCacheAlloc( sctx );
if ( !E ) E |= updateSclDerived( sctx );
if ( !E ) E |= updateSpanSpaceHist( sctx );
if ( !E ) E |= updateResult( sctx );
if ( E ) {
biffAddf( SEEK, "%s: trouble updating", me );
return 1;
}
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/*
** learned: don't take sqrt( FLT_EPSILON ) and expect it to still be
** negligible
*/
/*
******** !!!! NOTE NOTE NOTE NOTE NOTE !!!!
********
******** THIS CODE IS NOT REALLY MEANT TO BE EDITED BY HUMANS
******** ( only GLK : )
********
******** It is the worst possible example of the dangers of cut-and-paste
********
******** !!!! NOTE NOTE NOTE NOTE NOTE !!!!
*/
42 float _tenAnisoEval_Conf_f( const float eval[3] ) {
AIR_UNUSED( eval );
return 1.0;
}
46 double _tenAnisoEval_Conf_d( const double eval[3] ) {
AIR_UNUSED( eval );
return 1.0; return 1.0;
}
50 float _tenAnisoTen_Conf_f( const float ten[7] ) {
return ten[0];
}
53 double _tenAnisoTen_Conf_d( const double ten[7] ) {
return ten[0];
}
58 float _tenAnisoEval_Cl1_f( const float eval[3] ) {
float sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? ( eval[0] - eval[1] )/sum : 0.0f;
}
63 double _tenAnisoEval_Cl1_d( const double eval[3] ) {
double sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? ( eval[0] - eval[1] )/sum : 0.0;
}
68 float _tenAnisoTen_Cl1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cl1_f( eval );
}
73 double _tenAnisoTen_Cl1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cl1_d( eval );
}
80 float _tenAnisoEval_Cp1_f( const float eval[3] ) {
float sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? 2*( eval[1] - eval[2] )/sum : 0.0f;
}
85 double _tenAnisoEval_Cp1_d( const double eval[3] ) {
double sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? 2*( eval[1] - eval[2] )/sum : 0.0;
}
90 float _tenAnisoTen_Cp1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cp1_f( eval );
}
95 double _tenAnisoTen_Cp1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cp1_d( eval );
}
102 float _tenAnisoEval_Ca1_f( const float eval[3] ) {
float sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? ( eval[0] + eval[1] - 2*eval[2] )/sum : 0.0f;
}
107 double _tenAnisoEval_Ca1_d( const double eval[3] ) {
double sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? ( eval[0] + eval[1] - 2*eval[2] )/sum : 0.0;
}
112 float _tenAnisoTen_Ca1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Ca1_f( eval );
}
117 double _tenAnisoTen_Ca1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Ca1_d( eval );
}
124 float _tenAnisoEval_Clpmin1_f( const float eval[3] ) {
float cl, cp, sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
cl = sum ? ( eval[0] - eval[1] )/sum : 0.0f;
cp = sum ? 2*( eval[1] - eval[2] )/sum : 0.0f;
return AIR_MIN( cl, cp );
}
131 double _tenAnisoEval_Clpmin1_d( const double eval[3] ) {
double cl, cp, sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
cl = sum ? ( eval[0] - eval[1] )/sum : 0.0;
cp = sum ? 2*( eval[1] - eval[2] )/sum : 0.0;
return AIR_MIN( cl, cp );
}
138 float _tenAnisoTen_Clpmin1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Clpmin1_f( eval );
}
143 double _tenAnisoTen_Clpmin1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Clpmin1_d( eval );
}
150 float _tenAnisoEval_Cs1_f( const float eval[3] ) {
float sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? 3*eval[2]/sum : 0.0f;
}
155 double _tenAnisoEval_Cs1_d( const double eval[3] ) {
double sum = eval[0] + eval[1] + eval[2];
sum = AIR_MAX( 0, sum );
return sum ? 3*eval[2]/sum : 0.0;
}
160 float _tenAnisoTen_Cs1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cs1_f( eval );
}
165 double _tenAnisoTen_Cs1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cs1_d( eval );
}
172 float _tenAnisoEval_Ct1_f( const float _eval[3] ) {
float dem, mn, eval[3];
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
dem = eval[0] + eval[1] - 2*eval[2];
return dem ? 2*( eval[1] - eval[2] )/dem : 0.0f;
}
179 double _tenAnisoEval_Ct1_d( const double _eval[3] ) {
double dem, mn, eval[3];
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
dem = eval[0] + eval[1] - 2*eval[2];
return dem ? 2*( eval[1] - eval[2] )/dem : 0.0;
}
186 float _tenAnisoTen_Ct1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Ct1_f( eval );
}
191 double _tenAnisoTen_Ct1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Ct1_d( eval );
}
198 float _tenAnisoEval_Cl2_f( const float eval[3] ) {
float eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[0] - eval[1] )/eval0 : 0.0f;
}
202 double _tenAnisoEval_Cl2_d( const double eval[3] ) {
double eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[0] - eval[1] )/eval0 : 0.0;
}
206 float _tenAnisoTen_Cl2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cl2_f( eval );
}
211 double _tenAnisoTen_Cl2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cl2_d( eval );
}
218 float _tenAnisoEval_Cp2_f( const float eval[3] ) {
float eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[1] - eval[2] )/eval0 : 0.0f;
}
222 double _tenAnisoEval_Cp2_d( const double eval[3] ) {
double eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[1] - eval[2] )/eval0 : 0.0;
}
226 float _tenAnisoTen_Cp2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cp2_f( eval );
}
231 double _tenAnisoTen_Cp2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cp2_d( eval );
}
238 float _tenAnisoEval_Ca2_f( const float eval[3] ) {
float eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[0] - eval[2] )/eval0 : 0.0f;
}
242 double _tenAnisoEval_Ca2_d( const double eval[3] ) {
double eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? ( eval[0] - eval[2] )/eval0 : 0.0;
}
246 float _tenAnisoTen_Ca2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Ca2_f( eval );
}
251 double _tenAnisoTen_Ca2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Ca2_d( eval );
}
258 float _tenAnisoEval_Clpmin2_f( const float eval[3] ) {
float cl, cp, eval0 = AIR_MAX( 0, eval[0] );
cl = eval0 ? ( eval[0] - eval[1] )/eval0 : 0.0f;
cp = eval0 ? ( eval[1] - eval[2] )/eval0 : 0.0f;
return AIR_MIN( cl, cp );
}
264 double _tenAnisoEval_Clpmin2_d( const double eval[3] ) {
double cl, cp, eval0 = AIR_MAX( 0, eval[0] );
cl = eval0 ? ( eval[0] - eval[1] )/eval0 : 0.0;
cp = eval0 ? ( eval[1] - eval[2] )/eval0 : 0.0;
return AIR_MIN( cl, cp );
}
270 float _tenAnisoTen_Clpmin2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Clpmin2_f( eval );
}
275 double _tenAnisoTen_Clpmin2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Clpmin2_d( eval );
}
282 float _tenAnisoEval_Cs2_f( const float eval[3] ) {
float eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? eval[2]/eval0 : 0.0f;
}
286 double _tenAnisoEval_Cs2_d( const double eval[3] ) {
double eval0 = AIR_MAX( 0, eval[0] );
return eval0 ? eval[2]/eval0 : 0.0;
}
290 float _tenAnisoTen_Cs2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Cs2_f( eval );
}
295 double _tenAnisoTen_Cs2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Cs2_d( eval );
}
302 float _tenAnisoEval_Ct2_f( const float eval[3] ) {
float denom;
denom = eval[0] - eval[2];
return denom ? ( eval[1] - eval[2] )/denom : 0.0f;
}
307 double _tenAnisoEval_Ct2_d( const double eval[3] ) {
double denom;
denom = eval[0] - eval[2];
return denom ? ( eval[1] - eval[2] )/denom : 0.0;
}
312 float _tenAnisoTen_Ct2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return _tenAnisoEval_Ct2_f( eval );
}
317 double _tenAnisoTen_Ct2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return _tenAnisoEval_Ct2_d( eval );
}
#define SQRT6 2.44948974278317809819
325 float _tenAnisoEval_RA_f( const float eval[3] ) {
float mean, stdv;
mean = ( eval[0] + eval[1] + eval[2] )/3;
stdv = AIR_CAST( float,
sqrt( ( mean-eval[0] )*( mean-eval[0] ) /* not exactly stdv */
+ ( mean-eval[1] )*( mean-eval[1] )
+ ( mean-eval[2] )*( mean-eval[2] ) ) );
return mean ? AIR_CAST( float, stdv/( mean*SQRT6 ) ) : 0.0f;
}
334 double _tenAnisoEval_RA_d( const double eval[3] ) {
double mean, stdv;
mean = ( eval[0] + eval[1] + eval[2] )/3;
stdv = sqrt( ( mean-eval[0] )*( mean-eval[0] ) /* not exactly standard dev */
+ ( mean-eval[1] )*( mean-eval[1] )
+ ( mean-eval[2] )*( mean-eval[2] ) );
return mean ? stdv/( mean*SQRT6 ) : 0.0;
}
342 float _tenAnisoTen_RA_f( const float tt[7] ) {
float mn, stdv, dev[7];
mn = TEN_T_TRACE( tt )/3;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
stdv = AIR_CAST( float, sqrt( TEN_T_DOT( dev, dev ) ) );
return mn ? AIR_CAST( float, stdv/( mn*SQRT6 ) ) : 0.0f;
}
349 double _tenAnisoTen_RA_d( const double tt[7] ) {
double mn, stdv, dev[7];
mn = TEN_T_TRACE( tt )/3;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
stdv = sqrt( TEN_T_DOT( dev, dev ) );
return mn ? stdv/( mn*SQRT6 ) : 0.0;
}
358 float _tenAnisoEval_FA_f( const float eval[3] ) {
float denom, mean, stdv;
denom = 2.0f*( eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2] );
mean = ( eval[0] + eval[1] + eval[2] )/3;
stdv = AIR_CAST( float,
( mean-eval[0] )*( mean-eval[0] ) /* not exactly stdv */
+ ( mean-eval[1] )*( mean-eval[1] )
+ ( mean-eval[2] )*( mean-eval[2] ) );
return denom ? AIR_CAST( float, sqrt( 3.0*stdv/denom ) ) : 0.0f;
}
368 double _tenAnisoEval_FA_d( const double eval[3] ) {
double denom, mean, stdv;
denom = 2.0*( eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2] );
mean = ( eval[0] + eval[1] + eval[2] )/3;
stdv = ( ( mean-eval[0] )*( mean-eval[0] ) /* not exactly standard dev */
+ ( mean-eval[1] )*( mean-eval[1] )
+ ( mean-eval[2] )*( mean-eval[2] ) );
return denom ? sqrt( 3.0*stdv/denom ) : 0.0;
}
377 float _tenAnisoTen_FA_f( const float tt[7] ) {
float denom, mn, stdv, dev[7];
denom = AIR_CAST( float, 2.0*TEN_T_DOT( tt, tt ) );
mn = TEN_T_TRACE( tt )/3;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
stdv = TEN_T_DOT( dev, dev );
return denom ? AIR_CAST( float, sqrt( 3.0*stdv/denom ) ) : 0.0f;
}
385 double _tenAnisoTen_FA_d( const double tt[7] ) {
double denom, mn, stdv, dev[7];
denom = 2.0*TEN_T_DOT( tt, tt );
mn = TEN_T_TRACE( tt )/3;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
stdv = TEN_T_DOT( dev, dev );
return denom ? AIR_CAST( float, sqrt( 3.0*stdv/denom ) ) : 0.0;
}
395 float _tenAnisoEval_VF_f( const float eval[3] ) {
float mean;
mean = ( eval[0] + eval[1] + eval[2] )/3.0f;
mean = mean*mean*mean;
return 1.0f - ( mean ? eval[0]*eval[1]*eval[2]/mean : 0.0f );
}
401 double _tenAnisoEval_VF_d( const double eval[3] ) {
double mean;
mean = ( eval[0] + eval[1] + eval[2] )/3;
mean = mean*mean*mean;
return 1.0 - ( mean ? eval[0]*eval[1]*eval[2]/mean : 0.0 );
}
407 float _tenAnisoTen_VF_f( const float ten[7] ) {
float mean;
mean = TEN_T_TRACE( ten )/3.0f;
mean = mean*mean*mean;
return 1.0f - ( mean ? TEN_T_DET( ten )/mean : 0.0f );
}
413 double _tenAnisoTen_VF_d( const double ten[7] ) {
double mean;
mean = TEN_T_TRACE( ten )/3.0;
mean = mean*mean*mean;
return 1.0 - ( mean ? TEN_T_DET( ten )/mean : 0.0 );
}
421 float _tenAnisoEval_B_f( const float eval[3] ) {
return eval[0]*eval[1] + eval[0]*eval[2] + eval[1]*eval[2];
}
424 double _tenAnisoEval_B_d( const double eval[3] ) {
return eval[0]*eval[1] + eval[0]*eval[2] + eval[1]*eval[2];
}
427 float _tenAnisoTen_B_f( const float ten[7] ) {
return ( ten[1]*ten[4] + ten[1]*ten[6] + ten[4]*ten[6]
- ten[2]*ten[2] - ten[3]*ten[3] - ten[5]*ten[5] );
}
431 double _tenAnisoTen_B_d( const double ten[7] ) {
return ( ten[1]*ten[4] + ten[1]*ten[6] + ten[4]*ten[6]
- ten[2]*ten[2] - ten[3]*ten[3] - ten[5]*ten[5] );
}
437 float _tenAnisoEval_Q_f( const float eval[3] ) {
float A, B;
A = -( eval[0] + eval[1] + eval[2] );
B = _tenAnisoEval_B_f( eval );
A = ( A*A - 3.0f*B )/9.0f;
return AIR_MAX( 0, A );
}
444 double _tenAnisoEval_Q_d( const double eval[3] ) {
double A, B;
A = -( eval[0] + eval[1] + eval[2] );
B = _tenAnisoEval_B_d( eval );
A = ( A*A - 3.0*B )/9.0;
return AIR_MAX( 0, A );
}
451 float _tenAnisoTen_Q_f( const float ten[7] ) {
float A, B;
A = -TEN_T_TRACE( ten );
B = _tenAnisoTen_B_f( ten );
A = ( A*A - 3.0f*B )/9.0f;
return AIR_MAX( 0, A );
}
458 double _tenAnisoTen_Q_d( const double ten[7] ) {
double A, B;
A = -TEN_T_TRACE( ten );
B = _tenAnisoTen_B_d( ten );
A = ( A*A - 3.0*B )/9.0;
return AIR_MAX( 0, A );
}
467 float _tenAnisoEval_R_f( const float eval[3] ) {
float A, B, C;
A = -( eval[0] + eval[1] + eval[2] );
B = _tenAnisoEval_B_f( eval );
C = -eval[0]*eval[1]*eval[2];
return ( -2*A*A*A + 9*A*B - 27*C )/54;
}
474 double _tenAnisoEval_R_d( const double eval[3] ) {
double A, B, C;
A = -( eval[0] + eval[1] + eval[2] );
B = _tenAnisoEval_B_d( eval );
C = -eval[0]*eval[1]*eval[2];
return ( -2*A*A*A + 9*A*B - 27*C )/54;
}
481 float _tenAnisoTen_R_f( const float ten[7] ) {
float A, B, C;
A = -TEN_T_TRACE( ten );
B = _tenAnisoTen_B_f( ten );
C = -TEN_T_DET( ten );
return ( -2*A*A*A + 9*A*B - 27*C )/54;
}
488 double _tenAnisoTen_R_d( const double ten[7] ) {
double A, B, C;
A = -TEN_T_TRACE( ten );
B = _tenAnisoTen_B_d( ten );
C = -TEN_T_DET( ten );
return ( -2*A*A*A + 9*A*B - 27*C )/54;
}
497 float _tenAnisoEval_S_f( const float eval[3] ) {
return eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2];
}
500 double _tenAnisoEval_S_d( const double eval[3] ) {
return eval[0]*eval[0] + eval[1]*eval[1] + eval[2]*eval[2];
}
503 float _tenAnisoTen_S_f( const float ten[7] ) {
return TEN_T_DOT( ten, ten );
}
506 double _tenAnisoTen_S_d( const double ten[7] ) {
return TEN_T_DOT( ten, ten );
}
#define OOSQRT2 0.70710678118654752440
511 float _tenAnisoEval_Skew_f( const float _eval[3] ) {
float Q, num, dnm, ret, mn, eval[3];
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
Q = _tenAnisoEval_Q_f( eval );
num = _tenAnisoEval_R_f( eval );
dnm = AIR_CAST( float, Q*sqrt( 2*Q ) );
ret = dnm ? AIR_CAST( float, num/dnm ) : 0.0f;
return AIR_CAST( float, AIR_CLAMP( -OOSQRT2, ret, OOSQRT2 ) );
}
521 double _tenAnisoEval_Skew_d( const double _eval[3] ) {
double Q, num, dnm, ret, mn, eval[3];
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( eval, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
Q = _tenAnisoEval_Q_d( eval );
num = _tenAnisoEval_R_d( eval );
dnm = Q*sqrt( 2*Q );
ret = dnm ? num/dnm : 0.0;
return AIR_CLAMP( -OOSQRT2, ret, OOSQRT2 );
}
531 float _tenAnisoTen_Skew_f( const float _t[7] ) {
float Q, num, dnm, ret, mn, ten[7];
mn = TEN_T_TRACE( _t )/3;
TEN_T_SET( ten, _t[0], _t[1]-mn, _t[2], _t[3], _t[4]-mn, _t[5], _t[6]-mn );
Q = _tenAnisoTen_Q_f( ten );
num = _tenAnisoTen_R_f( ten );
dnm = AIR_CAST( float, Q*sqrt( 2*Q ) );
ret = dnm ? AIR_CAST( float, num/dnm ) : 0.0f;
return AIR_CAST( float, AIR_CLAMP( -OOSQRT2, ret, OOSQRT2 ) );
}
541 double _tenAnisoTen_Skew_d( const double _t[7] ) {
double Q, num, dnm, ret, mn, ten[7];
mn = TEN_T_TRACE( _t )/3;
TEN_T_SET( ten, _t[0], _t[1]-mn, _t[2], _t[3], _t[4]-mn, _t[5], _t[6]-mn );
Q = _tenAnisoTen_Q_d( ten );
num = _tenAnisoTen_R_d( ten );
dnm = Q*sqrt( 2*Q );
ret = dnm ? num/dnm : 0.0;
return AIR_CLAMP( -OOSQRT2, ret, OOSQRT2 );
}
553 float _tenAnisoEval_Mode_f( const float _eval[3] ) {
float n, d, mn, e[3], ret;
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( e, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
n = ( e[0] + e[1] - 2*e[2] )*( 2*e[0] - e[1] - e[2] )*( e[0] - 2*e[1] + e[2] );
d = ( e[0]*e[0] + e[1]*e[1] + e[2]*e[2]
- e[0]*e[1] - e[1]*e[2] - e[0]*e[2] );
d = AIR_CAST( float, sqrt( AIR_MAX( 0, d ) ) );
d = 2*d*d*d;
ret = d ? AIR_CAST( float, n/d ) : 0.0f;
return AIR_CLAMP( -1, ret, 1 );
}
565 double _tenAnisoEval_Mode_d( const double _eval[3] ) {
double n, d, mn, e[3], ret;
mn = ( _eval[0] + _eval[1] + _eval[2] )/3;
ELL_3V_SET( e, _eval[0] - mn, _eval[1] - mn, _eval[2] - mn );
n = ( e[0] + e[1] - 2*e[2] )*( 2*e[0] - e[1] - e[2] )*( e[0] - 2*e[1] + e[2] );
d = ( e[0]*e[0] + e[1]*e[1] + e[2]*e[2]
- e[0]*e[1] - e[1]*e[2] - e[0]*e[2] );
d = sqrt( AIR_MAX( 0, d ) );
d = 2*d*d*d;
ret = d ? n/d : 0.0;
return AIR_CLAMP( -1, ret, 1 );
}
577 float _tenAnisoTen_Mode_f( const float tt[7] ) {
float mn, dev[7], tmp, ret;
mn = TEN_T_TRACE( tt )/3.0f;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
tmp = AIR_CAST( float, TEN_T_NORM( dev ) );
tmp = tmp ? 1.0f/tmp : 0.0f;
TEN_T_SCALE( dev, tmp, dev );
ret = AIR_CAST( float, 3*SQRT6*TEN_T_DET( dev ) );
return AIR_CLAMP( -1, ret, 1 );
}
587 double _tenAnisoTen_Mode_d( const double tt[7] ) {
double mn, dev[7], tmp, ret;
mn = TEN_T_TRACE( tt )/3.0;
TEN_T_SET( dev, tt[0], tt[1]-mn, tt[2], tt[3], tt[4]-mn, tt[5], tt[6]-mn );
tmp = TEN_T_NORM( dev );
tmp = tmp ? 1.0/tmp : 0.0;
TEN_T_SCALE( dev, tmp, dev );
ret = 3*SQRT6*TEN_T_DET( dev );
return AIR_CLAMP( -1, ret, 1 );
}
/* NOTE: yes, the AIR_CLAMPs here are needed even though
** the _Skew_ functions clamp their output
*/
#define SQRT2 1.41421356237309504880
602 float _tenAnisoEval_Th_f( const float eval[3] ) {
float tmp;
tmp = AIR_CAST( float, SQRT2*_tenAnisoEval_Skew_f( eval ) );
return AIR_CAST( float, acos( AIR_CLAMP( -1, tmp, 1 ) )/3 );
}
607 double _tenAnisoEval_Th_d( const double eval[3] ) {
double tmp;
tmp = SQRT2*_tenAnisoEval_Skew_d( eval );
return acos( AIR_CLAMP( -1, tmp, 1 ) )/3;
}
612 float _tenAnisoTen_Th_f( const float ten[7] ) {
float tmp;
tmp = AIR_CAST( float, SQRT2*_tenAnisoTen_Skew_f( ten ) );
return AIR_CAST( float, acos( AIR_CLAMP( -1, tmp, 1 ) )/3 );
}
617 double _tenAnisoTen_Th_d( const double ten[7] ) {
double tmp;
tmp = SQRT2*_tenAnisoTen_Skew_d( ten );
return acos( AIR_CLAMP( -1, tmp, 1 ) )/3;
}
624 float _tenAnisoEval_Omega_f( const float eval[3] ) {
return _tenAnisoEval_FA_f( eval )*( 1.0f+_tenAnisoEval_Mode_f( eval ) )/2.0f;
}
627 double _tenAnisoEval_Omega_d( const double eval[3] ) {
return _tenAnisoEval_FA_d( eval )*( 1.0f+_tenAnisoEval_Mode_d( eval ) )/2.0f;
}
630 float _tenAnisoTen_Omega_f( const float ten[7] ) {
return _tenAnisoTen_FA_f( ten )*( 1.0f+_tenAnisoTen_Mode_f( ten ) )/2.0f;
}
633 double _tenAnisoTen_Omega_d( const double ten[7] ) {
return _tenAnisoTen_FA_d( ten )*( 1.0f+_tenAnisoTen_Mode_d( ten ) )/2.0f;
}
638 float _tenAnisoEval_Det_f( const float eval[3] ) {
return eval[0]*eval[1]*eval[2];
}
641 double _tenAnisoEval_Det_d( const double eval[3] ) {
return eval[0]*eval[1]*eval[2];
}
644 float _tenAnisoTen_Det_f( const float ten[7] ) {
return TEN_T_DET( ten );
}
647 double _tenAnisoTen_Det_d( const double ten[7] ) {
return TEN_T_DET( ten );
}
652 float _tenAnisoEval_Tr_f( const float eval[3] ) {
return eval[0] + eval[1] + eval[2];
}
655 double _tenAnisoEval_Tr_d( const double eval[3] ) {
return eval[0] + eval[1] + eval[2];
}
658 float _tenAnisoTen_Tr_f( const float ten[7] ) {
return TEN_T_TRACE( ten );
}
661 double _tenAnisoTen_Tr_d( const double ten[7] ) {
return TEN_T_TRACE( ten );
}
666 float _tenAnisoEval_eval0_f( const float eval[3] ) { return eval[0]; }
667 double _tenAnisoEval_eval0_d( const double eval[3] ) { return eval[0]; }
668 float _tenAnisoTen_eval0_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return eval[0];
}
673 double _tenAnisoTen_eval0_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return eval[0];
}
679 float _tenAnisoEval_eval1_f( const float eval[3] ) { return eval[1]; }
680 double _tenAnisoEval_eval1_d( const double eval[3] ) { return eval[1]; }
681 float _tenAnisoTen_eval1_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return eval[1];
}
686 double _tenAnisoTen_eval1_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return eval[1];
}
693 float _tenAnisoEval_eval2_f( const float eval[3] ) { return eval[2]; }
694 double _tenAnisoEval_eval2_d( const double eval[3] ) { return eval[2]; }
695 float _tenAnisoTen_eval2_f( const float ten[7] ) {
float eval[3];
tenEigensolve_f( eval, NULL, ten );
return eval[2];
}
700 double _tenAnisoTen_eval2_d( const double ten[7] ) {
double eval[3];
tenEigensolve_d( eval, NULL, ten );
return eval[2];
}
707 float ( *_tenAnisoEval_f[TEN_ANISO_MAX+1] )( const float eval[3] ) = {
NULL,
_tenAnisoEval_Conf_f,
_tenAnisoEval_Cl1_f,
_tenAnisoEval_Cp1_f,
_tenAnisoEval_Ca1_f,
_tenAnisoEval_Clpmin1_f,
_tenAnisoEval_Cs1_f,
_tenAnisoEval_Ct1_f,
_tenAnisoEval_Cl2_f,
_tenAnisoEval_Cp2_f,
_tenAnisoEval_Ca2_f,
_tenAnisoEval_Clpmin2_f,
_tenAnisoEval_Cs2_f,
_tenAnisoEval_Ct2_f,
_tenAnisoEval_RA_f,
_tenAnisoEval_FA_f,
_tenAnisoEval_VF_f,
_tenAnisoEval_B_f,
_tenAnisoEval_Q_f,
_tenAnisoEval_R_f,
_tenAnisoEval_S_f,
_tenAnisoEval_Skew_f,
_tenAnisoEval_Mode_f,
_tenAnisoEval_Th_f,
_tenAnisoEval_Omega_f,
_tenAnisoEval_Det_f,
_tenAnisoEval_Tr_f,
_tenAnisoEval_eval0_f,
_tenAnisoEval_eval1_f,
_tenAnisoEval_eval2_f
};
740 double ( *_tenAnisoEval_d[TEN_ANISO_MAX+1] )( const double eval[3] ) = {
NULL,
_tenAnisoEval_Conf_d,
_tenAnisoEval_Cl1_d,
_tenAnisoEval_Cp1_d,
_tenAnisoEval_Ca1_d,
_tenAnisoEval_Clpmin1_d,
_tenAnisoEval_Cs1_d,
_tenAnisoEval_Ct1_d,
_tenAnisoEval_Cl2_d,
_tenAnisoEval_Cp2_d,
_tenAnisoEval_Ca2_d,
_tenAnisoEval_Clpmin2_d,
_tenAnisoEval_Cs2_d,
_tenAnisoEval_Ct2_d,
_tenAnisoEval_RA_d,
_tenAnisoEval_FA_d,
_tenAnisoEval_VF_d,
_tenAnisoEval_B_d,
_tenAnisoEval_Q_d,
_tenAnisoEval_R_d,
_tenAnisoEval_S_d,
_tenAnisoEval_Skew_d,
_tenAnisoEval_Mode_d,
_tenAnisoEval_Th_d,
_tenAnisoEval_Omega_d,
_tenAnisoEval_Det_d,
_tenAnisoEval_Tr_d,
_tenAnisoEval_eval0_d,
_tenAnisoEval_eval1_d,
_tenAnisoEval_eval2_d
};
float ( *_tenAnisoTen_f[TEN_ANISO_MAX+1] )( const float ten[7] ) = {
NULL,
_tenAnisoTen_Conf_f,
_tenAnisoTen_Cl1_f,
_tenAnisoTen_Cp1_f,
_tenAnisoTen_Ca1_f,
_tenAnisoTen_Clpmin1_f,
_tenAnisoTen_Cs1_f,
_tenAnisoTen_Ct1_f,
_tenAnisoTen_Cl2_f,
_tenAnisoTen_Cp2_f,
_tenAnisoTen_Ca2_f,
_tenAnisoTen_Clpmin2_f,
_tenAnisoTen_Cs2_f,
_tenAnisoTen_Ct2_f,
_tenAnisoTen_RA_f,
_tenAnisoTen_FA_f,
_tenAnisoTen_VF_f,
_tenAnisoTen_B_f,
_tenAnisoTen_Q_f,
_tenAnisoTen_R_f,
_tenAnisoTen_S_f,
_tenAnisoTen_Skew_f,
_tenAnisoTen_Mode_f,
_tenAnisoTen_Th_f,
_tenAnisoTen_Omega_f,
_tenAnisoTen_Det_f,
_tenAnisoTen_Tr_f,
_tenAnisoTen_eval0_f,
_tenAnisoTen_eval1_f,
_tenAnisoTen_eval2_f
};
double ( *_tenAnisoTen_d[TEN_ANISO_MAX+1] )( const double ten[7] ) = {
NULL,
_tenAnisoTen_Conf_d,
_tenAnisoTen_Cl1_d,
_tenAnisoTen_Cp1_d,
_tenAnisoTen_Ca1_d,
_tenAnisoTen_Clpmin1_d,
_tenAnisoTen_Cs1_d,
_tenAnisoTen_Ct1_d,
_tenAnisoTen_Cl2_d,
_tenAnisoTen_Cp2_d,
_tenAnisoTen_Ca2_d,
_tenAnisoTen_Clpmin2_d,
_tenAnisoTen_Cs2_d,
_tenAnisoTen_Ct2_d,
_tenAnisoTen_RA_d,
_tenAnisoTen_FA_d,
_tenAnisoTen_VF_d,
_tenAnisoTen_B_d,
_tenAnisoTen_Q_d,
_tenAnisoTen_R_d,
_tenAnisoTen_S_d,
_tenAnisoTen_Skew_d,
_tenAnisoTen_Mode_d,
_tenAnisoTen_Th_d,
_tenAnisoTen_Omega_d,
_tenAnisoTen_Det_d,
_tenAnisoTen_Tr_d,
_tenAnisoTen_eval0_d,
_tenAnisoTen_eval1_d,
_tenAnisoTen_eval2_d
};
float
tenAnisoEval_f( const float eval[3], int aniso ) {
return ( AIR_IN_OP( tenAnisoUnknown, aniso, tenAnisoLast )
? _tenAnisoEval_f[aniso]( eval )
: 0 );
}
double
tenAnisoEval_d( const double eval[3], int aniso ) {
return ( AIR_IN_OP( tenAnisoUnknown, aniso, tenAnisoLast )
? _tenAnisoEval_d[aniso]( eval )
: 0 );
}
float
tenAnisoTen_f( const float ten[7], int aniso ) {
return ( AIR_IN_OP( tenAnisoUnknown, aniso, tenAnisoLast )
? _tenAnisoTen_f[aniso]( ten )
: 0 );
}
double
tenAnisoTen_d( const double ten[7], int aniso ) {
return ( AIR_IN_OP( tenAnisoUnknown, aniso, tenAnisoLast )
? _tenAnisoTen_d[aniso]( ten )
: 0 );
}
#if 0
/*
******** tenAnisoCalc_f
**
** !!! THIS FUNCTION HAS BEEN MADE OBSOLETE BY THE NEW
** !!! tenAnisoEval_{f, d} AND tenAnisoTen_{f, d} FUNCTIONS
** !!! THIS WILL LIKELY BE REMOVED FROM FUTURE RELEASES
**
** Because this function does not subtract out the eigenvalue mean
** when computing quantities like Skew and Mode, it has really lousy
** accuracy on those measures compared to tenAnisoEval_{f, d}.
**
** given an array of three SORTED ( descending ) eigenvalues "e",
** calculates the anisotropy coefficients of Westin et al.,
** as well as various others.
**
** NOTE: with time, so many metrics have ended up here that under
** no cases should this be used in any kind of time-critical operations
**
** This does NOT use biff.
*/
void
tenAnisoCalc_f( float c[TEN_ANISO_MAX+1], const float e[3] ) {
float e0, e1, e2, stdv, mean, sum, cl, cp, ca, ra, fa, vf, denom;
float A, B, C, R, Q, N, D;
if ( !( e[0] >= e[1] && e[1] >= e[2] ) ) {
fprintf( stderr, "tenAnisoCalc_f: eigen values not sorted: "
"%g %g %g ( %d %d )\n",
e[0], e[1], e[2], e[0] >= e[1], e[1] >= e[2] );
}
if ( ( tenVerbose > 1 ) && !( e[0] >= 0 && e[1] >= 0 && e[2] >= 0 ) ) {
fprintf( stderr, "tenAnisoCalc_f: eigen values not all >= 0: %g %g %g\n",
e[0], e[1], e[2] );
}
e0 = AIR_MAX( e[0], 0 );
e1 = AIR_MAX( e[1], 0 );
e2 = AIR_MAX( e[2], 0 );
sum = e0 + e1 + e2;
/* first version of cl, cp, cs */
cl = sum ? ( e0 - e1 )/sum : 0.0f;
c[tenAniso_Cl1] = cl;
cp = sum ? 2*( e1 - e2 )/sum : 0.0f;
c[tenAniso_Cp1] = cp;
ca = cl + cp;
c[tenAniso_Ca1] = ca;
c[tenAniso_Clpmin1] = AIR_MIN( cl, cp );
/* extra logic here for equality with expressions above */
c[tenAniso_Cs1] = sum ? 1 - ca : 0.0f;
c[tenAniso_Ct1] = ca ? cp/ca : 0;
/* second version of cl, cp, cs */
cl = e0 ? ( e0 - e1 )/e0 : 0.0f;
c[tenAniso_Cl2] = cl;
cp = e0 ? ( e1 - e2 )/e0 : 0.0f;
c[tenAniso_Cp2] = cp;
ca = cl + cp;
c[tenAniso_Ca2] = ca;
c[tenAniso_Clpmin2] = AIR_MIN( cl, cp );
/* extra logic here for equality with expressions above */
c[tenAniso_Cs2] = e0 ? 1 - ca : 0.0f;
c[tenAniso_Ct2] = ca ? cp/ca : 0.0f;
/* non-westin anisos */
mean = sum/3.0f;
stdv = AIR_CAST( float,
sqrt( ( mean-e0 )*( mean-e0 ) /* okay, not exactly standard dev */
+ ( mean-e1 )*( mean-e1 )
+ ( mean-e2 )*( mean-e2 ) ) );
ra = mean ? AIR_CAST( float, stdv/( mean*SQRT6 ) ) : 0.0f;
ra = AIR_CLAMP( 0.0f, ra, 1.0f );
c[tenAniso_RA] = ra;
denom = 2.0f*( e0*e0 + e1*e1 + e2*e2 );
if ( denom ) {
fa = AIR_CAST( float, stdv*sqrt( 3.0/denom ) );
fa = AIR_CLAMP( 0.0f, fa, 1.0f );
} else {
fa = 0.0f;
}
c[tenAniso_FA] = fa;
vf = 1 - ( mean ? e0*e1*e2/( mean*mean*mean ) : 0.0f );
vf = AIR_CLAMP( 0.0f, vf, 1.0f );
c[tenAniso_VF] = vf;
A = ( -e0 - e1 - e2 );
B = c[tenAniso_B] = e0*e1 + e0*e2 + e1*e2;
C = -e0*e1*e2;
Q = c[tenAniso_Q] = ( A*A - 3*B )/9;
R = c[tenAniso_R] = ( -2*A*A*A + 9*A*B - 27*C )/54;
c[tenAniso_S] = e0*e0 + e1*e1 + e2*e2;
c[tenAniso_Skew] = Q ? AIR_CAST( float, R/( Q*sqrt( 2*Q ) ) ) : 0.0f;
c[tenAniso_Skew] = AIR_CLAMP( -OOSQRT2, c[tenAniso_Skew], OOSQRT2 );
N = ( e0 + e1 - 2*e2 )*( 2*e0 - e1 - e2 )*( e0 - 2*e1 + e2 );
D = AIR_CAST( float, sqrt( e0*e0+e1*e1+e2*e2 - e0*e1-e1*e2-e0*e2 ) );
c[tenAniso_Mode] = D ? N/( 2*D*D*D ) : 0.0f;
c[tenAniso_Mode] = AIR_CLAMP( -1, c[tenAniso_Mode], 1 );
c[tenAniso_Th] =
AIR_CAST( float, acos( AIR_CLAMP( -1, sqrt( 2 )*c[tenAniso_Skew], 1 ) )/3 );
c[tenAniso_Omega] = c[tenAniso_FA]*( 1+c[tenAniso_Mode] )/2;
c[tenAniso_Det] = e0*e1*e2;
c[tenAniso_Tr] = e0 + e1 + e2;
c[tenAniso_eval0] = e0;
c[tenAniso_eval1] = e1;
c[tenAniso_eval2] = e2;
return;
}
#endif
int
tenAnisoPlot( Nrrd *nout, int aniso, unsigned int res,
int hflip, int whole, int nanout ) {
static const char me[]="tenAnisoMap";
float *out, tmp;
unsigned int x, y;
float m0[3], m1[3], m2[3], c0, c1, c2, e[3];
float S = 1/3.0f, L = 1.0f, P = 1/2.0f; /* these make Westin's original
( cl, cp, cs ) align with the
barycentric coordinates */
if ( airEnumValCheck( tenAniso, aniso ) ) {
biffAddf( TEN, "%s: invalid aniso ( %d )", me, aniso );
return 1;
}
if ( !( res > 2 ) ) {
biffAddf( TEN, "%s: resolution ( %d ) invalid", me, res );
return 1;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 2,
AIR_CAST( size_t, res ), AIR_CAST( size_t, res ) ) ) {
biffMovef( TEN, NRRD, "%s: ", me );
return 1;
}
out = ( float * )nout->data;
if ( whole ) {
ELL_3V_SET( m0, 1, 0, 0 );
ELL_3V_SET( m1, 0, 1, 0 );
ELL_3V_SET( m2, 0, 0, 1 );
} else {
ELL_3V_SET( m0, S, S, S );
if ( hflip ) {
ELL_3V_SET( m1, P, P, 0 );
ELL_3V_SET( m2, L, 0, 0 );
} else {
ELL_3V_SET( m1, L, 0, 0 );
ELL_3V_SET( m2, P, P, 0 );
}
}
for ( y=0; y<res; y++ ) {
for ( x=0; x<=y; x++ ) {
/* ( c0, c1, c2 ) are the barycentric coordinates */
c0 = AIR_CAST( float, 1.0 - AIR_AFFINE( -0.5, y, res-0.5, 0.0, 1.0 ) );
c2 = AIR_CAST( float, AIR_AFFINE( -0.5, x, res-0.5, 0.0, 1.0 ) );
c1 = 1 - c0 - c2;
e[0] = c0*m0[0] + c1*m1[0] + c2*m2[0];
e[1] = c0*m0[1] + c1*m1[1] + c2*m2[1];
e[2] = c0*m0[2] + c1*m1[2] + c2*m2[2];
ELL_SORT3( e[0], e[1], e[2], tmp ); /* got some warnings w/out this */
out[x + res*y] = tenAnisoEval_f( e, aniso );
}
if ( nanout ) {
for ( x=y+1; x<res; x++ ) {
out[x + res*y] = AIR_NAN;
}
}
}
return 0;
}
int
tenAnisoVolume( Nrrd *nout, const Nrrd *nin, int aniso, double confThresh ) {
static const char me[]="tenAnisoVolume";
size_t N, I;
float *out, *in, *tensor;
int map[NRRD_DIM_MAX];
size_t sx, sy, sz, size[3];
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a tensor nrrd", me );
return 1;
}
if ( airEnumValCheck( tenAniso, aniso ) ) {
biffAddf( TEN, "%s: invalid aniso ( %d )", me, aniso );
return 1;
}
confThresh = AIR_CLAMP( 0.0, confThresh, 1.0 );
size[0] = sx = nin->axis[1].size;
size[1] = sy = nin->axis[2].size;
size[2] = sz = nin->axis[3].size;
N = sx*sy*sz;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3, sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
out = ( float * )nout->data;
in = ( float * )nin->data;
for ( I=0; I<=N-1; I++ ) {
/* tenVerbose = ( I == 911327 ); */
tensor = in + I*7;
if ( tenAniso_Conf != aniso && tensor[0] < confThresh ) {
out[I] = 0.0;
continue;
}
/* no longer used
tenEigensolve_f( eval, NULL, tensor );
if ( !( AIR_EXISTS( eval[0] ) && AIR_EXISTS( eval[1] ) && AIR_EXISTS( eval[2] ) ) ) {
NRRD_COORD_GEN( coord, size, 3, I );
biffAddf( TEN, "%s: not all eigenvalues exist ( %g, %g, %g ) at sample "
"%d = ( %d, %d, %d )",
me, eval[0], eval[1], eval[2], ( int )I,
( int )coord[0], ( int )coord[1], ( int )coord[2] );
return 1;
}
*/
out[I] = tenAnisoTen_f( tensor, aniso );
if ( !AIR_EXISTS( out[I] ) ) {
size_t coord[3];
NRRD_COORD_GEN( coord, size, 3, I );
biffAddf( TEN, "%s: generated non-existent aniso %g from tensor "
"( %g ) %g %g %g %g %g %g at sample %u = ( %u, %u, %u )", me,
out[I],
tensor[0], tensor[1], tensor[2], tensor[3],
tensor[4], tensor[5], tensor[6],
AIR_CAST( unsigned int, I ),
AIR_CAST( unsigned int, coord[0] ),
AIR_CAST( unsigned int, coord[1] ),
AIR_CAST( unsigned int, coord[2] ) );
return 1;
}
}
ELL_3V_SET( map, 1, 2, 3 );
if ( nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_SIZE_BIT ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
return 0;
}
int
tenAnisoHistogram( Nrrd *nout, const Nrrd *nin, const Nrrd *nwght,
int right, int version, unsigned int res ) {
static const char me[]="tenAnisoHistogram";
size_t N, I;
int csIdx, clIdx, cpIdx;
float *tdata, *out, eval[3],
cs, cl, cp, ( *wlup )( const void *data, size_t idx ), weight;
unsigned int yres, xi, yi;
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a tensor nrrd", me );
return 1;
}
if ( nwght ) {
if ( nrrdCheck( nwght ) ) {
biffMovef( TEN, NRRD, "%s: trouble with weighting nrrd", me );
return 1;
}
if ( nrrdElementNumber( nwght )
!= nrrdElementNumber( nin )/nrrdKindSize( nrrdKind3DMaskedSymMatrix ) ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
size_t numten;
numten = nrrdElementNumber( nin )/nrrdKindSize( nrrdKind3DMaskedSymMatrix );
biffAddf( TEN, "%s: # elements in weight nrrd ( %s ) != # tensors ( %s )", me,
airSprintSize_t( stmp1, nrrdElementNumber( nwght ) ),
airSprintSize_t( stmp2, numten ) );
return 1;
}
}
if ( !( 1 == version || 2 == version ) ) {
biffAddf( TEN, "%s: version ( %d ) wasn't 1 or 2", me, version );
return 1;
}
if ( !( res > 10 ) ) {
biffAddf( TEN, "%s: resolution ( %d ) invalid", me, res );
return 1;
}
if ( right ) {
yres = AIR_CAST( unsigned int, AIR_CAST( double, res )/sqrt( 3 ) );
} else {
yres = res;
}
if ( nwght ) {
wlup = nrrdFLookup[nwght->type];
} else {
wlup = NULL;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 2,
AIR_CAST( size_t, res ), AIR_CAST( size_t, yres ) ) ) {
biffMovef( TEN, NRRD, "%s: ", me );
return 1;
}
out = ( float * )nout->data;
tdata = ( float * )nin->data;
if ( right || 1 == version ) {
clIdx = tenAniso_Cl1;
cpIdx = tenAniso_Cp1;
csIdx = tenAniso_Cs1;
} else {
clIdx = tenAniso_Cl2;
cpIdx = tenAniso_Cp2;
csIdx = tenAniso_Cs2;
}
N = nrrdElementNumber( nin )/nrrdKindSize( nrrdKind3DMaskedSymMatrix );
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, NULL, tdata );
cl = tenAnisoEval_f( eval, clIdx );
cp = tenAnisoEval_f( eval, cpIdx );
cs = tenAnisoEval_f( eval, csIdx );
if ( right ) {
xi = AIR_CAST( unsigned int, cs*0 + cl*( res-1 ) + cp*0 );
yi = AIR_CAST( unsigned int, cs*0 + cl*( yres-1 ) + cp*( yres-1 ) );
} else {
xi = AIR_CAST( unsigned int, cs*0 + cl*0 + cp*( res-1 ) );
yi = AIR_CAST( unsigned int, cs*0 + cl*( res-1 ) + cp*( res-1 ) );
}
weight = wlup ? wlup( nwght->data, I ) : 1.0f;
if ( xi < res && yi < yres-1 ) {
out[xi + res*yi] += tdata[0]*weight;
}
tdata += nrrdKindSize( nrrdKind3DMaskedSymMatrix );
}
return 0;
}
tenEvecRGBParm *
tenEvecRGBParmNew( ) {
tenEvecRGBParm *rgbp;
rgbp = AIR_CAST( tenEvecRGBParm *, calloc( 1, sizeof( tenEvecRGBParm ) ) );
if ( rgbp ) {
rgbp->which = 0;
rgbp->aniso = tenAniso_Cl2;
rgbp->confThresh = 0.5;
rgbp->anisoGamma = 1.0;
rgbp->gamma = 1.0;
rgbp->bgGray = 0.0;
rgbp->isoGray = 0.0;
rgbp->maxSat = 1.0;
rgbp->typeOut = nrrdTypeFloat;
rgbp->genAlpha = AIR_FALSE;
}
return rgbp;
}
tenEvecRGBParm *
tenEvecRGBParmNix( tenEvecRGBParm *rgbp ) {
if ( rgbp ) {
airFree( rgbp );
}
return NULL;
}
int
tenEvecRGBParmCheck( const tenEvecRGBParm *rgbp ) {
static const char me[]="tenEvecRGBParmCheck";
if ( !rgbp ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( rgbp->which <= 2 ) ) {
biffAddf( TEN, "%s: which must be 0, 1, or 2 ( not %u )", me, rgbp->which );
return 1;
}
if ( airEnumValCheck( tenAniso, rgbp->aniso ) ) {
biffAddf( TEN, "%s: anisotropy metric %d not valid", me, rgbp->aniso );
return 1;
}
if ( nrrdTypeDefault != rgbp->typeOut
&& airEnumValCheck( nrrdType, rgbp->typeOut ) ) {
biffAddf( TEN, "%s: output type ( %d ) not valid", me, rgbp->typeOut );
return 1;
}
return 0;
}
float
_tenEvecRGBComp_f( float conf, float aniso, float comp,
const tenEvecRGBParm *rgbp ) {
double X;
X = AIR_ABS( comp );
X = pow( X, 1.0/rgbp->gamma );
X = AIR_LERP( rgbp->maxSat*aniso, rgbp->isoGray, X );
return AIR_CAST( float, conf > rgbp->confThresh ? X : rgbp->bgGray );
}
double
_tenEvecRGBComp_d( double conf, double aniso, double comp,
const tenEvecRGBParm *rgbp ) {
double X;
X = AIR_ABS( comp );
X = pow( X, 1.0/rgbp->gamma );
X = AIR_LERP( rgbp->maxSat*aniso, rgbp->isoGray, X );
return conf > rgbp->confThresh ? X : rgbp->bgGray;
}
void
tenEvecRGBSingle_f( float RGB[3], float conf, const float eval[3],
const float evec[3], const tenEvecRGBParm *rgbp ) {
float aniso;
if ( RGB && eval && rgbp ) {
aniso = tenAnisoEval_f( eval, rgbp->aniso );
aniso = AIR_CAST( float, pow( aniso, 1.0/rgbp->anisoGamma ) );
ELL_3V_SET( RGB,
_tenEvecRGBComp_f( conf, aniso, evec[0], rgbp ),
_tenEvecRGBComp_f( conf, aniso, evec[1], rgbp ),
_tenEvecRGBComp_f( conf, aniso, evec[2], rgbp ) );
}
return;
}
void
tenEvecRGBSingle_d( double RGB[3], double conf, const double eval[3],
const double evec[3], const tenEvecRGBParm *rgbp ) {
double aniso;
if ( RGB && eval && rgbp ) {
aniso = tenAnisoEval_d( eval, rgbp->aniso );
aniso = pow( aniso, 1.0/rgbp->anisoGamma );
ELL_3V_SET( RGB,
_tenEvecRGBComp_d( conf, aniso, evec[0], rgbp ),
_tenEvecRGBComp_d( conf, aniso, evec[1], rgbp ),
_tenEvecRGBComp_d( conf, aniso, evec[2], rgbp ) );
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
tenEMBimodalParm*
28 tenEMBimodalParmNew( ) {
tenEMBimodalParm *biparm;
biparm = ( tenEMBimodalParm* )calloc( 1, sizeof( tenEMBimodalParm ) );
if ( biparm ) {
biparm->minProb = 0.0001;
biparm->minProb2 = 0.0001;
biparm->minDelta = 0.00001;
biparm->minFraction = 0.05; /* 5% */
biparm->minConfidence = 0.7;
biparm->maxIteration = 200;
biparm->verbose = AIR_FALSE;
biparm->histo = NULL;
biparm->pp1 = biparm->pp2 = NULL;
biparm->vmin = biparm->vmax = AIR_NAN;
biparm->N = 0;
}
return biparm;
}
tenEMBimodalParm*
50 tenEMBimodalParmNix( tenEMBimodalParm *biparm ) {
if ( biparm ) {
biparm->histo = ( double * )airFree( biparm->histo );
biparm->pp1 = ( double * )airFree( biparm->pp1 );
biparm->pp2 = ( double * )airFree( biparm->pp2 );
}
airFree( biparm );
return NULL;
}
int
62 _tenEMBimodalInit( tenEMBimodalParm *biparm, const Nrrd *_nhisto ) {
static const char me[]="_tenEMBimodalInit";
int i, median;
Nrrd *nhisto;
double medianD, sum;
airArray *mop;
if ( !( biparm->maxIteration > 5 ) ) {
biffAddf( TEN, "%s: biparm->maxIteration = %d too small", me,
biparm->maxIteration );
return 1;
}
mop = airMopNew( );
nhisto = nrrdNew( );
airMopAdd( mop, nhisto, ( airMopper )nrrdNuke, airMopOnError );
airMopAdd( mop, nhisto, ( airMopper )nrrdNix, airMopOnOkay );
if ( nrrdConvert( nhisto, _nhisto, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble converting histogram to double", me );
airMopError( mop ); return 1;
}
biparm->N = nhisto->axis[0].size;
biparm->histo = ( double* )( nhisto->data );
biparm->vmin = ( AIR_EXISTS( nhisto->axis[0].min )
? nhisto->axis[0].min
: -0.5 );
biparm->vmax = ( AIR_EXISTS( nhisto->axis[0].max )
? nhisto->axis[0].max
: biparm->N - 0.5 );
( nrrdMeasureLine[nrrdMeasureHistoMedian] )
( &medianD, nrrdTypeDouble,
biparm->histo, nrrdTypeDouble, biparm->N,
AIR_NAN, AIR_NAN );
( nrrdMeasureLine[nrrdMeasureSum] )
( &sum, nrrdTypeDouble,
biparm->histo, nrrdTypeDouble, biparm->N,
AIR_NAN, AIR_NAN );
for ( i=0; i<biparm->N; i++ ) {
biparm->histo[i] /= sum;
}
if ( !AIR_EXISTS( medianD ) ) {
biffMovef( TEN, NRRD,
"%s: got empty histogram? ( median calculation failed )", me );
airMopError( mop ); return 1;
}
median = ( int )medianD;
biparm->pp1 = ( double* )calloc( biparm->N, sizeof( double ) );
biparm->pp2 = ( double* )calloc( biparm->N, sizeof( double ) );
if ( !( biparm->pp1 && biparm->pp2 ) ) {
biffAddf( TEN, "%s: couldn't allocate posterior prob. buffers", me );
airMopError( mop ); return 1;
}
/* get mean and stdv of bins below median */
( nrrdMeasureLine[nrrdMeasureHistoMean] )
( &( biparm->mean1 ), nrrdTypeDouble,
biparm->histo, nrrdTypeDouble, median,
AIR_NAN, AIR_NAN );
( nrrdMeasureLine[nrrdMeasureHistoSD] )
( &( biparm->stdv1 ), nrrdTypeDouble,
biparm->histo, nrrdTypeDouble, median,
AIR_NAN, AIR_NAN );
/* get mean ( shift upwards by median ) and stdv of bins above median */
( nrrdMeasureLine[nrrdMeasureHistoMean] )
( &( biparm->mean2 ), nrrdTypeDouble,
biparm->histo + median, nrrdTypeDouble, biparm->N - median,
AIR_NAN, AIR_NAN );
( nrrdMeasureLine[nrrdMeasureHistoSD] )
( &( biparm->stdv2 ), nrrdTypeDouble,
biparm->histo + median, nrrdTypeDouble, biparm->N - median,
AIR_NAN, AIR_NAN );
biparm->mean2 += median;
biparm->fraction1 = 0.5;
if ( biparm->verbose ) {
fprintf( stderr, "%s: median = %d\n", me, median );
fprintf( stderr, "%s: m1, s1 = %g, %g; m2, s2 = %g, %g\n", me,
biparm->mean1, biparm->stdv1,
biparm->mean2, biparm->stdv2 );
}
airMopOkay( mop );
return 0;
}
void
152 _tenEMBimodalBoost( double *pp1P, double *pp2P, double b ) {
double p1, p2, tmp;
int sw=AIR_FALSE;
if ( *pp1P < *pp2P ) {
ELL_SWAP2( *pp1P, *pp2P, tmp );
sw = AIR_TRUE;
}
p1 = 1 - pow( 1 - *pp1P, b );
p2 = 1 - p1;
if ( sw ) {
*pp1P = p2;
*pp2P = p1;
} else {
*pp1P = p1;
*pp2P = p2;
}
}
/*
** what is posterior probability that measured value x comes from
** material 1 and 2, stored in pp1 and pp2
*/
void
176 _tenEMBimodalPP( tenEMBimodalParm *biparm ) {
int i;
double g1, g2, pp1, pp2, f1, min;
min = ( 1 == biparm->stage
? biparm->minProb
: biparm->minProb2 );
f1 = biparm->fraction1;
for ( i=0; i<biparm->N; i++ ) {
g1 = airGaussian( i, biparm->mean1, biparm->stdv1 );
g2 = airGaussian( i, biparm->mean2, biparm->stdv2 );
if ( g1 <= min && g2 <= min ) {
pp1 = pp2 = 0;
} else {
pp1 = f1*g1 / ( f1*g1 + ( 1-f1 )*g2 );
pp2 = 1 - pp1;
}
biparm->pp1[i] = pp1;
biparm->pp2[i] = pp2;
}
if ( biparm->verbose > 1 ) {
Nrrd *ntmp = nrrdNew( );
nrrdWrap_va( ntmp, biparm->pp1, nrrdTypeDouble, 1,
AIR_CAST( size_t, biparm->N ) );
nrrdSave( "pp1.nrrd", ntmp, NULL );
nrrdWrap_va( ntmp, biparm->pp2, nrrdTypeDouble, 1,
AIR_CAST( size_t, biparm->N ) );
nrrdSave( "pp2.nrrd", ntmp, NULL );
nrrdNix( ntmp );
}
return;
}
double
212 _tenEMBimodalNewFraction1( tenEMBimodalParm *biparm ) {
int i;
double pp1, pp2, h, sum1, sum2;
sum1 = sum2 = 0.0;
for ( i=0; i<biparm->N; i++ ) {
pp1 = biparm->pp1[i];
pp2 = biparm->pp2[i];
h = biparm->histo[i];
sum1 += pp1*h;
sum2 += pp2*h;
}
return sum1/( sum1 + sum2 );
}
void
228 _tenEMBimodalNewMean( double *m1P, double *m2P,
tenEMBimodalParm *biparm ) {
int i;
double pp1, pp2, h, sum1, isum1, sum2, isum2;
sum1 = isum1 = sum2 = isum2 = 0.0;
for ( i=0; i<biparm->N; i++ ) {
pp1 = biparm->pp1[i];
pp2 = biparm->pp2[i];
h = biparm->histo[i];
isum1 += i*pp1*h;
isum2 += i*pp2*h;
sum1 += pp1*h;
sum2 += pp2*h;
}
*m1P = isum1/sum1;
*m2P = isum2/sum2;
}
void
248 _tenEMBimodalNewSigma( double *s1P, double *s2P,
double m1, double m2,
tenEMBimodalParm *biparm ) {
int i;
double pp1, pp2, h, sum1, isum1, sum2, isum2;
sum1 = isum1 = sum2 = isum2 = 0.0;
for ( i=0; i<biparm->N; i++ ) {
pp1 = biparm->pp1[i];
pp2 = biparm->pp2[i];
h = biparm->histo[i];
isum1 += ( i-m1 )*( i-m1 )*pp1*h;
isum2 += ( i-m2 )*( i-m2 )*pp2*h;
sum1 += pp1*h;
sum2 += pp2*h;
}
*s1P = sqrt( isum1/sum1 );
*s2P = sqrt( isum2/sum2 );
}
void
269 _tenEMBimodalSaveImage( tenEMBimodalParm *biparm ) {
char name[AIR_STRLEN_MED];
Nrrd *nh, *nm, *nhi, *nmi, *ni;
NrrdRange *range;
const Nrrd *nhmhi[3];
double *m, max;
int i;
nh = nrrdNew( );
nm = nrrdNew( );
nhi = nrrdNew( );
nmi = nrrdNew( );
ni = nrrdNew( );
nrrdWrap_va( nh, biparm->histo, nrrdTypeDouble, 1,
AIR_CAST( size_t, biparm->N ) );
range = nrrdRangeNewSet( nh, nrrdBlind8BitRangeFalse );
max = range->max*1.1;
nrrdRangeNix( range );
nrrdCopy( nm, nh );
m = ( double* )( nm->data );
for ( i=0; i<biparm->N; i++ ) {
m[i] = biparm->fraction1*airGaussian( i, biparm->mean1, biparm->stdv1 );
m[i] += ( 1-biparm->fraction1 )*airGaussian( i, biparm->mean2, biparm->stdv2 );
}
nrrdHistoDraw( nmi, nm, 400, AIR_FALSE, max );
nrrdHistoDraw( nhi, nh, 400, AIR_FALSE, max );
ELL_3V_SET( nhmhi, nhi, nmi, nhi );
nrrdJoin( ni, nhmhi, 3, 0, AIR_TRUE );
sprintf( name, "%04d-%d.png", biparm->iteration, biparm->stage );
nrrdSave( name, ni, NULL );
nh = nrrdNix( nh );
nm = nrrdNuke( nm );
nhi = nrrdNuke( nhi );
nmi = nrrdNuke( nmi );
ni = nrrdNuke( ni );
return;
}
int
309 _tenEMBimodalIterate( tenEMBimodalParm *biparm ) {
static const char me[]="_tenEMBimodalIterate";
double om1, os1, om2, os2, of1, m1, s1, m2, s2, f1;
/* copy old values */
om1 = biparm->mean1;
os1 = biparm->stdv1;
of1 = biparm->fraction1;
om2 = biparm->mean2;
os2 = biparm->stdv2;
/* find new values, and calculate delta */
_tenEMBimodalPP( biparm );
f1 = _tenEMBimodalNewFraction1( biparm );
/* if ( 1 == biparm->stage ) { */
_tenEMBimodalNewMean( &m1, &m2, biparm );
/* } */
_tenEMBimodalNewSigma( &s1, &s2, m1, m2, biparm );
biparm->delta = ( ( fabs( m1 - om1 ) + fabs( m2 - om2 )
+ fabs( s1 - os1 ) + fabs( s2 - os2 ) )/biparm->N
+ fabs( f1 - of1 ) );
/* set new values */
biparm->mean1 = m1;
biparm->stdv1 = s1;
biparm->fraction1 = f1;
biparm->mean2 = m2;
biparm->stdv2 = s2;
if ( biparm->verbose ) {
fprintf( stderr, "%s( %d:%d ):\n", me, biparm->stage, biparm->iteration );
fprintf( stderr, " m1, s1 = %g, %g\n", m1, s1 );
fprintf( stderr, " m2, s2 = %g, %g\n", m2, s2 );
fprintf( stderr, " f1 = %g ; delta = %g\n", f1, biparm->delta );
}
if ( biparm->verbose > 1 ) {
_tenEMBimodalSaveImage( biparm );
}
return 0;
}
int
352 _tenEMBimodalConfThresh( tenEMBimodalParm *biparm ) {
static const char me[]="_tenEMBimodalConfThresh";
double m1, s1, m2, s2, f1, f2, A, B, C, D, t1, t2;
biparm->confidence = ( ( biparm->mean2 - biparm->mean1 )
/ ( biparm->stdv1 + biparm->stdv2 ) );
m1 = biparm->mean1;
s1 = biparm->stdv1;
f1 = biparm->fraction1;
m2 = biparm->mean2;
s2 = biparm->stdv2;
f2 = 1 - f1;
A = s1*s1 - s2*s2;
B = 2*( m1*s2*s2 - m2*s1*s1 );
C = s1*s1*m2*m2 - s2*s2*m1*m1 + 4*s1*s1*s2*s2*log( s2*f1/( s1*f2 ) );
D = B*B - 4*A*C;
if ( D < 0 ) {
biffAddf( TEN, "%s: threshold descriminant went negative ( %g )", me, D );
return 1;
}
t1 = ( -B + sqrt( D ) )/( 2*A );
if ( AIR_IN_OP( m1, t1, m2 ) ) {
biparm->threshold = t1;
} else {
t2 = ( -B - sqrt( D ) )/( 2*A );
if ( AIR_IN_OP( m1, t2, m2 ) ) {
biparm->threshold = t2;
} else {
biffAddf( TEN,
"%s: neither computed threshold %g, %g inside open interval "
"between means ( %g, %g )", me, t1, t2, m1, m2 );
return 1;
}
}
if ( biparm->verbose ) {
fprintf( stderr, "%s: conf = %g, thresh = %g\n", me,
biparm->confidence, biparm->threshold );
}
return 0;
}
int
395 _tenEMBimodalCheck( tenEMBimodalParm *biparm ) {
static const char me[]="_tenEMBimodalCheck";
if ( !( biparm->confidence > biparm->minConfidence ) ) {
biffAddf( TEN, "%s: confidence %g went below threshold %g", me,
biparm->confidence, biparm->minConfidence );
return 1;
}
if ( !( biparm->stdv1 > 0 && biparm->stdv2 > 0 ) ) {
biffAddf( TEN, "%s: stdv of material 1 ( %g ) or 2 ( %g ) went negative", me,
biparm->stdv1, biparm->stdv2 );
return 1;
}
if ( !( biparm->mean1 > 0 && biparm->mean1 < biparm->N-1
&& biparm->mean2 > 0 && biparm->mean2 < biparm->N-1 ) ) {
biffAddf( TEN, "%s: mean of material 1 ( %g ) or 2 ( %g ) went outside "
"given histogram range [0 .. %d]", me,
biparm->mean1, biparm->mean2, biparm->N-1 );
return 1;
}
if ( biparm->fraction1 < biparm->minFraction ) {
biffAddf( TEN, "%s: material 1 fraction ( %g ) fell below threshold %g", me,
biparm->fraction1, biparm->minFraction );
return 1;
}
if ( 1 - biparm->fraction1 < biparm->minFraction ) {
biffAddf( TEN, "%s: material 2 fraction ( %g ) fell below threshold %g", me,
1 - biparm->fraction1, biparm->minFraction );
return 1;
}
return 0;
}
int
429 tenEMBimodal( tenEMBimodalParm *biparm, const Nrrd *_nhisto ) {
static const char me[]="tenEMBimodal";
int done, _iter;
if ( !( biparm && _nhisto ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( 1 == _nhisto->dim ) ) {
biffAddf( TEN, "%s: histogram must be 1-D, not %d-D", me, _nhisto->dim );
return 1;
}
if ( _tenEMBimodalInit( biparm, _nhisto ) ) {
biffAddf( TEN, "%s: trouble initializing parameters", me );
return 1;
}
done = AIR_FALSE;
biparm->iteration = 0;
for ( biparm->stage = 1;
biparm->stage <= ( biparm->twoStage ? 2 : 1 );
biparm->stage++ ) {
for ( _iter=0;
biparm->iteration <= biparm->maxIteration;
biparm->iteration++, _iter++ ) {
if ( _tenEMBimodalIterate( biparm ) /* sets delta */
|| _tenEMBimodalConfThresh( biparm )
|| _tenEMBimodalCheck( biparm ) ) {
biffAddf( TEN, "%s: problem with fitting ( iter=%d )", me,
biparm->iteration );
return 1;
}
if ( biparm->delta < biparm->minDelta
&& ( !biparm->twoStage || 1 == biparm->stage || _iter > 10 ) ) {
done = AIR_TRUE;
break;
}
}
}
if ( !done ) {
biffAddf( TEN, "%s: didn't converge after %d iterations", me,
biparm->maxIteration );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
double
28 tenBVecNonLinearFit_error( double *bb, double *ss, double *ww, int len,
double amp, double dec ) {
int ii;
double err, tmp;
err = 0;
for ( ii=0; ii<len; ii++ ) {
tmp = ww[ii]*( amp*exp( -dec*bb[ii] ) - ss[ii] );
err += tmp*tmp;
}
return err;
}
void
42 tenBVecNonLinearFit_linear( double *amp, double *dec,
double *bb, double *ss, double *ww, int len ) {
double x, y, wi=0, xi=0, yi=0, xiyi=0, xisq=0, det;
int ii;
for ( ii=0; ii<len; ii++ ) {
x = bb[ii];
y = log( AIR_MAX( ss[ii], 0.01 ) );
xi += ww[ii]*x;
yi += ww[ii]*y;
xiyi += ww[ii]*x*y;
xisq += ww[ii]*x*x;
wi += ww[ii];
}
det = xisq*wi - xi*xi;
*dec = -( wi*xiyi - xi*yi )/det; /* negative sign assumed in model */
*amp = exp( ( -xi*xiyi + xisq*yi )/det );
return;
}
void
63 tenBVecNonLinearFit_GNstep( double *d_amp, double *d_dec,
double *bb, double *ss, double *ww, int len,
double amp, double dec ) {
double tmp, ff, dfdx1, dfdx2, AA=0, BB=0, CC=0, JTf[2], det;
int ii;
JTf[0] = JTf[1] = 0;
for ( ii=0; ii<len; ii++ ) {
tmp = exp( -dec*bb[ii] );
ff = ww[ii]*( amp*tmp - ss[ii] );
dfdx1 = ww[ii]*tmp;
dfdx2 = -ww[ii]*amp*bb[ii]*tmp;
AA += dfdx1*dfdx1;
BB += dfdx1*dfdx2;
CC += dfdx2*dfdx2;
JTf[0] += dfdx1*ff;
JTf[1] += dfdx2*ff;
}
det = AA*CC - BB*BB;
*d_amp = -( CC*JTf[0] - BB*JTf[1] )/det;
*d_dec = -( -BB*JTf[0] + AA*JTf[1] )/det;
return;
}
/*
******** tenBVecNonLinearFit
**
** Assuming that axis 0 represents a sequence of DWI measurements at a
** range of b values ( as described by bb[i] ), do non-linear least-squares
** fitting of those measurements, governed by weights ww[i] ( with at
** most iterMax interations, or terminated when L2 norm change < eps ).
**
** Based on model fit amp*exp( -b*dec ), output nrrd's axis 0 has three values:
** 0: amp
** 1: dec
** 2: error of fit
** and all other axes are unchanged from input. Output type is always double.
*/
int
103 tenBVecNonLinearFit( Nrrd *nout, const Nrrd *nin,
double *bb, double *ww, int iterMax, double eps ) {
static const char me[]="tenBVecNonLinearFit";
int map[NRRD_DIM_MAX], vecSize, iter;
size_t ii, size[NRRD_DIM_MAX], vecI, vecNum;
char *vec;
double *out, ss[AIR_STRLEN_SMALL], amp, dec, d_amp, d_dec, error, diff,
( *vecLup )( const void *v, size_t I );
if ( !( nout && nin && bb && ww ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( nin->dim >= 2 ) ) {
biffAddf( TEN, "%s: nin->dim ( %d ) not >= 2", me, nin->dim );
return 1;
}
if ( !( nin->axis[0].size < AIR_STRLEN_SMALL ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: sorry need nin->axis[0].size ( %s ) < %d", me,
airSprintSize_t( stmp, nin->axis[0].size ), AIR_STRLEN_SMALL );
return 1;
}
/* allocate/set-up output */
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
size[0] = 3;
if ( nrrdMaybeAlloc_nva( nout, nrrdTypeDouble, nin->dim, size ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
for ( ii=1; ii<nin->dim; ii++ ) {
map[ii] = ii;
}
map[0] = -1;
if ( nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_NONE ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis info", me );
return 1;
}
/* process all b vectors */
vecSize = nin->axis[0].size*nrrdTypeSize[nin->type];
vecNum = nrrdElementNumber( nin )/nin->axis[0].size;
vecLup = nrrdDLookup[nin->type];
vec = ( char* )nin->data;
out = ( double* )nout->data;
for ( vecI=0; vecI<vecNum; vecI++ ) {
/* copy DWI signal values */
for ( ii=0; ii<nin->axis[0].size; ii++ ) {
ss[ii] = vecLup( vec, ii );
}
/* start with linear fit */
tenBVecNonLinearFit_linear( &, &dec, bb, ss, ww, nin->axis[0].size );
error = tenBVecNonLinearFit_error( bb, ss, ww, nin->axis[0].size, amp, dec );
/* possibly refine with gauss-newton */
if ( iterMax > 0 ) {
iter = 0;
do {
iter++;
tenBVecNonLinearFit_GNstep( &d_amp, &d_dec,
bb, ss, ww, nin->axis[0].size, amp, dec );
amp += 0.3*d_amp;
dec += 0.3*d_dec;
diff = d_amp*d_amp + d_dec*d_dec;
} while ( iter < iterMax && diff > eps );
}
error = tenBVecNonLinearFit_error( bb, ss, ww, nin->axis[0].size, amp, dec );
out[0] = amp;
out[1] = dec;
out[2] = error;
vec += vecSize;
out += 3;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
const char *
tenDWMRIModalityKey = "modality";
const char *
tenDWMRIModalityVal = "DWMRI";
const char *
tenDWMRINAVal = "n/a";
const char *
tenDWMRIBValueKey = "DWMRI_b-value";
const char *
tenDWMRIGradKeyFmt = "DWMRI_gradient_%04u";
const char *
tenDWMRIBmatKeyFmt = "DWMRI_B-matrix_%04u";
const char *
tenDWMRINexKeyFmt = "DWMRI_NEX_%04u";
const char *
tenDWMRISkipKeyFmt = "DWMRI_skip_%04u";
/*
******** tenDWMRIKeyValueParse
**
** Parses through key-value pairs in the NRRD header to determine the
** list of diffusion-sensitizing gradient directions, or B-matrices
** ( depending to what was found ), according the NAMIC conventions.
** This requires, among other things, that ndwi be have exactly one
** axis with kind nrrdKindList ( or nrrdKindVector ), which is taken to
** be the DWI axis.
**
** Either *ngradP or *nbmatP is set to a newly- allocated nrrd
** containing this information, and the other one is set to NULL
** The ( scalar ) b-value is stored in *bP. The image values that are
** to be skipped are stored in the *skipP array ( allocated here ),
** the length of that array is stored in *skipNumP. Unlike the skip
** array used internally with tenEstimate, this is just a simple 1-D
** array; it is not a list of pairs of ( index, skipBool ).
*/
int
70 tenDWMRIKeyValueParse( Nrrd **ngradP, Nrrd **nbmatP, double *bP,
unsigned int **skipP, unsigned int *skipNumP,
const Nrrd *ndwi ) {
static const char me[]="tenDWMRIKeyValueParse";
char tmpKey[AIR_STRLEN_MED],
key[AIR_STRLEN_MED], *val;
const char *keyFmt;
int dwiAxis;
unsigned int axi, dwiIdx, dwiNum, valNum, valIdx, parsedNum,
nexNum, nexIdx, skipIdx, *skipLut;
Nrrd *ninfo;
double *info, normMax, norm;
airArray *mop, *skipArr;
if ( !( ngradP && nbmatP && skipP && skipNumP && bP && ndwi ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
/* check modality */
val = nrrdKeyValueGet( ndwi, tenDWMRIModalityKey );
if ( !val ) {
biffAddf( TEN, "%s: didn't have \"%s\" key", me, tenDWMRIModalityKey );
return 1;
}
if ( strncmp( tenDWMRIModalityVal, val + strspn( val, AIR_WHITESPACE ),
strlen( tenDWMRIModalityVal ) ) ) {
biffAddf( TEN, "%s: \"%s\" value was \"%s\", not \"%s\"", me,
tenDWMRIModalityKey, val, tenDWMRIModalityVal );
return 1;
}
val = ( char * )airFree( val );
/* learn b-value */
val = nrrdKeyValueGet( ndwi, tenDWMRIBValueKey );
if ( !val ) {
biffAddf( TEN, "%s: didn't have \"%s\" key", me, tenDWMRIBValueKey );
return 1;
}
if ( 1 != sscanf( val, "%lg", bP ) ) {
biffAddf( TEN, "%s: couldn't parse float from value \"%s\" "
"for key \"%s\"", me,
val, tenDWMRIBValueKey );
return 1;
}
val = ( char * )airFree( val );
/* find single DWI axis, set dwiNum to its size */
dwiAxis = -1;
for ( axi=0; axi<ndwi->dim; axi++ ) {
/* the use of nrrdKindVector here is out of deference to how ITK's
itkNrrdImageIO.cxx uses nrrdKindVector for VECTOR pixels */
if ( nrrdKindList == ndwi->axis[axi].kind
|| nrrdKindVector == ndwi->axis[axi].kind ) {
if ( -1 != dwiAxis ) {
biffAddf( TEN, "%s: already saw %s or %s kind on axis %d", me,
airEnumStr( nrrdKind, nrrdKindList ),
airEnumStr( nrrdKind, nrrdKindVector ), dwiAxis );
return 1;
}
dwiAxis = axi;
}
}
if ( -1 == dwiAxis ) {
biffAddf( TEN, "%s: did not see \"%s\" kind on any axis", me,
airEnumStr( nrrdKind, nrrdKindList ) );
return 1;
}
dwiNum = ndwi->axis[dwiAxis].size;
/* figure out if we're parsing gradients or b-matrices */
sprintf( tmpKey, tenDWMRIGradKeyFmt, 0 );
val = nrrdKeyValueGet( ndwi, tmpKey );
if ( val ) {
valNum = 3;
} else {
valNum = 6;
sprintf( key, tenDWMRIBmatKeyFmt, 0 );
val = nrrdKeyValueGet( ndwi, key );
if ( !val ) {
biffAddf( TEN, "%s: saw neither \"%s\" nor \"%s\" key", me,
tmpKey, key );
return 1;
}
}
val = ( char * )airFree( val );
/* set up parsing and allocate one of output nrrds */
if ( 3 == valNum ) {
keyFmt = tenDWMRIGradKeyFmt;
ninfo = *ngradP = nrrdNew( );
*nbmatP = NULL;
} else {
keyFmt = tenDWMRIBmatKeyFmt;
*ngradP = NULL;
ninfo = *nbmatP = nrrdNew( );
}
if ( nrrdMaybeAlloc_va( ninfo, nrrdTypeDouble, 2,
AIR_CAST( size_t, valNum ),
AIR_CAST( size_t, dwiNum ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
info = ( double * )( ninfo->data );
/* set up skip list recording */
mop = airMopNew( );
skipArr = airArrayNew( ( void** )skipP, skipNumP, sizeof( unsigned int ), 16 );
airMopAdd( mop, skipArr, ( airMopper )airArrayNix, airMopAlways );
skipLut = AIR_CALLOC( dwiNum, unsigned int );
airMopAdd( mop, skipLut, airFree, airMopAlways );
if ( !skipLut ) {
biffAddf( TEN, "%s: couldn't allocate skip LUT", me );
airMopError( mop ); return 1;
}
/* parse values in ninfo */
for ( dwiIdx=0; dwiIdx<dwiNum; dwiIdx++ ) {
sprintf( key, keyFmt, dwiIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( !val ) {
biffAddf( TEN, "%s: didn't see \"%s\" key", me, key );
airMopError( mop ); return 1;
}
airToLower( val );
if ( !strncmp( tenDWMRINAVal, val + strspn( val, AIR_WHITESPACE ),
strlen( tenDWMRINAVal ) ) ) {
/* have no sensible gradient or B-matrix info here, and must skip */
for ( valIdx=0; valIdx<valNum; valIdx++ ) {
info[valIdx] = AIR_NAN;
}
skipIdx = airArrayLenIncr( skipArr, 1 );
( *skipP )[skipIdx] = dwiIdx;
skipLut[dwiIdx] = AIR_TRUE;
/* can't have NEX on a skipped gradient or B-matrix */
val = ( char * )airFree( val );
sprintf( key, tenDWMRINexKeyFmt, dwiIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( val ) {
biffAddf( TEN, "%s: can't have NEX of skipped DWI %u", me, skipIdx );
airMopError( mop ); return 1;
}
nexNum = 1; /* for "info +=" below */
} else {
/* we probably do have sensible gradient or B-matrix info */
parsedNum = airParseStrD( info, val, AIR_WHITESPACE, valNum );
if ( valNum != parsedNum ) {
biffAddf( TEN, "%s: couldn't parse %d floats in value \"%s\" "
"for key \"%s\" ( only got %d )",
me, valNum, val, key, parsedNum );
airMopError( mop ); return 1;
}
val = ( char * )airFree( val );
sprintf( key, tenDWMRINexKeyFmt, dwiIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( !val ) {
/* there is no NEX indicated */
nexNum = 1;
} else {
if ( 1 != sscanf( val, "%u", &nexNum ) ) {
biffAddf( TEN, "%s: couldn't parse integer in value \"%s\" "
"for key \"%s\"", me, val, key );
airMopError( mop ); return 1;
}
val = ( char * )airFree( val );
if ( !( nexNum >= 1 ) ) {
biffAddf( TEN, "%s: NEX ( %d ) for DWI %d not >= 1",
me, nexNum, dwiIdx );
airMopError( mop ); return 1;
}
if ( !( dwiIdx + nexNum - 1 < dwiNum ) ) {
biffAddf( TEN, "%s: NEX %d for DWI %d implies %d DWI > real # DWI %d",
me, nexNum, dwiIdx, dwiIdx + nexNum, dwiNum );
airMopError( mop ); return 1;
}
for ( nexIdx=1; nexIdx<nexNum; nexIdx++ ) {
sprintf( key, keyFmt, dwiIdx+nexIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( val ) {
val = ( char * )airFree( val );
biffAddf( TEN, "%s: shouldn't have key \"%s\" with "
"NEX %d for DWI %d", me, key, nexNum, dwiIdx );
airMopError( mop ); return 1;
}
for ( valIdx=0; valIdx<valNum; valIdx++ ) {
info[valIdx + valNum*nexIdx] = info[valIdx];
}
}
dwiIdx += nexNum-1;
}
}
info += valNum*nexNum;
}
/* perhaps too paranoid: see if there are extra keys,
which probably implies confusion/mismatch between number of
gradients and number of values */
sprintf( key, keyFmt, dwiIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( val ) {
biffAddf( TEN, "%s: saw \"%s\" key, more than required %u keys, "
"likely mismatch between keys and actual gradients",
me, key, dwiIdx );
airMopError( mop ); return 1;
}
/* second pass: see which ones are skipped, even though gradient/B-matrix
information has been specified */
for ( dwiIdx=0; dwiIdx<dwiNum; dwiIdx++ ) {
sprintf( key, tenDWMRISkipKeyFmt, dwiIdx );
val = nrrdKeyValueGet( ndwi, key );
if ( val ) {
airToLower( val );
if ( !strncmp( "true", val + strspn( val, AIR_WHITESPACE ),
strlen( "true" ) ) ) {
skipIdx = airArrayLenIncr( skipArr, 1 );
( *skipP )[skipIdx] = dwiIdx;
skipLut[dwiIdx] = AIR_TRUE;
}
}
}
/* normalize so that maximal norm is 1.0 */
/* Thu Dec 20 03:25:20 CST 2012 this rescaling IS in fact what is
causing the small discrepency between ngrad before and after
saving to KVPs. The problem is not related to how the gradient
vector coefficients are recovered from the string-based
representation; that is likely bit-for-bit correct. The problem
is when everything is rescaled by 1.0/normMax: a "normalized"
vector will not have *exactly* length 1.0. So what can be done
to prevent pointlessly altering the lengths of vectors that were
close enough to unit-length? Is there some more 754-savvy
way of doing this normalization? */
normMax = 0;
info = ( double * )( ninfo->data );
for ( dwiIdx=0; dwiIdx<dwiNum; dwiIdx++ ) {
if ( !skipLut[dwiIdx] ) {
if ( 3 == valNum ) {
norm = ELL_3V_LEN( info );
} else {
norm = sqrt( info[0]*info[0] + 2*info[1]*info[1] + 2*info[2]*info[2]
+ info[3]*info[3] + 2*info[4]*info[4]
+ info[5]*info[5] );
}
normMax = AIR_MAX( normMax, norm );
}
info += valNum;
}
info = ( double * )( ninfo->data );
for ( dwiIdx=0; dwiIdx<dwiNum; dwiIdx++ ) {
if ( !skipLut[dwiIdx] ) {
if ( 3 == valNum ) {
ELL_3V_SCALE( info, 1.0/normMax, info );
} else {
ELL_6V_SCALE( info, 1.0/normMax, info );
}
}
info += valNum;
}
airMopOkay( mop );
return 0;
}
/*
******** tenBMatrixCalc
**
** given a list of gradient directions ( arbitrary type ), contructs the
** B-matrix that records how each coefficient of the diffusion tensor
** is weighted in the diffusion weighted images. Matrix will be a
** 6-by-N 2D array of doubles.
**
** NOTE 1: The ordering of the elements in each row is ( like the ordering
** of the tensor elements in all of ten ):
**
** Bxx Bxy Bxz Byy Byz Bzz
**
** NOTE 2: The off-diagonal elements are NOT pre-multiplied by two.
*/
int
350 tenBMatrixCalc( Nrrd *nbmat, const Nrrd *_ngrad ) {
static const char me[]="tenBMatrixCalc";
Nrrd *ngrad;
double *bmat, *G;
int DD, dd;
airArray *mop;
if ( !( nbmat && _ngrad && !tenGradientCheck( _ngrad, nrrdTypeDefault, 1 ) ) ) {
biffAddf( TEN, "%s: got NULL pointer or invalid arg", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, ngrad=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ngrad, _ngrad, nrrdTypeDouble )
|| nrrdMaybeAlloc_va( nbmat, nrrdTypeDouble, 2,
AIR_CAST( size_t, 6 ), ngrad->axis[1].size ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
airMopError( mop ); return 1;
}
DD = ngrad->axis[1].size;
G = ( double* )( ngrad->data );
bmat = ( double* )( nbmat->data );
for ( dd=0; dd<DD; dd++ ) {
ELL_6V_SET( bmat,
G[0]*G[0], G[0]*G[1], G[0]*G[2],
G[1]*G[1], G[1]*G[2],
G[2]*G[2] );
G += 3;
bmat += 6;
}
nbmat->axis[0].kind = nrrdKind3DSymMatrix;
airMopOkay( mop );
return 0;
}
/*
******** tenEMatrixCalc
**
*/
int
392 tenEMatrixCalc( Nrrd *nemat, const Nrrd *_nbmat, int knownB0 ) {
static const char me[]="tenEMatrixCalc";
Nrrd *nbmat, *ntmp;
airArray *mop;
ptrdiff_t padmin[2], padmax[2];
unsigned int ri;
double *bmat;
if ( !( nemat && _nbmat ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenBMatrixCheck( _nbmat, nrrdTypeDefault, 6 ) ) {
biffAddf( TEN, "%s: problem with B matrix", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nbmat=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( knownB0 ) {
if ( nrrdConvert( nbmat, _nbmat, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert given bmat to doubles", me );
airMopError( mop ); return 1;
}
} else {
airMopAdd( mop, ntmp=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ntmp, _nbmat, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert given bmat to doubles", me );
airMopError( mop ); return 1;
}
ELL_2V_SET( padmin, 0, 0 );
ELL_2V_SET( padmax, 6, _nbmat->axis[1].size-1 );
if ( nrrdPad_nva( nbmat, ntmp, padmin, padmax, nrrdBoundaryPad, -1 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't pad given bmat", me );
airMopError( mop ); return 1;
}
}
bmat = ( double* )( nbmat->data );
/* HERE is where the off-diagonal elements get multiplied by 2 */
for ( ri=0; ri<nbmat->axis[1].size; ri++ ) {
bmat[1] *= 2;
bmat[2] *= 2;
bmat[4] *= 2;
bmat += nbmat->axis[0].size;
}
if ( ell_Nm_pseudo_inv( nemat, nbmat ) ) {
biffMovef( TEN, ELL, "%s: trouble pseudo-inverting B-matrix", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** tenEstimateLinearSingle_d
**
** estimate one single tensor
**
** !! requires being passed a pre-allocated double array "vbuf" which is
** !! used for intermediate calculations ( details below )
**
** DD is always the length of the dwi[] array
**
** -------------- IF knownB0 -------------------------
** input:
** dwi[0] is the B0 image, dwi[1]..dwi[DD-1] are the ( DD-1 ) DWI values,
** emat is the ( DD-1 )-by-6 estimation matrix, which is the pseudo-inverse
** of the B-matrix ( after the off-diagonals have been multiplied by 2 ).
** vbuf[] is allocated for ( at least ) DD-1 doubles ( DD is fine )
**
** output:
** ten[0]..ten[6] will be the confidence value followed by the tensor
** if B0P, then *B0P is set to the B0 value used in calcs: max( b0, 1 )
** -------------- IF !knownB0 -------------------------
** input:
** dwi[0]..dwi[DD-1] are the DD DWI values, emat is the DD-by-7 estimation
** matrix. The 7th column is for estimating the B0 image.
** vbuf[] is allocated for DD doubles
**
** output:
** ten[0]..ten[6] will be the confidence value followed by the tensor
** if B0P, then *B0P is set to estimated B0 value.
** ----------------------------------------------------
*/
void
476 tenEstimateLinearSingle_d( double *ten, double *B0P, /* output */
const double *dwi, const double *emat, /* input .. */
double *vbuf, unsigned int DD, int knownB0,
double thresh, double soft, double b ) {
double logB0, tmp, mean;
unsigned int ii, jj;
/* static const char me[]="tenEstimateLinearSingle_d"; */
if ( knownB0 ) {
if ( B0P ) {
/* we save this as a courtesy */
*B0P = AIR_MAX( dwi[0], 1 );
}
logB0 = log( AIR_MAX( dwi[0], 1 ) );
mean = 0;
for ( ii=1; ii<DD; ii++ ) {
tmp = AIR_MAX( dwi[ii], 1 );
mean += tmp;
vbuf[ii-1] = ( logB0 - log( tmp ) )/b;
/* if ( tenVerbose ) {
fprintf( stderr, "vbuf[%d] = %f\n", ii-1, vbuf[ii-1] );
} */
}
mean /= DD-1;
if ( soft ) {
ten[0] = AIR_AFFINE( -1, airErf( ( mean - thresh )/( soft + 0.000001 ) ), 1,
0, 1 );
} else {
ten[0] = mean > thresh;
}
for ( jj=0; jj<6; jj++ ) {
tmp = 0;
for ( ii=0; ii<DD-1; ii++ ) {
tmp += emat[ii + ( DD-1 )*jj]*vbuf[ii];
}
ten[jj+1] = tmp;
}
} else {
/* !knownB0 */
mean = 0;
for ( ii=0; ii<DD; ii++ ) {
tmp = AIR_MAX( dwi[ii], 1 );
mean += tmp;
vbuf[ii] = -log( tmp )/b;
}
mean /= DD;
if ( soft ) {
ten[0] = AIR_AFFINE( -1, airErf( ( mean - thresh )/( soft + 0.000001 ) ), 1,
0, 1 );
} else {
ten[0] = mean > thresh;
}
for ( jj=0; jj<7; jj++ ) {
tmp = 0;
for ( ii=0; ii<DD; ii++ ) {
tmp += emat[ii + DD*jj]*vbuf[ii];
}
if ( jj < 6 ) {
ten[jj+1] = tmp;
} else {
/* we're on seventh row, for finding B0 */
if ( B0P ) {
*B0P = exp( b*tmp );
}
}
}
}
return;
}
void
547 tenEstimateLinearSingle_f( float *_ten, float *_B0P, /* output */
const float *_dwi, const double *emat, /* input .. */
double *vbuf, unsigned int DD, int knownB0,
float thresh, float soft, float b ) {
static const char me[]="tenEstimateLinearSingle_f";
#define DWI_NUM_MAX 256
double dwi[DWI_NUM_MAX], ten[7], B0;
unsigned int dwiIdx;
/* HEY: this is somewhat inelegant .. */
if ( DD > DWI_NUM_MAX ) {
fprintf( stderr, "%s: PANIC: sorry, DD=%u > compile-time DWI_NUM_MAX=%u\n",
me, DD, DWI_NUM_MAX );
exit( 1 );
}
for ( dwiIdx=0; dwiIdx<DD; dwiIdx++ ) {
dwi[dwiIdx] = _dwi[dwiIdx];
}
tenEstimateLinearSingle_d( ten, _B0P ? &B0 : NULL,
dwi, emat,
vbuf, DD, knownB0,
thresh, soft, b );
TEN_T_COPY_TT( _ten, float, ten );
if ( _B0P ) {
*_B0P = AIR_CAST( float, B0 );
}
return;
}
/*
******** tenEstimateLinear3D
**
** takes an array of DWIs ( starting with the B=0 image ), joins them up,
** and passes it all off to tenEstimateLinear4D
**
** Note: this will copy per-axis peripheral information from _ndwi[0]
*/
int
585 tenEstimateLinear3D( Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P,
const Nrrd *const *_ndwi, unsigned int dwiLen,
const Nrrd *_nbmat, int knownB0,
double thresh, double soft, double b ) {
static const char me[]="tenEstimateLinear3D";
Nrrd *ndwi;
airArray *mop;
int amap[4] = {-1, 0, 1, 2};
if ( !( _ndwi ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
ndwi = nrrdNew( );
airMopAdd( mop, ndwi, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdJoin( ndwi, ( const Nrrd*const* )_ndwi, dwiLen, 0, AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: trouble joining inputs", me );
airMopError( mop ); return 1;
}
nrrdAxisInfoCopy( ndwi, _ndwi[0], amap, NRRD_AXIS_INFO_NONE );
if ( tenEstimateLinear4D( nten, nterrP, nB0P,
ndwi, _nbmat, knownB0, thresh, soft, b ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** tenEstimateLinear4D
**
** given a stack of DWI volumes ( ndwi ) and the imaging B-matrix used
** for acquisiton ( _nbmat ), computes and stores diffusion tensors in
** nten.
**
** The mean of the diffusion-weighted images is thresholded at "thresh" with
** softness parameter "soft".
**
** This takes the B-matrix ( weighting matrix ), such as formed by tenBMatrix,
** or from a more complete account of the gradients present in an imaging
** sequence, and then does the pseudo inverse to get the estimation matrix
*/
int
632 tenEstimateLinear4D( Nrrd *nten, Nrrd **nterrP, Nrrd **nB0P,
const Nrrd *ndwi, const Nrrd *_nbmat, int knownB0,
double thresh, double soft, double b ) {
static const char me[]="tenEstimateLinear4D";
Nrrd *nemat, *nbmat, *ncrop, *nhist;
airArray *mop;
size_t cmin[4], cmax[4], sx, sy, sz, II, d, DD;
int E, amap[4];
float *ten, *dwi1, *dwi2, *terr,
_B0, *B0, ( *lup )( const void *, size_t );
double *emat, *bmat, *vbuf;
NrrdRange *range;
float te, d1, d2;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nten && ndwi && _nbmat ) ) {
/* nerrP and _NB0P can be NULL */
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( 4 == ndwi->dim && 7 <= ndwi->axis[0].size ) ) {
biffAddf( TEN, "%s: dwi should be 4-D array with axis 0 size >= 7", me );
return 1;
}
if ( tenBMatrixCheck( _nbmat, nrrdTypeDefault, 6 ) ) {
biffAddf( TEN, "%s: problem with B matrix", me );
return 1;
}
if ( knownB0 ) {
if ( !( ndwi->axis[0].size == 1 + _nbmat->axis[1].size ) ) {
biffAddf( TEN, "%s: ( knownB0 == true ) # input images ( %s ) "
"!= 1 + # B matrix rows ( 1+%s )", me,
airSprintSize_t( stmp[0], ndwi->axis[0].size ),
airSprintSize_t( stmp[1], _nbmat->axis[1].size ) );
return 1;
}
} else {
if ( !( ndwi->axis[0].size == _nbmat->axis[1].size ) ) {
biffAddf( TEN, "%s: ( knownB0 == false ) # dwi ( %s ) "
"!= # B matrix rows ( %s )", me,
airSprintSize_t( stmp[0], ndwi->axis[0].size ),
airSprintSize_t( stmp[1], _nbmat->axis[1].size ) );
return 1;
}
}
DD = ndwi->axis[0].size;
sx = ndwi->axis[1].size;
sy = ndwi->axis[2].size;
sz = ndwi->axis[3].size;
mop = airMopNew( );
airMopAdd( mop, nbmat=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nbmat, _nbmat, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble converting to doubles", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nemat=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( tenEMatrixCalc( nemat, nbmat, knownB0 ) ) {
biffAddf( TEN, "%s: trouble computing estimation matrix", me );
airMopError( mop ); return 1;
}
vbuf = AIR_CALLOC( knownB0 ? DD-1 : DD, double );
dwi1 = AIR_CALLOC( DD, float );
dwi2 = AIR_CALLOC( knownB0 ? DD : DD+1, float );
airMopAdd( mop, vbuf, airFree, airMopAlways );
airMopAdd( mop, dwi1, airFree, airMopAlways );
airMopAdd( mop, dwi2, airFree, airMopAlways );
if ( !( vbuf && dwi1 && dwi2 ) ) {
biffAddf( TEN, "%s: couldn't allocate temp buffers", me );
airMopError( mop ); return 1;
}
if ( !AIR_EXISTS( thresh ) ) {
airMopAdd( mop, ncrop=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nhist=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
ELL_4V_SET( cmin, knownB0 ? 1 : 0, 0, 0, 0 );
ELL_4V_SET( cmax, DD-1, sx-1, sy-1, sz-1 );
E = 0;
if ( !E ) E |= nrrdCrop( ncrop, ndwi, cmin, cmax );
if ( !E ) range = nrrdRangeNewSet( ncrop, nrrdBlind8BitRangeState );
if ( !E ) airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( !E ) E |= nrrdHisto( nhist, ncrop, range, NULL,
( int )AIR_MIN( 1024, range->max - range->min + 1 ),
nrrdTypeFloat );
if ( E ) {
biffMovef( TEN, NRRD,
"%s: trouble histograming to find DW threshold", me );
airMopError( mop ); return 1;
}
if ( _tenFindValley( &thresh, nhist, 0.75, AIR_FALSE ) ) {
biffAddf( TEN, "%s: problem finding DW histogram valley", me );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: using %g for DW confidence threshold\n", me, thresh );
}
if ( nrrdMaybeAlloc_va( nten, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
if ( nterrP ) {
if ( !( *nterrP ) ) {
*nterrP = nrrdNew( );
}
if ( nrrdMaybeAlloc_va( *nterrP, nrrdTypeFloat, 3,
sx, sy, sz ) ) {
biffAddf( NRRD, "%s: couldn't allocate error output", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nterrP, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, *nterrP, ( airMopper )nrrdNuke, airMopOnError );
terr = ( float* )( ( *nterrP )->data );
} else {
terr = NULL;
}
if ( nB0P ) {
if ( !( *nB0P ) ) {
*nB0P = nrrdNew( );
}
if ( nrrdMaybeAlloc_va( *nB0P, nrrdTypeFloat, 3,
sx, sy, sz ) ) {
biffAddf( NRRD, "%s: couldn't allocate error output", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nB0P, ( airMopper )airSetNull, airMopOnError );
airMopAdd( mop, *nB0P, ( airMopper )nrrdNuke, airMopOnError );
B0 = ( float* )( ( *nB0P )->data );
} else {
B0 = NULL;
}
bmat = ( double* )( nbmat->data );
emat = ( double* )( nemat->data );
ten = ( float* )( nten->data );
lup = nrrdFLookup[ndwi->type];
for ( II=0; II<sx*sy*sz; II++ ) {
/* tenVerbose = ( II == 42 + 190*( 96 + 196*0 ) ); */
for ( d=0; d<DD; d++ ) {
dwi1[d] = lup( ndwi->data, d + DD*II );
/* if ( tenVerbose ) {
fprintf( stderr, "%s: input dwi1[%d] = %g\n", me, d, dwi1[d] );
} */
}
tenEstimateLinearSingle_f( ten, &_B0, dwi1, emat,
vbuf, DD, knownB0,
AIR_CAST( float, thresh ),
AIR_CAST( float, soft ),
AIR_CAST( float, b ) );
if ( nB0P ) {
*B0 = _B0;
}
/* if ( tenVerbose ) {
fprintf( stderr, "%s: output ten = ( %g ) %g, %g, %g %g, %g %g\n", me,
ten[0], ten[1], ten[2], ten[3], ten[4], ten[5], ten[6] );
} */
if ( nterrP ) {
te = 0;
if ( knownB0 ) {
tenSimulateSingle_f( dwi2, _B0, ten, bmat, DD, AIR_CAST( float, b ) );
for ( d=1; d<DD; d++ ) {
d1 = AIR_MAX( dwi1[d], 1 );
d2 = AIR_MAX( dwi2[d], 1 );
te += ( d1 - d2 )*( d1 - d2 );
}
te /= ( DD-1 );
} else {
tenSimulateSingle_f( dwi2, _B0, ten, bmat, DD+1, AIR_CAST( float, b ) );
for ( d=0; d<DD; d++ ) {
d1 = AIR_MAX( dwi1[d], 1 );
/* tenSimulateSingle_f always puts the B0 in the beginning of
the dwi vector, but in this case we didn't have it in
the input dwi vector */
d2 = AIR_MAX( dwi2[d+1], 1 );
te += ( d1 - d2 )*( d1 - d2 );
}
te /= DD;
}
*terr = AIR_CAST( float, sqrt( te ) );
terr += 1;
}
ten += 7;
if ( B0 ) {
B0 += 1;
}
}
/* not our job: tenEigenvalueClamp( nten, nten, 0, AIR_NAN ); */
ELL_4V_SET( amap, -1, 1, 2, 3 );
nrrdAxisInfoCopy( nten, ndwi, amap, NRRD_AXIS_INFO_NONE );
nten->axis[0].kind = nrrdKind3DMaskedSymMatrix;
if ( nrrdBasicInfoCopy( nten, ndwi,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
airMopOkay( mop );
return 0;
}
/*
******** tenSimulateSingle_f
**
** given a tensor, simulate the set of diffusion weighted measurements
** represented by the given B matrix
**
** NOTE: the mindset of this function is very much "knownB0==true":
** B0 is required as an argument ( and its always copied to dwi[0] ),
** and the given bmat is assumed to have DD-1 rows ( similar to how
** tenEstimateLinearSingle_f( ) is set up ), and dwi[1] through dwi[DD-1]
** are set to the calculated DWIs.
**
** So: dwi must be allocated for DD values total
*/
void
845 tenSimulateSingle_f( float *dwi,
float B0, const float *ten, const double *bmat,
unsigned int DD, float b ) {
double vv;
/* this is how we multiply the off-diagonal entries by 2 */
double matwght[6] = {1, 2, 2, 1, 2, 1};
unsigned int ii, jj;
dwi[0] = B0;
/* if ( tenVerbose ) {
fprintf( stderr, "ten = %g, %g, %g %g, %g %g\n",
ten[1], ten[2], ten[3], ten[4], ten[5], ten[6] );
} */
for ( ii=0; ii<DD-1; ii++ ) {
vv = 0;
for ( jj=0; jj<6; jj++ ) {
vv += matwght[jj]*bmat[jj + 6*ii]*ten[jj+1];
}
dwi[ii+1] = AIR_CAST( float, AIR_MAX( B0, 1 )*exp( -b*vv ) );
/* if ( tenVerbose ) {
fprintf( stderr, "v[%d] = %g --> dwi = %g\n", ii, vv, dwi[ii+1] );
} */
}
return;
}
int
873 tenSimulate( Nrrd *ndwi, const Nrrd *nT2, const Nrrd *nten,
const Nrrd *_nbmat, double b ) {
static const char me[]="tenSimulate";
size_t II;
Nrrd *nbmat;
size_t DD, sx, sy, sz;
airArray *mop;
double *bmat;
float *dwi, *ten, ( *lup )( const void *, size_t I );
char stmp[6][AIR_STRLEN_SMALL];
if ( !ndwi || !nT2 || !nten || !_nbmat
|| tenTensorCheck( nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE )
|| tenBMatrixCheck( _nbmat, nrrdTypeDefault, 6 ) ) {
biffAddf( TEN, "%s: got NULL pointer or invalid args", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, nbmat=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nbmat, _nbmat, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert B matrix", me );
return 1;
}
DD = nbmat->axis[1].size+1;
sx = nT2->axis[0].size;
sy = nT2->axis[1].size;
sz = nT2->axis[2].size;
if ( !( 3 == nT2->dim
&& sx == nten->axis[1].size
&& sy == nten->axis[2].size
&& sz == nten->axis[3].size ) ) {
biffAddf( TEN, "%s: dimensions of %u-D T2 volume ( %s, %s, %s ) "
"don't match tensor volume ( %s, %s, %s )", me, nT2->dim,
airSprintSize_t( stmp[0], sx ),
airSprintSize_t( stmp[1], sy ),
airSprintSize_t( stmp[2], sz ),
airSprintSize_t( stmp[3], nten->axis[1].size ),
airSprintSize_t( stmp[4], nten->axis[2].size ),
airSprintSize_t( stmp[5], nten->axis[3].size ) );
return 1;
}
if ( nrrdMaybeAlloc_va( ndwi, nrrdTypeFloat, 4,
AIR_CAST( size_t, DD ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
dwi = ( float* )( ndwi->data );
ten = ( float* )( nten->data );
bmat = ( double* )( nbmat->data );
lup = nrrdFLookup[nT2->type];
for ( II=0; II<( size_t )( sx*sy*sz ); II++ ) {
/* tenVerbose = ( II == 42 + 190*( 96 + 196*0 ) ); */
tenSimulateSingle_f( dwi, lup( nT2->data, II ), ten, bmat, DD,
AIR_CAST( float, b ) );
dwi += DD;
ten += 7;
}
airMopOkay( mop );
return 0;
}
/* old stuff, prior to tenEstimationMatrix .. */
/*
******** tenCalcOneTensor1
**
** make one diffusion tensor from the measurements at one voxel, based
** on the gradient directions used by Andy Alexander
*/
void
963 tenCalcOneTensor1( float tens[7], float chan[7],
float thresh, float slope, float b ) {
double c[7], sum, d1, d2, d3, d4, d5, d6;
c[0] = AIR_MAX( chan[0], 1 );
c[1] = AIR_MAX( chan[1], 1 );
c[2] = AIR_MAX( chan[2], 1 );
c[3] = AIR_MAX( chan[3], 1 );
c[4] = AIR_MAX( chan[4], 1 );
c[5] = AIR_MAX( chan[5], 1 );
c[6] = AIR_MAX( chan[6], 1 );
sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6];
tens[0] = AIR_CAST( float, ( 1 + airErf( slope*( sum - thresh ) ) )/2.0 );
d1 = ( log( c[0] ) - log( c[1] ) )/b;
d2 = ( log( c[0] ) - log( c[2] ) )/b;
d3 = ( log( c[0] ) - log( c[3] ) )/b;
d4 = ( log( c[0] ) - log( c[4] ) )/b;
d5 = ( log( c[0] ) - log( c[5] ) )/b;
d6 = ( log( c[0] ) - log( c[6] ) )/b;
tens[1] = AIR_CAST( float, d1 + d2 - d3 - d4 + d5 + d6 ); /* Dxx */
tens[2] = AIR_CAST( float, d5 - d6 ); /* Dxy */
tens[3] = AIR_CAST( float, d1 - d2 ); /* Dxz */
tens[4] = AIR_CAST( float, -d1 - d2 + d3 + d4 + d5 + d6 ); /* Dyy */
tens[5] = AIR_CAST( float, d3 - d4 ); /* Dyz */
tens[6] = AIR_CAST( float, d1 + d2 + d3 + d4 - d5 - d6 ); /* Dzz */
return;
}
/*
******** tenCalcOneTensor2
**
** using gradient directions used by EK
*/
void
997 tenCalcOneTensor2( float tens[7], float chan[7],
float thresh, float slope, float b ) {
double c[7], sum, d1, d2, d3, d4, d5, d6;
c[0] = AIR_MAX( chan[0], 1 );
c[1] = AIR_MAX( chan[1], 1 );
c[2] = AIR_MAX( chan[2], 1 );
c[3] = AIR_MAX( chan[3], 1 );
c[4] = AIR_MAX( chan[4], 1 );
c[5] = AIR_MAX( chan[5], 1 );
c[6] = AIR_MAX( chan[6], 1 );
sum = c[1] + c[2] + c[3] + c[4] + c[5] + c[6];
tens[0] = AIR_CAST( float, ( 1 + airErf( slope*( sum - thresh ) ) )/2.0 );
d1 = ( log( c[0] ) - log( c[1] ) )/b;
d2 = ( log( c[0] ) - log( c[2] ) )/b;
d3 = ( log( c[0] ) - log( c[3] ) )/b;
d4 = ( log( c[0] ) - log( c[4] ) )/b;
d5 = ( log( c[0] ) - log( c[5] ) )/b;
d6 = ( log( c[0] ) - log( c[6] ) )/b;
tens[1] = AIR_CAST( float, d1 ); /* Dxx */
tens[2] = AIR_CAST( float, d6 - ( d1 + d2 )/2 ); /* Dxy */
tens[3] = AIR_CAST( float, d5 - ( d1 + d3 )/2 ); /* Dxz */
tens[4] = AIR_CAST( float, d2 ); /* Dyy */
tens[5] = AIR_CAST( float, d4 - ( d2 + d3 )/2 ); /* Dyz */
tens[6] = AIR_CAST( float, d3 ); /* Dzz */
return;
}
/*
******** tenCalcTensor
**
** Calculate a volume of tensors from measured data
*/
int
1031 tenCalcTensor( Nrrd *nout, Nrrd *nin, int version,
float thresh, float slope, float b ) {
static const char me[] = "tenCalcTensor";
char cmt[128];
float *out, tens[7], chan[7];
size_t I, sx, sy, sz;
void ( *calcten )( float tens[7], float chan[7],
float thresh, float slope, float b );
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( 1 == version || 2 == version ) ) {
biffAddf( TEN, "%s: version should be 1 or 2, not %d", me, version );
return 1;
}
switch ( version ) {
case 1:
calcten = tenCalcOneTensor1;
break;
case 2:
calcten = tenCalcOneTensor2;
break;
default:
biffAddf( TEN, "%s: PANIC, version = %d not handled", me, version );
return 1;
break;
}
if ( tenTensorCheck( nin, nrrdTypeDefault, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: wasn't given valid tensor nrrd", me );
return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: couldn't alloc output", me );
return 1;
}
nout->axis[0].label = airStrdup( "c, Dxx, Dxy, Dxz, Dyy, Dyz, Dzz" );
nout->axis[1].label = airStrdup( "x" );
nout->axis[2].label = airStrdup( "y" );
nout->axis[3].label = airStrdup( "z" );
nout->axis[0].spacing = AIR_NAN;
if ( AIR_EXISTS( nin->axis[1].spacing ) &&
AIR_EXISTS( nin->axis[2].spacing ) &&
AIR_EXISTS( nin->axis[3].spacing ) ) {
nout->axis[1].spacing = nin->axis[1].spacing;
nout->axis[2].spacing = nin->axis[2].spacing;
nout->axis[3].spacing = nin->axis[3].spacing;
}
else {
nout->axis[1].spacing = 1.0;
nout->axis[2].spacing = 1.0;
nout->axis[3].spacing = 1.0;
}
sprintf( cmt, "%s: using thresh = %g, slope = %g, b = %g\n",
me, thresh, slope, b );
nrrdCommentAdd( nout, cmt );
out = ( float * )nout->data;
for ( I=0; I<( size_t )( sx*sy*sz ); I++ ) {
if ( tenVerbose && !( I % ( sx*sy ) ) ) {
fprintf( stderr, "%s: z = %d of %d\n", me, ( int )( I/( sx*sy ) ), ( int )sz-1 );
}
chan[0] = nrrdFLookup[nin->type]( nin->data, 0 + 7*I );
chan[1] = nrrdFLookup[nin->type]( nin->data, 1 + 7*I );
chan[2] = nrrdFLookup[nin->type]( nin->data, 2 + 7*I );
chan[3] = nrrdFLookup[nin->type]( nin->data, 3 + 7*I );
chan[4] = nrrdFLookup[nin->type]( nin->data, 4 + 7*I );
chan[5] = nrrdFLookup[nin->type]( nin->data, 5 + 7*I );
chan[6] = nrrdFLookup[nin->type]( nin->data, 6 + 7*I );
calcten( tens, chan, thresh, slope, b );
out[0 + 7*I] = tens[0];
out[1 + 7*I] = tens[1];
out[2 + 7*I] = tens[2];
out[3 + 7*I] = tens[3];
out[4 + 7*I] = tens[4];
out[5 + 7*I] = tens[5];
out[6 + 7*I] = tens[6];
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
const int
tenPresent = 42;
const char *
tenBiffKey = "ten";
const char
tenDefFiberKernel[] = "cubic:0, 0.5";
double
tenDefFiberStepSize = 0.01;
int
tenDefFiberUseIndexSpace = AIR_FALSE;
int
tenDefFiberMaxNumSteps = 0;
double
tenDefFiberMaxHalfLen = 3;
int
tenDefFiberAnisoStopType = tenAniso_Cl2;
double
tenDefFiberAnisoThresh = 0.5;
int
tenDefFiberIntg = tenFiberIntgEuler;
double
tenDefFiberWPunct = 0;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/* -------------------------------------------------------------- */
const char *
_tenAnisoStr[TEN_ANISO_MAX+1] = {
"( unknown aniso )",
"Conf",
"Cl1",
"Cp1",
"Ca1",
"Clpmin1",
"Cs1",
"Ct1",
"Cl2",
"Cp2",
"Ca2",
"Clpmin2",
"Cs2",
"Ct2",
"RA",
"FA",
"VF",
"B",
"Q",
"R",
"S",
"Skew",
"Mode",
"Th",
"Omega",
"Det",
"Tr",
"eval0",
"eval1",
"eval2"
};
const airEnum
_tenAniso = {
"anisotropy metric",
TEN_ANISO_MAX,
_tenAnisoStr, NULL,
NULL,
NULL, NULL,
AIR_FALSE
};
72 const airEnum *const
tenAniso = &_tenAniso;
/* --------------------------------------------------------------------- */
const char *
_tenInterpTypeStr[TEN_INTERP_TYPE_MAX+1] = {
"( unknown interp type )",
"lin",
"loglin",
"affinv",
"wang",
"geoloxk",
"geoloxr",
"loxk",
"loxr",
"qgeoloxk",
"qgeoloxr",
"rtplin"
};
const char *
_tenInterpTypeStrEqv[] = {
"lin", "linear", "lerp",
"loglin", "loglinear", "loglerp",
"affinv",
"wang",
"geoloxk", "glk",
"geoloxr", "glr",
"loxk",
"loxr",
"qgeoloxk", "qglk",
"qgeoloxr", "qglr",
"rtplin",
""
};
const int
_tenInterpTypeValEqv[] = {
tenInterpTypeLinear, tenInterpTypeLinear, tenInterpTypeLinear,
tenInterpTypeLogLinear, tenInterpTypeLogLinear, tenInterpTypeLogLinear,
tenInterpTypeAffineInvariant,
tenInterpTypeWang,
tenInterpTypeGeoLoxK, tenInterpTypeGeoLoxK,
tenInterpTypeGeoLoxR, tenInterpTypeGeoLoxR,
tenInterpTypeLoxK,
tenInterpTypeLoxR,
tenInterpTypeQuatGeoLoxK, tenInterpTypeQuatGeoLoxK,
tenInterpTypeQuatGeoLoxR, tenInterpTypeQuatGeoLoxR,
tenInterpTypeRThetaPhiLinear
};
const airEnum
_tenInterpType = {
"interp type",
TEN_INTERP_TYPE_MAX,
_tenInterpTypeStr, NULL,
NULL,
_tenInterpTypeStrEqv, _tenInterpTypeValEqv,
AIR_FALSE
};
133 const airEnum *const
tenInterpType = &_tenInterpType;
/* --------------------------------------------------------------------- */
const char *
_tenGageStr[] = {
"( unknown tenGage )",
"tensor",
"confidence",
"trace",
"N",
"B",
"det",
"S",
"Q",
"FA",
"R",
"mode",
"theta",
"modew",
"omega",
"evals",
"eval0",
"eval1",
"eval2",
"evecs",
"evec0",
"evec1",
"evec2",
"delnk2",
"delnk3",
"delnr1",
"delnr2",
"delnphi1",
"delnphi2",
"delnphi3",
"tensor grad",
"tensor grad mag",
"tensor grad mag mag",
"trace grad vec",
"trace grad mag",
"trace normal",
"norm grad vec",
"norm grad mag",
"norm normal",
"B grad vec",
"B grad mag",
"B normal",
"det grad vec",
"det grad mag",
"det normal",
"S grad vec",
"S grad mag",
"S normal",
"Q grad vec",
"Q grad mag",
"Q normal",
"FA grad vec",
"FA grad mag",
"FA normal",
"R grad vec",
"R grad mag",
"R normal",
"mode grad vec",
"mode grad mag",
"mode normal",
"theta grad vec",
"theta grad mag",
"theta normal",
"omega grad vec",
"omega grad mag",
"omega normal",
"invariant K gradients",
"invariant K gradient mags",
"invariant R gradients",
"invariant R gradient mags",
"rotation tangents",
"rotation tangent mags",
"eigenvalue gradients",
"Cl1",
"Cp1",
"Ca1",
"Clpmin1",
"Cl2",
"Cp2",
"Ca2",
"Clpmin2",
"hessian",
"trace hessian",
"trace hessian evals",
"trace hessian eval 0",
"trace hessian eval 1",
"trace hessian eval 2",
"trace hessian evecs",
"trace hessian evec 0",
"trace hessian evec 1",
"trace hessian evec 2",
"trace hessian frob",
"B hessian",
"det hessian",
"S hessian",
"Q hessian",
"FA hessian",
"FA hessian evals",
"FA hessian eval 0",
"FA hessian eval 1",
"FA hessian eval 2",
"FA hessian evecs",
"FA hessian evec 0",
"FA hessian evec 1",
"FA hessian evec 2",
"FA hessian frob",
"FA ridge surface strength",
"FA valley surface strength",
"FA laplacian",
"FA hessian eval mode",
"FA ridge line alignment",
"FA ridge surface alignment",
"FA 2nd DD",
"FA geometry tensor",
"FA kappa1",
"FA kappa2",
"FA total curv",
"FA shape index",
"FA mean curv",
"FA gauss curv",
"FA curv dir1",
"FA curv dir2",
"FA flowline curv",
"R hessian",
"mode hessian",
"mode hessian evals",
"mode hessian eval 0",
"mode hessian eval 1",
"mode hessian eval 2",
"mode hessian evecs",
"mode hessian evec 0",
"mode hessian evec 1",
"mode hessian evec 2",
"mode hessian frob",
"omega hessian",
"omega hessian evals",
"omega hessian eval 0",
"omega hessian eval 1",
"omega hessian eval 2",
"omega hessian evecs",
"omega hessian evec 0",
"omega hessian evec 1",
"omega hessian evec 2",
"omega laplacian",
"omega 2nd DD",
"omega hessian contracted with ten evec 0",
"omega hessian contracted with ten evec 1",
"omega hessian contracted with ten evec 2",
"trace gradvec dot evec0",
"diffusionAlign( trace )",
"diffusionFraction( trace )",
"FA gradvec dot evec0",
"diffusionAlign( FA )",
"diffusionFraction( FA )",
"omega gradvec dot evec0",
"diffusionAlign( Omega )",
"diffusionFraction( Omega )",
"conf gradvec dot evec0",
"diffusionAlign( conf )",
"diffusionFraction( conf )",
"cov",
"covr",
"covk",
"logeuclid",
"qglk",
"qglr",
"rtpl",
"cl1gv",
"cl1gm",
"cl1gn",
"cp1gv",
"cp1gm",
"cp1gn",
"ca1gv",
"ca1gm",
"ca1gn",
"tgrote",
"eval hessian",
"cl1 hessian",
"cl1 hessian evals",
"cl1 hessian eval 0",
"cl1 hessian eval 1",
"cl1 hessian eval 2",
"cl1 hessian evecs",
"cl1 hessian evec 0",
"cl1 hessian evec 1",
"cl1 hessian evec 2",
"cp1 hessian",
"cp1 hessian evals",
"cp1 hessian eval 0",
"cp1 hessian eval 1",
"cp1 hessian eval 2",
"cp1 hessian evecs",
"cp1 hessian evec 0",
"cp1 hessian evec 1",
"cp1 hessian evec 2",
"ca1 hessian",
"ca1 hessian evals",
"ca1 hessian eval 0",
"ca1 hessian eval 1",
"ca1 hessian eval 2",
"ca1 hessian evecs",
"ca1 hessian evec 0",
"ca1 hessian evec 1",
"ca1 hessian evec 2",
"fiber curving index",
"fiber dispersion index",
"anisotropies"
};
const char *
_tenGageDesc[] = {
"( unknown tenGage item )",
"tensor",
"confidence",
"trace",
"norm",
"B",
"determinant",
"S",
"Q",
"FA",
"R",
"mode",
"theta",
"warped mode",
"omega",
"3 eigenvalues",
"eigenvalue 0",
"eigenvalue 1",
"eigenvalue 2",
"3 eigenvectors",
"eigenvector 0",
"eigenvector 1",
"eigenvector 2",
"delnk2", /* sorry */
"delnk3", /* sorry */
"delnr1", /* sorry */
"delnr2", /* sorry */
"delnphi1", /* sorry */
"delnphi2", /* sorry */
"delnphi3", /* sorry */
"tensor gradients",
"tensor gradients magnitudes",
"tensor gradients magnitude magnitudes",
"trace grad vec",
"trace grad mag",
"trace normal",
"norm grad vec",
"norm grad mag",
"norm normal",
"B grad vec",
"B grad mag",
"B normal",
"determinant grad vec",
"determinant grad mag",
"determinant normal",
"S grad vec",
"S grad mag",
"S normal",
"Q grad vec",
"Q grad mag",
"Q normal",
"FA grad vec",
"FA grad mag",
"FA normal",
"R grad vec",
"R grad mag",
"R normal",
"mode grad vec",
"mode grad mag",
"mode normal",
"theta grad vec",
"theta grad mag",
"theta normal",
"omega grad vec",
"omega grad mag",
"omega normal",
"invariant K gradients",
"invariant K gradient mags",
"invariant R gradients",
"invariant R gradient mags",
"rotation tangents",
"rotation tangent mags",
"eigenvalue gradients",
"linear anisotropy ( 1 )",
"planar anisotropy ( 1 )",
"linear+planar anisotropy ( 1 )",
"min( linear, planar ) anisotropy ( 1 )",
"linear anisotropy ( 2 )",
"planar anisotropy ( 2 )",
"linear+planar anisotropy ( 2 )",
"min( linear, planar ) anisotropy ( 2 )",
"hessian",
"trace hessian",
"trace hessian evals",
"trace hessian eval 0",
"trace hessian eval 1",
"trace hessian eval 2",
"trace hessian evecs",
"trace hessian evec 0",
"trace hessian evec 1",
"trace hessian evec 2",
"trace hessian frob",
"B hessian",
"det hessian",
"S hessian",
"Q hessian",
"FA hessian",
"FA hessian evals",
"FA hessian eval 0",
"FA hessian eval 1",
"FA hessian eval 2",
"FA hessian evecs",
"FA hessian evec 0",
"FA hessian evec 1",
"FA hessian evec 2",
"FA hessian frob",
"FA ridge surface strength",
"FA valley surface strength",
"FA laplacian",
"FA hessian eval mode",
"FA ridge line alignment",
"FA ridge surface alignment",
"FA 2nd DD",
"FA geometry tensor",
"FA kappa1",
"FA kappa2",
"FA total curv",
"FA shape index",
"FA mean curv",
"FA gauss curv",
"FA curv dir1",
"FA curv dir2",
"FA flowline curv",
"R hessian",
"mode hessian",
"mode hessian evals",
"mode hessian eval 0",
"mode hessian eval 1",
"mode hessian eval 2",
"mode hessian evecs",
"mode hessian evec 0",
"mode hessian evec 1",
"mode hessian evec 2",
"mode hessian frob",
"omega hessian",
"omega hessian evals",
"omega hessian eval 0",
"omega hessian eval 1",
"omega hessian eval 2",
"omega hessian evecs",
"omega hessian evec 0",
"omega hessian evec 1",
"omega hessian evec 2",
"omega laplacian",
"omega 2nd DD",
"omega hessian contracted with ten evec 0",
"omega hessian contracted with ten evec 1",
"omega hessian contracted with ten evec 2",
"trace gradvec dot evec0",
"diffusion align of trace",
"diffusion fraction of trace",
"FA gradvec dot evec0",
"diffusion align of FA",
"diffusion fraction of FA",
"omega gradvec dot evec0",
"diffusion align of omega",
"diffusion fraction of omega",
"conf gradvec dot evec0",
"diffusion align of conf",
"diffusion fraction of conf",
"covariance",
"covarianceR",
"covarianceK",
"log-euclidean",
"QuatGeoLoxK",
"QuatGeoLoxR",
"RThetaPhiLinear interp",
"gradient vector of cl1",
"gradient magnitude of cl1",
"normal of cl1",
"gradient vector of cp1",
"gradient magnitude of cp1",
"normal of cp1",
"gradient vector of ca1",
"gradient magnitude of ca1",
"normal of ca1",
"all tensor component gradients, starting with confidence gradient, "
/* !! CONCAT !! */
"rotated such that eigenvalue derivatives are on the diagonal",
"eigenvalue hessians",
"cl1 hessian",
"cl1 hessian evals",
"cl1 hessian eval 0",
"cl1 hessian eval 1",
"cl1 hessian eval 2",
"cl1 hessian evecs",
"cl1 hessian evec 0",
"cl1 hessian evec 1",
"cl1 hessian evec 2",
"cp1 hessian",
"cp1 hessian evals",
"cp1 hessian eval 0",
"cp1 hessian eval 1",
"cp1 hessian eval 2",
"cp1 hessian evecs",
"cp1 hessian evec 0",
"cp1 hessian evec 1",
"cp1 hessian evec 2",
"ca1 hessian",
"ca1 hessian evals",
"ca1 hessian eval 0",
"ca1 hessian eval 1",
"ca1 hessian eval 2",
"ca1 hessian evecs",
"ca1 hessian evec 0",
"ca1 hessian evec 1",
"ca1 hessian evec 2",
"fiber curving",
"fiber dispersion",
"anisotropies"
};
const int
_tenGageVal[] = {
tenGageUnknown,
tenGageTensor, /* "t", the reconstructed tensor: GT[7] */
tenGageConfidence, /* "c", first of seven tensor values: GT[1] */
tenGageTrace, /* "tr", trace of tensor: GT[1] */
tenGageNorm,
tenGageB, /* "b": GT[1] */
tenGageDet, /* "det", determinant of tensor: GT[1] */
tenGageS, /* "s", square of frobenius norm: GT[1] */
tenGageQ, /* "q", ( S - B )/9: GT[1] */
tenGageFA, /* "fa", fractional anisotropy: GT[1] */
tenGageR, /* "r", 9*A*B - 2*A^3 - 27*C: GT[1] */
tenGageMode, /* "mode", sqrt( 2 )*R/sqrt( Q^3 ): GT[1] */
tenGageTheta, /* "th", arccos( mode/sqrt( 2 ) )/AIR_PI: GT[1] */
tenGageModeWarp, /* */
tenGageOmega, /* */
tenGageEval, /* "eval", all eigenvalues of tensor : GT[3] */
tenGageEval0, /* "eval0", major eigenvalue of tensor : GT[1] */
tenGageEval1, /* "eval1", medium eigenvalue of tensor : GT[1] */
tenGageEval2, /* "eval2", minor eigenvalue of tensor : GT[1] */
tenGageEvec, /* "evec", major eigenvectors of tensor: GT[9] */
tenGageEvec0, /* "evec0", major eigenvectors of tensor: GT[3] */
tenGageEvec1, /* "evec1", medium eigenvectors of tensor: GT[3] */
tenGageEvec2, /* "evec2", minor eigenvectors of tensor: GT[3] */
tenGageDelNormK2,
tenGageDelNormK3,
tenGageDelNormR1,
tenGageDelNormR2,
tenGageDelNormPhi1,
tenGageDelNormPhi2,
tenGageDelNormPhi3,
tenGageTensorGrad, /* "tg", all tensor component gradients: GT[21] */
tenGageTensorGradMag, /* "tgm" */
tenGageTensorGradMagMag, /* "tgmm" */
tenGageTraceGradVec, /* "trgv": gradient ( vector ) of trace: GT[3] */
tenGageTraceGradMag, /* "trgm": gradient magnitude of trace: GT[1] */
tenGageTraceNormal, /* "trn": normal of trace: GT[3] */
tenGageNormGradVec,
tenGageNormGradMag,
tenGageNormNormal,
tenGageBGradVec, /* "bgv", gradient ( vector ) of B: GT[3] */
tenGageBGradMag, /* "bgm", gradient magnitude of B: GT[1] */
tenGageBNormal, /* "bn", normal of B: GT[3] */
tenGageDetGradVec, /* "detgv", gradient ( vector ) of Det: GT[3] */
tenGageDetGradMag, /* "detgm", gradient magnitude of Det: GT[1] */
tenGageDetNormal, /* "detn", normal of Det: GT[3] */
tenGageSGradVec, /* "sgv", gradient ( vector ) of S: GT[3] */
tenGageSGradMag, /* "sgm", gradient magnitude of S: GT[1] */
tenGageSNormal, /* "sn", normal of S: GT[3] */
tenGageQGradVec, /* "qgv", gradient vector of Q: GT[3] */
tenGageQGradMag, /* "qgm", gradient magnitude of Q: GT[1] */
tenGageQNormal, /* "qn", normalized gradient of Q: GT[3] */
tenGageFAGradVec, /* "fagv", gradient vector of FA: GT[3] */
tenGageFAGradMag, /* "fagm", gradient magnitude of FA: GT[1] */
tenGageFANormal, /* "fan", normalized gradient of FA: GT[3] */
tenGageRGradVec, /* "rgv", gradient vector of Q: GT[3] */
tenGageRGradMag, /* "rgm", gradient magnitude of Q: GT[1] */
tenGageRNormal, /* "rn", normalized gradient of Q: GT[3] */
tenGageModeGradVec, /* "mgv", gradient vector of mode: GT[3] */
tenGageModeGradMag, /* "mgm", gradient magnitude of mode: GT[1] */
tenGageModeNormal, /* "mn", normalized gradient of moe: GT[3] */
tenGageThetaGradVec, /* "thgv", gradient vector of theta: GT[3] */
tenGageThetaGradMag, /* "thgm", gradient magnitude of theta: GT[1] */
tenGageThetaNormal, /* "thn", normalized gradient of theta: GT[3] */
tenGageOmegaGradVec, /* */
tenGageOmegaGradMag, /* */
tenGageOmegaNormal, /* */
tenGageInvarKGrads,
tenGageInvarKGradMags,
tenGageInvarRGrads,
tenGageInvarRGradMags,
tenGageRotTans, /* "rts" */
tenGageRotTanMags, /* "rtms" */
tenGageEvalGrads, /* "evgs" */
tenGageCl1,
tenGageCp1,
tenGageCa1,
tenGageClpmin1,
tenGageCl2,
tenGageCp2,
tenGageCa2,
tenGageClpmin2,
tenGageHessian,
tenGageTraceHessian,
tenGageTraceHessianEval,
tenGageTraceHessianEval0,
tenGageTraceHessianEval1,
tenGageTraceHessianEval2,
tenGageTraceHessianEvec,
tenGageTraceHessianEvec0,
tenGageTraceHessianEvec1,
tenGageTraceHessianEvec2,
tenGageTraceHessianFrob,
tenGageBHessian,
tenGageDetHessian,
tenGageSHessian,
tenGageQHessian,
tenGageFAHessian,
tenGageFAHessianEval,
tenGageFAHessianEval0,
tenGageFAHessianEval1,
tenGageFAHessianEval2,
tenGageFAHessianEvec,
tenGageFAHessianEvec0,
tenGageFAHessianEvec1,
tenGageFAHessianEvec2,
tenGageFAHessianFrob,
tenGageFARidgeSurfaceStrength,
tenGageFAValleySurfaceStrength,
tenGageFALaplacian,
tenGageFAHessianEvalMode,
tenGageFARidgeLineAlignment,
tenGageFARidgeSurfaceAlignment,
tenGageFA2ndDD,
tenGageFAGeomTens,
tenGageFAKappa1,
tenGageFAKappa2,
tenGageFATotalCurv,
tenGageFAShapeIndex,
tenGageFAMeanCurv,
tenGageFAGaussCurv,
tenGageFACurvDir1,
tenGageFACurvDir2,
tenGageFAFlowlineCurv,
tenGageRHessian,
tenGageModeHessian,
tenGageModeHessianEval,
tenGageModeHessianEval0,
tenGageModeHessianEval1,
tenGageModeHessianEval2,
tenGageModeHessianEvec,
tenGageModeHessianEvec0,
tenGageModeHessianEvec1,
tenGageModeHessianEvec2,
tenGageModeHessianFrob,
tenGageOmegaHessian,
tenGageOmegaHessianEval,
tenGageOmegaHessianEval0,
tenGageOmegaHessianEval1,
tenGageOmegaHessianEval2,
tenGageOmegaHessianEvec,
tenGageOmegaHessianEvec0,
tenGageOmegaHessianEvec1,
tenGageOmegaHessianEvec2,
tenGageOmegaLaplacian,
tenGageOmega2ndDD,
tenGageOmegaHessianContrTenEvec0,
tenGageOmegaHessianContrTenEvec1,
tenGageOmegaHessianContrTenEvec2,
tenGageTraceGradVecDotEvec0,
tenGageTraceDiffusionAlign,
tenGageTraceDiffusionFraction,
tenGageFAGradVecDotEvec0,
tenGageFADiffusionAlign,
tenGageFADiffusionFraction,
tenGageOmegaGradVecDotEvec0,
tenGageOmegaDiffusionAlign,
tenGageOmegaDiffusionFraction,
tenGageConfGradVecDotEvec0,
tenGageConfDiffusionAlign,
tenGageConfDiffusionFraction,
tenGageCovariance,
tenGageCovarianceRGRT,
tenGageCovarianceKGRT,
tenGageTensorLogEuclidean,
tenGageTensorQuatGeoLoxK,
tenGageTensorQuatGeoLoxR,
tenGageTensorRThetaPhiLinear,
tenGageCl1GradVec,
tenGageCl1GradMag,
tenGageCl1Normal,
tenGageCp1GradVec,
tenGageCp1GradMag,
tenGageCp1Normal,
tenGageCa1GradVec,
tenGageCa1GradMag,
tenGageCa1Normal,
tenGageTensorGradRotE,
tenGageEvalHessian, /* Hessian of the eigenvalues: [27] */
tenGageCl1Hessian, /* Hessian of cl1: [9] */
tenGageCl1HessianEval, /* Hessian eigenvalues of cl1: [3] */
tenGageCl1HessianEval0, /* First Hessian eigenvalue of cl1: [1] */
tenGageCl1HessianEval1, /* Second Hessian eigenvalue of cl1: [1] */
tenGageCl1HessianEval2, /* Third Hessian eigenvalue of cl1: [1] */
tenGageCl1HessianEvec, /* Hessian eigenvectors of cl1: [9] */
tenGageCl1HessianEvec0, /* First Hessian eigenvector of cl1: [3] */
tenGageCl1HessianEvec1, /* Second Hessian eigenvector of cl1: [3] */
tenGageCl1HessianEvec2, /* Third Hessian eigenvector of cl1: [3] */
tenGageCp1Hessian, /* Hessian of cp1: [9] */
tenGageCp1HessianEval, /* Hessian eigenvalues of cp1: [3] */
tenGageCp1HessianEval0, /* First Hessian eigenvalue of cp1: [1] */
tenGageCp1HessianEval1, /* Second Hessian eigenvalue of cp1: [1] */
tenGageCp1HessianEval2, /* Third Hessian eigenvalue of cp1: [1] */
tenGageCp1HessianEvec, /* Hessian eigenvectors of cp1: [9] */
tenGageCp1HessianEvec0, /* First Hessian eigenvector of cp1: [3] */
tenGageCp1HessianEvec1, /* Second Hessian eigenvector of cp1: [3] */
tenGageCp1HessianEvec2, /* Third Hessian eigenvector of cp1: [3] */
tenGageCa1Hessian, /* Hessian of cp1: [9] */
tenGageCa1HessianEval, /* Hessian eigenvalues of cp1: [3] */
tenGageCa1HessianEval0, /* First Hessian eigenvalue of cp1: [1] */
tenGageCa1HessianEval1, /* Second Hessian eigenvalue of cp1: [1] */
tenGageCa1HessianEval2, /* Third Hessian eigenvalue of cp1: [1] */
tenGageCa1HessianEvec, /* Hessian eigenvectors of cp1: [9] */
tenGageCa1HessianEvec0, /* First Hessian eigenvector of cp1: [3] */
tenGageCa1HessianEvec1, /* Second Hessian eigenvector of cp1: [3] */
tenGageCa1HessianEvec2, /* Third Hessian eigenvector of cp1: [3] */
tenGageFiberCurving,
tenGageFiberDispersion,
tenGageAniso,
};
const char *
_tenGageStrEqv[] = {
"t", "ten", "tensor",
"c", "conf", "confidence",
"tr", "trace",
"n", "norm", "r1",
"b",
"det",
"s",
"q",
"fa",
"r",
"mode", "m",
"th", "theta",
"modew", "mw",
"omega", "om",
"eval", "evals",
"eval0",
"eval1",
"eval2",
"evec", "evecs",
"evec0",
"evec1",
"evec2",
"delnk2",
"delnk3", "delnr3",
"delnr1",
"delnr2",
"delnphi1",
"delnphi2",
"delnphi3",
"tg", "tensor grad",
"tgm", "tensor grad mag",
"tgmm", "tensor grad mag mag",
"trgv", "tracegv", "trace grad vec",
"trgm", "tracegm", "trace grad mag",
"trn", "tracen", "trace normal",
"ngv", "r1gv", "normgv", "norm grad vec",
"ngm", "r1gm", "normgm", "norm grad mag",
"nn", "r1n", "normn", "norm normal",
"bgv", "b grad vec",
"bgm", "b grad mag",
"bn", "b normal",
"detgv", "det grad vec",
"detgm", "det grad mag",
"detn", "det normal",
"sgv", "s grad vec",
"sgm", "s grad mag",
"sn", "s normal",
"qgv", "q grad vec",
"qgm", "q grad mag",
"qn", "q normal",
"fagv", "fa grad vec",
"fagm", "fa grad mag",
"fan", "fa normal",
"rgv", "r grad vec",
"rgm", "r grad mag",
"rn", "r normal",
"mgv", "mode grad vec",
"mgm", "mode grad mag",
"mn", "mode normal",
"thgv", "th grad vec", "theta grad vec",
"thgm", "th grad mag", "theta grad mag",
"thn", "th normal", "theta normal",
"omgv", "omega grad vec",
"omgm", "omega grad mag",
"omn", "omega normal",
"ikgs", "invariant K gradients",
"ikgms", "invariant K gradient mags",
"irgs", "invariant R gradients",
"irgms", "invariant R gradient mags",
"rts", "rotation tangents",
"rtms", "rotation tangent mags",
"evgs", "eigenvalue gradients",
"cl1",
"cp1",
"ca1",
"clpmin1",
"cl2",
"cp2",
"ca2",
"clpmin2",
"hess", "hessian",
"trhess", "trace hessian",
"trhesseval", "trace hessian evals",
"trhesseval0", "trace hessian eval 0",
"trhesseval1", "trace hessian eval 1",
"trhesseval2", "trace hessian eval 2",
"trhessevec", "trace hessian evecs",
"trhessevec0", "trace hessian evec 0",
"trhessevec1", "trace hessian evec 1",
"trhessevec2", "trace hessian evec 2",
"trhessfrob", "trace hessian frob",
"bhess", "B hessian",
"dethess", "det hessian",
"shess", "S hessian",
"qhess", "Q hessian",
"fahess", "FA hessian",
"fahesseval", "FA hessian evals",
"fahesseval0", "FA hessian eval 0",
"fahesseval1", "FA hessian eval 1",
"fahesseval2", "FA hessian eval 2",
"fahessevec", "FA hessian evecs",
"fahessevec0", "FA hessian evec 0",
"fahessevec1", "FA hessian evec 1",
"fahessevec2", "FA hessian evec 2",
"fahessfrob", "FA hessian frob",
"farsurfstrn", "FA ridge surface strength",
"favsurfstrn", "FA valley surface strength",
"falapl", "FA laplacian",
"fahessevalmode", "FA hessian eval mode",
"farlinealn", "FA ridge line alignment",
"farsurfaln", "FA ridge surface alignment",
"fa2d", "fa2dd", "FA 2nd DD",
"fagten", "FA geometry tensor",
"fak1", "FA kappa1",
"fak2", "FA kappa2",
"fatc", "FA total curv",
"fasi", "FA shape index",
"famc", "FA mean curv",
"fagc", "FA gauss curv",
"facdir1", "FA curv dir1",
"facdir2", "FA curv dir2",
"fafc", "FA flowline curv",
"rhess", "R hessian",
"mhess", "mode hessian",
"mhesseval", "mode hessian evals",
"mhesseval0", "mode hessian eval 0",
"mhesseval1", "mode hessian eval 1",
"mhesseval2", "mode hessian eval 2",
"mhessevec", "mode hessian evecs",
"mhessevec0", "mode hessian evec 0",
"mhessevec1", "mode hessian evec 1",
"mhessevec2", "mode hessian evec 2",
"mhessfrob", "mode hessian frob",
"omhess", "omega hessian",
"omhesseval", "omega hessian evals",
"omhesseval0", "omega hessian eval 0",
"omhesseval1", "omega hessian eval 1",
"omhesseval2", "omega hessian eval 2",
"omhessevec", "omega hessian evecs",
"omhessevec0", "omega hessian evec 0",
"omhessevec1", "omega hessian evec 1",
"omhessevec2", "omega hessian evec 2",
"omlapl", "omega laplacian",
"om2d", "om2dd", "omega 2nd DD",
"omhesscte0", "omega hessian contracted with ten evec 0",
"omhesscte1", "omega hessian contracted with ten evec 1",
"omhesscte2", "omega hessian contracted with ten evec 2",
"trgvdotevec0", "trace gradvec dot evec0",
"datr", "diffusionAlign( trace )",
"dftr", "diffusionFraction( trace )",
"fagvdotevec0", "FA gradvec dot evec0",
"dafa", "diffusionAlign( FA )",
"dffa", "diffusionFraction( FA )",
"omgvdotevec0", "omega gradvec dot evec0",
"daom", "diffusionAlign( Omega )",
"dfom", "diffusionFraction( Omega )",
"confgvdotevec0", "conf gradvec dot evec0",
"daconf", "diffusionAlign( Conf )",
"dfconf", "diffusionFraction( Conf )",
"cov",
"covr",
"covk",
"logeuclidean", "logeuc", "logeuclid",
"quatgeoloxk", "qglk",
"quatgeoloxr", "qglr",
"rtpl",
"cl1gv",
"cl1gm",
"cl1gn",
"cp1gv",
"cp1gm",
"cp1gn",
"ca1gv",
"ca1gm",
"ca1gn",
"tgrote",
"evalhess", "eval hessian",
"cl1hess", "cl1 hessian",
"cl1hesseval", "cl1 hessian evals",
"cl1hesseval0", "cl1 hessian eval 0",
"cl1hesseval1", "cl1 hessian eval 1",
"cl1hesseval2", "cl1 hessian eval 2",
"cl1hessevec", "cl1 hessian evecs",
"cl1hessevec0", "cl1 hessian evec 0",
"cl1hessevec1", "cl1 hessian evec 1",
"cl1hessevec2", "cl1 hessian evec 2",
"cp1hess", "cp1 hessian",
"cp1hesseval", "cp1 hessian evals",
"cp1hesseval0", "cp1 hessian eval 0",
"cp1hesseval1", "cp1 hessian eval 1",
"cp1hesseval2", "cp1 hessian eval 2",
"cp1hessevec", "cp1 hessian evecs",
"cp1hessevec0", "cp1 hessian evec 0",
"cp1hessevec1", "cp1 hessian evec 1",
"cp1hessevec2", "cp1 hessian evec 2",
"ca1hess", "ca1 hessian",
"ca1hesseval", "ca1 hessian evals",
"ca1hesseval0", "ca1 hessian eval 0",
"ca1hesseval1", "ca1 hessian eval 1",
"ca1hesseval2", "ca1 hessian eval 2",
"ca1hessevec", "ca1 hessian evecs",
"ca1hessevec0", "ca1 hessian evec 0",
"ca1hessevec1", "ca1 hessian evec 1",
"ca1hessevec2", "ca1 hessian evec 2",
"fcurv", "fibcurv", "fiber curving", "fiber curving index",
"fdisp", "fibdisp", "fiber dispersion", "fiber dispersion index",
"an", "aniso", "anisotropies",
""
};
const int
_tenGageValEqv[] = {
tenGageTensor, tenGageTensor, tenGageTensor,
tenGageConfidence, tenGageConfidence, tenGageConfidence,
tenGageTrace, tenGageTrace,
tenGageNorm, tenGageNorm, tenGageNorm,
tenGageB,
tenGageDet,
tenGageS,
tenGageQ,
tenGageFA,
tenGageR,
tenGageMode, tenGageMode,
tenGageTheta, tenGageTheta,
tenGageModeWarp, tenGageModeWarp,
tenGageOmega, tenGageOmega,
tenGageEval, tenGageEval,
tenGageEval0,
tenGageEval1,
tenGageEval2,
tenGageEvec, tenGageEvec,
tenGageEvec0,
tenGageEvec1,
tenGageEvec2,
tenGageDelNormK2,
tenGageDelNormK3, tenGageDelNormK3,
tenGageDelNormR1,
tenGageDelNormR2,
tenGageDelNormPhi1,
tenGageDelNormPhi2,
tenGageDelNormPhi3,
tenGageTensorGrad, tenGageTensorGrad,
tenGageTensorGradMag, tenGageTensorGradMag,
tenGageTensorGradMagMag, tenGageTensorGradMagMag,
tenGageTraceGradVec, tenGageTraceGradVec, tenGageTraceGradVec,
tenGageTraceGradMag, tenGageTraceGradMag, tenGageTraceGradMag,
tenGageTraceNormal, tenGageTraceNormal, tenGageTraceNormal,
tenGageNormGradVec, tenGageNormGradVec, tenGageNormGradVec, tenGageNormGradVec,
tenGageNormGradMag, tenGageNormGradMag, tenGageNormGradMag, tenGageNormGradMag,
tenGageNormNormal, tenGageNormNormal, tenGageNormNormal, tenGageNormNormal,
tenGageBGradVec, tenGageBGradVec,
tenGageBGradMag, tenGageBGradMag,
tenGageBNormal, tenGageBNormal,
tenGageDetGradVec, tenGageDetGradVec,
tenGageDetGradMag, tenGageDetGradMag,
tenGageDetNormal, tenGageDetNormal,
tenGageSGradVec, tenGageSGradVec,
tenGageSGradMag, tenGageSGradMag,
tenGageSNormal, tenGageSNormal,
tenGageQGradVec, tenGageQGradVec,
tenGageQGradMag, tenGageQGradMag,
tenGageQNormal, tenGageQNormal,
tenGageFAGradVec, tenGageFAGradVec,
tenGageFAGradMag, tenGageFAGradMag,
tenGageFANormal, tenGageFANormal,
tenGageRGradVec, tenGageRGradVec,
tenGageRGradMag, tenGageRGradMag,
tenGageRNormal, tenGageRNormal,
tenGageModeGradVec, tenGageModeGradVec,
tenGageModeGradMag, tenGageModeGradMag,
tenGageModeNormal, tenGageModeNormal,
tenGageThetaGradVec, tenGageThetaGradVec, tenGageThetaGradVec,
tenGageThetaGradMag, tenGageThetaGradMag, tenGageThetaGradMag,
tenGageThetaNormal, tenGageThetaNormal, tenGageThetaNormal,
tenGageOmegaGradVec, tenGageOmegaGradVec,
tenGageOmegaGradMag, tenGageOmegaGradMag,
tenGageOmegaNormal, tenGageOmegaNormal,
tenGageInvarKGrads, tenGageInvarKGrads,
tenGageInvarKGradMags, tenGageInvarKGradMags,
tenGageInvarRGrads, tenGageInvarRGrads,
tenGageInvarRGradMags, tenGageInvarRGradMags,
tenGageRotTans, tenGageRotTans,
tenGageRotTanMags, tenGageRotTanMags,
tenGageEvalGrads, tenGageEvalGrads,
tenGageCl1,
tenGageCp1,
tenGageCa1,
tenGageClpmin1,
tenGageCl2,
tenGageCp2,
tenGageCa2,
tenGageClpmin2,
tenGageHessian, tenGageHessian,
tenGageTraceHessian, tenGageTraceHessian,
tenGageTraceHessianEval, tenGageTraceHessianEval,
tenGageTraceHessianEval0, tenGageTraceHessianEval0,
tenGageTraceHessianEval1, tenGageTraceHessianEval1,
tenGageTraceHessianEval2, tenGageTraceHessianEval2,
tenGageTraceHessianEvec, tenGageTraceHessianEvec,
tenGageTraceHessianEvec0, tenGageTraceHessianEvec0,
tenGageTraceHessianEvec1, tenGageTraceHessianEvec1,
tenGageTraceHessianEvec2, tenGageTraceHessianEvec2,
tenGageTraceHessianFrob, tenGageTraceHessianFrob,
tenGageBHessian, tenGageBHessian,
tenGageDetHessian, tenGageDetHessian,
tenGageSHessian, tenGageSHessian,
tenGageQHessian, tenGageQHessian,
tenGageFAHessian, tenGageFAHessian,
tenGageFAHessianEval, tenGageFAHessianEval,
tenGageFAHessianEval0, tenGageFAHessianEval0,
tenGageFAHessianEval1, tenGageFAHessianEval1,
tenGageFAHessianEval2, tenGageFAHessianEval2,
tenGageFAHessianEvec, tenGageFAHessianEvec,
tenGageFAHessianEvec0, tenGageFAHessianEvec0,
tenGageFAHessianEvec1, tenGageFAHessianEvec1,
tenGageFAHessianEvec2, tenGageFAHessianEvec2,
tenGageFAHessianFrob, tenGageFAHessianFrob,
tenGageFARidgeSurfaceStrength, tenGageFARidgeSurfaceStrength,
tenGageFAValleySurfaceStrength, tenGageFAValleySurfaceStrength,
tenGageFALaplacian, tenGageFALaplacian,
tenGageFAHessianEvalMode, tenGageFAHessianEvalMode,
tenGageFARidgeLineAlignment, tenGageFARidgeLineAlignment,
tenGageFARidgeSurfaceAlignment, tenGageFARidgeSurfaceAlignment,
tenGageFA2ndDD, tenGageFA2ndDD, tenGageFA2ndDD,
tenGageFAGeomTens, tenGageFAGeomTens,
tenGageFAKappa1, tenGageFAKappa1,
tenGageFAKappa2, tenGageFAKappa2,
tenGageFATotalCurv, tenGageFATotalCurv,
tenGageFAShapeIndex, tenGageFAShapeIndex,
tenGageFAMeanCurv, tenGageFAMeanCurv,
tenGageFAGaussCurv, tenGageFAGaussCurv,
tenGageFACurvDir1, tenGageFACurvDir1,
tenGageFACurvDir2, tenGageFACurvDir2,
tenGageFAFlowlineCurv, tenGageFAFlowlineCurv,
tenGageRHessian, tenGageRHessian,
tenGageModeHessian, tenGageModeHessian,
tenGageModeHessianEval, tenGageModeHessianEval,
tenGageModeHessianEval0, tenGageModeHessianEval0,
tenGageModeHessianEval1, tenGageModeHessianEval1,
tenGageModeHessianEval2, tenGageModeHessianEval2,
tenGageModeHessianEvec, tenGageModeHessianEvec,
tenGageModeHessianEvec0, tenGageModeHessianEvec0,
tenGageModeHessianEvec1, tenGageModeHessianEvec1,
tenGageModeHessianEvec2, tenGageModeHessianEvec2,
tenGageModeHessianFrob, tenGageModeHessianFrob,
tenGageOmegaHessian, tenGageOmegaHessian,
tenGageOmegaHessianEval, tenGageOmegaHessianEval,
tenGageOmegaHessianEval0, tenGageOmegaHessianEval0,
tenGageOmegaHessianEval1, tenGageOmegaHessianEval1,
tenGageOmegaHessianEval2, tenGageOmegaHessianEval2,
tenGageOmegaHessianEvec, tenGageOmegaHessianEvec,
tenGageOmegaHessianEvec0, tenGageOmegaHessianEvec0,
tenGageOmegaHessianEvec1, tenGageOmegaHessianEvec1,
tenGageOmegaHessianEvec2, tenGageOmegaHessianEvec2,
tenGageOmegaLaplacian, tenGageOmegaLaplacian,
tenGageOmega2ndDD, tenGageOmega2ndDD, tenGageOmega2ndDD,
tenGageOmegaHessianContrTenEvec0, tenGageOmegaHessianContrTenEvec0,
tenGageOmegaHessianContrTenEvec1, tenGageOmegaHessianContrTenEvec1,
tenGageOmegaHessianContrTenEvec2, tenGageOmegaHessianContrTenEvec2,
tenGageTraceGradVecDotEvec0, tenGageTraceGradVecDotEvec0,
tenGageTraceDiffusionAlign, tenGageTraceDiffusionAlign,
tenGageTraceDiffusionFraction, tenGageTraceDiffusionFraction,
tenGageFAGradVecDotEvec0, tenGageFAGradVecDotEvec0,
tenGageFADiffusionAlign, tenGageFADiffusionAlign,
tenGageFADiffusionFraction, tenGageFADiffusionFraction,
tenGageOmegaGradVecDotEvec0, tenGageOmegaGradVecDotEvec0,
tenGageOmegaDiffusionAlign, tenGageOmegaDiffusionAlign,
tenGageOmegaDiffusionFraction, tenGageOmegaDiffusionFraction,
tenGageConfGradVecDotEvec0, tenGageConfGradVecDotEvec0,
tenGageConfDiffusionAlign, tenGageConfDiffusionAlign,
tenGageConfDiffusionFraction, tenGageConfDiffusionFraction,
tenGageCovariance,
tenGageCovarianceRGRT,
tenGageCovarianceKGRT,
tenGageTensorLogEuclidean, tenGageTensorLogEuclidean, tenGageTensorLogEuclidean,
tenGageTensorQuatGeoLoxK, tenGageTensorQuatGeoLoxK,
tenGageTensorQuatGeoLoxR, tenGageTensorQuatGeoLoxR,
tenGageTensorRThetaPhiLinear,
tenGageCl1GradVec,
tenGageCl1GradMag,
tenGageCl1Normal,
tenGageCp1GradVec,
tenGageCp1GradMag,
tenGageCp1Normal,
tenGageCa1GradVec,
tenGageCa1GradMag,
tenGageCa1Normal,
tenGageTensorGradRotE,
tenGageEvalHessian, tenGageEvalHessian,
tenGageCl1Hessian, tenGageCl1Hessian,
tenGageCl1HessianEval, tenGageCl1HessianEval,
tenGageCl1HessianEval0, tenGageCl1HessianEval0,
tenGageCl1HessianEval1, tenGageCl1HessianEval1,
tenGageCl1HessianEval2, tenGageCl1HessianEval2,
tenGageCl1HessianEvec, tenGageCl1HessianEvec,
tenGageCl1HessianEvec0, tenGageCl1HessianEvec0,
tenGageCl1HessianEvec1, tenGageCl1HessianEvec1,
tenGageCl1HessianEvec2, tenGageCl1HessianEvec2,
tenGageCp1Hessian, tenGageCp1Hessian,
tenGageCp1HessianEval, tenGageCp1HessianEval,
tenGageCp1HessianEval0, tenGageCp1HessianEval0,
tenGageCp1HessianEval1, tenGageCp1HessianEval1,
tenGageCp1HessianEval2, tenGageCp1HessianEval2,
tenGageCp1HessianEvec, tenGageCp1HessianEvec,
tenGageCp1HessianEvec0, tenGageCp1HessianEvec0,
tenGageCp1HessianEvec1, tenGageCp1HessianEvec1,
tenGageCp1HessianEvec2, tenGageCp1HessianEvec2,
tenGageCa1Hessian, tenGageCa1Hessian,
tenGageCa1HessianEval, tenGageCa1HessianEval,
tenGageCa1HessianEval0, tenGageCa1HessianEval0,
tenGageCa1HessianEval1, tenGageCa1HessianEval1,
tenGageCa1HessianEval2, tenGageCa1HessianEval2,
tenGageCa1HessianEvec, tenGageCa1HessianEvec,
tenGageCa1HessianEvec0, tenGageCa1HessianEvec0,
tenGageCa1HessianEvec1, tenGageCa1HessianEvec1,
tenGageCa1HessianEvec2, tenGageCa1HessianEvec2,
tenGageFiberCurving, tenGageFiberCurving, tenGageFiberCurving, tenGageFiberCurving,
tenGageFiberDispersion, tenGageFiberDispersion, tenGageFiberDispersion, tenGageFiberDispersion,
tenGageAniso, tenGageAniso, tenGageAniso
};
const airEnum
_tenGage = {
"tenGage",
TEN_GAGE_ITEM_MAX,
_tenGageStr, _tenGageVal,
_tenGageDesc,
_tenGageStrEqv, _tenGageValEqv,
AIR_FALSE
};
1258 const airEnum *const
tenGage = &_tenGage;
/* --------------------------------------------------------------------- */
const char *
_tenFiberTypeStr[] = {
"( unknown tenFiberType )",
"evec0",
"evec1",
"evec2",
"tensorline",
"pureline",
"zhukov"
};
const char *
_tenFiberTypeDesc[] = {
"unknown tenFiber type",
"simply follow principal eigenvector",
"follow medium eigenvector",
"follow minor eigenvector",
"Weinstein-Kindlmann tensorlines",
"based on tensor multiplication only",
"Zhukov\'s oriented tensors"
};
const char *
_tenFiberTypeStrEqv[] = {
"ev0", "evec0",
"ev1", "evec1",
"ev2", "evec2",
"tline", "tensorline",
"pline", "pureline",
"z", "zhukov",
""
};
const int
_tenFiberTypeValEqv[] = {
tenFiberTypeEvec0, tenFiberTypeEvec0,
tenFiberTypeEvec1, tenFiberTypeEvec1,
tenFiberTypeEvec2, tenFiberTypeEvec2,
tenFiberTypeTensorLine, tenFiberTypeTensorLine,
tenFiberTypePureLine, tenFiberTypePureLine,
tenFiberTypeZhukov, tenFiberTypeZhukov
};
const airEnum
_tenFiberType = {
"tenFiberType",
TEN_FIBER_TYPE_MAX,
_tenFiberTypeStr, NULL,
_tenFiberTypeDesc,
_tenFiberTypeStrEqv, _tenFiberTypeValEqv,
AIR_FALSE
};
1315 const airEnum *const
tenFiberType = &_tenFiberType;
/* --------------------------------------------------------------------- */
const char *
_tenDwiFiberTypeStr[] = {
"( unknown tenDwiFiberType )",
"1evec0",
"2evec0",
"12BlendEvec0"
};
const char *
_tenDwiFiberTypeDesc[] = {
"unknown tenDwiFiber type",
"single tensor evec0-based",
"two-tensor evec0-based",
"parameterized blend between 1- and 2-tensor fits"
};
const char *
_tenDwiFiberTypeStrEqv[] = {
"1evec0", "1e0",
"2evec0", "2e0",
"12BlendEvec0", "12be0",
""
};
const int
_tenDwiFiberTypeValEqv[] = {
tenDwiFiberType1Evec0, tenDwiFiberType1Evec0,
tenDwiFiberType2Evec0, tenDwiFiberType2Evec0,
tenDwiFiberType12BlendEvec0, tenDwiFiberType12BlendEvec0
};
const airEnum
_tenDwiFiberType = {
"tenDwiFiberType",
TEN_DWI_FIBER_TYPE_MAX,
_tenDwiFiberTypeStr, NULL,
_tenDwiFiberTypeDesc,
_tenDwiFiberTypeStrEqv, _tenDwiFiberTypeValEqv,
AIR_FALSE
};
1360 const airEnum *const
tenDwiFiberType = &_tenDwiFiberType;
/* ----------------------------------------------------------------------- */
const char *
_tenFiberStopStr[] = {
"( unknown tenFiberStop )",
"aniso",
"length",
"steps",
"confidence",
"radius",
"bounds",
"fraction",
"stub",
"minlen",
"minsteps",
};
const char *
_tenFiberStopStrEqv[] = {
"aniso",
"length", "len",
"steps",
"confidence", "conf", "c",
"radius",
"bounds",
"fraction", "frac", "f",
"stub",
"minlen", "minlength",
"minsteps", "minnumsteps",
""
};
const int
_tenFiberStopValEqv[] = {
tenFiberStopAniso,
tenFiberStopLength, tenFiberStopLength,
tenFiberStopNumSteps,
tenFiberStopConfidence, tenFiberStopConfidence, tenFiberStopConfidence,
tenFiberStopRadius,
tenFiberStopBounds,
tenFiberStopFraction, tenFiberStopFraction, tenFiberStopFraction,
tenFiberStopStub,
tenFiberStopMinLength, tenFiberStopMinLength,
tenFiberStopMinNumSteps, tenFiberStopMinNumSteps,
};
const char *
_tenFiberStopDesc[] = {
"unknown tenFiber stop",
"anisotropy went below threshold",
"fiber length exceeded normalcy bounds",
"number of steps along fiber too many",
"tensor \"confidence\" value too low",
"radius of curvature of path got too small",
"fiber went outside bounding box",
"fractional constituency of tracked tensor got too small",
"neither fiber half got anywhere; fiber has single vert",
"whole fiber has insufficient length",
"whole fiber has too few numbers of steps"
};
const airEnum
_tenFiberStop = {
"fiber stopping criteria",
TEN_FIBER_STOP_MAX,
_tenFiberStopStr, NULL,
_tenFiberStopDesc,
_tenFiberStopStrEqv, _tenFiberStopValEqv,
AIR_FALSE
};
1433 const airEnum *const
tenFiberStop = &_tenFiberStop;
/* ----------------------------------------------------------------------- */
const char *
_tenFiberIntgStr[] = {
"( unknown tenFiberIntg )",
"euler",
"midpoint",
"rk4"
};
const char *
_tenFiberIntgStrEqv[] = {
"euler",
"midpoint", "rk2",
"rk4",
""
};
const int
_tenFiberIntgValEqv[] = {
tenFiberIntgEuler,
tenFiberIntgMidpoint, tenFiberIntgMidpoint,
tenFiberIntgRK4
};
const char *
_tenFiberIntgDesc[] = {
"unknown tenFiber intg",
"plain Euler",
"midpoint method, 2nd order Runge-Kutta",
"4rth order Runge-Kutta"
};
const airEnum
_tenFiberIntg = {
"fiber integration method",
TEN_FIBER_INTG_MAX,
_tenFiberIntgStr, NULL,
_tenFiberIntgDesc,
_tenFiberIntgStrEqv, _tenFiberIntgValEqv,
AIR_FALSE
};
1478 const airEnum *const
tenFiberIntg = &_tenFiberIntg;
/* ----------------------------------------------------------------------- */
const char *
_tenGlyphTypeStr[] = {
"( unknown tenGlyphType )",
"box",
"sphere",
"cylinder",
"superquad",
"betterquad",
"polarplot"
};
#define BOX tenGlyphTypeBox
#define SPH tenGlyphTypeSphere
#define CYL tenGlyphTypeCylinder
#define SQD tenGlyphTypeSuperquad
const char *
_tenGlyphTypeStrEqv[] = {
"b", "box",
"s", "sph", "sphere",
"c", "cyl", "cylind", "cylinder",
"q", "superq", "sqd", "superquad", "superquadric",
"bqd", "betterquad",
"pplot", "polarplot",
""
};
const int
_tenGlyphTypeValEqv[] = {
BOX, BOX,
SPH, SPH, SPH,
CYL, CYL, CYL, CYL,
SQD, SQD, SQD, SQD, SQD,
tenGlyphTypeBetterquad, tenGlyphTypeBetterquad,
tenGlyphTypePolarPlot, tenGlyphTypePolarPlot
};
const char *
_tenGlyphTypeDesc[] = {
"unknown tenGlyph type",
"box/cube ( rectangular prisms )",
"sphere ( ellipsoids )",
"cylinders aligned along major eigenvector",
"superquadric ( superellipsoids )",
"better superquadric",
"polar plot",
};
const airEnum
_tenGlyphType = {
"tenGlyphType",
TEN_GLYPH_TYPE_MAX,
_tenGlyphTypeStr, NULL,
_tenGlyphTypeDesc,
_tenGlyphTypeStrEqv, _tenGlyphTypeValEqv,
AIR_FALSE
};
1540 const airEnum *const
tenGlyphType = &_tenGlyphType;
/* ---------------------------------------------- */
const char *
_tenEstimate1MethodStr[] = {
"( unknown tenEstimate1Method )",
"LLS",
"WLS",
"NLS",
"MLE"
};
const char *
_tenEstimate1MethodDesc[] = {
"unknown tenEstimate1Method",
"linear least-squares fit of log( DWI )",
"weighted least-squares fit of log( DWI )",
"non-linear least-squares fit of DWI",
"maximum likelihood estimate from DWI"
};
const airEnum
_tenEstimate1Method = {
"single-tensor-estimation",
TEN_ESTIMATE_1_METHOD_MAX,
_tenEstimate1MethodStr, NULL,
_tenEstimate1MethodDesc,
NULL, NULL,
AIR_FALSE
};
1572 const airEnum *const
tenEstimate1Method= &_tenEstimate1Method;
/* ---------------------------------------------- */
const char *
_tenEstimate2MethodStr[] = {
"( unknown tenEstimate2Method )",
"QSegLLS",
"Peled"
};
const char *
_tenEstimate2MethodDesc[] = {
"unknown tenEstimate2Method",
"Q-ball segmentation",
"Peled"
};
const airEnum
_tenEstimate2Method = {
"two-tensor-estimation",
TEN_ESTIMATE_2_METHOD_MAX,
_tenEstimate2MethodStr, NULL,
_tenEstimate2MethodDesc,
NULL, NULL,
AIR_FALSE
};
1600 const airEnum *const
tenEstimate2Method= &_tenEstimate2Method;
/* ---------------------------------------------- */
const char *
_tenTripleTypeStr[] = {
"( unknown tenTriple )",
"eigenvalue",
"moment",
"XYZ",
"RThetaZ",
"RThetaPhi",
"J",
"K",
"R",
"wheelParms"
};
const char *
_tenTripleTypeDesc[] = {
"unknown tenTriple",
"eigenvalues sorted in descending order",
"central moments ( mu1, mu2, mu3 )",
"rotation of evals, like Bahn 1999 JMR:141( 68-77 )",
"cylindrical coords of rotated evals",
"spherical coords of rotated evals",
"principal invariants ( J1, J2, J3 )",
"cylindrical invariants ( K1, K2, K3 )",
"spherical invariants ( R1, R2, R3 )",
"eigenvalue wheel ( center, radius, angle )"
};
const char *
_tenTripleTypeStrEqv[] = {
"eigenvalue", "eval", "ev",
"moment", "mu",
"XYZ",
"RThetaZ", "RThZ", "rtz",
"RThetaPhi", "RThPh", "rtp",
"J",
"K",
"R",
"wheelParm", "wheelParms", "WP",
""
};
const int
_tenTripleTypeValEqv[] = {
tenTripleTypeEigenvalue, tenTripleTypeEigenvalue, tenTripleTypeEigenvalue,
tenTripleTypeMoment, tenTripleTypeMoment,
tenTripleTypeXYZ,
tenTripleTypeRThetaZ, tenTripleTypeRThetaZ, tenTripleTypeRThetaZ,
tenTripleTypeRThetaPhi, tenTripleTypeRThetaPhi, tenTripleTypeRThetaPhi,
tenTripleTypeJ,
tenTripleTypeK,
tenTripleTypeR,
tenTripleTypeWheelParm, tenTripleTypeWheelParm, tenTripleTypeWheelParm
};
const airEnum
_tenTripleType = {
"tenTripleType",
TEN_TRIPLE_TYPE_MAX,
_tenTripleTypeStr, NULL,
_tenTripleTypeDesc,
_tenTripleTypeStrEqv, _tenTripleTypeValEqv,
AIR_FALSE
};
1669 const airEnum *const
tenTripleType = &_tenTripleType;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
int
28 _tenEpiRegSave( const char *fname, Nrrd *nsingle, Nrrd **nmulti,
int len, const char *desc ) {
static const char me[]="_tenEpiRegSave";
Nrrd *nout;
airArray *mop;
mop = airMopNew( );
if ( nsingle ) {
nout = nsingle;
} else {
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdJoin( nout, ( const Nrrd*const* )nmulti, len, 0, AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: couldn't join %s for output", me, desc );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( fname, nout, NULL ) ) {
biffMovef( TEN, NRRD, "%s: trouble saving %s to \"%s\"", me, desc, fname );
airMopError( mop ); return 1;
}
fprintf( stderr, "%s: saved %s to \"%s\"\n", me, desc, fname );
airMopOkay( mop );
return 0;
}
int
55 _tenEpiRegCheck( Nrrd **nout, Nrrd **ndwi, unsigned int dwiLen, Nrrd *ngrad,
int reference,
double bwX, double bwY, double DWthr,
const NrrdKernel *kern, double *kparm ) {
static const char me[]="_tenEpiRegCheck";
unsigned int ni;
AIR_UNUSED( DWthr );
if ( !( nout && ndwi && ngrad && kern && kparm ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenGradientCheck( ngrad, nrrdTypeDefault, 6 /* <-- HEY: not sure */ ) ) {
biffAddf( TEN, "%s: problem with given gradient list", me );
return 1;
}
if ( dwiLen != ngrad->axis[1].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: got %u DWIs, but %s gradient directions", me,
dwiLen, airSprintSize_t( stmp, ngrad->axis[1].size ) );
return 1;
}
for ( ni=0; ni<dwiLen; ni++ ) {
if ( !nout[ni] ) {
biffAddf( TEN, "%s: nout[%d] is NULL", me, ni );
return 1;
}
if ( nrrdCheck( ndwi[ni] ) ) {
biffMovef( TEN, NRRD,
"%s: basic nrrd validity failed on ndwi[%d]", me, ni );
return 1;
}
if ( !nrrdSameSize( ndwi[0], ndwi[ni], AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: ndwi[%d] is different from ndwi[0]", me, ni );
return 1;
}
}
if ( !( 3 == ndwi[0]->dim ) ) {
biffAddf( TEN, "%s: didn't get a set of 3-D arrays ( got %d-D )", me,
ndwi[0]->dim );
return 1;
}
if ( !( AIR_IN_CL( -1, reference, ( int )dwiLen-1 ) ) ) {
biffAddf( TEN, "%s: reference index %d not in valid range [-1, %d]",
me, reference, dwiLen-1 );
return 1;
}
if ( !( AIR_EXISTS( bwX ) && AIR_EXISTS( bwY ) ) ) {
biffAddf( TEN, "%s: bwX, bwY don't both exist", me );
return 1;
}
if ( !( bwX >= 0 && bwY >= 0 ) ) {
biffAddf( TEN, "%s: bwX ( %g ) and bwY ( %g ) are not both non-negative",
me, bwX, bwY );
return 1;
}
return 0;
}
/*
** this assumes that all nblur[i] are valid nrrds, and does nothing
** to manage them
*/
int
119 _tenEpiRegBlur( Nrrd **nblur, Nrrd **ndwi, unsigned int dwiLen,
double bwX, double bwY, int verb ) {
static const char me[]="_tenEpiRegBlur";
NrrdResampleInfo *rinfo;
airArray *mop;
size_t ni, sx, sy, sz;
double savemin[2], savemax[2];
if ( !( bwX || bwY ) ) {
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
for ( ni=0; ni<dwiLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2u ", ( unsigned int )ni ); fflush( stderr );
}
if ( nrrdCopy( nblur[ni], ndwi[ni] ) ) {
biffMovef( TEN, NRRD, "%s: trouble copying ndwi[%u]",
me, ( unsigned int )ni );
return 1;
}
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
return 0;
}
/* else we need to blur */
sx = ndwi[0]->axis[0].size;
sy = ndwi[0]->axis[1].size;
sz = ndwi[0]->axis[2].size;
mop = airMopNew( );
rinfo = nrrdResampleInfoNew( );
airMopAdd( mop, rinfo, ( airMopper )nrrdResampleInfoNix, airMopAlways );
if ( bwX ) {
rinfo->kernel[0] = nrrdKernelGaussian;
rinfo->parm[0][0] = bwX;
rinfo->parm[0][1] = 3.0; /* how many stnd devs do we cut-off at */
} else {
rinfo->kernel[0] = NULL;
}
if ( bwY ) {
rinfo->kernel[1] = nrrdKernelGaussian;
rinfo->parm[1][0] = bwY;
rinfo->parm[1][1] = 3.0; /* how many stnd devs do we cut-off at */
} else {
rinfo->kernel[1] = NULL;
}
rinfo->kernel[2] = NULL;
ELL_3V_SET( rinfo->samples, sx, sy, sz );
ELL_3V_SET( rinfo->min, 0, 0, 0 );
ELL_3V_SET( rinfo->max, sx-1, sy-1, sz-1 );
rinfo->boundary = nrrdBoundaryBleed;
rinfo->type = nrrdTypeDefault;
rinfo->renormalize = AIR_TRUE;
rinfo->clamp = AIR_TRUE;
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
for ( ni=0; ni<dwiLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2u ", ( unsigned int )ni ); fflush( stderr );
}
savemin[0] = ndwi[ni]->axis[0].min; savemax[0] = ndwi[ni]->axis[0].max;
savemin[1] = ndwi[ni]->axis[1].min; savemax[1] = ndwi[ni]->axis[1].max;
ndwi[ni]->axis[0].min = 0; ndwi[ni]->axis[0].max = sx-1;
ndwi[ni]->axis[1].min = 0; ndwi[ni]->axis[1].max = sy-1;
if ( nrrdSpatialResample( nblur[ni], ndwi[ni], rinfo ) ) {
biffMovef( TEN, NRRD, "%s: trouble blurring ndwi[%u]",
me, ( unsigned int )ni );
airMopError( mop ); return 1;
}
ndwi[ni]->axis[0].min = savemin[0]; ndwi[ni]->axis[0].max = savemax[0];
ndwi[ni]->axis[1].min = savemin[1]; ndwi[ni]->axis[1].max = savemax[1];
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
airMopOkay( mop );
return 0;
}
int
202 _tenEpiRegThresholdFind( double *DWthrP, Nrrd **nin, int ninLen,
int save, double expo ) {
static const char me[]="_tenEpiRegThresholdFind";
Nrrd *nhist, *ntmp;
airArray *mop;
int ni, bins, E;
double min=0, max=0;
NrrdRange *range;
mop = airMopNew( );
airMopAdd( mop, nhist=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmp=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
for ( ni=0; ni<ninLen; ni++ ) {
range = nrrdRangeNewSet( nin[ni], nrrdBlind8BitRangeFalse );
if ( !ni ) {
min = range->min;
max = range->max;
} else {
min = AIR_MIN( min, range->min );
max = AIR_MAX( max, range->max );
}
range = nrrdRangeNix( range );
}
bins = AIR_MIN( 1024, ( int )( max - min + 1 ) );
ntmp->axis[0].min = min;
ntmp->axis[0].max = max;
for ( ni=0; ni<ninLen; ni++ ) {
if ( nrrdHisto( ntmp, nin[ni], NULL, NULL, bins, nrrdTypeFloat ) ) {
biffMovef( TEN, NRRD,
"%s: problem forming histogram of DWI %d", me, ni );
airMopError( mop ); return 1;
}
if ( !ni ) {
E = nrrdCopy( nhist, ntmp );
} else {
E = nrrdArithBinaryOp( nhist, nrrdBinaryOpAdd, nhist, ntmp );
}
if ( E ) {
biffMovef( TEN, NRRD,
"%s: problem updating histogram sum on DWI %d",
me, ni );
airMopError( mop ); return 1;
}
}
if ( save ) {
nrrdSave( "regtmp-dwihist.nrrd", nhist, NULL );
}
/*
if ( _tenFindValley( DWthrP, nhist, 0.65, save ) ) {
biffAddf( TEN, "%s: problem finding DWI histogram valley", me );
airMopError( mop ); return 1;
}
*/
if ( nrrdHistoThresholdOtsu( DWthrP, nhist, expo ) ) {
biffMovef( TEN, NRRD, "%s: problem finding DWI threshold", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
266 _tenEpiRegThreshold( Nrrd **nthresh, Nrrd **nblur, unsigned int ninLen,
double DWthr, int verb, int progress, double expo ) {
static const char me[]="_tenEpiRegThreshold";
airArray *mop;
size_t I, sx, sy, sz, ni;
float val;
unsigned char *thr;
if ( !( AIR_EXISTS( DWthr ) ) ) {
if ( _tenEpiRegThresholdFind( &DWthr, nblur, ninLen, progress, expo ) ) {
biffAddf( TEN, "%s: trouble with automatic threshold determination", me );
return 1;
}
fprintf( stderr, "%s: using %g for DWI threshold\n", me, DWthr );
}
mop = airMopNew( );
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
sx = nblur[0]->axis[0].size;
sy = nblur[0]->axis[1].size;
sz = nblur[0]->axis[2].size;
for ( ni=0; ni<ninLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2u ", ( unsigned int )ni ); fflush( stderr );
}
if ( nrrdMaybeAlloc_va( nthresh[ni], nrrdTypeUChar, 3,
sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating threshold %u",
me, ( unsigned int )ni );
airMopError( mop ); return 1;
}
thr = ( unsigned char * )( nthresh[ni]->data );
for ( I=0; I<sx*sy*sz; I++ ) {
val = nrrdFLookup[nblur[ni]->type]( nblur[ni]->data, I );
val -= AIR_CAST( float, DWthr );
thr[I] = ( val >= 0 ? 1 : 0 );
}
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
airMopOkay( mop );
return 0;
}
/*
** _tenEpiRegBB: find the biggest bright CC
*/
int
318 _tenEpiRegBB( Nrrd *nval, Nrrd *nsize ) {
unsigned char *val;
int *size, big;
unsigned int ci;
val = ( unsigned char * )( nval->data );
size = ( int * )( nsize->data );
big = 0;
for ( ci=0; ci<nsize->axis[0].size; ci++ ) {
big = val[ci] ? AIR_MAX( big, size[ci] ) : big;
}
return big;
}
int
333 _tenEpiRegCC( Nrrd **nthr, int ninLen, int conny, int verb ) {
static const char me[]="_tenEpiRegCC";
Nrrd *nslc, *ncc, *nval, *nsize;
airArray *mop;
int ni, z, sz, big;
mop = airMopNew( );
airMopAdd( mop, nslc=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nval=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ncc=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nsize=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
sz = nthr[0]->axis[2].size;
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
for ( ni=0; ni<ninLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2d ", ni ); fflush( stderr );
}
/* for each volume, we find the biggest bright 3-D CC, and merge
down ( to dark ) all smaller bright pieces. Then, within each
slice, we do 2-D CCs, find the biggest bright CC ( size == big ),
and merge up ( to bright ) all small dark pieces, where
( currently ) small is big/2 */
big = -1;
if ( nrrdCCFind( ncc, &nval, nthr[ni], nrrdTypeDefault, conny )
|| nrrdCCSize( nsize, ncc )
|| !( big = _tenEpiRegBB( nval, nsize ) )
|| nrrdCCMerge( ncc, ncc, nval, -1, big-1, 0, conny )
|| nrrdCCRevalue( nthr[ni], ncc, nval ) ) {
if ( big ) {
biffMovef( TEN, NRRD,
"%s: trouble with 3-D processing nthr[%d]", me, ni );
return 1;
} else {
biffAddf( TEN, "%s: got size 0 for biggest bright CC of nthr[%d]",
me, ni );
return 1;
}
}
for ( z=0; z<sz; z++ ) {
big = -1;
if ( nrrdSlice( nslc, nthr[ni], 2, z )
|| nrrdCCFind( ncc, &nval, nslc, nrrdTypeDefault, conny )
|| nrrdCCSize( nsize, ncc )
|| !( big = _tenEpiRegBB( nval, nsize ) )
|| nrrdCCMerge( ncc, ncc, nval, 1, big/2, 0, conny )
|| nrrdCCRevalue( nslc, ncc, nval )
|| nrrdSplice( nthr[ni], nthr[ni], nslc, 2, z ) ) {
if ( big ) {
biffMovef( TEN, NRRD, "%s: trouble processing slice %d of nthr[%d]",
me, z, ni );
return 1;
} else {
/* biggest bright CC on this slice had size 0
<==> there was no bright CC on this slice, move on */
}
}
}
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
airMopOkay( mop );
return 0;
}
#define MEAN_X 0
#define MEAN_Y 1
#define M_02 2
#define M_11 3
#define M_20 4
/*
** _tenEpiRegMoments( )
**
** the moments are stored in ( of course ) a nrrd, one scanline per slice,
** with each scanline containing:
**
** 0 1 2 3 4
** mean( x ) mean( y ) M_02 M_11 M_20
*/
int
417 _tenEpiRegMoments( Nrrd **nmom, Nrrd **nthresh, unsigned int ninLen,
int verb ) {
static const char me[]="_tenEpiRegMoments";
size_t sx, sy, sz, xi, yi, zi, ni;
double N, mx, my, cx, cy, x, y, M02, M11, M20, *mom;
float val;
unsigned char *thr;
sx = nthresh[0]->axis[0].size;
sy = nthresh[0]->axis[1].size;
sz = nthresh[0]->axis[2].size;
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
for ( ni=0; ni<ninLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2u ", ( unsigned int )ni ); fflush( stderr );
}
if ( nrrdMaybeAlloc_va( nmom[ni], nrrdTypeDouble, 2,
AIR_CAST( size_t, 5 ), sz ) ) {
biffMovef( TEN, NRRD,
"%s: couldn't allocate nmom[%u]", me, ( unsigned int )ni );
return 1;
}
nrrdAxisInfoSet_va( nmom[ni], nrrdAxisInfoLabel, "mx, my, h, s, t", "z" );
thr = ( unsigned char * )( nthresh[ni]->data );
mom = ( double * )( nmom[ni]->data );
for ( zi=0; zi<sz; zi++ ) {
/* ------ find mx, my */
N = 0;
mx = my = 0.0;
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
val = thr[xi + sx*yi];
N += val;
mx += xi*val;
my += yi*val;
}
}
if ( N == sx*sy ) {
biffAddf( TEN, "%s: saw only non-zero pixels in nthresh[%u]; "
"DWI hreshold too low?", me, ( unsigned int )ni );
return 1;
}
if ( N ) {
/* there were non-zero pixels */
mx /= N;
my /= N;
cx = sx/2.0;
cy = sy/2.0;
/* ------ find M02, M11, M20 */
M02 = M11 = M20 = 0.0;
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
val = thr[xi + sx*yi];
x = xi - cx;
y = yi - cy;
M02 += y*y*val;
M11 += x*y*val;
M20 += x*x*val;
}
}
M02 /= N;
M11 /= N;
M20 /= N;
/* ------ set output */
mom[MEAN_X] = mx;
mom[MEAN_Y] = my;
mom[M_02] = M02;
mom[M_11] = M11;
mom[M_20] = M20;
} else {
/* there were no non-zero pixels */
mom[MEAN_X] = 0;
mom[MEAN_Y] = 0;
mom[M_02] = 0;
mom[M_11] = 0;
mom[M_20] = 0;
}
thr += sx*sy;
mom += 5;
}
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
return 0;
}
/*
** _tenEpiRegPairXforms
**
** uses moment information to compute all pair-wise transforms, which are
** stored in the 3 x ninLen x ninLen x sizeZ output. If xfr = npxfr->data,
** xfr[0 + 3*( zi + sz*( A + ninLen*B ) )] is shear,
** xfr[1 + " ] is scale, and
** xfr[2 + " ] is translate in the transform
** that maps slice zi from volume A to volume B.
*/
int
518 _tenEpiRegPairXforms( Nrrd *npxfr, Nrrd **nmom, int ninLen ) {
static const char me[]="_tenEpiRegPairXforms";
double *xfr, *A, *B, hh, ss, tt;
int ai, bi, zi, sz;
sz = nmom[0]->axis[1].size;
if ( nrrdMaybeAlloc_va( npxfr, nrrdTypeDouble, 4,
AIR_CAST( size_t, 5 ),
AIR_CAST( size_t, sz ),
AIR_CAST( size_t, ninLen ),
AIR_CAST( size_t, ninLen ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate transform nrrd", me );
return 1;
}
nrrdAxisInfoSet_va( npxfr, nrrdAxisInfoLabel,
"mx, my, h, s, t", "zi", "orig", "target" );
xfr = ( double * )( npxfr->data );
for ( bi=0; bi<ninLen; bi++ ) {
for ( ai=0; ai<ninLen; ai++ ) {
for ( zi=0; zi<sz; zi++ ) {
A = ( double* )( nmom[ai]->data ) + 5*zi;
B = ( double* )( nmom[bi]->data ) + 5*zi;
ss = sqrt( ( A[M_20]*B[M_02] - B[M_11]*B[M_11] ) /
( A[M_20]*A[M_02] - A[M_11]*A[M_11] ) );
hh = ( B[M_11] - ss*A[M_11] )/A[M_20];
tt = B[MEAN_Y] - A[MEAN_Y];
ELL_5V_SET( xfr + 5*( zi + sz*( ai + ninLen*bi ) ),
A[MEAN_X], A[MEAN_Y], hh, ss, tt );
}
}
}
return 0;
}
#define SHEAR 2
#define SCALE 3
#define TRAN 4
int
557 _tenEpiRegEstimHST( Nrrd *nhst, Nrrd *npxfr, int ninLen, Nrrd *ngrad ) {
static const char me[]="_tenEpiRegEstimHST";
double *hst, *grad, *mat, *vec, *ans, *pxfr, *gA, *gB;
int z, sz, A, B, npairs, ri;
Nrrd **nmat, *nvec, **ninv, *nans;
airArray *mop;
int order;
order = 1;
sz = npxfr->axis[1].size;
npairs = ninLen*( ninLen-1 );
mop = airMopNew( );
nmat = ( Nrrd** )calloc( sz, sizeof( Nrrd* ) );
ninv = ( Nrrd** )calloc( sz, sizeof( Nrrd* ) );
airMopAdd( mop, nmat, airFree, airMopAlways );
airMopAdd( mop, ninv, airFree, airMopAlways );
for ( z=0; z<sz; z++ ) {
airMopAdd( mop, nmat[z]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nmat[z], nrrdTypeDouble, 2,
AIR_CAST( size_t, ( 1 == order ? 3 : 9 ) ),
AIR_CAST( size_t, npairs ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate fitting matrices", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, ninv[z]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
}
airMopAdd( mop, nvec=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nans=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nhst, nrrdTypeDouble, 2,
AIR_CAST( size_t, ( 1 == order ? 9 : 27 ) ),
AIR_CAST( size_t, sz ) )
|| nrrdMaybeAlloc_va( nvec, nrrdTypeDouble, 2,
AIR_CAST( size_t, 1 ),
AIR_CAST( size_t, npairs ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate HST nrrd", me );
airMopError( mop ); return 1;
}
nrrdAxisInfoSet_va( nhst, nrrdAxisInfoLabel,
( 1 == order
? "Hx, Hy, Hz, Sx, Sy, Sz, Tx, Ty, Tz"
: "HST parms" ), "z" );
/* ------ per-slice: compute model fitting matrix and its pseudo-inverse */
grad = ( double * )( ngrad->data );
for ( z=0; z<sz; z++ ) {
hst = ( double * )( nhst->data ) + ( 1 == order ? 9 : 27 )*z;
mat = ( double * )( nmat[z]->data );
ri = 0;
for ( A=0; A<ninLen; A++ ) {
for ( B=0; B<ninLen; B++ ) {
if ( A == B ) continue;
pxfr = ( double * )( npxfr->data ) + 0 + 5*( z + sz*( A + ninLen*B ) );
gA = grad + 0 + 3*A;
gB = grad + 0 + 3*B;
if ( 1 == order ) {
ELL_3V_SET( mat + 3*ri,
gB[0] - pxfr[SCALE]*gA[0],
gB[1] - pxfr[SCALE]*gA[1],
gB[2] - pxfr[SCALE]*gA[2] );
} else {
ELL_9V_SET( mat + 9*ri,
gB[0] - pxfr[SCALE]*gA[0],
gB[1] - pxfr[SCALE]*gA[1],
gB[2] - pxfr[SCALE]*gA[2],
gB[0]*gB[0]*gB[0] - pxfr[SCALE]*gA[0]*gA[0]*gA[0],
gB[1]*gB[1]*gB[1] - pxfr[SCALE]*gA[1]*gA[1]*gA[1],
gB[2]*gB[2]*gB[2] - pxfr[SCALE]*gA[2]*gA[2]*gA[2],
gB[0]*gB[1]*gB[2] - pxfr[SCALE]*gA[0]*gA[1]*gA[2],
1, 1 );
/*
gB[0]*gB[1] - pxfr[SCALE]*gA[0]*gA[1],
gB[0]*gB[2] - pxfr[SCALE]*gA[0]*gA[2],
gB[1]*gB[2] - pxfr[SCALE]*gA[1]*gA[2] );
*/
}
ri += 1;
}
}
if ( nrrdHasNonExist( nmat[z] ) ) {
/* as happens if there were zero slices in the segmentation output */
if ( 1 == order ) {
ELL_3V_SET( hst + 0*3, 0, 0, 0 );
ELL_3V_SET( hst + 1*3, 0, 0, 0 );
ELL_3V_SET( hst + 2*3, 0, 0, 0 );
} else {
ELL_9V_SET( hst + 0*9, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
ELL_9V_SET( hst + 1*9, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
ELL_9V_SET( hst + 2*9, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
}
} else {
if ( ell_Nm_pseudo_inv( ninv[z], nmat[z] ) ) {
biffMovef( TEN, ELL, "%s: trouble estimating model ( slice %d )", me, z );
airMopError( mop ); return 1;
}
}
}
vec = ( double * )( nvec->data );
/* ------ find Sx, Sy, Sz per slice */
for ( z=0; z<sz; z++ ) {
if ( nrrdHasNonExist( nmat[z] ) ) {
/* we've already zero-ed out this row in the HST nrrd */
continue;
}
hst = ( double * )( nhst->data ) + ( 1 == order ? 9 : 27 )*z;
ri = 0;
for ( A=0; A<ninLen; A++ ) {
for ( B=0; B<ninLen; B++ ) {
if ( A == B ) continue;
pxfr = ( double * )( npxfr->data ) + 0 + 5*( z + sz*( A + ninLen*B ) );
vec[ri] = pxfr[SCALE] - 1;
ri += 1;
}
}
if ( ell_Nm_mul( nans, ninv[z], nvec ) ) {
biffMovef( TEN, ELL,
"%s: trouble estimating model ( slice %d ): Sx, Sy, Sz",
me, z );
airMopError( mop ); return 1;
}
ans = ( double * )( nans->data );
if ( 1 == order ) {
ELL_3V_COPY( hst + 1*3, ans );
} else {
ELL_9V_COPY( hst + 1*9, ans );
}
}
/* ------ find Hx, Hy, Hz per slice */
for ( z=0; z<sz; z++ ) {
if ( nrrdHasNonExist( nmat[z] ) ) {
/* we've already zero-ed out this row in the HST nrrd */
continue;
}
hst = ( double * )( nhst->data ) + ( 1 == order ? 9 : 27 )*z;
ri = 0;
for ( A=0; A<ninLen; A++ ) {
for ( B=0; B<ninLen; B++ ) {
if ( A == B ) continue;
pxfr = ( double * )( npxfr->data ) + 0 + 5*( z + sz*( A + ninLen*B ) );
vec[ri] = pxfr[SHEAR];
ri += 1;
}
}
if ( ell_Nm_mul( nans, ninv[z], nvec ) ) {
biffAddf( TEN, ELL, "%s: trouble estimating model ( slice %d ): Hx, Hy, Hz",
me, z );
airMopError( mop ); return 1;
}
ans = ( double * )( nans->data );
if ( 1 == order ) {
ELL_3V_COPY( hst + 0*3, ans );
} else {
ELL_9V_COPY( hst + 0*9, ans );
}
}
/* ------ find Tx, Ty, Tz per slice */
for ( z=0; z<sz; z++ ) {
if ( nrrdHasNonExist( nmat[z] ) ) {
/* we've already zero-ed out this row in the HST nrrd */
continue;
}
hst = ( double * )( nhst->data ) + ( 1 == order ? 9 : 27 )*z;
ri = 0;
for ( A=0; A<ninLen; A++ ) {
for ( B=0; B<ninLen; B++ ) {
if ( A == B ) continue;
pxfr = ( double * )( npxfr->data ) + 0 + 5*( z + sz*( A + ninLen*B ) );
vec[ri] = pxfr[TRAN];
ri += 1;
}
}
if ( ell_Nm_mul( nans, ninv[z], nvec ) ) {
biffMovef( TEN, ELL,
"%s: trouble estimating model ( slice %d ): Tx, Ty, Tz",
me, z );
airMopError( mop ); return 1;
}
ans = ( double * )( nans->data );
if ( 1 == order ) {
ELL_3V_COPY( hst + 2*3, ans );
} else {
ELL_9V_COPY( hst + 2*9, ans );
}
}
airMopOkay( mop );
return 0;
}
int
751 _tenEpiRegFitHST( Nrrd *nhst, Nrrd **_ncc, int ninLen,
double goodFrac, int prog, int verb ) {
static const char me[]="_tenEpiRegFitHST";
airArray *mop;
Nrrd *ncc, *ntA, *ntB, *nsd, *nl2;
unsigned int cc, sz, zi, sh, hi;
float *mess, *two, tmp;
double *hst, x, y, xx, xy, mm, bb;
mop = airMopNew( );
airMopAdd( mop, ncc=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nsd=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nl2=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
/* do SD and L2 projections of the CCs along the DWI axis,
integrate these over the X and Y axes of the slices,
and define per-slice "messiness" as the quotient of the
SD integral with the L2 integral */
if ( verb ) {
fprintf( stderr, "%s: measuring segmentation uncertainty ... ", me );
fflush( stderr );
}
if ( nrrdJoin( ncc, ( const Nrrd*const* )_ncc, ninLen, 0, AIR_TRUE )
|| nrrdProject( ntA, ncc, 0, nrrdMeasureSD, nrrdTypeFloat )
|| nrrdProject( ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat )
|| nrrdProject( nsd, ntB, 0, nrrdMeasureSum, nrrdTypeFloat )
|| nrrdProject( ntA, ncc, 0, nrrdMeasureL2, nrrdTypeFloat )
|| nrrdProject( ntB, ntA, 0, nrrdMeasureSum, nrrdTypeFloat )
|| nrrdProject( nl2, ntB, 0, nrrdMeasureSum, nrrdTypeFloat )
|| nrrdArithBinaryOp( ntA, nrrdBinaryOpDivide, nsd, nl2 ) ) {
biffMovef( TEN, NRRD, "%s: trouble doing CC projections", me );
airMopError( mop ); return 1;
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
if ( prog && _tenEpiRegSave( "regtmp-messy.txt", ntA,
NULL, 0, "segmentation uncertainty" ) ) {
biffMovef( TEN, NRRD, "%s: EpiRegSave failed", me );
airMopError( mop ); return 1;
}
/* now ntA stores the per-slice messiness */
mess = AIR_CAST( float*, ntA->data );
/* allocate an array of 2 floats per slice */
sz = ntA->axis[0].size;
two = AIR_CAST( float*, calloc( 2*sz, sizeof( float ) ) );
if ( !two ) {
biffAddf( TEN, "%s: couldn't allocate tmp buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, two, airFree, airMopAlways );
/* initial ordering: messiness, slice index */
for ( zi=0; zi<sz; zi++ ) {
two[0 + 2*zi] = ( AIR_EXISTS( mess[zi] )
? mess[zi]
: 666 ); /* don't use empty slices */
two[1 + 2*zi] = AIR_CAST( float, zi );
}
/* sort into ascending messiness */
qsort( two, zi, 2*sizeof( float ), nrrdValCompare[nrrdTypeFloat] );
/* flip ordering while thresholding messiness into usability */
for ( zi=0; zi<sz; zi++ ) {
tmp = two[1 + 2*zi];
two[1 + 2*zi] = AIR_AFFINE( 0, zi, sz-1, 0, 1 ) <= goodFrac ? 1.0f : 0.0f;
two[0 + 2*zi] = tmp;
}
/* sort again, now into ascending slice order */
qsort( two, zi, 2*sizeof( float ), nrrdValCompare[nrrdTypeFloat] );
if ( verb ) {
fprintf( stderr, "%s: using slices", me );
for ( zi=0; zi<sz; zi++ ) {
if ( two[1 + 2*zi] ) {
fprintf( stderr, " %u", zi );
}
}
fprintf( stderr, " for fitting\n" );
}
/* perform fitting for each column in hst ( regardless of
whether we're using a 1st or 2nd order model */
hst = ( double* )( nhst->data );
sh = nhst->axis[0].size;
for ( hi=0; hi<sh; hi++ ) {
x = y = xy = xx = 0;
cc = 0;
for ( zi=0; zi<sz; zi++ ) {
if ( !two[1 + 2*zi] )
continue;
cc += 1;
x += zi;
xx += zi*zi;
y += hst[hi + sh*zi];
xy += zi*hst[hi + sh*zi];
}
x /= cc; xx /= cc; y /= cc; xy /= cc;
mm = ( xy - x*y )/( xx - x*x );
bb = y - mm*x;
for ( zi=0; zi<sz; zi++ ) {
hst[hi + sh*zi] = mm*zi + bb;
}
}
airMopOkay( mop );
return 0;
}
int
860 _tenEpiRegGetHST( double *hhP, double *ssP, double *ttP,
int reference, int ni, int zi,
Nrrd *npxfr, Nrrd *nhst, Nrrd *ngrad ) {
double *xfr, *hst, *_g, grad[9]; /* big enough for 2nd order */
int sz, ninLen;
int order;
order = 1;
/* these could also have been passed to us, but we can also discover them */
sz = npxfr->axis[1].size;
ninLen = npxfr->axis[2].size;
if ( -1 == reference ) {
/* we use the estimated H, S, T vectors to determine distortion
as a function of gradient direction, and then invert this */
_g = ( double* )( ngrad->data ) + 0 + 3*ni;
if ( 1 == order ) {
hst = ( double* )( nhst->data ) + 0 + 9*zi;
*hhP = ELL_3V_DOT( _g, hst + 0*3 );
*ssP = 1 + ELL_3V_DOT( _g, hst + 1*3 );
*ttP = ELL_3V_DOT( _g, hst + 2*3 );
} else {
hst = ( double* )( nhst->data ) + 0 + 27*zi;
ELL_9V_SET( grad, _g[0], _g[1], _g[2],
_g[0]*_g[0]*_g[0], _g[1]*_g[1]*_g[1], _g[2]*_g[2]*_g[2],
_g[0]*_g[1]*_g[2],
0, 0 );
/*
_g[0]*_g[1], _g[0]*_g[2], _g[1]*_g[2] );
*/
*hhP = ELL_9V_DOT( grad, hst + 0*9 );
*ssP = 1 + ELL_9V_DOT( grad, hst + 1*9 );
*ttP = ELL_9V_DOT( grad, hst + 2*9 );
}
} else {
/* we register against a specific DWI */
xfr = ( double* )( npxfr->data ) + 0 + 5*( zi + sz*( reference + ninLen*ni ) );
*hhP = xfr[2];
*ssP = xfr[3];
*ttP = xfr[4];
}
return 0;
}
/*
** _tenEpiRegSliceWarp
**
** Apply [hh, ss, tt] transform to nin, putting results in nout, but with
** some trickiness:
** - nwght and nidx are already allocated to the the weights ( type float )
** and indices for resampling nin with "kern" and "kparm"
** - nout is already allocated to the correct size and type
** - nin is type float, but output must be type nout->type
** - nin is been transposed to have the resampled axis fastest in memory,
** but nout output will not be transposed
*/
int
919 _tenEpiRegSliceWarp( Nrrd *nout, Nrrd *nin, Nrrd *nwght, Nrrd *nidx,
const NrrdKernel *kern, double *kparm,
double hh, double ss, double tt, double cx, double cy ) {
float *wght, *in, pp, pf, tmp;
int *idx;
unsigned int supp;
size_t sx, sy, xi, yi, pb, pi;
double ( *ins )( void *, size_t, double ), ( *clamp )( double );
sy = nin->axis[0].size;
sx = nin->axis[1].size;
supp = AIR_CAST( unsigned int, kern->support( kparm ) );
ins = nrrdDInsert[nout->type];
clamp = nrrdDClamp[nout->type];
in = AIR_CAST( float*, nin->data );
for ( xi=0; xi<sx; xi++ ) {
idx = AIR_CAST( int*, nidx->data );
wght = AIR_CAST( float*, nwght->data );
for ( yi=0; yi<sy; yi++ ) {
pp = AIR_CAST( float, hh*( xi - cx ) + ss*( yi - cy ) + tt + cy );
pb = AIR_CAST( size_t, floor( pp ) );
pf = pp - pb;
for ( pi=0; pi<2*supp; pi++ ) {
idx[pi] = AIR_MIN( pb + pi - ( supp-1 ), sy-1 );
wght[pi] = pi - ( supp-1 ) - pf;
}
idx += 2*supp;
wght += 2*supp;
}
idx = AIR_CAST( int*, nidx->data );
wght = AIR_CAST( float*, nwght->data );
kern->evalN_f( wght, wght, 2*supp*sy, kparm );
for ( yi=0; yi<sy; yi++ ) {
tmp = 0;
for ( pi=0; pi<2*supp; pi++ ) {
tmp += in[idx[pi]]*wght[pi];
}
ins( nout->data, xi + sx*yi, clamp( ss*tmp ) );
idx += 2*supp;
wght += 2*supp;
}
in += sy;
}
return 0;
}
/*
** _tenEpiRegWarp( )
**
*/
int
972 _tenEpiRegWarp( Nrrd **ndone, Nrrd *npxfr, Nrrd *nhst, Nrrd *ngrad,
Nrrd **nin, int ninLen,
int reference, const NrrdKernel *kern, double *kparm,
int verb ) {
static const char me[]="_tenEpiRegWarp";
Nrrd *ntmp, *nfin, *nslcA, *nslcB, *nwght, *nidx;
airArray *mop;
int sx, sy, sz, ni, zi, supp;
double hh, ss, tt, cx, cy;
mop = airMopNew( );
airMopAdd( mop, ntmp=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nfin=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nslcA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nslcB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nwght=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nidx=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( verb ) {
fprintf( stderr, "%s:\n ", me ); fflush( stderr );
}
sx = nin[0]->axis[0].size;
sy = nin[0]->axis[1].size;
sz = nin[0]->axis[2].size;
cx = sx/2.0;
cy = sy/2.0;
supp = ( int )kern->support( kparm );
if ( nrrdMaybeAlloc_va( nwght, nrrdTypeFloat, 2,
AIR_CAST( size_t, 2*supp ),
AIR_CAST( size_t, sy ) )
|| nrrdMaybeAlloc_va( nidx, nrrdTypeInt, 2,
AIR_CAST( size_t, 2*supp ),
AIR_CAST( size_t, sy ) ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating buffers", me );
airMopError( mop ); return 1;
}
for ( ni=0; ni<ninLen; ni++ ) {
if ( verb ) {
fprintf( stderr, "%2d ", ni ); fflush( stderr );
}
if ( nrrdCopy( ndone[ni], nin[ni] )
|| ( ( !ni ) && nrrdSlice( nslcB, ndone[ni], 2, 0 ) ) /* only when 0==ni */
|| nrrdAxesSwap( ntmp, nin[ni], 0, 1 )
|| nrrdConvert( nfin, ntmp, nrrdTypeFloat ) ) {
biffMovef( TEN, NRRD, "%s: trouble prepping at ni=%d", me, ni );
airMopError( mop ); return 1;
}
for ( zi=0; zi<sz; zi++ ) {
if ( _tenEpiRegGetHST( &hh, &ss, &tt, reference,
ni, zi, npxfr, nhst, ngrad )
|| nrrdSlice( nslcA, nfin, 2, zi )
|| _tenEpiRegSliceWarp( nslcB, nslcA, nwght, nidx, kern, kparm,
hh, ss, tt, cx, cy )
|| nrrdSplice( ndone[ni], ndone[ni], nslcB, 2, zi ) ) {
biffMovef( TEN, NRRD, "%s: trouble on slice %d if ni=%d", me, zi, ni );
/* because the _tenEpiReg calls above don't use biff */
airMopError( mop ); return 1;
}
}
}
if ( verb ) {
fprintf( stderr, "done\n" );
}
airMopOkay( mop );
return 0;
}
int
1041 tenEpiRegister3D( Nrrd **nout, Nrrd **nin, unsigned int ninLen, Nrrd *_ngrad,
int reference,
double bwX, double bwY, double fitFrac,
double DWthr, int doCC,
const NrrdKernel *kern, double *kparm,
int progress, int verbose ) {
static const char me[]="tenEpiRegister3D";
airArray *mop;
Nrrd **nbuffA, **nbuffB, *npxfr, *nhst, *ngrad;
int hack1, hack2;
unsigned int i;
hack1 = nrrdStateAlwaysSetContent;
hack2 = nrrdStateDisableContent;
nrrdStateAlwaysSetContent = AIR_FALSE;
nrrdStateDisableContent = AIR_TRUE;
mop = airMopNew( );
if ( _tenEpiRegCheck( nout, nin, ninLen, _ngrad, reference,
bwX, bwY, DWthr,
kern, kparm ) ) {
biffAddf( TEN, "%s: trouble with input", me );
airMopError( mop ); return 1;
}
nbuffA = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
nbuffB = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
if ( !( nbuffA && nbuffB ) ) {
biffAddf( TEN, "%s: couldn't allocate tmp nrrd pointer arrays", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nbuffA, airFree, airMopAlways );
airMopAdd( mop, nbuffB, airFree, airMopAlways );
for ( i=0; i<ninLen; i++ ) {
airMopAdd( mop, nbuffA[i] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nbuffB[i] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
nrrdAxisInfoCopy( nout[i], nin[i], NULL, NRRD_AXIS_INFO_NONE );
}
airMopAdd( mop, npxfr = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nhst = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ngrad = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ngrad, _ngrad, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble converting gradients to doubles", me );
airMopError( mop ); return 1;
}
/* ------ blur */
if ( _tenEpiRegBlur( nbuffA, nin, ninLen, bwX, bwY, verbose ) ) {
biffAddf( TEN, "%s: trouble %s", me, ( bwX || bwY ) ? "blurring" : "copying" );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-blur.nrrd", NULL,
nbuffA, ninLen, "blurred DWIs" ) ) {
airMopError( mop ); return 1;
}
/* ------ threshold */
if ( _tenEpiRegThreshold( nbuffB, nbuffA, ninLen,
DWthr, verbose, progress, 1.5 ) ) {
biffAddf( TEN, "%s: trouble thresholding", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-thresh.nrrd", NULL,
nbuffB, ninLen, "thresholded DWIs" ) ) {
airMopError( mop ); return 1;
}
/* ------ connected components */
if ( doCC ) {
if ( _tenEpiRegCC( nbuffB, ninLen, 1, verbose ) ) {
biffAddf( TEN, "%s: trouble doing connected components", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-ccs.nrrd", NULL,
nbuffB, ninLen, "connected components" ) ) {
airMopError( mop ); return 1;
}
}
/* ------ moments */
if ( _tenEpiRegMoments( nbuffA, nbuffB, ninLen, verbose ) ) {
biffAddf( TEN, "%s: trouble finding moments", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-mom.nrrd", NULL,
nbuffA, ninLen, "moments" ) ) {
airMopError( mop ); return 1;
}
/* ------ transforms */
if ( _tenEpiRegPairXforms( npxfr, nbuffA, ninLen ) ) {
biffAddf( TEN, "%s: trouble calculating transforms", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-pxfr.nrrd", npxfr,
NULL, 0, "pair-wise xforms" ) ) {
airMopError( mop ); return 1;
}
if ( -1 == reference ) {
/* ------ HST estimation */
if ( _tenEpiRegEstimHST( nhst, npxfr, ninLen, ngrad ) ) {
biffAddf( TEN, "%s: trouble estimating HST", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-hst.txt", nhst,
NULL, 0, "HST estimates" ) ) {
airMopError( mop ); return 1;
}
if ( fitFrac ) {
/* ------ HST parameter fitting */
if ( _tenEpiRegFitHST( nhst, nbuffB, ninLen, fitFrac, progress, verbose ) ) {
biffAddf( TEN, "%s: trouble fitting HST", me );
airMopError( mop ); return 1;
}
if ( progress && _tenEpiRegSave( "regtmp-fit-hst.txt", nhst,
NULL, 0, "fitted HST" ) ) {
airMopError( mop ); return 1;
}
}
}
/* ------ doit */
if ( _tenEpiRegWarp( nout, npxfr, nhst, ngrad, nin, ninLen,
reference, kern, kparm, verbose ) ) {
biffAddf( TEN, "%s: trouble performing final registration", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
nrrdStateAlwaysSetContent = hack1;
nrrdStateDisableContent = hack2;
return 0;
}
int
1178 tenEpiRegister4D( Nrrd *_nout, Nrrd *_nin, Nrrd *_ngrad,
int reference,
double bwX, double bwY, double fitFrac,
double DWthr, int doCC,
const NrrdKernel *kern, double *kparm,
int progress, int verbose ) {
static const char me[]="tenEpiRegister4D";
unsigned int ninIdx, ninLen,
dwiAx, rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX];
int dwiIdx;
Nrrd **nout, **nin, **ndwi, **ndwiOut, *ngrad, *ndwigrad;
airArray *mop;
double *grad, *dwigrad, glen;
if ( !( _nout && _nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( 4 != _nin->dim ) {
biffAddf( TEN, "%s: need a 4-D input array, not %d-D", me, _nin->dim );
return 1;
}
if ( tenGradientCheck( _ngrad, nrrdTypeDefault, 6 /* <-- HEY: not sure */ ) ) {
biffAddf( TEN, "%s: problem with given gradient list", me );
return 1;
}
rangeAxisNum = nrrdRangeAxesGet( _nin, rangeAxisIdx );
if ( 0 == rangeAxisNum ) {
/* we fall back on old behavior */
dwiAx = 0;
} else if ( 1 == rangeAxisNum ) {
/* thankfully there's exactly one range axis */
dwiAx = rangeAxisIdx[0];
} else {
biffAddf( TEN, "%s: have %u range axes instead of 1, don't know which "
"is DWI axis", me, rangeAxisNum );
return 1;
}
ninLen = _nin->axis[dwiAx].size;
/* outdated
if ( !( AIR_IN_CL( 6, ninLen, 120 ) ) ) {
biffAddf( TEN, "%s: %u ( size of axis %u, and # DWIs ) is unreasonable",
me, ninLen, dwiAx );
return 1;
}
*/
if ( ninLen != _ngrad->axis[1].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: ninLen %u != # grads %s", me, ninLen,
airSprintSize_t( stmp, _ngrad->axis[1].size ) );
return 1;
}
mop = airMopNew( );
ngrad = nrrdNew( );
ndwigrad = nrrdNew( );
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ndwigrad, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ngrad, _ngrad, nrrdTypeDouble )
|| nrrdConvert( ndwigrad, _ngrad, nrrdTypeDouble ) ) { /* HACK applies */
biffMovef( TEN, NRRD, "%s: trouble converting gradients to doubles", me );
airMopError( mop ); return 1;
}
nin = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
ndwi = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
nout = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
ndwiOut = ( Nrrd ** )calloc( ninLen, sizeof( Nrrd* ) );
if ( !( nin && ndwi && nout && ndwiOut ) ) {
biffAddf( TEN, "%s: trouble allocating local arrays", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, nin, airFree, airMopAlways );
airMopAdd( mop, ndwi, airFree, airMopAlways );
airMopAdd( mop, nout, airFree, airMopAlways );
airMopAdd( mop, ndwiOut, airFree, airMopAlways );
dwiIdx = -1;
grad = AIR_CAST( double *, ngrad->data );
dwigrad = AIR_CAST( double *, ndwigrad->data );
for ( ninIdx=0; ninIdx<ninLen; ninIdx++ ) {
glen = ELL_3V_LEN( grad + 3*ninIdx );
if ( -1 != reference || glen > 0.0 ) {
/* we're not allowed to use only those images that are truly DWIs,
or,
( we are so allowed and ) this is a true DWI */
dwiIdx++;
ndwi[dwiIdx] = nrrdNew( );
ndwiOut[dwiIdx] = nrrdNew( );
airMopAdd( mop, ndwi[dwiIdx], ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ndwiOut[dwiIdx], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( ndwi[dwiIdx], _nin, dwiAx,
AIR_CAST( unsigned int, ninIdx ) ) ) {
biffMovef( TEN, NRRD, "%s: trouble slicing at %d on axis %u",
me, ninIdx, dwiAx );
airMopError( mop ); return 1;
}
/* NOTE: this works because dwiIdx <= ninIdx */
ELL_3V_COPY( dwigrad + 3*dwiIdx, grad + 3*ninIdx );
} else {
/* we are only looking at true DWIs, and this isn't one of them */
continue;
}
}
if ( -1 == dwiIdx ) {
biffAddf( TEN, "%s: somehow got no DWIs", me );
airMopError( mop ); return 1;
}
/* HEY: HACK! */
ndwigrad->axis[1].size = 1 + AIR_CAST( unsigned int, dwiIdx );
if ( tenEpiRegister3D( ndwiOut, ndwi, ndwigrad->axis[1].size, ndwigrad,
reference,
bwX, bwY, fitFrac, DWthr,
doCC,
kern, kparm,
progress, verbose ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
dwiIdx = -1;
for ( ninIdx=0; ninIdx<ninLen; ninIdx++ ) {
glen = ELL_3V_LEN( grad + 3*ninIdx );
if ( -1 != reference || glen > 0.0 ) {
/* we're not allowed to use only those images that are truly DWIs,
or,
( we are so allowed and ) this is a true DWI */
dwiIdx++;
nout[ninIdx] = ndwiOut[dwiIdx];
/*
fprintf( stderr, "!%s: ( A ) nout[%u] = %p\n", me, ninIdx, nout[ninIdx] );
*/
} else {
/* we are only looking at true DWIs, and this isn't one of them */
nout[ninIdx] = nrrdNew( );
airMopAdd( mop, nout[ninIdx], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( nout[ninIdx], _nin, dwiAx, ninIdx ) ) {
biffMovef( TEN, NRRD, "%s: trouble slicing at %d on axis %u",
me, dwiIdx, dwiAx );
airMopError( mop ); return 1;
}
/*
fprintf( stderr, "!%s: ( B ) nout[%u] = %p\n", me, ninIdx, nout[ninIdx] );
*/
}
}
if ( nrrdJoin( _nout, ( const Nrrd*const* )nout, ninLen, dwiAx, AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: trouble joining output", me );
airMopError( mop ); return 1;
}
nrrdAxisInfoCopy( _nout, _nin, NULL, NRRD_AXIS_INFO_NONE );
if ( nrrdBasicInfoCopy( _nout, _nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT ) ) {
/* note that we're ALWAYS copying the key/value pairs- its just
too annoying to have to set nrrdStateKeyValuePairsPropagate
in order for the DWI-specific key/value pairs to be set */
biffMovef( TEN, NRRD, "%s:", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/*
** learned: when it looks like good-old LLS estimation is producing
** nothing but zero tensors, see if your tec->valueMin is larger
** than ( what are problably ) floating-point DWIs
*/
/*
http://www.mathworks.com/access/helpdesk/help/toolbox/curvefit/ch_fitt5.html#40515
*/
/* ---------------------------------------------- */
int
42 _tenGaussian( double *retP, double m, double t, double s ) {
static const char me[]="_tenGaussian";
double diff, earg, den;
if ( !retP ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
diff = ( m-t )/2;
earg = -diff*diff/2;
den = s*sqrt( 2*AIR_PI );
*retP = exp( earg )/den;
if ( !AIR_EXISTS( *retP ) ) {
biffAddf( TEN, "%s: m=%g, t=%g, s=%g", me, m, t, s );
biffAddf( TEN, "%s: diff=%g, earg=%g, den=%g", me, diff, earg, den );
biffAddf( TEN, "%s: failed with ret = exp( %g )/%g = %g/%g = %g",
me, earg, den, exp( earg ), den, *retP );
*retP = AIR_NAN; return 1;
}
return 0;
}
int
65 _tenRicianTrue( double *retP,
double m /* measured */,
double t /* truth */,
double s /* sigma */ ) {
static const char me[]="_tenRicianTrue";
double mos, moss, mos2, tos2, tos, ss, earg, barg;
if ( !retP ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
mos = m/s;
moss = mos/s;
tos = t/s;
ss = s*s;
mos2 = mos*mos;
tos2 = tos*tos;
earg = -( mos2 + tos2 )/2;
barg = mos*tos;
*retP = exp( earg )*airBesselI0( barg )*moss;
if ( !AIR_EXISTS( *retP ) ) {
biffAddf( TEN, "%s: m=%g, t=%g, s=%g", me, m, t, s );
biffAddf( TEN, "%s: mos=%g, moss=%g, tos=%g, ss=%g",
me, mos, moss, tos, ss );
biffAddf( TEN, "%s: mos2=%g, tos2=%g, earg=%g, barg=%g", me,
mos2, tos2, earg, barg );
biffAddf( TEN, "%s: failed: ret=exp( %g )*bessi0( %g )*%g = %g * %g * %g = %g",
me, earg, barg, moss, exp( earg ), airBesselI0( barg ), moss, *retP );
*retP = AIR_NAN; return 1;
}
return 0;
}
int
101 _tenRicianSafe( double *retP, double m, double t, double s ) {
static const char me[]="_tenRicianSafe";
double diff, ric, gau, neer=10, faar=20;
int E;
if ( !retP ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
diff = AIR_ABS( m-t )/s;
E = 0;
if ( diff < neer ) {
if ( !E ) E |= _tenRicianTrue( retP, m, t, s );
} else if ( diff < faar ) {
if ( !E ) E |= _tenRicianTrue( &ric, m, t, s );
if ( !E ) E |= _tenGaussian( &gau, m, t, s );
if ( !E ) *retP = AIR_AFFINE( neer, diff, faar, ric, gau );
} else {
if ( !E ) E |= _tenGaussian( retP, m, t, s );
}
if ( E ) {
biffAddf( TEN, "%s: failed with m=%g, t=%g, s=%g -> diff=%g",
me, m, t, s, diff );
*retP = AIR_NAN; return 1;
}
return 0;
}
int
131 _tenRician( double *retP,
double m /* measured */,
double t /* truth */,
double s /* sigma */ ) {
static const char me[]="_tenRician";
double tos, ric, gau, loSignal=4.0, hiSignal=8.0;
int E;
if ( !retP ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( m >= 0 && t >= 0 && s > 0 ) ) {
biffAddf( TEN, "%s: got bad args: m=%g t=%g s=%g", me, m, t, s );
*retP = AIR_NAN; return 1;
}
tos = t/s;
E = 0;
if ( tos < loSignal ) {
if ( !E ) E |= _tenRicianSafe( retP, m, t, s );
} else if ( tos < hiSignal ) {
if ( !E ) E |= _tenRicianSafe( &ric, m, t, s );
if ( !E ) E |= _tenGaussian( &gau, m, t, s );
if ( !E ) *retP = AIR_AFFINE( loSignal, tos, hiSignal, ric, gau );
} else {
if ( !E ) E |= _tenGaussian( retP, m, t, s );
}
if ( E ) {
biffAddf( TEN, "%s: failed with m=%g, t=%g, s=%g -> tos=%g",
me, m, t, s, tos );
*retP = AIR_NAN; return 1;
}
return 0;
}
enum {
flagUnknown,
flagEstimateMethod,
flagBInfo,
flagAllNum,
flagDwiNum,
flagAllAlloc,
flagDwiAlloc,
flagAllSet,
flagDwiSet,
flagSkipSet,
flagWght,
flagEmat,
flagLast
};
void
184 _tenEstimateOutputInit( tenEstimateContext *tec ) {
tec->estimatedB0 = AIR_NAN;
TEN_T_SET( tec->ten, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN,
AIR_NAN );
tec->conf = AIR_NAN;
tec->mdwi = AIR_NAN;
tec->time = AIR_NAN;
tec->errorDwi = AIR_NAN;
tec->errorLogDwi = AIR_NAN;
tec->likelihoodDwi = AIR_NAN;
}
tenEstimateContext *
200 tenEstimateContextNew( ) {
tenEstimateContext *tec;
unsigned int fi;
airPtrPtrUnion appu;
tec = AIR_CAST( tenEstimateContext *, malloc( sizeof( tenEstimateContext ) ) );
if ( tec ) {
tec->bValue = AIR_NAN;
tec->valueMin = AIR_NAN;
tec->sigma = AIR_NAN;
tec->dwiConfThresh = AIR_NAN;
tec->dwiConfSoft = AIR_NAN;
tec->_ngrad = NULL;
tec->_nbmat = NULL;
tec->skipList = NULL;
appu.ui = &( tec->skipList );
tec->skipListArr = airArrayNew( appu.v, NULL,
2*sizeof( unsigned int ), 128 );
tec->skipListArr->noReallocWhenSmaller = AIR_TRUE;
tec->all_f = NULL;
tec->all_d = NULL;
tec->simulate = AIR_FALSE;
tec->estimate1Method = tenEstimate1MethodUnknown;
tec->estimateB0 = AIR_TRUE;
tec->recordTime = AIR_FALSE;
tec->recordErrorDwi = AIR_FALSE;
tec->recordErrorLogDwi = AIR_FALSE;
tec->recordLikelihoodDwi = AIR_FALSE;
tec->verbose = 0;
tec->progress = AIR_FALSE;
tec->WLSIterNum = 3;
for ( fi=flagUnknown+1; fi<flagLast; fi++ ) {
tec->flag[fi] = AIR_FALSE;
}
tec->allNum = 0;
tec->dwiNum = 0;
tec->nbmat = nrrdNew( );
tec->nwght = nrrdNew( );
tec->nemat = nrrdNew( );
tec->knownB0 = AIR_NAN;
tec->all = NULL;
tec->bnorm = NULL;
tec->allTmp = NULL;
tec->dwiTmp = NULL;
tec->dwi = NULL;
tec->skipLut = NULL;
_tenEstimateOutputInit( tec );
}
return tec;
}
tenEstimateContext *
253 tenEstimateContextNix( tenEstimateContext *tec ) {
if ( tec ) {
nrrdNuke( tec->nbmat );
nrrdNuke( tec->nwght );
nrrdNuke( tec->nemat );
airArrayNuke( tec->skipListArr );
airFree( tec->all );
airFree( tec->bnorm );
airFree( tec->allTmp );
airFree( tec->dwiTmp );
airFree( tec->dwi );
airFree( tec->skipLut );
airFree( tec );
}
return NULL;
}
void
272 tenEstimateVerboseSet( tenEstimateContext *tec,
int verbose ) {
if ( tec ) {
tec->verbose = verbose;
}
return;
}
void
281 tenEstimateNegEvalShiftSet( tenEstimateContext *tec, int doit ) {
if ( tec ) {
tec->negEvalShift = !!doit;
}
return;
}
int
290 tenEstimateMethodSet( tenEstimateContext *tec, int estimateMethod ) {
static const char me[]="tenEstimateMethodSet";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( tenEstimate1Method, estimateMethod ) ) {
biffAddf( TEN, "%s: estimateMethod %d not a valid %s", me,
estimateMethod, tenEstimate1Method->name );
return 1;
}
if ( tec->estimate1Method != estimateMethod ) {
tec->estimate1Method = estimateMethod;
tec->flag[flagEstimateMethod] = AIR_TRUE;
}
return 0;
}
int
312 tenEstimateSigmaSet( tenEstimateContext *tec, double sigma ) {
static const char me[]="tenEstimateSigmaSet";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( sigma ) && sigma >= 0.0 ) ) {
biffAddf( TEN, "%s: given sigma ( %g ) not existent and >= 0.0", me, sigma );
return 1;
}
tec->sigma = sigma;
return 0;
}
int
330 tenEstimateValueMinSet( tenEstimateContext *tec, double valueMin ) {
static const char me[]="tenEstimateValueMinSet";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( valueMin ) && valueMin > 0.0 ) ) {
biffAddf( TEN, "%s: given valueMin ( %g ) not existent and > 0.0",
me, valueMin );
return 1;
}
tec->valueMin = valueMin;
return 0;
}
int
349 tenEstimateGradientsSet( tenEstimateContext *tec,
const Nrrd *ngrad, double bValue, int estimateB0 ) {
static const char me[]="tenEstimateGradientsSet";
if ( !( tec && ngrad ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_EXISTS( bValue ) ) {
biffAddf( TEN, "%s: given b value doesn't exist", me );
return 1;
}
if ( tenGradientCheck( ngrad, nrrdTypeDefault, 7 ) ) {
biffAddf( TEN, "%s: problem with gradient list", me );
return 1;
}
tec->bValue = bValue;
tec->_ngrad = ngrad;
tec->_nbmat = NULL;
tec->estimateB0 = estimateB0;
tec->flag[flagBInfo] = AIR_TRUE;
return 0;
}
int
376 tenEstimateBMatricesSet( tenEstimateContext *tec,
const Nrrd *nbmat, double bValue, int estimateB0 ) {
static const char me[]="tenEstimateBMatricesSet";
if ( !( tec && nbmat ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !AIR_EXISTS( bValue ) ) {
biffAddf( TEN, "%s: given b value doesn't exist", me );
return 1;
}
if ( tenBMatrixCheck( nbmat, nrrdTypeDefault, 7 ) ) {
biffAddf( TEN, "%s: problem with b-matrix list", me );
return 1;
}
tec->bValue = bValue;
tec->_ngrad = NULL;
tec->_nbmat = nbmat;
tec->estimateB0 = estimateB0;
tec->flag[flagBInfo] = AIR_TRUE;
return 0;
}
int
403 tenEstimateSkipSet( tenEstimateContext *tec,
unsigned int valIdx, int doSkip ) {
static const char me[]="tenEstimateSkipSet";
unsigned int skipIdx;
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
skipIdx = airArrayLenIncr( tec->skipListArr, 1 );
tec->skipList[0 + 2*skipIdx] = valIdx;
tec->skipList[1 + 2*skipIdx] = !!doSkip;
tec->flag[flagSkipSet] = AIR_TRUE;
return 0;
}
int
422 tenEstimateSkipReset( tenEstimateContext *tec ) {
static const char me[]="tenEstimateSkipReset";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
airArrayLenSet( tec->skipListArr, 0 );
tec->flag[flagSkipSet] = AIR_TRUE;
return 0;
}
int
437 tenEstimateThresholdFind( double *threshP, unsigned char *isB0, Nrrd *nin4d ) {
static const char me[]="tenEstimateThresholdFind";
Nrrd **ndwi;
airArray *mop;
unsigned int slIdx, slNum, dwiAx, dwiNum,
rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX];
int dwiIdx;
mop = airMopNew( );
if ( !( threshP && isB0 && nin4d ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
airMopError( mop ); return 1;
}
/* HEY: copied from tenEpiRegister4D( ) */
rangeAxisNum = nrrdRangeAxesGet( nin4d, rangeAxisIdx );
if ( 0 == rangeAxisNum ) {
/* we fall back on old behavior */
dwiAx = 0;
} else if ( 1 == rangeAxisNum ) {
/* thankfully there's exactly one range axis */
dwiAx = rangeAxisIdx[0];
} else {
biffAddf( TEN, "%s: have %u range axes instead of 1, don't know which "
"is DWI axis", me, rangeAxisNum );
airMopError( mop ); return 1;
}
slNum = nin4d->axis[dwiAx].size;
dwiNum = 0;
for ( slIdx=0; slIdx<slNum; slIdx++ ) {
dwiNum += !isB0[slIdx];
}
if ( 0 == dwiNum ) {
biffAddf( TEN, "%s: somehow got zero DWIs", me );
airMopError( mop ); return 1;
}
ndwi = AIR_CALLOC( dwiNum, Nrrd * );
airMopAdd( mop, ndwi, ( airMopper )airFree, airMopAlways );
dwiIdx = -1;
for ( slIdx=0; slIdx<slNum; slIdx++ ) {
if ( !isB0[slIdx] ) {
dwiIdx++;
ndwi[dwiIdx] = nrrdNew( );
airMopAdd( mop, ndwi[dwiIdx], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( ndwi[dwiIdx], nin4d, dwiAx, slIdx ) ) {
biffMovef( TEN, NRRD,
"%s: trouble slicing DWI at index %u", me, slIdx );
airMopError( mop ); return 1;
}
}
}
if ( _tenEpiRegThresholdFind( threshP, ndwi, dwiNum, AIR_FALSE, 1.5 ) ) {
biffAddf( TEN, "%s: trouble finding thresh", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
500 tenEstimateThresholdSet( tenEstimateContext *tec,
double thresh, double soft ) {
static const char me[]="tenEstimateThresholdSet";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( thresh ) && AIR_EXISTS( soft ) ) ) {
biffAddf( TEN, "%s: not both threshold ( %g ) and softness ( %g ) exist", me,
thresh, soft );
return 1;
}
tec->dwiConfThresh = thresh;
tec->dwiConfSoft = soft;
return 0;
}
int
521 _tenEstimateCheck( tenEstimateContext *tec ) {
static const char me[]="_tenEstimateCheck";
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( tec->valueMin ) && tec->valueMin > 0.0 ) ) {
biffAddf( TEN, "%s: need a positive valueMin set ( not %g )",
me, tec->valueMin );
return 1;
}
if ( !tec->simulate ) {
if ( !AIR_EXISTS( tec->bValue ) ) {
biffAddf( TEN, "%s: b-value not set", me );
return 1;
}
if ( airEnumValCheck( tenEstimate1Method, tec->estimate1Method ) ) {
biffAddf( TEN, "%s: estimation method not set", me );
return 1;
}
if ( tenEstimate1MethodMLE == tec->estimate1Method
&& !( AIR_EXISTS( tec->sigma ) && ( tec->sigma >= 0.0 ) )
) {
biffAddf( TEN, "%s: can't do %s estim w/out non-negative sigma set", me,
airEnumStr( tenEstimate1Method, tenEstimate1MethodMLE ) );
return 1;
}
if ( !( AIR_EXISTS( tec->dwiConfThresh ) && AIR_EXISTS( tec->dwiConfSoft ) ) ) {
biffAddf( TEN, "%s: not both threshold ( %g ) and softness ( %g ) exist", me,
tec->dwiConfThresh, tec->dwiConfSoft );
return 1;
}
}
if ( !( tec->_ngrad || tec->_nbmat ) ) {
biffAddf( TEN, "%s: need to set either gradients or B-matrices", me );
return 1;
}
return 0;
}
/*
** allNum includes the skipped images
** dwiNum does not include the skipped images
*/
int
568 _tenEstimateNumUpdate( tenEstimateContext *tec ) {
static const char me[]="_tenEstimateNumUpdate";
unsigned int newAllNum, newDwiNum, allIdx,
skipListIdx, skipIdx, skipDo, skipNotNum;
double ( *lup )( const void *, size_t ), gg[3], bb[6];
if ( tec->flag[flagBInfo]
|| tec->flag[flagSkipSet] ) {
if ( tec->_ngrad ) {
newAllNum = AIR_CAST( unsigned int, tec->_ngrad->axis[1].size );
lup = nrrdDLookup[tec->_ngrad->type];
} else {
newAllNum = AIR_CAST( unsigned int, tec->_nbmat->axis[1].size );
lup = nrrdDLookup[tec->_nbmat->type];
}
if ( tec->allNum != newAllNum ) {
tec->allNum = newAllNum;
tec->flag[flagAllNum] = AIR_TRUE;
}
/* HEY: this should probably be its own update function, but its very
convenient to allocate these allNum-length arrays here, immediately */
airFree( tec->skipLut );
tec->skipLut = AIR_CAST( unsigned char *, calloc( tec->allNum,
sizeof( unsigned char ) ) );
airFree( tec->bnorm );
tec->bnorm = AIR_CAST( double *, calloc( tec->allNum, sizeof( double ) ) );
if ( !( tec->skipLut && tec->bnorm ) ) {
biffAddf( TEN, "%s: couldn't allocate skipLut, bnorm vectors length %u\n",
me, tec->allNum );
return 1;
}
for ( skipListIdx=0; skipListIdx<tec->skipListArr->len; skipListIdx++ ) {
skipIdx = tec->skipList[0 + 2*skipListIdx];
skipDo = tec->skipList[1 + 2*skipListIdx];
if ( !( skipIdx < tec->allNum ) ) {
biffAddf( TEN, "%s: skipList entry %u value index %u not < # vals %u",
me, skipListIdx, skipIdx, tec->allNum );
return 1;
}
tec->skipLut[skipIdx] = skipDo;
}
skipNotNum = 0;
for ( skipIdx=0; skipIdx<tec->allNum; skipIdx++ ) {
skipNotNum += !tec->skipLut[skipIdx];
}
if ( !( skipNotNum >= 7 ) ) {
biffAddf( TEN, "%s: number of not-skipped ( used ) values %u < minimum 7",
me, skipNotNum );
return 1;
}
newDwiNum = 0;
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
if ( tec->skipLut[allIdx] ) {
tec->bnorm[allIdx] = AIR_NAN;
} else {
if ( tec->_ngrad ) {
gg[0] = lup( tec->_ngrad->data, 0 + 3*allIdx );
gg[1] = lup( tec->_ngrad->data, 1 + 3*allIdx );
gg[2] = lup( tec->_ngrad->data, 2 + 3*allIdx );
bb[0] = gg[0]*gg[0];
bb[1] = gg[1]*gg[0];
bb[2] = gg[2]*gg[0];
bb[3] = gg[1]*gg[1];
bb[4] = gg[2]*gg[1];
bb[5] = gg[2]*gg[2];
} else {
bb[0] = lup( tec->_nbmat->data, 0 + 6*allIdx );
bb[1] = lup( tec->_nbmat->data, 1 + 6*allIdx );
bb[2] = lup( tec->_nbmat->data, 2 + 6*allIdx );
bb[3] = lup( tec->_nbmat->data, 3 + 6*allIdx );
bb[4] = lup( tec->_nbmat->data, 4 + 6*allIdx );
bb[5] = lup( tec->_nbmat->data, 5 + 6*allIdx );
}
tec->bnorm[allIdx] = sqrt( bb[0]*bb[0] + 2*bb[1]*bb[1] + 2*bb[2]*bb[2]
+ bb[3]*bb[3] + 2*bb[4]*bb[4]
+ bb[5]*bb[5] );
if ( tec->estimateB0 ) {
++newDwiNum;
} else {
newDwiNum += ( 0.0 != tec->bnorm[allIdx] );
}
}
}
if ( tec->dwiNum != newDwiNum ) {
tec->dwiNum = newDwiNum;
tec->flag[flagDwiNum] = AIR_TRUE;
}
if ( !tec->estimateB0 && ( tec->allNum == tec->dwiNum ) ) {
biffAddf( TEN, "%s: don't want to estimate B0, but all values are DW", me );
return 1;
}
}
return 0;
}
int
667 _tenEstimateAllAllocUpdate( tenEstimateContext *tec ) {
static const char me[]="_tenEstimateAllAllocUpdate";
if ( tec->flag[flagAllNum] ) {
airFree( tec->all );
airFree( tec->allTmp );
tec->all = AIR_CAST( double *, calloc( tec->allNum, sizeof( double ) ) );
tec->allTmp = AIR_CAST( double *, calloc( tec->allNum, sizeof( double ) ) );
if ( !( tec->all && tec->allTmp ) ) {
biffAddf( TEN, "%s: couldn't allocate \"all\" arrays ( length %u )", me,
tec->allNum );
return 1;
}
tec->flag[flagAllAlloc] = AIR_TRUE;
}
return 0;
}
int
686 _tenEstimateDwiAllocUpdate( tenEstimateContext *tec ) {
static const char me[]="_tenEstimateDwiAllocUpdate";
size_t size[2];
int E;
if ( tec->flag[flagDwiNum] ) {
airFree( tec->dwi );
airFree( tec->dwiTmp );
tec->dwi = AIR_CAST( double *, calloc( tec->dwiNum, sizeof( double ) ) );
tec->dwiTmp = AIR_CAST( double *, calloc( tec->dwiNum, sizeof( double ) ) );
if ( !( tec->dwi && tec->dwiTmp ) ) {
biffAddf( TEN, "%s: couldn't allocate DWI arrays ( length %u )", me,
tec->dwiNum );
return 1;
}
E = 0;
if ( !E ) size[0] = ( tec->estimateB0 ? 7 : 6 );
if ( !E ) size[1] = tec->dwiNum;
if ( !E ) E |= nrrdMaybeAlloc_nva( tec->nbmat, nrrdTypeDouble, 2, size );
if ( !E ) size[0] = tec->dwiNum;
if ( !E ) size[1] = tec->dwiNum;
if ( !E ) E |= nrrdMaybeAlloc_nva( tec->nwght, nrrdTypeDouble, 2, size );
if ( E ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate dwi nrrds", me );
return 1;
}
/* nrrdSave( "0-nbmat.txt", tec->nbmat, NULL ); */
tec->flag[flagDwiAlloc] = AIR_TRUE;
}
return 0;
}
int
719 _tenEstimateAllSetUpdate( tenEstimateContext *tec ) {
/* static const char me[]="_tenEstimateAllSetUpdate"; */
/* unsigned int skipListIdx, skipIdx, skip, dwiIdx */;
if ( tec->flag[flagAllAlloc]
|| tec->flag[flagDwiNum] ) {
}
return 0;
}
int
731 _tenEstimateDwiSetUpdate( tenEstimateContext *tec ) {
/* static const char me[]="_tenEstimateDwiSetUpdate"; */
double ( *lup )( const void *, size_t I ), gg[3], *bmat;
unsigned int allIdx, dwiIdx, bmIdx;
if ( tec->flag[flagAllNum]
|| tec->flag[flagDwiAlloc] ) {
if ( tec->_ngrad ) {
lup = nrrdDLookup[tec->_ngrad->type];
} else {
lup = nrrdDLookup[tec->_nbmat->type];
}
dwiIdx = 0;
bmat = AIR_CAST( double*, tec->nbmat->data );
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
if ( !tec->skipLut[allIdx]
&& ( tec->estimateB0 || tec->bnorm[allIdx] ) ) {
if ( tec->_ngrad ) {
gg[0] = lup( tec->_ngrad->data, 0 + 3*allIdx );
gg[1] = lup( tec->_ngrad->data, 1 + 3*allIdx );
gg[2] = lup( tec->_ngrad->data, 2 + 3*allIdx );
bmat[0] = gg[0]*gg[0];
bmat[1] = gg[1]*gg[0];
bmat[2] = gg[2]*gg[0];
bmat[3] = gg[1]*gg[1];
bmat[4] = gg[2]*gg[1];
bmat[5] = gg[2]*gg[2];
} else {
for ( bmIdx=0; bmIdx<6; bmIdx++ ) {
bmat[bmIdx] = lup( tec->_nbmat->data, bmIdx + 6*allIdx );
}
}
bmat[1] *= 2.0;
bmat[2] *= 2.0;
bmat[4] *= 2.0;
if ( tec->estimateB0 ) {
bmat[6] = -1;
}
bmat += tec->nbmat->axis[0].size;
dwiIdx++;
}
}
}
return 0;
}
int
778 _tenEstimateWghtUpdate( tenEstimateContext *tec ) {
/* static const char me[]="_tenEstimateWghtUpdate"; */
unsigned int dwiIdx;
double *wght;
wght = AIR_CAST( double *, tec->nwght->data );
if ( tec->flag[flagDwiAlloc]
|| tec->flag[flagEstimateMethod] ) {
/* HEY: this is only useful for linear LS, no? */
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
wght[dwiIdx + tec->dwiNum*dwiIdx] = 1.0;
}
tec->flag[flagEstimateMethod] = AIR_FALSE;
tec->flag[flagWght] = AIR_TRUE;
}
return 0;
}
int
799 _tenEstimateEmatUpdate( tenEstimateContext *tec ) {
static const char me[]="tenEstimateEmatUpdate";
if ( tec->flag[flagDwiSet]
|| tec->flag[flagWght] ) {
if ( !tec->simulate ) {
/* HEY: ignores weights! */
if ( ell_Nm_pseudo_inv( tec->nemat, tec->nbmat ) ) {
biffMovef( TEN, ELL, "%s: trouble pseudo-inverting %ux%u B-matrix", me,
AIR_CAST( unsigned int, tec->nbmat->axis[1].size ),
AIR_CAST( unsigned int, tec->nbmat->axis[0].size ) );
return 1;
}
}
tec->flag[flagDwiSet] = AIR_FALSE;
tec->flag[flagWght] = AIR_FALSE;
}
return 0;
}
int
822 tenEstimateUpdate( tenEstimateContext *tec ) {
static const char me[]="tenEstimateUpdate";
int EE;
EE = 0;
if ( !EE ) EE |= _tenEstimateCheck( tec );
if ( !EE ) EE |= _tenEstimateNumUpdate( tec );
if ( !EE ) EE |= _tenEstimateAllAllocUpdate( tec );
if ( !EE ) EE |= _tenEstimateDwiAllocUpdate( tec );
if ( !EE ) EE |= _tenEstimateAllSetUpdate( tec );
if ( !EE ) EE |= _tenEstimateDwiSetUpdate( tec );
if ( !EE ) EE |= _tenEstimateWghtUpdate( tec );
if ( !EE ) EE |= _tenEstimateEmatUpdate( tec );
if ( EE ) {
biffAddf( TEN, "%s: problem updating", me );
return 1;
}
return 0;
}
/*
** from given tec->all_f or tec->all_d ( whichever is non-NULL ), sets:
** tec->all[],
** tec->dwi[]
** tec->knownB0, if !tec->estimateB0,
** tec->mdwi,
** tec->conf ( from tec->mdwi )
*/
void
851 _tenEstimateValuesSet( tenEstimateContext *tec ) {
unsigned int allIdx, dwiIdx, B0Num;
double normSum;
if ( !tec->estimateB0 ) {
tec->knownB0 = 0;
} else {
tec->knownB0 = AIR_NAN;
}
normSum = 0;
tec->mdwi = 0;
B0Num = 0;
dwiIdx = 0;
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
if ( !tec->skipLut[allIdx] ) {
tec->all[allIdx] = ( tec->all_f
? tec->all_f[allIdx]
: tec->all_d[allIdx] );
tec->mdwi += tec->bnorm[allIdx]*tec->all[allIdx];
normSum += tec->bnorm[allIdx];
if ( tec->estimateB0 || tec->bnorm[allIdx] ) {
tec->dwi[dwiIdx++] = tec->all[allIdx];
} else {
tec->knownB0 += tec->all[allIdx];
B0Num += 1;
}
}
}
if ( !tec->estimateB0 ) {
tec->knownB0 /= B0Num;
}
tec->mdwi /= normSum;
if ( tec->dwiConfSoft > 0 ) {
tec->conf = AIR_AFFINE( -1, airErf( ( tec->mdwi - tec->dwiConfThresh )
/tec->dwiConfSoft ), 1,
0, 1 );
} else {
tec->conf = tec->mdwi > tec->dwiConfThresh;
}
return;
}
/*
** ASSUMES THAT dwiTmp[] has been stuff with all values simulated from model
*/
double
897 _tenEstimateErrorDwi( tenEstimateContext *tec ) {
unsigned int dwiIdx;
double err, diff;
err = 0;
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
diff = tec->dwi[dwiIdx] - tec->dwiTmp[dwiIdx];
/*
avg = ( tec->dwi[dwiIdx] + tec->dwiTmp[dwiIdx] )/2;
avg = AIR_ABS( avg );
if ( avg ) {
err += diff*diff/( avg*avg );
}
*/
err += diff*diff;
}
err /= tec->dwiNum;
return sqrt( err );
}
double
917 _tenEstimateErrorLogDwi( tenEstimateContext *tec ) {
unsigned int dwiIdx;
double err, diff;
err = 0;
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
diff = ( log( AIR_MAX( tec->valueMin, tec->dwi[dwiIdx] ) )
- log( AIR_MAX( tec->valueMin, tec->dwiTmp[dwiIdx] ) ) );
err += diff*diff;
}
err /= tec->dwiNum;
return sqrt( err );
}
/*
** sets:
** tec->dwiTmp[]
** and sets of all of them, regardless of estimateB0
*/
int
937 _tenEstimate1TensorSimulateSingle( tenEstimateContext *tec,
double sigma, double bValue, double B0,
const double ten[7] ) {
static const char me[]="_tenEstimate1TensorSimulateSingle";
unsigned int dwiIdx, jj;
double nr, ni, vv;
const double *bmat;
if ( !( ten && ten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( sigma ) && sigma >= 0
&& AIR_EXISTS( bValue ) && AIR_EXISTS( B0 ) ) ) {
biffAddf( TEN, "%s: got bad args: sigma %g, bValue %g, B0 %g\n", me,
sigma, bValue, B0 );
return 1;
}
bmat = AIR_CAST( const double *, tec->nbmat->data );
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
vv = 0;
for ( jj=0; jj<6; jj++ ) {
vv += bmat[jj]*ten[1+jj];
}
/*
fprintf( stderr, "!%s: sigma = %g, bValue = %g, B0 = %g\n", me,
sigma, bValue, B0 );
fprintf( stderr, "!%s[%u]: bmat=( %g %g %g %g %g %g )."
"ten=( %g %g %g %g %g %g )\n",
me, dwiIdx,
bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5],
ten[1], ten[2], ten[3], ten[4], ten[5], ten[6] );
fprintf( stderr, "!%s: %g * exp( - %g * %g ) = %g * exp( %g ) = "
"%g * %g = ... \n", me,
B0, bValue, vv, B0, -bValue*vv, B0, exp( -bValue*vv ) );
*/
/* need AIR_MAX( 0, vv ) because B:D might be negative */
vv = B0*exp( -bValue*AIR_MAX( 0, vv ) );
/*
fprintf( stderr, "!%s: vv = %g\n", me, vv );
*/
if ( sigma > 0 ) {
airNormalRand( &nr, &ni );
nr *= sigma;
ni *= sigma;
vv = sqrt( ( vv+nr )*( vv+nr ) + ni*ni );
}
tec->dwiTmp[dwiIdx] = vv;
if ( !AIR_EXISTS( tec->dwiTmp[dwiIdx] ) ) {
fprintf( stderr, "**********************************\n" );
}
/*
if ( tec->verbose ) {
fprintf( stderr, "%s: dwi[%u] = %g\n", me, dwiIdx, tec->dwiTmp[dwiIdx] );
}
*/
bmat += tec->nbmat->axis[0].size;
}
return 0;
}
int
1001 tenEstimate1TensorSimulateSingle_f( tenEstimateContext *tec,
float *simval,
float sigma, float bValue, float B0,
const float _ten[7] ) {
static const char me[]="tenEstimate1TensorSimulateSingle_f";
unsigned int allIdx, dwiIdx;
double ten[7];
if ( !( tec && simval && _ten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
TEN_T_COPY( ten, _ten );
if ( _tenEstimate1TensorSimulateSingle( tec, sigma, bValue, B0, ten ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
dwiIdx = 0;
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
if ( tec->estimateB0 || tec->bnorm[allIdx] ) {
simval[allIdx] = AIR_CAST( float, tec->dwiTmp[dwiIdx++] );
} else {
simval[allIdx] = B0;
}
}
return 0;
}
int
1031 tenEstimate1TensorSimulateSingle_d( tenEstimateContext *tec,
double *simval,
double sigma, double bValue, double B0,
const double ten[7] ) {
static const char me[]="tenEstimate1TensorSimulateSingle_d";
unsigned int allIdx, dwiIdx;
if ( !( tec && simval && ten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_EXISTS( sigma ) && sigma >= 0
&& AIR_EXISTS( bValue ) && AIR_EXISTS( B0 ) ) ) {
biffAddf( TEN, "%s: got bad bargs sigma %g, bValue %g, B0 %g\n", me,
sigma, bValue, B0 );
return 1;
}
if ( _tenEstimate1TensorSimulateSingle( tec, sigma, bValue, B0, ten ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
dwiIdx = 0;
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
if ( tec->estimateB0 || tec->bnorm[allIdx] ) {
simval[allIdx] = tec->dwiTmp[dwiIdx++];
} else {
simval[allIdx] = B0;
}
}
return 0;
}
int
1065 tenEstimate1TensorSimulateVolume( tenEstimateContext *tec,
Nrrd *ndwi,
double sigma, double bValue,
const Nrrd *nB0, const Nrrd *nten,
int outType, int keyValueSet ) {
static const char me[]="tenEstimate1TensorSimulateVolume";
size_t sizeTen, sizeX, sizeY, sizeZ, NN, II;
double ( *tlup )( const void *, size_t ), ( *blup )( const void *, size_t ),
( *lup )( const void *, size_t ), ten_d[7], *dwi_d, B0;
float *dwi_f, ten_f[7];
unsigned int tt, allIdx;
int axmap[4], E;
airArray *mop;
char stmp[3][AIR_STRLEN_SMALL];
if ( !( tec && ndwi && nB0 && nten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
/* this should have been done by update( ), but why not */
if ( _tenEstimateCheck( tec ) ) {
biffAddf( TEN, "%s: problem in given context", me );
return 1;
}
if ( !( AIR_EXISTS( sigma ) && sigma >= 0.0
&& AIR_EXISTS( bValue ) && bValue >= 0.0 ) ) {
biffAddf( TEN, "%s: got invalid sigma ( %g ) or bValue ( %g )\n", me,
sigma, bValue );
return 1;
}
if ( airEnumValCheck( nrrdType, outType ) ) {
biffAddf( TEN, "%s: requested output type %d not valid", me, outType );
return 1;
}
if ( !( nrrdTypeFloat == outType || nrrdTypeDouble == outType ) ) {
biffAddf( TEN, "%s: requested output type ( %s ) not %s or %s", me,
airEnumStr( nrrdType, outType ),
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ) );
return 1;
}
mop = airMopNew( );
sizeTen = nrrdKindSize( nrrdKind3DMaskedSymMatrix );
sizeX = nten->axis[1].size;
sizeY = nten->axis[2].size;
sizeZ = nten->axis[3].size;
if ( !( 3 == nB0->dim &&
sizeX == nB0->axis[0].size &&
sizeY == nB0->axis[1].size &&
sizeZ == nB0->axis[2].size ) ) {
biffAddf( TEN, "%s: given B0 ( %u-D ) volume not 3-D %sx%sx%s", me, nB0->dim,
airSprintSize_t( stmp[0], sizeX ),
airSprintSize_t( stmp[1], sizeY ),
airSprintSize_t( stmp[2], sizeZ ) );
return 1;
}
if ( nrrdMaybeAlloc_va( ndwi, outType, 4,
AIR_CAST( size_t, tec->allNum ), sizeX, sizeY, sizeZ ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate DWI output", me );
airMopError( mop ); return 1;
}
NN = sizeX * sizeY * sizeZ;
tlup = nrrdDLookup[nten->type];
blup = nrrdDLookup[nB0->type];
dwi_d = AIR_CAST( double *, ndwi->data );
dwi_f = AIR_CAST( float *, ndwi->data );
E = 0;
for ( II=0; !E && II<NN; II++ ) {
B0 = blup( nB0->data, II );
if ( nrrdTypeDouble == outType ) {
for ( tt=0; tt<7; tt++ ) {
ten_d[tt] = tlup( nten->data, tt + sizeTen*II );
}
E = tenEstimate1TensorSimulateSingle_d( tec, dwi_d, sigma,
bValue, B0, ten_d );
dwi_d += tec->allNum;
} else {
for ( tt=0; tt<7; tt++ ) {
ten_f[tt] = AIR_CAST( float, tlup( nten->data, tt + sizeTen*II ) );
}
E = tenEstimate1TensorSimulateSingle_f( tec, dwi_f,
AIR_CAST( float, sigma ),
AIR_CAST( float, bValue ),
AIR_CAST( float, B0 ),
ten_f );
dwi_f += tec->allNum;
}
if ( E ) {
biffAddf( TEN, "%s: failed at sample %s", me,
airSprintSize_t( stmp[0], II ) );
airMopError( mop ); return 1;
}
}
ELL_4V_SET( axmap, -1, 1, 2, 3 );
nrrdAxisInfoCopy( ndwi, nten, axmap, NRRD_AXIS_INFO_NONE );
ndwi->axis[0].kind = nrrdKindList;
if ( nrrdBasicInfoCopy( ndwi, nten,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffMovef( TEN, NRRD, "%s:", me );
airMopError( mop ); return 1;
}
if ( keyValueSet ) {
char keystr[AIR_STRLEN_MED], valstr[AIR_STRLEN_MED];
nrrdKeyValueAdd( ndwi, tenDWMRIModalityKey, tenDWMRIModalityVal );
sprintf( valstr, "%g", bValue );
nrrdKeyValueAdd( ndwi, tenDWMRIBValueKey, valstr );
if ( tec->_ngrad ) {
lup = nrrdDLookup[tec->_ngrad->type];
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
sprintf( keystr, tenDWMRIGradKeyFmt, allIdx );
sprintf( valstr, "%g %g %g",
lup( tec->_ngrad->data, 0 + 3*allIdx ),
lup( tec->_ngrad->data, 1 + 3*allIdx ),
lup( tec->_ngrad->data, 2 + 3*allIdx ) );
nrrdKeyValueAdd( ndwi, keystr, valstr );
}
} else {
lup = nrrdDLookup[tec->_nbmat->type];
for ( allIdx=0; allIdx<tec->allNum; allIdx++ ) {
sprintf( keystr, tenDWMRIBmatKeyFmt, allIdx );
sprintf( valstr, "%g %g %g %g %g %g",
lup( tec->_nbmat->data, 0 + 6*allIdx ),
lup( tec->_nbmat->data, 1 + 6*allIdx ),
lup( tec->_nbmat->data, 2 + 6*allIdx ),
lup( tec->_nbmat->data, 3 + 6*allIdx ),
lup( tec->_nbmat->data, 4 + 6*allIdx ),
lup( tec->_nbmat->data, 5 + 6*allIdx ) );
nrrdKeyValueAdd( ndwi, keystr, valstr );
}
}
}
airMopOkay( mop );
return 0;
}
/*
** sets:
** tec->ten[1..6]
** tec->B0, if tec->estimateB0
*/
int
1210 _tenEstimate1Tensor_LLS( tenEstimateContext *tec ) {
static const char me[]="_tenEstimate1Tensor_LLS";
double *emat, tmp, logB0;
unsigned int ii, jj;
emat = AIR_CAST( double *, tec->nemat->data );
if ( tec->verbose ) {
fprintf( stderr, "!%s: estimateB0 = %d\n", me, tec->estimateB0 );
}
if ( tec->estimateB0 ) {
for ( ii=0; ii<tec->allNum; ii++ ) {
tmp = AIR_MAX( tec->valueMin, tec->all[ii] );
tec->allTmp[ii] = -log( tmp )/( tec->bValue );
}
for ( jj=0; jj<7; jj++ ) {
tmp = 0;
for ( ii=0; ii<tec->allNum; ii++ ) {
tmp += emat[ii + tec->allNum*jj]*tec->allTmp[ii];
}
if ( jj < 6 ) {
tec->ten[1+jj] = tmp;
if ( !AIR_EXISTS( tmp ) ) {
biffAddf( TEN, "%s: estimated non-existent tensor coef ( %u ) %g",
me, jj, tmp );
return 1;
}
} else {
/* we're on seventh row, for finding B0 */
tec->estimatedB0 = exp( tec->bValue*tmp );
tec->estimatedB0 = AIR_MIN( FLT_MAX, tec->estimatedB0 );
if ( !AIR_EXISTS( tec->estimatedB0 ) ) {
biffAddf( TEN, "%s: estimated non-existent B0 %g ( b=%g, tmp=%g )",
me, tec->estimatedB0, tec->bValue, tmp );
return 1;
}
}
}
} else {
logB0 = log( AIR_MAX( tec->valueMin, tec->knownB0 ) );
for ( ii=0; ii<tec->dwiNum; ii++ ) {
tmp = AIR_MAX( tec->valueMin, tec->dwi[ii] );
tec->dwiTmp[ii] = ( logB0 - log( tmp ) )/( tec->bValue );
}
for ( jj=0; jj<6; jj++ ) {
tmp = 0;
for ( ii=0; ii<tec->dwiNum; ii++ ) {
tmp += emat[ii + tec->dwiNum*jj]*tec->dwiTmp[ii];
if ( tec->verbose > 5 ) {
fprintf( stderr, "%s: emat[( %u, %u )=%u]*dwi[%u] = %g*%g --> %g\n", me,
ii, jj, ii + tec->dwiNum*jj, ii,
emat[ii + tec->dwiNum*jj], tec->dwiTmp[ii],
tmp );
}
}
tec->ten[1+jj] = tmp;
}
}
return 0;
}
int
1271 _tenEstimate1Tensor_WLS( tenEstimateContext *tec ) {
static const char me[]="_tenEstimate1Tensor_WLS";
unsigned int dwiIdx, iter;
double *wght, dwi, sum;
if ( !tec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
wght = AIR_CAST( double *, tec->nwght->data );
sum = 0;
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
dwi = tec->dwi[dwiIdx];
dwi = AIR_MAX( tec->valueMin, dwi );
sum += dwi*dwi;
}
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
dwi = tec->dwi[dwiIdx];
dwi = AIR_MAX( tec->valueMin, dwi );
wght[dwiIdx + tec->dwiNum*dwiIdx] = dwi*dwi/sum;
}
if ( ell_Nm_wght_pseudo_inv( tec->nemat, tec->nbmat, tec->nwght ) ) {
biffMovef( TEN, ELL,
"%s( 1 ): trouble wght-pseudo-inverting %ux%u B-matrix", me,
AIR_CAST( unsigned int, tec->nbmat->axis[1].size ),
AIR_CAST( unsigned int, tec->nbmat->axis[0].size ) );
return 1;
}
/*
nrrdSave( "wght.txt", tec->nwght, NULL );
nrrdSave( "bmat.txt", tec->nbmat, NULL );
nrrdSave( "emat.txt", tec->nemat, NULL );
*/
if ( _tenEstimate1Tensor_LLS( tec ) ) {
biffAddf( TEN, "%s: initial weighted LLS failed", me );
return 1;
}
for ( iter=0; iter<tec->WLSIterNum; iter++ ) {
/*
fprintf( stderr, "!%s: bValue = %g, B0 = %g, ten = %g %g %g %g %g %g\n",
me,
tec->bValue, ( tec->estimateB0 ? tec->estimatedB0 : tec->knownB0 ),
tec->ten[1], tec->ten[2], tec->ten[3],
tec->ten[4], tec->ten[5], tec->ten[6] );
*/
if ( _tenEstimate1TensorSimulateSingle( tec, 0.0, tec->bValue,
( tec->estimateB0 ?
tec->estimatedB0
: tec->knownB0 ), tec->ten ) ) {
biffAddf( TEN, "%s: iter %u", me, iter );
return 1;
}
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
dwi = tec->dwiTmp[dwiIdx];
if ( !AIR_EXISTS( dwi ) ) {
biffAddf( TEN, "%s: bad simulated dwi[%u] == %g ( iter %u )",
me, dwiIdx, dwi, iter );
return 1;
}
wght[dwiIdx + tec->dwiNum*dwiIdx] = AIR_MAX( FLT_MIN, dwi*dwi );
}
if ( ell_Nm_wght_pseudo_inv( tec->nemat, tec->nbmat, tec->nwght ) ) {
biffMovef( TEN, ELL, "%s( 2 ): trouble w/ %ux%u B-matrix ( iter %u )", me,
AIR_CAST( unsigned int, tec->nbmat->axis[1].size ),
AIR_CAST( unsigned int, tec->nbmat->axis[0].size ), iter );
return 1;
}
_tenEstimate1Tensor_LLS( tec );
}
return 0;
}
int
1347 _tenEstimate1TensorGradient( tenEstimateContext *tec,
double *gradB0P, double gradTen[7],
double B0, double ten[7],
double epsilon,
int ( *gradientCB )( tenEstimateContext *tec,
double *gradB0P, double gTen[7],
double B0, double ten[7] ),
int ( *badnessCB )( tenEstimateContext *tec,
double *badP,
double B0, double ten[7] ) ) {
static const char me[]="_tenEstimate1TensorGradper";
double forwTen[7], backTen[7], forwBad, backBad;
unsigned int ti;
if ( !( tec && gradB0P && gradTen && badnessCB && ten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( gradientCB ) {
if ( gradientCB( tec, gradB0P, gradTen, B0, ten ) ) {
biffAddf( TEN, "%s: problem with grad callback", me );
return 1;
}
} else {
/* we find gradient manually */
gradTen[0] = 0;
for ( ti=0; ti<6; ti++ ) {
TEN_T_COPY( forwTen, ten );
TEN_T_COPY( backTen, ten );
forwTen[ti+1] += epsilon;
backTen[ti+1] -= epsilon;
if ( badnessCB( tec, &forwBad, B0, forwTen )
|| badnessCB( tec, &backBad, B0, backTen ) ) {
biffAddf( TEN, "%s: trouble at ti=%u", me, ti );
return 1;
}
gradTen[ti+1] = ( forwBad - backBad )/( 2*epsilon );
}
}
return 0;
}
int
1392 _tenEstimate1TensorDescent( tenEstimateContext *tec,
int ( *gradientCB )( tenEstimateContext *tec,
double *gradB0,
double gradTen[7],
double B0,
double ten[7] ),
int ( *badnessCB )( tenEstimateContext *tec,
double *badP,
double B0,
double ten[7] ) ) {
static const char me[]="_tenEstimate1TensorDescent";
double currB0, lastB0, currTen[7], lastTen[7], gradB0=AIR_NAN, gradTen[7],
epsilon,
stepSize, badInit, bad, badDelta, stepSizeMin = 0.00000000001, badLast;
unsigned int iter, iterMax = 100000;
/* start with WLS fit since its probably close */
_tenEstimate1Tensor_WLS( tec );
if ( tec->verbose ) {
fprintf( stderr, "%s: WLS gave %g %g %g %g %g %g\n", me,
tec->ten[1], tec->ten[2], tec->ten[3],
tec->ten[4], tec->ten[5], tec->ten[6] );
}
if ( badnessCB( tec, &badInit,
( tec->estimateB0 ? tec->estimatedB0 : tec->knownB0 ), tec->ten )
|| !AIR_EXISTS( badInit ) ) {
biffAddf( TEN, "%s: problem getting initial bad", me );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "\n%s: ________________________________________\n", me );
fprintf( stderr, "%s: start: badInit = %g ---------------\n", me, badInit );
}
epsilon = 0.0000001;
newepsilon:
if ( _tenEstimate1TensorGradient( tec, &gradB0, gradTen,
( tec->estimateB0
? tec->estimatedB0
: tec->knownB0 ),
tec->ten, epsilon,
gradientCB, badnessCB ) ) {
biffAddf( TEN, "%s: problem getting initial gradient", me );
return 1;
}
if ( !( AIR_EXISTS( gradB0 ) || 0 <= TEN_T_NORM( gradTen ) ) ) {
biffAddf( TEN, "%s: got bad gradB0 %g or zero-norm tensor grad",
me, gradB0 );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s: gradTen ( %s ) = %g %g %g %g %g %g\n", me,
gradientCB ? "analytic" : "cent-diff",
gradTen[1], gradTen[2], gradTen[3],
gradTen[4], gradTen[5], gradTen[6] );
}
stepSize = 0.1;
do {
stepSize /= 10;
TEN_T_SCALE_ADD2( currTen, 1.0, tec->ten, -stepSize, gradTen );
if ( tec->estimateB0 ) {
currB0 = tec->estimatedB0 + -stepSize*gradB0;
} else {
currB0 = tec->knownB0;
}
if ( badnessCB( tec, &bad, currB0, currTen )
|| !AIR_EXISTS( bad ) ) {
biffAddf( TEN, "%s: problem getting badness for stepSize", me );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s: ************ stepSize = %g --> bad = %g\n",
me, stepSize, bad );
}
} while ( bad > badInit && stepSize > stepSizeMin );
if ( stepSize <= stepSizeMin ) {
if ( epsilon > FLT_MIN ) {
epsilon /= 10;
fprintf( stderr, "%s: re-trying initial step w/ eps %g\n", me, epsilon );
goto newepsilon;
} else {
biffAddf( TEN, "%s: never found a usable step size", me );
return 1;
}
} else if ( tec->verbose ) {
biffAddf( TEN, "%s: using step size %g\n", me, stepSize );
}
iter = 0;
badLast = bad;
do {
iter++;
TEN_T_COPY( lastTen, currTen );
lastB0 = currB0;
if ( 0 == ( iter % 3 ) ) {
if ( _tenEstimate1TensorGradient( tec, &gradB0, gradTen,
currB0, currTen, stepSize/5,
gradientCB, badnessCB )
|| !AIR_EXISTS( gradB0 ) ) {
biffAddf( TEN, "%s[%u]: problem getting iter grad", me, iter );
return 1;
}
}
TEN_T_SCALE_INCR( currTen, -stepSize, gradTen );
if ( tec->estimateB0 ) {
currB0 -= stepSize*gradB0;
}
if ( badnessCB( tec, &bad, currB0, currTen )
|| !AIR_EXISTS( bad ) ) {
biffAddf( TEN, "%s[%u]: problem getting badness during grad", me, iter );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s: %u bad = %g\n", me, iter, bad );
}
badDelta = bad - badLast;
badLast = bad;
if ( badDelta > 0 ) {
stepSize /= 10;
if ( tec->verbose ) {
fprintf( stderr, "%s: badDelta %g > 0 ---> stepSize = %g\n",
me, badDelta, stepSize );
}
badDelta = -1; /* bogus improvement for loop continuation */
TEN_T_COPY( currTen, lastTen );
currB0 = lastB0;
}
} while ( iter < iterMax && ( iter < 2 || badDelta < -0.00005 ) );
if ( iter >= iterMax ) {
biffAddf( TEN, "%s: didn't converge after %u iterations", me, iter );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s: finished\n", me );
}
ELL_6V_COPY( tec->ten+1, currTen+1 );
tec->estimatedB0 = currB0;
return 0;
}
int
1538 _tenEstimate1Tensor_GradientNLS( tenEstimateContext *tec,
double *gradB0P, double gradTen[7],
double currB0, double currTen[7] ) {
static const char me[]="_tenEstimate1Tensor_GradientNLS";
double *bmat, dot, tmp, diff, scl;
unsigned int dwiIdx;
if ( !( tec && gradB0P && gradTen && currTen ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
*gradB0P = 0;
TEN_T_SET( gradTen, 0, 0, 0, 0, 0, 0, 0 );
bmat = AIR_CAST( double *, tec->nbmat->data );
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
dot = ELL_6V_DOT( bmat, currTen+1 );
tmp = currB0*exp( -( tec->bValue )*dot );
diff = tec->dwi[dwiIdx] - tmp;
scl = 2*diff*tmp*( tec->bValue );
ELL_6V_SCALE_INCR( gradTen+1, scl, bmat );
bmat += tec->nbmat->axis[0].size;
/* HEY: increment *gradB0P */
}
ELL_6V_SCALE_INCR( gradTen+1, 1.0/tec->dwiNum, gradTen+1 );
return 0;
}
int
1566 _tenEstimate1Tensor_BadnessNLS( tenEstimateContext *tec,
double *retP,
double currB0, double currTen[7] ) {
static const char me[]="_tenEstimate1Tensor_BadnessNLS";
if ( !( retP && tec ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( _tenEstimate1TensorSimulateSingle( tec, 0.0, tec->bValue,
currB0, currTen ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
if ( tec->verbose > 2 ) {
unsigned int di;
fprintf( stderr, "%s: simdwi =", me );
for ( di=0; di<tec->dwiNum; di++ ) {
fprintf( stderr, " %g", tec->dwiTmp[di] );
}
fprintf( stderr, "\n" );
}
*retP = _tenEstimateErrorDwi( tec );
if ( tec->verbose > 2 ) {
fprintf( stderr, "!%s: badness( %g, ( %g ) %g %g %g %g %g %g ) = %g\n",
me, currB0, currTen[0],
currTen[1], currTen[2], currTen[3],
currTen[4], currTen[5],
currTen[6], *retP );
}
return 0;
}
int
1600 _tenEstimate1Tensor_NLS( tenEstimateContext *tec ) {
static const char me[]="_tenEstimate1Tensor_NLS";
if ( _tenEstimate1TensorDescent( tec,
NULL
/* _tenEstimate1Tensor_GradientNLS */
,
_tenEstimate1Tensor_BadnessNLS ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
return 0;
}
int
1615 _tenEstimate1Tensor_GradientMLE( tenEstimateContext *tec,
double *gradB0P, double gradTen[7],
double currB0, double currTen[7] ) {
static const char me[]="_tenEstimate1Tensor_GradientMLE";
double *bmat, dot, barg, tmp, scl, dwi, sigma, bval;
unsigned int dwiIdx;
if ( !( tec && gradB0P && gradTen && currTen ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s grad ( currTen = %g %g %g %g %g %g )\n", me,
currTen[1], currTen[2], currTen[3],
currTen[4], currTen[5],
currTen[6] );
}
TEN_T_SET( gradTen, 0, 0, 0, 0, 0, 0, 0 );
*gradB0P = 0;
sigma = tec->sigma;
bval = tec->bValue;
bmat = AIR_CAST( double *, tec->nbmat->data );
for ( dwiIdx=0; dwiIdx<tec->dwiNum; dwiIdx++ ) {
dwi = tec->dwi[dwiIdx];
dot = ELL_6V_DOT( bmat, currTen+1 );
barg = exp( -bval*dot )*( dwi/sigma )*( currB0/sigma );
tmp = ( exp( bval*dot )/sigma )*dwi/airBesselI0( barg );
if ( tec->verbose ) {
fprintf( stderr, "%s[%u]: dot = %g, barg = %g, tmp = %g\n", me, dwiIdx,
dot, barg, tmp );
}
if ( tmp > DBL_MIN ) {
tmp = currB0/sigma - tmp*airBesselI1( barg );
} else {
tmp = currB0/sigma;
}
if ( tec->verbose ) {
fprintf( stderr, " ---- tmp = %g\n", tmp );
}
scl = tmp*exp( -2*bval*dot )*bval*currB0/sigma;
ELL_6V_SCALE_INCR( gradTen+1, scl, bmat );
if ( tec->verbose ) {
fprintf( stderr, "%s[%u]: bmat = %g %g %g %g %g %g\n",
me, dwiIdx,
bmat[0], bmat[1], bmat[2],
bmat[3], bmat[4],
bmat[5] );
fprintf( stderr, "%s[%u]: scl = %g -> gradTen = %g %g %g %g %g %g\n",
me, dwiIdx, scl,
gradTen[1], gradTen[2], gradTen[3],
gradTen[4], gradTen[5],
gradTen[6] );
}
if ( !AIR_EXISTS( scl ) ) {
TEN_T_SET( gradTen, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
*gradB0P = AIR_NAN;
biffAddf( TEN, "%s: scl = %g, very sorry", me, scl );
return 1;
}
bmat += tec->nbmat->axis[0].size;
/* HEY: increment gradB0 */
}
ELL_6V_SCALE_INCR( gradTen+1, 1.0/tec->dwiNum, gradTen+1 );
if ( tec->verbose ) {
fprintf( stderr, "%s: final gradTen = %g %g %g %g %g %g\n", me,
gradTen[1], gradTen[2], gradTen[3],
gradTen[4], gradTen[5],
gradTen[6] );
}
return 0;
}
int
1690 _tenEstimate1Tensor_BadnessMLE( tenEstimateContext *tec,
double *retP,
double currB0, double curt[7] ) {
static const char me[]="_tenEstimate1Tensor_BadnessMLE";
unsigned int dwiIdx;
double *bmat, sum, rice, logrice=0, mesdwi=0, simdwi=0, dot=0;
int E;
E = 0;
sum = 0;
bmat = AIR_CAST( double *, tec->nbmat->data );
for ( dwiIdx=0; !E && dwiIdx<tec->dwiNum; dwiIdx++ ) {
dot = ELL_6V_DOT( bmat, curt+1 );
simdwi = currB0*exp( -( tec->bValue )*dot );
mesdwi = tec->dwi[dwiIdx];
if ( !E ) E |= _tenRician( &rice, mesdwi, simdwi, tec->sigma );
if ( !E ) E |= !AIR_EXISTS( rice );
if ( !E ) logrice = log( rice + DBL_MIN );
if ( !E ) sum += logrice;
if ( !E ) E |= !AIR_EXISTS( sum );
if ( !E ) bmat += tec->nbmat->axis[0].size;
}
if ( E ) {
biffAddf( TEN, "%s[%u]: dot = ( %g %g %g %g %g %g ).( %g %g %g %g %g %g ) = %g",
me, dwiIdx,
bmat[0], bmat[1], bmat[2], bmat[3], bmat[4], bmat[5],
curt[1], curt[2], curt[3], curt[4], curt[5], curt[6], dot );
biffAddf( TEN, "%s[%u]: simdwi = %g * exp( -%g * %g ) = %g * exp( %g ) "
"= %g * %g = %g", me, dwiIdx,
currB0, tec->bValue, dot,
currB0, -( tec->bValue )*dot,
currB0, exp( -( tec->bValue )*dot ),
currB0*exp( -( tec->bValue )*dot ) );
biffAddf( TEN, "%s[%u]: mesdwi = %g, simdwi = %g, sigma = %g", me, dwiIdx,
mesdwi, simdwi, tec->sigma );
biffAddf( TEN, "%s[%u]: rice = %g, logrice = %g, sum = %g", me, dwiIdx,
rice, logrice, sum );
*retP = AIR_NAN;
return 1;
}
*retP = -sum/tec->dwiNum;
return 0;
}
int
1735 _tenEstimate1Tensor_MLE( tenEstimateContext *tec ) {
static const char me[]="_tenEstimate1Tensor_MLE";
if ( _tenEstimate1TensorDescent( tec, NULL,
_tenEstimate1Tensor_BadnessMLE ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
return 0;
}
/*
** sets:
** tec->ten[0] ( from tec->conf )
** tec->time, if tec->recordTime
** tec->errorDwi, if tec->recordErrorDwi
** tec->errorLogDwi, if tec->recordErrorLogDwi
** tec->likelihoodDwi, if tec->recordLikelihoodDwi
*/
int
1756 _tenEstimate1TensorSingle( tenEstimateContext *tec ) {
static const char me[]="_tenEstimate1TensorSingle";
double time0, B0;
int E;
_tenEstimateOutputInit( tec );
time0 = tec->recordTime ? airTime( ) : 0;
_tenEstimateValuesSet( tec );
tec->ten[0] = tec->conf;
switch( tec->estimate1Method ) {
case tenEstimate1MethodLLS:
E = _tenEstimate1Tensor_LLS( tec );
break;
case tenEstimate1MethodWLS:
E = _tenEstimate1Tensor_WLS( tec );
break;
case tenEstimate1MethodNLS:
E = _tenEstimate1Tensor_NLS( tec );
break;
case tenEstimate1MethodMLE:
E = _tenEstimate1Tensor_MLE( tec );
break;
default:
biffAddf( TEN, "%s: estimation method %d unimplemented",
me, tec->estimate1Method );
return 1;
}
tec->time = tec->recordTime ? airTime( ) - time0 : 0;
if ( tec->negEvalShift ) {
double eval[3];
tenEigensolve_d( eval, NULL, tec->ten );
if ( eval[2] < 0 ) {
tec->ten[1] += -eval[2];
tec->ten[4] += -eval[2];
tec->ten[6] += -eval[2];
}
}
if ( E ) {
TEN_T_SET( tec->ten, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
if ( tec->estimateB0 ) {
tec->estimatedB0 = AIR_NAN;
}
biffAddf( TEN, "%s: estimation failed", me );
return 1;
}
if ( tec->recordErrorDwi
|| tec->recordErrorLogDwi ) {
B0 = tec->estimateB0 ? tec->estimatedB0 : tec->knownB0;
if ( _tenEstimate1TensorSimulateSingle( tec, 0.0, tec->bValue,
B0, tec->ten ) ) {
biffAddf( TEN, "%s: simulation failed", me );
return 1;
}
if ( tec->recordErrorDwi ) {
tec->errorDwi = _tenEstimateErrorDwi( tec );
}
if ( tec->recordErrorLogDwi ) {
tec->errorLogDwi = _tenEstimateErrorLogDwi( tec );
}
}
/* HEY: record likelihood! */
return 0;
}
int
1826 tenEstimate1TensorSingle_f( tenEstimateContext *tec,
float ten[7], const float *all ) {
static const char me[]="tenEstimate1TensorSingle_f";
if ( !( tec && ten && all ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
tec->all_f = all;
tec->all_d = NULL;
/*
fprintf( stderr, "!%s( %u ): B0 = %g, %g\n", me, __LINE__,
tec->knownB0, tec->estimatedB0 );
*/
if ( _tenEstimate1TensorSingle( tec ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
/*
fprintf( stderr, "!%s( %u ): B0 = %g, %g\n", me, __LINE__,
tec->knownB0, tec->estimatedB0 );
*/
TEN_T_COPY_TT( ten, float, tec->ten );
return 0;
}
int
1855 tenEstimate1TensorSingle_d( tenEstimateContext *tec,
double ten[7], const double *all ) {
static const char me[]="tenEstimate1TensorSingle_d";
unsigned int ii;
if ( !( tec && ten && all ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
tec->all_f = NULL;
tec->all_d = all;
if ( tec->verbose ) {
for ( ii=0; ii<tec->allNum; ii++ ) {
fprintf( stderr, "%s: dwi[%u] = %g\n", me, ii,
tec->all_d ? tec->all_d[ii] : tec->all_f[ii] );
}
fprintf( stderr, "%s: will estimate by %d ( %s ) \n"
" estimateB0 %d; valueMin %g\n", me,
tec->estimate1Method,
airEnumStr( tenEstimate1Method, tec->estimate1Method ),
tec->estimateB0, tec->valueMin );
}
if ( _tenEstimate1TensorSingle( tec ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
if ( tec->verbose ) {
fprintf( stderr, "%s: ten = %g %g %g %g %g %g %g\n", me,
tec->ten[0],
tec->ten[1], tec->ten[2], tec->ten[3],
tec->ten[4], tec->ten[5],
tec->ten[6] );
}
TEN_T_COPY( ten, tec->ten );
return 0;
}
int
1894 tenEstimate1TensorVolume4D( tenEstimateContext *tec,
Nrrd *nten, Nrrd **nB0P, Nrrd **nterrP,
const Nrrd *ndwi, int outType ) {
static const char me[]="tenEstimate1TensorVolume4D";
char doneStr[20];
size_t sizeTen, sizeX, sizeY, sizeZ, NN, II, tick;
double *all, ten[7], ( *lup )( const void *, size_t ),
( *ins )( void *v, size_t I, double d );
unsigned int dd;
airArray *mop;
int axmap[4];
char stmp[AIR_STRLEN_SMALL];
#if 0
#define NUM 800
double val[NUM], minVal=0, maxVal=10, arg;
unsigned int valIdx;
Nrrd *nval;
for ( valIdx=0; valIdx<NUM; valIdx++ ) {
arg = AIR_AFFINE( 0, valIdx, NUM-1, minVal, maxVal );
if ( _tenRician( val + valIdx, arg, 1, 1 ) ) {
biffAddf( TEN, "%s: you are out of luck", me );
return 1;
}
}
nval = nrrdNew( );
nrrdWrap( nval, val, nrrdTypeDouble, 1, AIR_CAST( size_t, NUM ) );
nrrdSave( "nval.nrrd", nval, NULL );
nrrdNix( nval );
#endif
if ( !( tec && nten && ndwi ) ) {
/* nerrP and _NB0P can be NULL */
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( ndwi ) ) {
biffMovef( TEN, NRRD, "%s: DWI volume not valid", me );
return 1;
}
if ( !( 4 == ndwi->dim && 7 <= ndwi->axis[0].size ) ) {
biffAddf( TEN, "%s: DWI volume should be 4-D with axis 0 size >= 7", me );
return 1;
}
if ( tec->allNum != ndwi->axis[0].size ) {
biffAddf( TEN, "%s: from %s info, expected %u values per sample, "
"but have %s in volume", me,
tec->_ngrad ? "gradient" : "B-matrix", tec->allNum,
airSprintSize_t( stmp, ndwi->axis[0].size ) );
return 1;
}
if ( nrrdTypeBlock == ndwi->type ) {
biffAddf( TEN, "%s: DWI volume has non-scalar type %s", me,
airEnumStr( nrrdType, ndwi->type ) );
return 1;
}
if ( airEnumValCheck( nrrdType, outType ) ) {
biffAddf( TEN, "%s: requested output type %d not valid", me, outType );
return 1;
}
if ( !( nrrdTypeFloat == outType || nrrdTypeDouble == outType ) ) {
biffAddf( TEN, "%s: requested output type ( %s ) not %s or %s", me,
airEnumStr( nrrdType, outType ),
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ) );
return 1;
}
if ( nterrP ) {
int recE, recEL, recLK;
recE = !!( tec->recordErrorDwi );
recEL = !!( tec->recordErrorLogDwi );
recLK = !!( tec->recordLikelihoodDwi );
if ( 1 != recE + recEL + recLK ) {
biffAddf( TEN, "%s: requested error volume but need exactly one of "
"recordErrorDwi, recordErrorLogDwi, recordLikelihoodDwi "
"to be set", me );
return 1;
}
}
mop = airMopNew( );
sizeTen = nrrdKindSize( nrrdKind3DMaskedSymMatrix );
sizeX = ndwi->axis[1].size;
sizeY = ndwi->axis[2].size;
sizeZ = ndwi->axis[3].size;
all = AIR_CAST( double *, calloc( tec->allNum, sizeof( double ) ) );
if ( !all ) {
biffAddf( TEN, "%s: couldn't allocate length %u array", me, tec->allNum );
airMopError( mop ); return 1;
}
airMopAdd( mop, all, airFree, airMopAlways );
if ( nrrdMaybeAlloc_va( nten, outType, 4,
sizeTen, sizeX, sizeY, sizeZ ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate tensor output", me );
airMopError( mop ); return 1;
}
if ( nB0P ) {
*nB0P = nrrdNew( );
if ( nrrdMaybeAlloc_va( *nB0P, outType, 3,
sizeX, sizeY, sizeZ ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate B0 output", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, *nB0P, ( airMopper )nrrdNuke, airMopOnError );
airMopAdd( mop, nB0P, ( airMopper )airSetNull, airMopOnError );
}
if ( nterrP ) {
*nterrP = nrrdNew( );
if ( nrrdMaybeAlloc_va( *nterrP, outType, 3,
sizeX, sizeY, sizeZ )
|| nrrdBasicInfoCopy( *nterrP, ndwi,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_MEASUREMENTFRAME_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't creatting fitting error output", me );
airMopError( mop ); return 1;
}
ELL_3V_SET( axmap, 1, 2, 3 );
nrrdAxisInfoCopy( *nterrP, ndwi, axmap, NRRD_AXIS_INFO_NONE );
airMopAdd( mop, *nterrP, ( airMopper )nrrdNuke, airMopOnError );
airMopAdd( mop, nterrP, ( airMopper )airSetNull, airMopOnError );
}
NN = sizeX * sizeY * sizeZ;
lup = nrrdDLookup[ndwi->type];
ins = nrrdDInsert[outType];
if ( tec->progress ) {
fprintf( stderr, "%s: ", me );
}
fflush( stderr );
tick = NN / 200;
tick = AIR_MAX( 1, tick );
for ( II=0; II<NN; II++ ) {
if ( tec->progress && 0 == II%tick ) {
fprintf( stderr, "%s", airDoneStr( 0, II, NN-1, doneStr ) );
}
for ( dd=0; dd<tec->allNum; dd++ ) {
all[dd] = lup( ndwi->data, dd + tec->allNum*II );
}
/*
tec->verbose = 10*( II == 42509 );
*/
if ( tec->verbose ) {
fprintf( stderr, "!%s: hello; II=%u\n", me, AIR_CAST( unsigned int, II ) );
}
if ( tenEstimate1TensorSingle_d( tec, ten, all ) ) {
biffAddf( TEN, "%s: failed at sample %s", me,
airSprintSize_t( stmp, II ) );
airMopError( mop ); return 1;
}
ins( nten->data, 0 + sizeTen*II, ten[0] );
ins( nten->data, 1 + sizeTen*II, ten[1] );
ins( nten->data, 2 + sizeTen*II, ten[2] );
ins( nten->data, 3 + sizeTen*II, ten[3] );
ins( nten->data, 4 + sizeTen*II, ten[4] );
ins( nten->data, 5 + sizeTen*II, ten[5] );
ins( nten->data, 6 + sizeTen*II, ten[6] );
if ( nB0P ) {
ins( ( *nB0P )->data, II, ( tec->estimateB0
? tec->estimatedB0
: tec->knownB0 ) );
}
if ( nterrP ) {
/* this works because above we checked that only one of the
tec->record* flags is set */
if ( tec->recordErrorDwi ) {
ins( ( *nterrP )->data, II, tec->errorDwi );
} else if ( tec->recordErrorLogDwi ) {
ins( ( *nterrP )->data, II, tec->errorLogDwi );
} else if ( tec->recordLikelihoodDwi ) {
ins( ( *nterrP )->data, II, tec->likelihoodDwi );
}
}
}
if ( tec->progress ) {
fprintf( stderr, "%s\n", airDoneStr( 0, II, NN-1, doneStr ) );
}
ELL_4V_SET( axmap, -1, 1, 2, 3 );
nrrdAxisInfoCopy( nten, ndwi, axmap, NRRD_AXIS_INFO_NONE );
nten->axis[0].kind = nrrdKind3DMaskedSymMatrix;
if ( nrrdBasicInfoCopy( nten, ndwi,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( NRRD, "%s:", me );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
static int
28 _experAlloc( tenExperSpec* espec, unsigned int num ) {
static char me[]="_experAlloc";
airFree( espec->bval ); espec->bval = NULL;
airFree( espec->grad ); espec->grad = NULL;
/* espec->wght = airFree( espec->wght ); */
if ( !num ) {
biffAddf( TEN, "%s: need a non-zero number of images", me );
return 1;
}
espec->imgNum = num;
espec->bval = AIR_CALLOC( num, double );
espec->grad = AIR_CALLOC( 3*num, double );
/* espec->wght = AIR_CALLOC( num, double ); */
if ( !( espec->bval && espec->grad /* && espec->wght */ ) ) {
biffAddf( TEN, "%s: couldn't allocate for %u images", me, num );
return 1;
}
return 0;
}
tenExperSpec*
50 tenExperSpecNew( void ) {
tenExperSpec* espec;
espec = AIR_CALLOC( 1, tenExperSpec );
espec->set = AIR_FALSE;
espec->imgNum = 0;
espec->bval = NULL;
espec->grad = NULL;
/* espec->wght = NULL; */
return espec;
}
int
63 tenExperSpecGradSingleBValSet( tenExperSpec *espec,
int insertB0,
double bval,
const double *grad,
unsigned int gradNum ) {
static const char me[]="tenExperSpecGradSingleBValSet";
unsigned int ii, imgNum, ei;
if ( !espec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( insertB0 && !ELL_3V_LEN( grad + 3*0 ) ) {
biffAddf( TEN, "%s: wanted insertB0 but gradients "
"already start with ( 0, 0, 0 )", me );
return 1;
}
imgNum = gradNum + !!insertB0;
if ( _experAlloc( espec, imgNum ) ) {
biffAddf( TEN, "%s: couldn't allocate", me );
return 1;
}
if ( insertB0 ) {
espec->bval[0] = 0;
ELL_3V_SET( espec->grad + 3*0, 1, 0, 0 );
ei = 1;
} else {
ei = 0;
}
for ( ii=0; ii<gradNum; ei++, ii++ ) {
espec->bval[ei] = bval;
ELL_3V_COPY( espec->grad + 3*ei, grad + 3*ii );
/* espec->wght[ii] = 1.0; */
}
return 0;
}
int
102 tenExperSpecGradBValSet( tenExperSpec *espec,
int insertB0,
const double *bval,
const double *grad,
unsigned int bgNum ) {
static const char me[]="tenExperSpecGradBValSet";
unsigned int ii, imgNum, ei;
if ( !espec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( insertB0 && ( !ELL_3V_LEN( grad + 3*0 ) || !bval[0] ) ) {
biffAddf( TEN, "%s: wanted insertB0 but gradients "
"already start with ( 0, 0, 0 ) or bvals start with 0", me );
return 1;
}
imgNum = bgNum + !!insertB0;
if ( _experAlloc( espec, imgNum ) ) {
biffAddf( TEN, "%s: couldn't allocate", me );
return 1;
}
if ( insertB0 ) {
espec->bval[0] = 0;
ELL_3V_SET( espec->grad + 3*0, 0, 0, 0 );
ei = 1;
} else {
ei = 0;
}
for ( ii=0; ii<bgNum; ei++, ii++ ) {
espec->bval[ei] = bval[ii];
ELL_3V_COPY( espec->grad + 3*ei, grad + 3*ii );
/* espec->wght[ii] = 1.0; */
}
return 0;
}
/*
int
tenExperSpecGradBValWghtSet( tenExperSpec *espec,
unsigned int imgNum,
const double *bval,
const double *grad,
const double *wght ) {
static const char me[]="tenExperSpecGradBValWghtSet";
unsigned int ii;
if ( !espec ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( _experAlloc( espec, imgNum ) ) {
biffAddf( TEN, "%s: couldn't allocate", me );
return 1;
}
for ( ii=0; ii<imgNum; ii++ ) {
espec->bval[ii] = bval[ii];
ELL_3V_COPY( espec->grad + 3*ii, grad + 3*ii );
espec->wght[ii] = wght[ii];
}
return 0;
}
*/
int
169 tenExperSpecFromKeyValueSet( tenExperSpec *espec, const Nrrd *ndwi ) {
static const char me[]="tenExperSpecFromKeyValueSet";
unsigned int *skip, skipNum, ii, imgNum, dwiax;
Nrrd *ngrad, *nbmat;
airArray *mop;
double len, singleBval, *bval, *grad;
for ( dwiax=0; dwiax<ndwi->dim; dwiax++ ) {
if ( nrrdKindList == ndwi->axis[dwiax].kind
|| nrrdKindVector == ndwi->axis[dwiax].kind ) {
break;
}
}
if ( ndwi->dim == dwiax ) {
biffAddf( TEN, "%s: need dwis to have a kind %s or %s axis", me,
airEnumStr( nrrdKind, nrrdKindList ),
airEnumStr( nrrdKind, nrrdKindVector ) );
return 1;
} else {
if ( 0 != dwiax ) {
biffAddf( TEN, "%s: need dwis ( kind %s or %s ) along axis 0, not %u", me,
airEnumStr( nrrdKind, nrrdKindList ),
airEnumStr( nrrdKind, nrrdKindVector ), dwiax );
return 1;
}
}
for ( ii=dwiax+1; ii<ndwi->dim; ii++ ) {
if ( nrrdKindList == ndwi->axis[ii].kind
|| nrrdKindVector == ndwi->axis[ii].kind ) {
break;
}
}
if ( ii < ndwi->dim ) {
biffAddf( TEN, "%s: saw on %u another %s or %s kind axis, after 0", me,
ii, airEnumStr( nrrdKind, nrrdKindList ),
airEnumStr( nrrdKind, nrrdKindVector ) );
return 1;
}
if ( tenDWMRIKeyValueParse( &ngrad, &nbmat, &singleBval,
&skip, &skipNum, ndwi ) ) {
biffAddf( TEN, "%s: trouble parsing DWI info from key/value pairs", me );
return 1;
}
mop = airMopNew( );
if ( ngrad ) {
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopAlways );
}
if ( nbmat ) {
airMopAdd( mop, nbmat, ( airMopper )nrrdNuke, airMopAlways );
}
if ( skip ) {
airMopAdd( mop, skip, airFree, airMopAlways );
}
if ( nbmat ) {
biffAddf( TEN, "%s: sorry, currently can't handle B-matrices here", me );
airMopError( mop ); return 1;
}
if ( skipNum ) {
biffAddf( TEN, "%s: sorry, currently can't handle skipping ( %u ) here", me,
skipNum );
airMopError( mop ); return 1;
}
imgNum = ngrad->axis[1].size;
bval = AIR_CALLOC( imgNum, double );
airMopAdd( mop, bval, airFree, airMopAlways );
grad = AIR_CAST( double *, ngrad->data );
for ( ii=0; ii<imgNum; ii++ ) {
len = ELL_3V_LEN( grad + 3*ii );
bval[ii] = singleBval*len*len;
if ( len ) {
ELL_3V_SCALE( grad + 3*ii, 1/len, grad + 3*ii );
} else {
ELL_3V_SET( grad + 3*ii, 0, 0, -1 );
}
}
if ( tenExperSpecGradBValSet( espec, AIR_FALSE, bval, grad, imgNum ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
tenExperSpec*
255 tenExperSpecNix( tenExperSpec *espec ) {
if ( espec ) {
airFree( espec->bval );
airFree( espec->grad );
/* espec->wght = airFree( espec->wght ); */
airFree( espec );
}
return NULL;
}
double
267 _tenExperSpec_sqe( const double *dwiMeas, const double *dwiSim,
const tenExperSpec *espec, int knownB0 ) {
unsigned int ii;
double sqe;
sqe = 0;
if ( knownB0 ) {
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dd;
if ( !espec->bval[ii] ) {
continue;
}
dd = dwiMeas[ii] - dwiSim[ii];
sqe += dd*dd;
/*
fprintf( stderr, "!%s: dwi[%u]: %g - %g -> %g\n", "_tenExperSpec_sqe",
ii, dwiMeas[ii], dwiSim[ii], sqe );
*/
}
} else {
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dd;
dd = dwiMeas[ii] - dwiSim[ii];
sqe += dd*dd;
}
}
return sqe;
}
double
297 _tenExperSpec_nll( const double *dwiMeas, const double *dwiSim,
const tenExperSpec *espec,
int rician, double sigma, int knownB0 ) {
double nll;
unsigned int ii;
nll = 0;
if ( rician ) {
for ( ii=0; ii<espec->imgNum; ii++ ) {
if ( knownB0 && !espec->bval[ii] ) {
continue;
}
nll += -airLogRician( dwiMeas[ii], dwiSim[ii], sigma );
}
} else {
double dd, ladd, denom;
ladd = log( sigma*sqrt( 2*AIR_PI ) );
denom = 1.0/( 2*sigma*sigma );
for ( ii=0; ii<espec->imgNum; ii++ ) {
if ( knownB0 && !espec->bval[ii] ) {
continue;
}
dd = dwiMeas[ii] - dwiSim[ii];
nll += dd*dd*denom + ladd;
}
}
return nll;
}
int
327 tenDWMRIKeyValueFromExperSpecSet( Nrrd *ndwi, const tenExperSpec *espec ) {
static char me[]="tenDWMRIKeyValueFromExperSpecSet";
char keystr[AIR_STRLEN_MED], valstr[AIR_STRLEN_MED];
double maxb, bb;
unsigned int ii;
if ( !( ndwi && espec ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
nrrdKeyValueAdd( ndwi, tenDWMRIModalityKey, tenDWMRIModalityVal );
maxb = tenExperSpecMaxBGet( espec );
sprintf( valstr, "%.17g", maxb );
nrrdKeyValueAdd( ndwi, tenDWMRIBValueKey, valstr );
for ( ii=0; ii<espec->imgNum; ii++ ) {
double vec[3];
sprintf( keystr, tenDWMRIGradKeyFmt, ii );
ELL_3V_COPY( vec, espec->grad + 3*ii );
bb = espec->bval[ii];
/* Thu Dec 20 03:25:20 CST 2012 this rescaling is not, btw,
what is causing the small discrepency between ngrad before
and after saving to KVPs */
ELL_3V_SCALE( vec, sqrt( bb/maxb ), vec );
sprintf( valstr, "%.17g %.17g %.17g", vec[0], vec[1], vec[2] );
nrrdKeyValueAdd( ndwi, keystr, valstr );
}
/* HEY what if its a full B-matrix? */
return 0;
}
/*
** learns B0 from DWIs by simple averaging of all the dwi[ii]
** without any diffusion weighting, as indicated by espec->bval[ii],
** or, returns AIR_NAN when there are no such dwi[ii]
*/
double
365 tenExperSpecKnownB0Get( const tenExperSpec *espec, const double *dwi ) {
unsigned int ii, nb;
double ret, b0;
if ( !( dwi && espec ) ) {
return AIR_NAN;
}
nb = 0;
b0 = 0.0;
for ( ii=0; ii<espec->imgNum; ii++ ) {
if ( 0 == espec->bval[ii] ) {
b0 += dwi[ii];
++nb;
}
}
if ( nb ) {
ret = b0/nb;
} else {
ret = AIR_NAN;
}
return ret;
}
double
390 tenExperSpecMaxBGet( const tenExperSpec *espec ) {
unsigned int ii;
double bval;
if ( !( espec ) ) {
return AIR_NAN;
}
bval = -1;
for ( ii=0; ii<espec->imgNum; ii++ ) {
bval = AIR_MAX( bval, espec->bval[ii] );
}
return bval;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define TEN_FIBER_INCR 512
/*
** _tenFiberProbe
**
** The job here is to probe at ( world space ) "wPos" and then set:
** tfx->fiberTen
** tfx->fiberEval ( all 3 evals )
** tfx->fiberEvec ( all 3 eigenvectors )
** if ( tfx->stop & ( 1 << tenFiberStopAniso ): tfx->fiberAnisoStop
**
** In the case of non-single-tensor tractography, we do so based on
** ten2Which ( when at the seedpoint ) or
**
** Note that for performance reasons, a non-zero return value
** ( indicating error ) and the associated use of biff, is only possible
** if seedProbe is non-zero, the reason being that problems can be
** detected at the seedpoint, and won't arise after the seedpoint.
**
** Errors from gage are indicated by *gageRet, which includes leaving
** the domain of the volume, which is used to terminate fibers.
**
** Our use of tfx->seedEvec ( shared with _tenFiberAlign ), as well as that
** of tfx->lastDir and tfx->lastDirSet, could stand to have further
** debugging and documentation ...
*/
int
54 _tenFiberProbe( tenFiberContext *tfx, int *gageRet,
double wPos[3], int seedProbe ) {
static const char me[]="_tenFiberProbe";
double iPos[3];
int ret = 0;
double tens2[2][7];
gageShapeWtoI( tfx->gtx->shape, iPos, wPos );
*gageRet = gageProbe( tfx->gtx, iPos[0], iPos[1], iPos[2] );
if ( tfx->verbose > 2 ) {
fprintf( stderr, "%s( %g, %g, %g, %s ): hi ----- %s\n", me,
iPos[0], iPos[1], iPos[2], seedProbe ? "***TRUE***" : "false",
tfx->useDwi ? "using DWIs" : "" );
}
if ( !tfx->useDwi ) {
/* normal single-tensor tracking */
TEN_T_COPY( tfx->fiberTen, tfx->gageTen );
ELL_3V_COPY( tfx->fiberEval, tfx->gageEval );
ELL_3M_COPY( tfx->fiberEvec, tfx->gageEvec );
if ( tfx->stop & ( 1 << tenFiberStopAniso ) ) {
tfx->fiberAnisoStop = tfx->gageAnisoStop[0];
}
if ( seedProbe ) {
ELL_3V_COPY( tfx->seedEvec, tfx->fiberEvec );
}
} else { /* tracking in DWIs */
if ( tfx->verbose > 2 && seedProbe ) {
fprintf( stderr, "%s: fiber type = %s\n", me,
airEnumStr( tenDwiFiberType, tfx->fiberType ) );
}
switch ( tfx->fiberType ) {
double evec[2][9], eval[2][3];
case tenDwiFiberType1Evec0:
if ( tfx->mframeUse ) {
double matTmpA[9], matTmpB[9];
TEN_T2M( matTmpA, tfx->gageTen );
ELL_3M_MUL( matTmpB, tfx->mframe, matTmpA );
ELL_3M_MUL( matTmpA, matTmpB, tfx->mframeT );
TEN_M2T( tfx->fiberTen, matTmpA );
tfx->fiberTen[0] = tfx->gageTen[0];
} else {
TEN_T_COPY( tfx->fiberTen, tfx->gageTen );
}
tenEigensolve_d( tfx->fiberEval, tfx->fiberEvec, tfx->fiberTen );
if ( tfx->stop & ( 1 << tenFiberStopAniso ) ) {
double tmp;
tmp = tenAnisoTen_d( tfx->fiberTen, tfx->anisoStopType );
tfx->fiberAnisoStop = AIR_CLAMP( 0, tmp, 1 );
}
if ( seedProbe ) {
ELL_3V_COPY( tfx->seedEvec, tfx->fiberEvec );
}
break;
case tenDwiFiberType2Evec0:
/* Estimate principal diffusion direction of each tensor */
if ( tfx->mframeUse ) {
/* Transform both the tensors */
double matTmpA[9], matTmpB[9];
TEN_T2M( matTmpA, tfx->gageTen2 + 0 );
ELL_3M_MUL( matTmpB, tfx->mframe, matTmpA );
ELL_3M_MUL( matTmpA, matTmpB, tfx->mframeT );
TEN_M2T( tens2[0], matTmpA );
/* new eigen values and vectors */
tenEigensolve_d( eval[0], evec[0], tens2[0] );
TEN_T2M( matTmpA, tfx->gageTen2 + 7 );
ELL_3M_MUL( matTmpB, tfx->mframe, matTmpA );
ELL_3M_MUL( matTmpA, matTmpB, tfx->mframeT );
TEN_M2T( tens2[1], matTmpA );
tenEigensolve_d( eval[1], evec[1], tens2[1] );
} else {
tenEigensolve_d( eval[0], evec[0], tfx->gageTen2 + 0 );
tenEigensolve_d( eval[1], evec[1], tfx->gageTen2 + 7 );
}
/* set ten2Use */
if ( seedProbe ) { /* we're on the *very* 1st probe per tract,
at the seed pt */
ELL_3V_COPY( tfx->seedEvec, evec[tfx->ten2Which] );
tfx->ten2Use = tfx->ten2Which;
if ( tfx->verbose > 2 ) {
fprintf( stderr, "%s: ** ten2Use == ten2Which == %d\n", me,
tfx->ten2Use );
}
} else {
double *lastVec, dot[2];
if ( !tfx->lastDirSet ) {
/* we're on some probe of the first step */
lastVec = tfx->seedEvec;
} else {
/* we're past the first step */
/* Arish says: "Bug len has not been initialized and don't think
its needed". The first part is not a problem; "len" is in the
*output* argument of ELL_3V_NORM. The second part seems to be
true, even though Gordon can't currently see why! */
/* ELL_3V_NORM( tfx->lastDir, tfx->lastDir, len ); */
lastVec = tfx->lastDir;
}
dot[0] = ELL_3V_DOT( lastVec, evec[0] );
dot[1] = ELL_3V_DOT( lastVec, evec[1] );
if ( dot[0] < 0 ) {
dot[0] *= -1;
ELL_3M_SCALE( evec[0], -1, evec[0] );
}
if ( dot[1] < 0 ) {
dot[1] *= -1;
ELL_3M_SCALE( evec[1], -1, evec[1] );
}
tfx->ten2Use = ( dot[0] > dot[1] ) ? 0 : 1;
if ( tfx->verbose > 2 ) {
fprintf( stderr, "%s( %g, %g, %g ): dot[0, 1] = %f, %f -> use %u\n",
me, wPos[0], wPos[1], wPos[2], dot[0], dot[1],
tfx->ten2Use );
}
}
/* based on ten2Use, set the rest of the needed fields */
if ( tfx->mframeUse ) {
TEN_T_COPY( tfx->fiberTen, tens2[tfx->ten2Use] );
} else {
TEN_T_COPY( tfx->fiberTen, tfx->gageTen2 + 7*( tfx->ten2Use ) );
}
tfx->fiberTen[0] = tfx->gageTen2[0]; /* copy confidence */
ELL_3V_COPY( tfx->fiberEval, eval[tfx->ten2Use] );
ELL_3M_COPY( tfx->fiberEvec, evec[tfx->ten2Use] );
if ( tfx->stop & ( 1 << tenFiberStopAniso ) ) {
double tmp;
tmp = tenAnisoEval_d( tfx->fiberEval, tfx->anisoStopType );
tfx->fiberAnisoStop = AIR_CLAMP( 0, tmp, 1 );
/* HEY: what about speed? */
} else {
tfx->fiberAnisoStop = AIR_NAN;
}
break;
default:
biffAddf( TEN, "%s: %s %s ( %d ) unimplemented!", me,
tenDwiFiberType->name,
airEnumStr( tenDwiFiberType, tfx->fiberType ), tfx->fiberType );
ret = 1;
} /* switch ( tfx->fiberType ) */
}
if ( tfx->verbose > 2 ) {
fprintf( stderr, "%s: fiberEvec = %g %g %g\n", me,
tfx->fiberEvec[0], tfx->fiberEvec[1], tfx->fiberEvec[2] );
}
return ret;
}
int
208 _tenFiberStopCheck( tenFiberContext *tfx ) {
static const char me[]="_tenFiberStopCheck";
if ( tfx->numSteps[tfx->halfIdx] >= TEN_FIBER_NUM_STEPS_MAX ) {
fprintf( stderr, "%s: numSteps[%d] exceeded sanity check value of %d!!\n",
me, tfx->halfIdx, TEN_FIBER_NUM_STEPS_MAX );
fprintf( stderr, "%s: Check fiber termination conditions, or recompile "
"with a larger value for TEN_FIBER_NUM_STEPS_MAX\n", me );
return tenFiberStopNumSteps;
}
if ( tfx->stop & ( 1 << tenFiberStopConfidence ) ) {
if ( tfx->fiberTen[0] < tfx->confThresh ) {
return tenFiberStopConfidence;
}
}
if ( tfx->stop & ( 1 << tenFiberStopRadius ) ) {
if ( tfx->radius < tfx->minRadius ) {
return tenFiberStopRadius;
}
}
if ( tfx->stop & ( 1 << tenFiberStopAniso ) ) {
if ( tfx->fiberAnisoStop < tfx->anisoThresh ) {
return tenFiberStopAniso;
}
}
if ( tfx->stop & ( 1 << tenFiberStopNumSteps ) ) {
if ( tfx->numSteps[tfx->halfIdx] > tfx->maxNumSteps ) {
return tenFiberStopNumSteps;
}
}
if ( tfx->stop & ( 1 << tenFiberStopLength ) ) {
if ( tfx->halfLen[tfx->halfIdx] >= tfx->maxHalfLen ) {
return tenFiberStopLength;
}
}
if ( tfx->useDwi
&& tfx->stop & ( 1 << tenFiberStopFraction )
&& tfx->gageTen2 ) { /* not all DWI fiber types use gageTen2 */
double fracUse;
fracUse = ( 0 == tfx->ten2Use
? tfx->gageTen2[7]
: 1 - tfx->gageTen2[7] );
if ( fracUse < tfx->minFraction ) {
return tenFiberStopFraction;
}
}
return 0;
}
void
258 _tenFiberAlign( tenFiberContext *tfx, double vec[3] ) {
static const char me[]="_tenFiberAlign";
double scale, dot;
if ( tfx->verbose > 2 ) {
fprintf( stderr, "%s: hi %s ( lds %d ):\t%g %g %g\n", me,
!tfx->lastDirSet ? "**" : " ",
tfx->lastDirSet, vec[0], vec[1], vec[2] );
}
if ( !( tfx->lastDirSet ) ) {
dot = ELL_3V_DOT( tfx->seedEvec, vec );
/* this is the first step ( or one of the intermediate steps
for RK ) in this fiber half; 1st half follows the
eigenvector determined at seed point, 2nd goes opposite */
if ( tfx->verbose > 2 ) {
fprintf( stderr, "!%s: dir=%d, dot=%g\n", me, tfx->halfIdx, dot );
}
if ( !tfx->halfIdx ) {
/* 1st half */
scale = dot < 0 ? -1 : 1;
} else {
/* 2nd half */
scale = dot > 0 ? -1 : 1;
}
} else {
dot = ELL_3V_DOT( tfx->lastDir, vec );
/* we have some history in this fiber half */
scale = dot < 0 ? -1 : 1;
}
ELL_3V_SCALE( vec, scale, vec );
if ( tfx->verbose > 2 ) {
fprintf( stderr, "!%s: scl = %g -> \t%g %g %g\n",
me, scale, vec[0], vec[1], vec[2] );
}
return;
}
/*
** parm[0]: lerp between 1 and the stuff below
** parm[1]: "t": ( parm[1], 0 ) is control point between ( 0, 0 ) and ( 1, 1 )
** parm[2]: "d": parabolic blend between parm[1]-parm[2] and parm[1]+parm[2]
*/
void
301 _tenFiberAnisoSpeed( double *step, double xx, double parm[3] ) {
double aa, dd, tt, yy;
tt = parm[1];
dd = parm[2];
aa = 1.0/( DBL_EPSILON + 4*dd*( 1.0-tt ) );
yy = xx - tt + dd;
xx = ( xx < tt - dd
? 0
: ( xx < tt + dd
? aa*yy*yy
: ( xx - tt )/( 1 - tt ) ) );
xx = AIR_LERP( parm[0], 1, xx );
ELL_3V_SCALE( step, xx, step );
}
/*
** -------------------------------------------------------------------
** -------------------------------------------------------------------
** The _tenFiberStep_* routines are responsible for putting a step into
** the given step[] vector. Without anisoStepSize, this should be
** UNIT LENGTH, with anisoStepSize, its scaled by that anisotropy measure
*/
void
325 _tenFiberStep_Evec( tenFiberContext *tfx, double step[3] ) {
/* fiberEvec points to the correct gage answer based on fiberType */
ELL_3V_COPY( step, tfx->fiberEvec + 3*0 );
_tenFiberAlign( tfx, step );
if ( tfx->anisoSpeedType ) {
_tenFiberAnisoSpeed( step, tfx->fiberAnisoSpeed,
tfx->anisoSpeedFunc );
}
}
void
337 _tenFiberStep_TensorLine( tenFiberContext *tfx, double step[3] ) {
double cl, evec0[3], vout[3], vin[3], len;
ELL_3V_COPY( evec0, tfx->fiberEvec + 3*0 );
_tenFiberAlign( tfx, evec0 );
if ( tfx->lastDirSet ) {
ELL_3V_COPY( vin, tfx->lastDir );
TEN_T3V_MUL( vout, tfx->fiberTen, tfx->lastDir );
ELL_3V_NORM( vout, vout, len );
_tenFiberAlign( tfx, vout ); /* HEY: is this needed? */
} else {
ELL_3V_COPY( vin, evec0 );
ELL_3V_COPY( vout, evec0 );
}
/* HEY: should be using one of the tenAnisoEval[] functions */
cl = ( tfx->fiberEval[0] - tfx->fiberEval[1] )/( tfx->fiberEval[0] + 0.00001 );
ELL_3V_SCALE_ADD3( step,
cl, evec0,
( 1-cl )*( 1-tfx->wPunct ), vin,
( 1-cl )*tfx->wPunct, vout );
/* _tenFiberAlign( tfx, step ); */
ELL_3V_NORM( step, step, len );
if ( tfx->anisoSpeedType ) {
_tenFiberAnisoSpeed( step, tfx->fiberAnisoSpeed,
tfx->anisoSpeedFunc );
}
}
void
369 _tenFiberStep_PureLine( tenFiberContext *tfx, double step[3] ) {
static const char me[]="_tenFiberStep_PureLine";
AIR_UNUSED( tfx );
AIR_UNUSED( step );
fprintf( stderr, "%s: sorry, unimplemented!\n", me );
}
void
378 _tenFiberStep_Zhukov( tenFiberContext *tfx, double step[3] ) {
static const char me[]="_tenFiberStep_Zhukov";
AIR_UNUSED( tfx );
AIR_UNUSED( step );
fprintf( stderr, "%s: sorry, unimplemented!\n", me );
}
386 void ( *
_tenFiberStep[TEN_FIBER_TYPE_MAX+1] )( tenFiberContext *, double * ) = {
NULL,
_tenFiberStep_Evec,
_tenFiberStep_Evec,
_tenFiberStep_Evec,
_tenFiberStep_TensorLine,
_tenFiberStep_PureLine,
_tenFiberStep_Zhukov
};
/*
** -------------------------------------------------------------------
** -------------------------------------------------------------------
** The _tenFiberIntegrate_* routines must assume that
** _tenFiberProbe( tfx, tfx->wPos, AIR_FALSE ) has just been called
*/
int
405 _tenFiberIntegrate_Euler( tenFiberContext *tfx, double forwDir[3] ) {
_tenFiberStep[tfx->fiberType]( tfx, forwDir );
ELL_3V_SCALE( forwDir, tfx->stepSize, forwDir );
return 0;
}
int
413 _tenFiberIntegrate_Midpoint( tenFiberContext *tfx, double forwDir[3] ) {
double loc[3], half[3];
int gret;
_tenFiberStep[tfx->fiberType]( tfx, half );
ELL_3V_SCALE_ADD2( loc, 1, tfx->wPos, 0.5*tfx->stepSize, half );
_tenFiberProbe( tfx, &gret, loc, AIR_FALSE ); if ( gret ) return 1;
_tenFiberStep[tfx->fiberType]( tfx, forwDir );
ELL_3V_SCALE( forwDir, tfx->stepSize, forwDir );
return 0;
}
int
426 _tenFiberIntegrate_RK4( tenFiberContext *tfx, double forwDir[3] ) {
double loc[3], k1[3], k2[3], k3[3], k4[3], c1, c2, c3, c4, h;
int gret;
h = tfx->stepSize;
c1 = h/6.0; c2 = h/3.0; c3 = h/3.0; c4 = h/6.0;
_tenFiberStep[tfx->fiberType]( tfx, k1 );
ELL_3V_SCALE_ADD2( loc, 1, tfx->wPos, 0.5*h, k1 );
_tenFiberProbe( tfx, &gret, loc, AIR_FALSE ); if ( gret ) return 1;
_tenFiberStep[tfx->fiberType]( tfx, k2 );
ELL_3V_SCALE_ADD2( loc, 1, tfx->wPos, 0.5*h, k2 );
_tenFiberProbe( tfx, &gret, loc, AIR_FALSE ); if ( gret ) return 1;
_tenFiberStep[tfx->fiberType]( tfx, k3 );
ELL_3V_SCALE_ADD2( loc, 1, tfx->wPos, h, k3 );
_tenFiberProbe( tfx, &gret, loc, AIR_FALSE ); if ( gret ) return 1;
_tenFiberStep[tfx->fiberType]( tfx, k4 );
ELL_3V_SET( forwDir,
c1*k1[0] + c2*k2[0] + c3*k3[0] + c4*k4[0],
c1*k1[1] + c2*k2[1] + c3*k3[1] + c4*k4[1],
c1*k1[2] + c2*k2[2] + c3*k3[2] + c4*k4[2] );
return 0;
}
452 int ( *
_tenFiberIntegrate[TEN_FIBER_INTG_MAX+1] )( tenFiberContext *tfx, double * ) = {
NULL,
_tenFiberIntegrate_Euler,
_tenFiberIntegrate_Midpoint,
_tenFiberIntegrate_RK4
};
/*
** modified body of previous tenFiberTraceSet, in order to
** permit passing the nval for storing desired probed values
*/
static int
465 _fiberTraceSet( tenFiberContext *tfx, Nrrd *nval, Nrrd *nfiber,
double *buff, unsigned int halfBuffLen,
unsigned int *startIdxP, unsigned int *endIdxP,
double seed[3] ) {
static const char me[]="_fiberTraceSet";
airArray *fptsArr[2], /* airArrays of backward ( 0 ) and forward ( 1 )
fiber points */
*pansArr[2]; /* airArrays of backward ( 0 ) and forward ( 1 )
probed values */
double *fpts[2], /* arrays storing forward and backward
fiber points */
*pans[2], /* arrays storing forward and backward
probed values */
tmp[3],
iPos[3],
currPoint[3],
forwDir[3],
*fiber, /* array of both forward and backward points,
when finished */
*valOut; /* same for probed values */
const double *pansP; /* pointer to gage's probed values */
int gret, whyStop, buffIdx, fptsIdx, pansIdx, outIdx, oldStop, keepfiber;
unsigned int i, pansLen;
airArray *mop;
airPtrPtrUnion appu;
if ( !( tfx ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( nval ) {
if ( !tfx->fiberProbeItem ) {
biffAddf( TEN, "%s: want to record probed values but no item set", me );
return 1;
}
pansLen = gageAnswerLength( tfx->gtx, tfx->pvl, tfx->fiberProbeItem );
pansP = gageAnswerPointer( tfx->gtx, tfx->pvl, tfx->fiberProbeItem );
} else {
pansLen = 0;
pansP = NULL;
}
/*
fprintf( stderr, "!%s: =========================== \n", me );
fprintf( stderr, "!%s: \n", me );
fprintf( stderr, "!%s: item %d -> pansLen = %u\n", me,
tfx->fiberProbeItem, pansLen );
fprintf( stderr, "!%s: \n", me );
fprintf( stderr, "!%s: =========================== \n", me );
*/
/* HEY: a hack to preserve the state inside tenFiberContext so that
we have fewer side effects ( tfx->maxNumSteps may still be set ) */
oldStop = tfx->stop;
if ( !nfiber ) {
if ( !( buff && halfBuffLen > 0 && startIdxP && startIdxP ) ) {
biffAddf( TEN, "%s: need either non-NULL nfiber or fpts buffer info", me );
return 1;
}
if ( tenFiberStopSet( tfx, tenFiberStopNumSteps, halfBuffLen ) ) {
biffAddf( TEN, "%s: error setting new fiber stop", me );
return 1;
}
}
/* initialize the quantities which describe the fiber halves */
tfx->halfLen[0] = tfx->halfLen[1] = 0.0;
tfx->numSteps[0] = tfx->numSteps[1] = 0;
tfx->whyStop[0] = tfx->whyStop[1] = tenFiberStopUnknown;
/*
fprintf( stderr, "!%s: try probing once, at seed %g %g %g\n", me,
seed[0], seed[1], seed[2] );
*/
/* try probing once, at seed point */
if ( tfx->useIndexSpace ) {
gageShapeItoW( tfx->gtx->shape, tmp, seed );
} else {
ELL_3V_COPY( tmp, seed );
}
if ( _tenFiberProbe( tfx, &gret, tmp, AIR_TRUE ) ) {
biffAddf( TEN, "%s: first _tenFiberProbe failed", me );
return 1;
}
if ( gret ) {
if ( gageErrBoundsSpace != tfx->gtx->errNum ) {
biffAddf( TEN, "%s: gage problem on first _tenFiberProbe: %s ( %d )",
me, tfx->gtx->errStr, tfx->gtx->errNum );
return 1;
} else {
/* the problem on the first probe was that it was out of bounds,
which is not a catastrophe; its handled the same as below */
tfx->whyNowhere = tenFiberStopBounds;
if ( nval ) {
nrrdEmpty( nval );
}
if ( nfiber ) {
nrrdEmpty( nfiber );
} else {
*startIdxP = *endIdxP = 0;
}
return 0;
}
}
/* see if we're doomed ( tract dies before it gets anywhere ) */
/* have to fake out the possible radius check, since at this point
there is no radius of curvature; this will always pass */
tfx->radius = DBL_MAX;
if ( ( whyStop = _tenFiberStopCheck( tfx ) ) ) {
/* stopped immediately at seed point, but that's not an error */
tfx->whyNowhere = whyStop;
if ( nval ) {
nrrdEmpty( nval );
}
if ( nfiber ) {
nrrdEmpty( nfiber );
} else {
*startIdxP = *endIdxP = 0;
}
return 0;
} else {
/* did not immediately halt */
tfx->whyNowhere = tenFiberStopUnknown;
}
/* airMop{Error, Okay}( ) can safely be called on NULL */
mop = ( nfiber || nval ) ? airMopNew( ) : NULL;
for ( tfx->halfIdx=0; tfx->halfIdx<=1; tfx->halfIdx++ ) {
if ( nval ) {
appu.d = &( pans[tfx->halfIdx] );
pansArr[tfx->halfIdx] = airArrayNew( appu.v, NULL,
pansLen*sizeof( double ),
TEN_FIBER_INCR );
airMopAdd( mop, pansArr[tfx->halfIdx],
( airMopper )airArrayNuke, airMopAlways );
} else {
pansArr[tfx->halfIdx] = NULL;
}
pansIdx = -1;
if ( nfiber ) {
appu.d = &( fpts[tfx->halfIdx] );
fptsArr[tfx->halfIdx] = airArrayNew( appu.v, NULL,
3*sizeof( double ), TEN_FIBER_INCR );
airMopAdd( mop, fptsArr[tfx->halfIdx],
( airMopper )airArrayNuke, airMopAlways );
buffIdx = -1;
} else {
fptsArr[tfx->halfIdx] = NULL;
fpts[tfx->halfIdx] = NULL;
buffIdx = halfBuffLen;
}
fptsIdx = -1;
tfx->halfLen[tfx->halfIdx] = 0;
if ( tfx->useIndexSpace ) {
ELL_3V_COPY( iPos, seed );
gageShapeItoW( tfx->gtx->shape, tfx->wPos, iPos );
} else {
/*
fprintf( stderr, "!%s( A ): %p %p %p\n", me,
tfx->gtx->shape, iPos, seed );
*/
gageShapeWtoI( tfx->gtx->shape, iPos, seed );
ELL_3V_COPY( tfx->wPos, seed );
}
/* have to initially pass the possible radius check in
_tenFiberStopCheck( ); this will always pass */
tfx->radius = DBL_MAX;
ELL_3V_SET( tfx->lastDir, 0, 0, 0 );
tfx->lastDirSet = AIR_FALSE;
for ( tfx->numSteps[tfx->halfIdx] = 0;
AIR_TRUE;
tfx->numSteps[tfx->halfIdx]++ ) {
_tenFiberProbe( tfx, &gret, tfx->wPos, AIR_FALSE );
if ( gret ) {
/* even if gageProbe had an error OTHER than going out of bounds,
we're not going to report it any differently here, alas */
tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds;
/*
fprintf( stderr, "!%s: A tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
airEnumStr( tenFiberStop, tfx->whyStop[tfx->halfIdx] ) );
*/
break;
}
if ( ( whyStop = _tenFiberStopCheck( tfx ) ) ) {
if ( tenFiberStopNumSteps == whyStop ) {
/* we stopped along this direction because
tfx->numSteps[tfx->halfIdx] exceeded tfx->maxNumSteps.
Okay. But tfx->numSteps[tfx->halfIdx] is supposed to be
a record of how steps were ( successfully ) taken. So we
need to decrementing before moving on ... */
tfx->numSteps[tfx->halfIdx]--;
}
tfx->whyStop[tfx->halfIdx] = whyStop;
/*
fprintf( stderr, "!%s: B tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
airEnumStr( tenFiberStop, tfx->whyStop[tfx->halfIdx] ) );
*/
break;
}
if ( tfx->useIndexSpace ) {
/*
fprintf( stderr, "!%s( B ): %p %p %p\n", me,
tfx->gtx->shape, iPos, tfx->wPos );
*/
gageShapeWtoI( tfx->gtx->shape, iPos, tfx->wPos );
ELL_3V_COPY( currPoint, iPos );
} else {
ELL_3V_COPY( currPoint, tfx->wPos );
}
if ( nval ) {
pansIdx = airArrayLenIncr( pansArr[tfx->halfIdx], 1 );
/* HEY: speed this up */
memcpy( pans[tfx->halfIdx] + pansLen*pansIdx, pansP,
pansLen*sizeof( double ) );
/*
fprintf( stderr, "!%s: ( dir %d ) saving to %d: %g @ ( %g, %g, %g )\n", me,
tfx->halfIdx, pansIdx, pansP[0],
currPoint[0], currPoint[1], currPoint[2] );
*/
}
if ( nfiber ) {
fptsIdx = airArrayLenIncr( fptsArr[tfx->halfIdx], 1 );
ELL_3V_COPY( fpts[tfx->halfIdx] + 3*fptsIdx, currPoint );
} else {
ELL_3V_COPY( buff + 3*buffIdx, currPoint );
/*
fprintf( stderr, "!%s: ( dir %d ) saving to %d pnt %g %g %g\n", me,
tfx->halfIdx, buffIdx,
currPoint[0], currPoint[1], currPoint[2] );
*/
buffIdx += !tfx->halfIdx ? -1 : 1;
}
/* forwDir is set by this to point to the next fiber point */
if ( _tenFiberIntegrate[tfx->intg]( tfx, forwDir ) ) {
tfx->whyStop[tfx->halfIdx] = tenFiberStopBounds;
/*
fprintf( stderr, "!%s: C tfx->whyStop[%d] = %s\n", me, tfx->halfIdx,
airEnumStr( tenFiberStop, tfx->whyStop[tfx->halfIdx] ) );
*/
break;
}
/*
fprintf( stderr, "!%s: forwDir = %g %g %g\n", me,
forwDir[0], forwDir[1], forwDir[2] );
*/
if ( tfx->stop & ( 1 << tenFiberStopRadius ) ) {
/* some more work required to compute radius of curvature */
double svec[3], dvec[3], SS, DD, dlen; /* sum, diff length squared */
/* tfx->lastDir and forwDir are not normalized to unit-length */
if ( tfx->lastDirSet ) {
ELL_3V_ADD2( svec, tfx->lastDir, forwDir );
ELL_3V_SUB( dvec, tfx->lastDir, forwDir );
SS = ELL_3V_DOT( svec, svec );
DD = ELL_3V_DOT( dvec, dvec );
/* Sun Nov 2 00:04:05 EDT 2008: GLK can't recover how he
derived this, and can't see why it would be corrrect,
even though it seems to work correctly...
tfx->radius = sqrt( SS*( SS+DD )/DD )/4;
*/
dlen = sqrt( DD );
tfx->radius = dlen ? ( SS + DD )/( 4*dlen ) : DBL_MAX;
} else {
tfx->radius = DBL_MAX;
}
}
/*
if ( !tfx->lastDirSet ) {
fprintf( stderr, "!%s: now setting lastDirSet to ( %g, %g, %g )\n", me,
forwDir[0], forwDir[1], forwDir[2] );
}
*/
ELL_3V_COPY( tfx->lastDir, forwDir );
tfx->lastDirSet = AIR_TRUE;
ELL_3V_ADD2( tfx->wPos, tfx->wPos, forwDir );
tfx->halfLen[tfx->halfIdx] += ELL_3V_LEN( forwDir );
}
}
keepfiber = AIR_TRUE;
if ( ( tfx->stop & ( 1 << tenFiberStopStub ) )
&& ( 2 == fptsArr[0]->len + fptsArr[1]->len ) ) {
/* seed point was actually valid, but neither half got anywhere,
and the user has set tenFiberStopStub, so we report this as
a non-starter, via tfx->whyNowhere. */
tfx->whyNowhere = tenFiberStopStub;
keepfiber = AIR_FALSE;
}
if ( ( tfx->stop & ( 1 << tenFiberStopMinNumSteps ) )
&& ( fptsArr[0]->len + fptsArr[1]->len < tfx->minNumSteps ) ) {
/* whole fiber didn't have enough steps */
tfx->whyNowhere = tenFiberStopMinNumSteps;
keepfiber = AIR_FALSE;
}
if ( ( tfx->stop & ( 1 << tenFiberStopMinLength ) )
&& ( tfx->halfLen[0] + tfx->halfLen[1] < tfx->minWholeLen ) ) {
/* whole fiber wasn't long enough */
tfx->whyNowhere = tenFiberStopMinLength;
keepfiber = AIR_FALSE;
}
if ( !keepfiber ) {
/* for the curious, tfx->whyStop[0, 1], tfx->numSteps[0, 1], and
tfx->halfLen[1, 2] remain set, from above */
if ( nval ) {
nrrdEmpty( nval );
}
if ( nfiber ) {
nrrdEmpty( nfiber );
} else {
*startIdxP = *endIdxP = 0;
}
} else {
if ( nval ) {
if ( nrrdMaybeAlloc_va( nval, nrrdTypeDouble, 2,
AIR_CAST( size_t, pansLen ),
AIR_CAST( size_t, ( pansArr[0]->len
+ pansArr[1]->len - 1 ) ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate probed value nrrd", me );
airMopError( mop ); return 1;
}
valOut = AIR_CAST( double*, nval->data );
outIdx = 0;
/* HEY: speed up memcpy */
for ( i=pansArr[0]->len-1; i>=1; i-- ) {
memcpy( valOut + pansLen*outIdx, pans[0] + pansLen*i,
pansLen*sizeof( double ) );
outIdx++;
}
for ( i=0; i<=pansArr[1]->len-1; i++ ) {
memcpy( valOut + pansLen*outIdx, pans[1] + pansLen*i,
pansLen*sizeof( double ) );
outIdx++;
}
}
if ( nfiber ) {
if ( nrrdMaybeAlloc_va( nfiber, nrrdTypeDouble, 2,
AIR_CAST( size_t, 3 ),
AIR_CAST( size_t, ( fptsArr[0]->len
+ fptsArr[1]->len - 1 ) ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate fiber nrrd", me );
airMopError( mop ); return 1;
}
fiber = AIR_CAST( double*, nfiber->data );
outIdx = 0;
for ( i=fptsArr[0]->len-1; i>=1; i-- ) {
ELL_3V_COPY( fiber + 3*outIdx, fpts[0] + 3*i );
outIdx++;
}
for ( i=0; i<=fptsArr[1]->len-1; i++ ) {
ELL_3V_COPY( fiber + 3*outIdx, fpts[1] + 3*i );
outIdx++;
}
} else {
*startIdxP = halfBuffLen - tfx->numSteps[0];
*endIdxP = halfBuffLen + tfx->numSteps[1];
}
}
tfx->stop = oldStop;
airMopOkay( mop );
return 0;
}
/*
******** tenFiberTraceSet
**
** slightly more flexible API for fiber tracking than tenFiberTrace
**
** EITHER: pass a non-NULL nfiber, and NULL, 0, NULL, NULL for
** the following arguments, and things are the same as with tenFiberTrace:
** data inside the nfiber is allocated, and the tract vertices are copied
** into it, having been stored in dynamically allocated airArrays
**
** OR: pass a NULL nfiber, and a buff allocated for 3*( 2*halfBuffLen + 1 )
** ( note the "+ 1" !!! ) doubles. The fiber tracking on each half will stop
** at halfBuffLen points. The given seedpoint will be stored in
** buff[0, 1, 2 + 3*halfBuffLen]. The linear ( 1-D ) indices for the end of
** the first tract half, and the end of the second tract half, will be set in
** *startIdxP and *endIdxP respectively ( this does not include a multiply
** by 3 )
**
** it is worth pointing out here that internally, all tractography is done
** in gage's world space, regardless of tfx->useIndexSpace. The conversion
** from/to index is space ( if tfx->useIndexSpace is non-zero ) is only done
** for seedpoints and when fiber vertices are saved out, respectively.
**
** As of Sun Aug 1 20:40:55 CDT 2010 this is just a wrapper around
** _fiberTraceSet; this will probably change in Teem 2.0
*/
int
855 tenFiberTraceSet( tenFiberContext *tfx, Nrrd *nfiber,
double *buff, unsigned int halfBuffLen,
unsigned int *startIdxP, unsigned int *endIdxP,
double seed[3] ) {
static const char me[]="tenFiberTraceSet";
if ( _fiberTraceSet( tfx, NULL, nfiber, buff, halfBuffLen,
startIdxP, endIdxP, seed ) ) {
biffAddf( TEN, "%s: problem", me );
return 1;
}
return 0;
}
/*
******** tenFiberTrace
**
** takes a starting position in index or world space, depending on the
** value of tfx->useIndexSpace
*/
int
877 tenFiberTrace( tenFiberContext *tfx, Nrrd *nfiber, double seed[3] ) {
static const char me[]="tenFiberTrace";
if ( _fiberTraceSet( tfx, NULL, nfiber, NULL, 0, NULL, NULL, seed ) ) {
biffAddf( TEN, "%s: problem computing tract", me );
return 1;
}
return 0;
}
/*
******** tenFiberDirectionNumber
**
** NOTE: for the time being, a return of zero indicates an error, not
** that we're being clever and detect that the seedpoint is in such
** isotropy that no directions are possible ( though such cleverness
** will hopefully be implemented soon )
*/
unsigned int
897 tenFiberDirectionNumber( tenFiberContext *tfx, double seed[3] ) {
static const char me[]="tenFiberDirectionNumber";
unsigned int ret;
if ( !( tfx && seed ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 0;
}
/* HEY: eventually this stuff will be specific to the seedpoint ... */
if ( tfx->useDwi ) {
switch ( tfx->fiberType ) {
case tenDwiFiberType1Evec0:
ret = 1;
break;
case tenDwiFiberType2Evec0:
ret = 2;
break;
case tenDwiFiberType12BlendEvec0:
biffAddf( TEN, "%s: sorry, type %s not yet implemented", me,
airEnumStr( tenDwiFiberType, tenDwiFiberType12BlendEvec0 ) );
ret = 0;
break;
default:
biffAddf( TEN, "%s: type %d unknown!", me, tfx->fiberType );
ret = 0;
break;
}
} else {
/* not using DWIs */
ret = 1;
}
return ret;
}
/*
******** tenFiberSingleTrace
**
** fiber tracing API that uses new tenFiberSingle, as well as being
** aware of multi-direction tractography
**
** NOTE: this will not try any cleverness in setting "num"
** according to whether the seedpoint is a non-starter
*/
int
944 tenFiberSingleTrace( tenFiberContext *tfx, tenFiberSingle *tfbs,
double seed[3], unsigned int which ) {
static const char me[]="tenFiberSingleTrace";
if ( !( tfx && tfbs && seed ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
/* set input fields in tfbs */
ELL_3V_COPY( tfbs->seedPos, seed );
tfbs->dirIdx = which;
/* not our job to set tfbx->dirNum ... */
/* set tfbs->nvert */
/* no harm in setting this even when there are no multiple fibers */
tfx->ten2Which = which;
if ( _fiberTraceSet( tfx, ( tfx->fiberProbeItem ? tfbs->nval : NULL ),
tfbs->nvert, NULL, 0, NULL, NULL, seed ) ) {
biffAddf( TEN, "%s: problem computing tract", me );
return 1;
}
/* set other fields based on tfx output */
tfbs->halfLen[0] = tfx->halfLen[0];
tfbs->halfLen[1] = tfx->halfLen[1];
tfbs->seedIdx = tfx->numSteps[0];
tfbs->stepNum[0] = tfx->numSteps[0];
tfbs->stepNum[1] = tfx->numSteps[1];
tfbs->whyStop[0] = tfx->whyStop[0];
tfbs->whyStop[1] = tfx->whyStop[1];
tfbs->whyNowhere = tfx->whyNowhere;
return 0;
}
typedef union {
tenFiberSingle **f;
void **v;
} fiberunion;
/* uses biff */
tenFiberMulti *
987 tenFiberMultiNew( ) {
static const char me[]="tenFiberMultiNew";
tenFiberMulti *ret;
fiberunion tfu;
ret = AIR_CAST( tenFiberMulti *, calloc( 1, sizeof( tenFiberMulti ) ) );
if ( ret ) {
ret->fiber = NULL;
ret->fiberNum = 0;
tfu.f = &( ret->fiber );
ret->fiberArr = airArrayNew( tfu.v, &( ret->fiberNum ),
sizeof( tenFiberSingle ), 512 /* incr */ );
if ( ret->fiberArr ) {
airArrayStructCB( ret->fiberArr,
AIR_CAST( void ( * )( void * ), tenFiberSingleInit ),
AIR_CAST( void ( * )( void * ), tenFiberSingleDone ) );
} else {
biffAddf( TEN, "%s: couldn't create airArray", me );
return NULL;
}
} else {
biffAddf( TEN, "%s: couldn't create tenFiberMulti", me );
return NULL;
}
return ret;
}
int
1015 tenFiberMultiCheck( airArray *arr ) {
static const char me[]="tenFiberMultiCheck";
if ( !arr ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( sizeof( tenFiberSingle ) != arr->unit ) {
biffAddf( TEN, "%s: given airArray cannot be for fibers", me );
return 1;
}
if ( !( AIR_CAST( void ( * )( void * ), tenFiberSingleInit ) == arr->initCB
&& AIR_CAST( void ( * )( void * ), tenFiberSingleDone ) == arr->doneCB ) ) {
biffAddf( TEN, "%s: given airArray not set up with fiber callbacks", me );
return 1;
}
return 0;
}
tenFiberMulti *
1035 tenFiberMultiNix( tenFiberMulti *tfm ) {
if ( tfm ) {
airArrayNuke( tfm->fiberArr );
airFree( tfm );
}
return NULL;
}
/*
******** tenFiberMultiTrace
**
** does tractography for a list of seedpoints
**
** tfml has been returned from tenFiberMultiNew( )
*/
int
1052 tenFiberMultiTrace( tenFiberContext *tfx, tenFiberMulti *tfml,
const Nrrd *_nseed ) {
static const char me[]="tenFiberMultiTrace";
airArray *mop;
const double *seedData;
double seed[3];
unsigned int seedNum, seedIdx, fibrNum, dirNum, dirIdx;
Nrrd *nseed;
if ( !( tfx && tfml && _nseed ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenFiberMultiCheck( tfml->fiberArr ) ) {
biffAddf( TEN, "%s: problem with fiber array", me );
return 1;
}
if ( !( 2 == _nseed->dim && 3 == _nseed->axis[0].size ) ) {
biffAddf( TEN, "%s: seed list should be a 2-D ( not %u-D ) "
"3-by-X ( not %u-by-X ) array", me, _nseed->dim,
AIR_CAST( unsigned int, _nseed->axis[0].size ) );
return 1;
}
mop = airMopNew( );
seedNum = _nseed->axis[1].size;
if ( nrrdTypeDouble == _nseed->type ) {
seedData = AIR_CAST( const double *, _nseed->data );
} else {
nseed = nrrdNew( );
airMopAdd( mop, nseed, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
if ( nrrdConvert( nseed, _nseed, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert seed list", me );
return 1;
}
seedData = AIR_CAST( const double *, nseed->data );
}
/* HEY: the correctness of the use of the airArray here is quite subtle */
fibrNum = 0;
for ( seedIdx=0; seedIdx<seedNum; seedIdx++ ) {
dirNum = tenFiberDirectionNumber( tfx, seed );
if ( !dirNum ) {
biffAddf( TEN, "%s: couldn't learn dirNum at seed ( %g, %g, %g )", me,
seed[0], seed[1], seed[2] );
return 1;
}
for ( dirIdx=0; dirIdx<dirNum; dirIdx++ ) {
if ( tfx->verbose > 1 ) {
fprintf( stderr, "%s: dir %u/%u on seed %u/%u; len %u; # %u\n",
me, dirIdx, dirNum, seedIdx, seedNum,
tfml->fiberArr->len, fibrNum );
}
/* tfml->fiberArr->len can never be < fibrNum */
if ( tfml->fiberArr->len == fibrNum ) {
airArrayLenIncr( tfml->fiberArr, 1 );
}
ELL_3V_COPY( tfml->fiber[fibrNum].seedPos, seedData + 3*seedIdx );
tfml->fiber[fibrNum].dirIdx = dirIdx;
tfml->fiber[fibrNum].dirNum = dirNum;
ELL_3V_COPY( seed, seedData + 3*seedIdx );
if ( tenFiberSingleTrace( tfx, &( tfml->fiber[fibrNum] ), seed, dirIdx ) ) {
biffAddf( TEN, "%s: trouble on seed ( %g, %g, %g ) %u/%u, dir %u/%u", me,
seed[0], seed[1], seed[2], seedIdx, seedNum, dirIdx, dirNum );
return 1;
}
if ( tfx->verbose ) {
if ( tenFiberStopUnknown == tfml->fiber[fibrNum].whyNowhere ) {
fprintf( stderr, "%s: ( %g, %g, %g ) ->\n"
" steps = %u, %u; len = %g, %g; whyStop = %s, %s\n",
me, seed[0], seed[1], seed[2],
tfml->fiber[fibrNum].stepNum[0],
tfml->fiber[fibrNum].stepNum[1],
tfml->fiber[fibrNum].halfLen[0],
tfml->fiber[fibrNum].halfLen[1],
airEnumStr( tenFiberStop, tfml->fiber[fibrNum].whyStop[0] ),
airEnumStr( tenFiberStop, tfml->fiber[fibrNum].whyStop[1] ) );
} else {
fprintf( stderr, "%s: ( %g, %g, %g ) -> whyNowhere: %s\n",
me, seed[0], seed[1], seed[2],
airEnumStr( tenFiberStop, tfml->fiber[fibrNum].whyNowhere ) );
}
}
fibrNum++;
}
}
/* if the airArray got to be its length only because of the work above,
then the following will be a no-op. Otherwise, via the callbacks,
it will clear out the tenFiberSingle's that we didn't create here */
airArrayLenSet( tfml->fiberArr, fibrNum );
airMopOkay( mop );
return 0;
}
static int
1149 _fiberMultiExtract( tenFiberContext *tfx, Nrrd *nval,
limnPolyData *lpld, tenFiberMulti *tfml ) {
static const char me[]="_fiberMultiExtract";
unsigned int seedIdx, vertTotalNum, fiberNum, fiberIdx, vertTotalIdx,
pansLen, pvNum;
double *valOut;
if ( !( tfx && ( lpld || nval ) && tfml ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenFiberMultiCheck( tfml->fiberArr ) ) {
biffAddf( TEN, "%s: problem with fiber array", me );
return 1;
}
if ( nval ) {
if ( !tfx->fiberProbeItem ) {
biffAddf( TEN, "%s: want probed values but no item set", me );
return 1;
}
pansLen = gageAnswerLength( tfx->gtx, tfx->pvl, tfx->fiberProbeItem );
} else {
pansLen = 0;
}
/*
fprintf( stderr, "!%s: =========================== \n", me );
fprintf( stderr, "!%s: \n", me );
fprintf( stderr, "!%s: item %d -> pansLen = %u\n", me,
tfx->fiberProbeItem, pansLen );
fprintf( stderr, "!%s: \n", me );
fprintf( stderr, "!%s: =========================== \n", me );
*/
/* we have to count the real fibers that went somewhere, excluding
fibers that went nowhere ( counted in tfml->fiberNum ) */
vertTotalNum = 0;
fiberNum = 0;
pvNum = 0;
for ( seedIdx=0; seedIdx<tfml->fiberArr->len; seedIdx++ ) {
tenFiberSingle *tfs;
tfs = tfml->fiber + seedIdx;
if ( !( tenFiberStopUnknown == tfs->whyNowhere ) ) {
continue;
}
if ( nval ) {
if ( tfs->nval ) {
if ( !( 2 == tfs->nval->dim
&& pansLen == tfs->nval->axis[0].size
&& tfs->nvert->axis[1].size == tfs->nval->axis[1].size ) ) {
biffAddf( TEN, "%s: fiber[%u]->nval seems wrong", me, seedIdx );
return 1;
}
pvNum++;
}
}
vertTotalNum += tfs->nvert->axis[1].size;
fiberNum++;
}
if ( nval && pvNum != fiberNum ) {
biffAddf( TEN, "%s: pvNum %u != fiberNum %u", me, pvNum, fiberNum );
return 1;
}
if ( nval ) {
if ( nrrdMaybeAlloc_va( nval, nrrdTypeDouble, 2,
AIR_CAST( size_t, pansLen ),
AIR_CAST( size_t, vertTotalNum ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
valOut = AIR_CAST( double *, nval->data );
} else {
valOut = NULL;
}
if ( lpld ) {
if ( limnPolyDataAlloc( lpld, 0, /* no extra per-vertex info */
vertTotalNum, vertTotalNum, fiberNum ) ) {
biffMovef( TEN, LIMN, "%s: couldn't allocate output", me );
return 1;
}
}
fiberIdx = 0;
vertTotalIdx = 0;
for ( seedIdx=0; seedIdx<tfml->fiberArr->len; seedIdx++ ) {
double *vert, *pans;
unsigned int vertIdx, vertNum;
tenFiberSingle *tfs;
tfs = tfml->fiber + seedIdx;
if ( !( tenFiberStopUnknown == tfs->whyNowhere ) ) {
continue;
}
vertNum = tfs->nvert->axis[1].size;
pans = ( nval
? AIR_CAST( double*, tfs->nval->data )
: NULL );
vert = ( lpld
? AIR_CAST( double*, tfs->nvert->data )
: NULL );
for ( vertIdx=0; vertIdx<vertNum; vertIdx++ ) {
if ( lpld ) {
ELL_3V_COPY_TT( lpld->xyzw + 4*vertTotalIdx, float, vert + 3*vertIdx );
( lpld->xyzw + 4*vertTotalIdx )[3] = 1.0;
lpld->indx[vertTotalIdx] = vertTotalIdx;
}
if ( nval ) {
/* HEY speed up memcpy */
memcpy( valOut + pansLen*vertTotalIdx,
pans + pansLen*vertIdx,
pansLen*sizeof( double ) );
}
vertTotalIdx++;
}
if ( lpld ) {
lpld->type[fiberIdx] = limnPrimitiveLineStrip;
lpld->icnt[fiberIdx] = vertNum;
}
fiberIdx++;
}
return 0;
}
/*
******** tenFiberMultiPolyData
**
** converts tenFiberMulti to polydata.
**
** currently the tenFiberContext *tfx arg is not used, but it will
** probably be needed in the future as the way that parameters to the
** polydata creation process are passed.
*/
int
1282 tenFiberMultiPolyData( tenFiberContext *tfx,
limnPolyData *lpld, tenFiberMulti *tfml ) {
static const char me[]="tenFiberMultiPolyData";
if ( _fiberMultiExtract( tfx, NULL, lpld, tfml ) ) {
biffAddf( TEN, "%s: problem", me );
return 1;
}
return 0;
}
int
1295 tenFiberMultiProbeVals( tenFiberContext *tfx,
Nrrd *nval, tenFiberMulti *tfml ) {
static const char me[]="tenFiberMultiProbeVals";
if ( _fiberMultiExtract( tfx, nval, NULL, tfml ) ) {
biffAddf( TEN, "%s: problem", me );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
void
29 tenFiberSingleInit( tenFiberSingle *tfbs ) {
/* char me[]="tenFiberSingleInit"; */
unsigned idx;
ELL_3V_SET( tfbs->seedPos, AIR_NAN, AIR_NAN, AIR_NAN );
tfbs->dirIdx = tfbs->dirNum = 0;
tfbs->nvert = nrrdNew( );
tfbs->halfLen[0] = tfbs->halfLen[1] = AIR_NAN;
tfbs->seedIdx = tfbs->stepNum[0] = tfbs->stepNum[1] = 0;
tfbs->whyStop[0] = tfbs->whyStop[1] = tenFiberStopUnknown;
tfbs->whyNowhere = tenFiberStopUnknown; /* actually, the semantics of this
field is reversed, so this is not
really the way it should be set */
tfbs->nval = nrrdNew( );
for ( idx=0; idx<=NRRD_MEASURE_MAX; idx++ ) {
tfbs->measr[idx] = AIR_NAN;
}
return;
}
void
50 tenFiberSingleDone( tenFiberSingle *tfbs ) {
tfbs->nvert = nrrdNuke( tfbs->nvert );
tfbs->nval = nrrdNuke( tfbs->nval );
}
tenFiberSingle *
57 tenFiberSingleNew( ) {
tenFiberSingle *ret;
ret = AIR_CALLOC( 1, tenFiberSingle );
if ( ret ) {
tenFiberSingleInit( ret );
}
return ret;
}
tenFiberSingle *
68 tenFiberSingleNix( tenFiberSingle *tfbs ) {
if ( tfbs ) {
tenFiberSingleDone( tfbs );
airFree( tfbs );
}
return NULL;
}
static
tenFiberContext *
79 _tenFiberContextCommonNew( const Nrrd *vol, int useDwi,
double thresh, double soft, double valueMin,
int ten1method, int ten2method ) {
static const char me[]="_tenFiberContextCommonNew";
tenFiberContext *tfx;
gageKind *kind;
airArray *mop;
if ( !( tfx = AIR_CALLOC( 1, tenFiberContext ) ) ) {
biffAddf( TEN, "%s: couldn't allocate new context", me );
return NULL;
}
mop = airMopNew( );
airMopAdd( mop, tfx, airFree, airMopOnError );
if ( useDwi ) {
Nrrd *ngrad=NULL, *nbmat=NULL;
double bval=0;
unsigned int *skip=NULL, skipNum;
tfx->useDwi = AIR_TRUE;
/* default fiber type */
tfx->fiberType = tenDwiFiberTypeUnknown;
if ( tenDWMRIKeyValueParse( &ngrad, &nbmat, &bval, &skip, &skipNum, vol ) ) {
biffAddf( TEN, "%s: trouble parsing DWI info", me );
airMopError( mop ); return NULL;
}
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopOnError );
airMopAdd( mop, nbmat, ( airMopper )nrrdNuke, airMopOnError );
airMopAdd( mop, skip, airFree, airMopOnError );
if ( skipNum ) {
biffAddf( TEN, "%s: sorry, can't do DWI skipping here", me );
airMopError( mop ); return NULL;
}
kind = tenDwiGageKindNew( );
airMopAdd( mop, kind, ( airMopper )tenDwiGageKindNix, airMopOnError );
if ( tenDwiGageKindSet( kind,
thresh, soft, bval, valueMin,
ngrad, NULL,
ten1method, ten2method, 42 ) ) {
biffAddf( TEN, "%s: trouble setting DWI kind", me );
airMopError( mop ); return NULL;
}
} else {
/* it should be a tensor volume */
tfx->useDwi = AIR_FALSE;
/* default fiber type */
tfx->fiberType = tenFiberTypeUnknown;
if ( tenTensorCheck( vol, nrrdTypeUnknown, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a tensor volume", me );
airMopError( mop ); return NULL;
}
kind = tenGageKind;
}
tfx->gtx = gageContextNew( );
airMopAdd( mop, tfx->gtx, ( airMopper )gageContextNix, airMopOnError );
tfx->pvl = gagePerVolumeNew( tfx->gtx, vol, kind );
airMopAdd( mop, tfx->pvl, ( airMopper )gagePerVolumeNix, airMopOnError );
if ( !( tfx->gtx && tfx->pvl && !gagePerVolumeAttach( tfx->gtx, tfx->pvl ) ) ) {
biffMovef( TEN, GAGE, "%s: gage trouble", me );
airMopError( mop ); return NULL;
}
tfx->nin = vol;
tfx->ksp = nrrdKernelSpecNew( );
airMopAdd( mop, tfx->ksp, ( airMopper )nrrdKernelSpecNix, airMopOnError );
if ( nrrdKernelSpecParse( tfx->ksp, tenDefFiberKernel ) ) {
biffMovef( TEN, NRRD, "%s: couldn't parse tenDefFiberKernel \"%s\"",
me, tenDefFiberKernel );
airMopError( mop ); return NULL;
}
if ( tenFiberKernelSet( tfx, tfx->ksp->kernel, tfx->ksp->parm ) ) {
biffAddf( TEN, "%s: couldn't set default kernel", me );
airMopError( mop ); return NULL;
}
tfx->fiberProbeItem = 0; /* unknown for any gageKind */
/* looks to GK like GK says that we must set some stop criterion */
tfx->intg = tenDefFiberIntg;
tfx->anisoStopType = tenDefFiberAnisoStopType;
tfx->anisoSpeedType = tenAnisoUnknown;
tfx->stop = 0;
tfx->anisoThresh = tenDefFiberAnisoThresh;
/* so I'm not using the normal default mechanism, shoot me */
tfx->anisoSpeedFunc[0] = 0;
tfx->anisoSpeedFunc[1] = 0;
tfx->anisoSpeedFunc[2] = 0;
tfx->maxNumSteps = tenDefFiberMaxNumSteps;
tfx->minNumSteps = 0;
tfx->useIndexSpace = tenDefFiberUseIndexSpace;
tfx->verbose = 0;
tfx->stepSize = tenDefFiberStepSize;
tfx->maxHalfLen = tenDefFiberMaxHalfLen;
tfx->minWholeLen = 0.0;
tfx->confThresh = 0.5; /* why do I even bother setting these- they'll
only get read if the right tenFiberStopSet has
been called, in which case they'll be set... */
tfx->minRadius = 1; /* above lament applies here as well */
tfx->minFraction = 0.5; /* and here */
tfx->wPunct = tenDefFiberWPunct;
GAGE_QUERY_RESET( tfx->query );
tfx->mframe[0] = vol->measurementFrame[0][0];
tfx->mframe[1] = vol->measurementFrame[1][0];
tfx->mframe[2] = vol->measurementFrame[2][0];
tfx->mframe[3] = vol->measurementFrame[0][1];
tfx->mframe[4] = vol->measurementFrame[1][1];
tfx->mframe[5] = vol->measurementFrame[2][1];
tfx->mframe[6] = vol->measurementFrame[0][2];
tfx->mframe[7] = vol->measurementFrame[1][2];
tfx->mframe[8] = vol->measurementFrame[2][2];
if ( ELL_3M_EXISTS( tfx->mframe ) ) {
tfx->mframeUse = AIR_TRUE;
ELL_3M_TRANSPOSE( tfx->mframeT, tfx->mframe );
} else {
tfx->mframeUse = AIR_FALSE;
}
tfx->gageAnisoStop = NULL;
tfx->gageAnisoSpeed = NULL;
tfx->ten2AnisoStop = AIR_NAN;
/* ... don't really see the point of initializing the ten2 stuff here;
its properly done in tenFiberTraceSet( ) ... */
tfx->radius = AIR_NAN;
airMopOkay( mop );
return tfx;
}
tenFiberContext *
210 tenFiberContextDwiNew( const Nrrd *dwivol,
double thresh, double soft, double valueMin,
int ten1method, int ten2method ) {
static const char me[]="tenFiberContextDwiNew";
tenFiberContext *tfx;
if ( !( tfx = _tenFiberContextCommonNew( dwivol, AIR_TRUE,
thresh, soft, valueMin,
ten1method, ten2method ) ) ) {
biffAddf( TEN, "%s: couldn't create new context", me );
return NULL;
}
return tfx;
}
tenFiberContext *
226 tenFiberContextNew( const Nrrd *dtvol ) {
static const char me[]="tenFiberContextNew";
tenFiberContext *tfx;
if ( !( tfx = _tenFiberContextCommonNew( dtvol, AIR_FALSE,
AIR_NAN, AIR_NAN, AIR_NAN,
tenEstimate1MethodUnknown,
tenEstimate2MethodUnknown ) ) ) {
biffAddf( TEN, "%s: couldn't create new context", me );
return NULL;
}
return tfx;
}
void
242 tenFiberVerboseSet( tenFiberContext *tfx, int verbose ) {
if ( tfx ) {
tfx->verbose = verbose;
}
return;
}
int
251 tenFiberTypeSet( tenFiberContext *tfx, int ftype ) {
static const char me[]="tenFiberTypeSet";
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tfx->useDwi ) {
fprintf( stderr, "!%s( %d )--- hello\n", me, ftype );
switch ( ftype ) {
case tenDwiFiberType1Evec0:
GAGE_QUERY_ITEM_ON( tfx->query, tenDwiGageTensorLLS );
tfx->gageTen = gageAnswerPointer( tfx->gtx, tfx->pvl,
tenDwiGageTensorLLS );
tfx->gageTen2 = NULL;
break;
case tenDwiFiberType2Evec0:
GAGE_QUERY_ITEM_ON( tfx->query, tenDwiGage2TensorPeled );
tfx->gageTen = NULL;
tfx->gageTen2 = gageAnswerPointer( tfx->gtx, tfx->pvl,
tenDwiGage2TensorPeled );
break;
case tenDwiFiberType12BlendEvec0:
GAGE_QUERY_ITEM_ON( tfx->query, tenDwiGageTensorLLS );
tfx->gageTen = gageAnswerPointer( tfx->gtx, tfx->pvl,
tenDwiGageTensorLLS );
GAGE_QUERY_ITEM_ON( tfx->query, tenDwiGage2TensorPeled );
tfx->gageTen2 = gageAnswerPointer( tfx->gtx, tfx->pvl,
tenDwiGage2TensorPeled );
break;
default:
biffAddf( TEN, "%s: unimplemented %s %d", me,
tenDwiFiberType->name, ftype );
return 1;
break;
}
tfx->gageEval = NULL;
tfx->gageEvec = NULL;
} else {
/* working with tensor volume */
switch( ftype ) {
case tenFiberTypeEvec0:
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec0 );
/* HEY: COPY AND PASTE */
tfx->gageEvec
= gageAnswerPointer( tfx->gtx, tfx->pvl,
( tenFiberTypeEvec0 == tfx->fiberType
? tenGageEvec0
: ( tenFiberTypeEvec1 == tfx->fiberType
? tenGageEvec1
: tenGageEvec2 ) ) );
break;
case tenFiberTypeEvec1:
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec1 );
/* HEY: COPY AND PASTE */
tfx->gageEvec
= gageAnswerPointer( tfx->gtx, tfx->pvl,
( tenFiberTypeEvec0 == tfx->fiberType
? tenGageEvec0
: ( tenFiberTypeEvec1 == tfx->fiberType
? tenGageEvec1
: tenGageEvec2 ) ) );
break;
case tenFiberTypeEvec2:
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec2 );
/* HEY: COPY AND PASTE */
tfx->gageEvec
= gageAnswerPointer( tfx->gtx, tfx->pvl,
( tenFiberTypeEvec0 == ftype
? tenGageEvec0
: ( tenFiberTypeEvec1 == ftype
? tenGageEvec1
: tenGageEvec2 ) ) );
break;
case tenFiberTypeTensorLine:
GAGE_QUERY_ITEM_ON( tfx->query, tenGageTensor );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEval0 );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEval1 );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEval2 );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec0 );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec1 );
GAGE_QUERY_ITEM_ON( tfx->query, tenGageEvec2 );
tfx->gageEvec = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageEvec0 );
tfx->gageTen = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageTensor );
tfx->gageEval = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageEval );
break;
case tenFiberTypePureLine:
GAGE_QUERY_ITEM_ON( tfx->query, tenGageTensor );
break;
case tenFiberTypeZhukov:
biffAddf( TEN, "%s: sorry, Zhukov oriented tensors not implemented", me );
return 1;
break;
default:
biffAddf( TEN, "%s: fiber type %d not recognized", me, ftype );
return 1;
break;
} /* switch */
if ( tenFiberTypeEvec0 == ftype
|| tenFiberTypeEvec1 == ftype
|| tenFiberTypeEvec2 == ftype
|| tenFiberTypeTensorLine == ftype ) {
tfx->gageTen = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageTensor );
tfx->gageEval = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageEval0 );
tfx->gageEvec
= gageAnswerPointer( tfx->gtx, tfx->pvl,
( tenFiberTypeEvec0 == ftype
? tenGageEvec0
: ( tenFiberTypeEvec1 == ftype
? tenGageEvec1
: ( tenFiberTypeEvec2 == ftype
? tenGageEvec2
: tenGageEvec ) ) ) );
tfx->gageTen2 = NULL;
}
tfx->ten2Which = 0;
}
tfx->fiberType = ftype;
return 0;
}
/*
******** tenFiberStopSet
**
** how to set stop criteria and their parameters. a little tricky because
** of the use of varargs
**
** valid calls:
** tenFiberStopSet( tfx, tenFiberStopLength, double max )
** tenFiberStopSet( tfx, tenFiberStopMinLength, double min )
** tenFiberStopSet( tfx, tenFiberStopAniso, int anisoType, double anisoThresh )
** tenFiberStopSet( tfx, tenFiberStopNumSteps, unsigned int num )
** tenFiberStopSet( tfx, tenFiberStopMinNumSteps, unsigned int num )
** tenFiberStopSet( tfx, tenFiberStopConfidence, double conf )
** tenFiberStopSet( tfx, tenFiberStopRadius, double radius )
** tenFiberStopSet( tfx, tenFiberStopBounds )
** tenFiberStopSet( tfx, tenFiberStopFraction, double fraction )
** tenFiberStopSet( tfx, tenFiberStopStub )
*/
int
392 tenFiberStopSet( tenFiberContext *tfx, int stop, ... ) {
static const char me[]="tenFiberStopSet";
va_list ap;
int ret=0;
int anisoGage;
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
va_start( ap, stop );
switch( stop ) {
case tenFiberStopAniso:
tfx->anisoStopType = va_arg( ap, int );
tfx->anisoThresh = va_arg( ap, double );
if ( !( AIR_IN_OP( tenAnisoUnknown, tfx->anisoStopType, tenAnisoLast ) ) ) {
biffAddf( TEN, "%s: given aniso stop type %d not valid", me,
tfx->anisoStopType );
ret = 1; goto end;
}
if ( !( AIR_EXISTS( tfx->anisoThresh ) ) ) {
biffAddf( TEN, "%s: given aniso threshold doesn't exist", me );
ret = 1; goto end;
}
if ( tfx->useDwi ) {
/* the tensor of which we measure anisotropy can come from lots of
places, not just a 1-tensor gage item, so there's no specific
item to turn on here... */
tfx->gageAnisoStop = NULL;
} else { /* using tensors */
switch( tfx->anisoStopType ) {
case tenAniso_FA:
anisoGage = tenGageFA;
break;
case tenAniso_Cl1:
anisoGage = tenGageCl1;
break;
case tenAniso_Cp1:
anisoGage = tenGageCp1;
break;
case tenAniso_Ca1:
anisoGage = tenGageCa1;
break;
case tenAniso_Clpmin1:
anisoGage = tenGageClpmin1;
break;
case tenAniso_Cl2:
anisoGage = tenGageCl2;
break;
case tenAniso_Cp2:
anisoGage = tenGageCp2;
break;
case tenAniso_Ca2:
anisoGage = tenGageCa2;
break;
case tenAniso_Clpmin2:
anisoGage = tenGageClpmin2;
break;
default:
biffAddf( TEN, "%s: sorry, currently don't have fast %s computation "
"via gage", me, airEnumStr( tenAniso, tfx->anisoStopType ) );
ret = 1; goto end;
break;
}
/* NOTE: we are no longer computing ALL anisotropy measures ...
GAGE_QUERY_ITEM_ON( tfx->query, tenGageAniso );
*/
GAGE_QUERY_ITEM_ON( tfx->query, anisoGage );
tfx->gageAnisoStop = gageAnswerPointer( tfx->gtx, tfx->pvl, anisoGage );
/*
fprintf( stderr, "!%s: stopping on aniso %s < %g\n", me,
airEnumStr( tenAniso, tfx->anisoStopType ), tfx->anisoThresh );
*/
}
break;
case tenFiberStopLength:
tfx->maxHalfLen = va_arg( ap, double );
if ( !( AIR_EXISTS( tfx->maxHalfLen ) && tfx->maxHalfLen > 0.0 ) ) {
biffAddf( TEN, "%s: given maxHalfLen %g doesn't exist or isn't > 0.0",
me, tfx->maxHalfLen );
ret = 1; goto end;
}
/* no query modifications needed */
break;
case tenFiberStopMinLength:
tfx->minWholeLen = va_arg( ap, double );
if ( !( AIR_EXISTS( tfx->minWholeLen ) && tfx->minWholeLen >= 0.0 ) ) {
biffAddf( TEN, "%s: given minWholeLen %g doesn't exist or isn't >= 0.0",
me, tfx->minWholeLen );
ret = 1; goto end;
}
/* no query modifications needed */
break;
case tenFiberStopNumSteps:
tfx->maxNumSteps = va_arg( ap, unsigned int );
if ( !( tfx->maxNumSteps > 0 ) ) {
biffAddf( TEN, "%s: given maxNumSteps isn't > 0.0", me );
ret = 1; goto end;
}
/* no query modifications needed */
break;
case tenFiberStopMinNumSteps:
tfx->minNumSteps = va_arg( ap, unsigned int );
/* no query modifications needed */
break;
case tenFiberStopConfidence:
tfx->confThresh = va_arg( ap, double );
if ( !( AIR_EXISTS( tfx->confThresh ) ) ) {
biffAddf( TEN, "%s: given confThresh doesn't exist", me );
ret = 1; goto end;
}
GAGE_QUERY_ITEM_ON( tfx->query, tenGageTensor );
break;
case tenFiberStopRadius:
tfx->minRadius = va_arg( ap, double );
if ( !( AIR_EXISTS( tfx->minRadius ) ) ) {
biffAddf( TEN, "%s: given minimum radius doesn't exist", me );
ret = 1; goto end;
}
/* no query modifications needed */
break;
case tenFiberStopBounds:
/* nothing to set; always used as a stop criterion */
break;
case tenFiberStopFraction:
if ( !tfx->useDwi ) {
biffAddf( TEN, "%s: can only use %s-based termination in DWI tractography",
me, airEnumStr( tenFiberStop, tenFiberStopFraction ) );
ret = 1; goto end;
}
tfx->minFraction = va_arg( ap, double );
if ( !( AIR_EXISTS( tfx->minFraction ) ) ) {
biffAddf( TEN, "%s: given minimum fraction doesn't exist", me );
ret = 1; goto end;
}
/* no query modifications needed */
break;
case tenFiberStopStub:
/* no var-args to grab */
/* no query modifications needed */
break;
default:
biffAddf( TEN, "%s: stop criterion %d not recognized", me, stop );
ret = 1; goto end;
}
tfx->stop = tfx->stop | ( 1 << stop );
end:
va_end( ap );
return ret;
}
/* to avoid var-args */
int
545 tenFiberStopAnisoSet( tenFiberContext *tfx, int anisoType, double anisoThresh ) {
static const char me[]="tenFiberStopAnisoSet";
if ( tenFiberStopSet( tfx, tenFiberStopAniso, anisoType, anisoThresh ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/* to avoid var-args */
int
557 tenFiberStopDoubleSet( tenFiberContext *tfx, int stop, double val ) {
static const char me[]="tenFiberStopDoubleSet";
switch ( stop ) {
case tenFiberStopLength:
case tenFiberStopMinLength:
case tenFiberStopConfidence:
case tenFiberStopRadius:
case tenFiberStopFraction:
if ( tenFiberStopSet( tfx, stop, val ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
break;
default:
biffAddf( TEN, "%s: given stop criterion %d ( %s ) isn't a double", me,
stop, airEnumStr( tenFiberStop, stop ) );
return 1;
}
return 0;
}
/* to avoid var-args */
int
581 tenFiberStopUIntSet( tenFiberContext *tfx, int stop, unsigned int val ) {
static const char me[]="tenFiberStopUIntSet";
switch ( stop ) {
case tenFiberStopNumSteps:
case tenFiberStopMinNumSteps:
if ( tenFiberStopSet( tfx, stop, val ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
break;
default:
biffAddf( TEN, "%s: given stop criterion %d ( %s ) isn't an unsigned int", me,
stop, airEnumStr( tenFiberStop, stop ) );
return 1;
}
return 0;
}
void
601 tenFiberStopOn( tenFiberContext *tfx, int stop ) {
if ( tfx && !airEnumValCheck( tenFiberStop, stop ) ) {
tfx->stop = tfx->stop | ( 1 << stop );
}
return;
}
void
610 tenFiberStopOff( tenFiberContext *tfx, int stop ) {
if ( tfx && !airEnumValCheck( tenFiberStop, stop ) ) {
tfx->stop = tfx->stop & ~( 1 << stop );
}
return;
}
void
619 tenFiberStopReset( tenFiberContext *tfx ) {
if ( tfx ) {
tfx->stop = 0;
}
return;
}
int
628 tenFiberAnisoSpeedSet( tenFiberContext *tfx, int aniso,
double lerp, double thresh, double soft ) {
static const char me[]="tenFiberAnisoSpeedSet";
int anisoGage;
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tfx->useDwi ) {
fprintf( stderr, "!%s: sorry, can't yet work on DWIs; bye.\n", me );
exit( 1 );
}
if ( airEnumValCheck( tenAniso, aniso ) ) {
biffAddf( TEN, "%s: aniso %d not valid", me, aniso );
return 1;
}
switch( aniso ) {
case tenAniso_FA:
anisoGage = tenGageFA;
break;
case tenAniso_Cl1:
anisoGage = tenGageCl1;
break;
case tenAniso_Cp1:
anisoGage = tenGageCp1;
break;
case tenAniso_Ca1:
anisoGage = tenGageCa1;
break;
case tenAniso_Cl2:
anisoGage = tenGageCl2;
break;
case tenAniso_Cp2:
anisoGage = tenGageCp2;
break;
case tenAniso_Ca2:
anisoGage = tenGageCa2;
break;
default:
biffAddf( TEN, "%s: sorry, currently don't have fast %s computation "
"via gage", me, airEnumStr( tenAniso, tfx->anisoStopType ) );
return 1;
break;
}
tfx->anisoSpeedType = aniso;
if ( tfx->useDwi ) {
/* actually, finding anisotropy in the context of 2-tensor
tracking is not currently done by gage */
} else {
GAGE_QUERY_ITEM_ON( tfx->query, anisoGage );
tfx->gageAnisoSpeed = gageAnswerPointer( tfx->gtx, tfx->pvl, anisoGage );
}
tfx->anisoSpeedFunc[0] = lerp;
tfx->anisoSpeedFunc[1] = thresh;
tfx->anisoSpeedFunc[2] = soft;
return 0;
}
int
691 tenFiberAnisoSpeedReset( tenFiberContext *tfx ) {
static const char me[]="tenFiberAnisoSpeedReset";
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
tfx->anisoSpeedType = tenAnisoUnknown;
/* HEY: GAGE_QUERY_ITEM_OFF something? */
/* HEY: for both tensor and DWI */
tfx->gageAnisoSpeed = NULL;
return 0;
}
int
706 tenFiberKernelSet( tenFiberContext *tfx,
const NrrdKernel *kern,
const double parm[NRRD_KERNEL_PARMS_NUM] ) {
static const char me[]="tenFiberKernelSet";
if ( !( tfx && kern ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
nrrdKernelSpecSet( tfx->ksp, kern, parm );
if ( gageKernelSet( tfx->gtx, gageKernel00,
tfx->ksp->kernel, tfx->ksp->parm ) ) {
biffMovef( TEN, GAGE, "%s: problem setting kernel", me );
return 1;
}
return 0;
}
int
726 tenFiberProbeItemSet( tenFiberContext *tfx, int item ) {
static const char me[]="tenFiberProbeItemSet";
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
tfx->fiberProbeItem = item;
return 0;
}
int
738 tenFiberIntgSet( tenFiberContext *tfx, int intg ) {
static const char me[]="tenFiberIntTypeSet";
if ( !( tfx ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_OP( tenFiberIntgUnknown, intg, tenFiberIntgLast ) ) ) {
biffAddf( TEN, "%s: got invalid integration type %d", me, intg );
return 1;
}
tfx->intg = intg;
return 0;
}
int
755 tenFiberParmSet( tenFiberContext *tfx, int parm, double val ) {
static const char me[]="tenFiberParmSet";
if ( tfx ) {
switch( parm ) {
case tenFiberParmStepSize:
tfx->stepSize = val;
break;
case tenFiberParmUseIndexSpace:
tfx->useIndexSpace = !!val;
break;
case tenFiberParmWPunct:
tfx->wPunct = val;
break;
case tenFiberParmVerbose:
tfx->verbose = AIR_CAST( int, val );
break;
default:
fprintf( stderr, "%s: WARNING!!! tenFiberParm %d not handled\n",
me, parm );
break;
}
}
return 0;
}
int
782 tenFiberUpdate( tenFiberContext *tfx ) {
static const char me[]="tenFiberUpdate";
if ( !tfx ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenFiberTypeUnknown == tfx->fiberType ) {
biffAddf( TEN, "%s: fiber type not set", me );
return 1;
}
if ( !( AIR_IN_OP( tenFiberTypeUnknown, tfx->fiberType, tenFiberTypeLast ) ) ) {
biffAddf( TEN, "%s: tfx->fiberType set to bogus value ( %d )", me,
tfx->fiberType );
return 1;
}
if ( tenFiberIntgUnknown == tfx->intg ) {
biffAddf( TEN, "%s: integration type not set", me );
return 1;
}
if ( !( AIR_IN_OP( tenFiberIntgUnknown, tfx->intg, tenFiberIntgLast ) ) ) {
biffAddf( TEN, "%s: tfx->intg set to bogus value ( %d )", me, tfx->intg );
return 1;
}
if ( 0 == tfx->stop ) {
biffAddf( TEN, "%s: no fiber stopping criteria set", me );
return 1;
}
/* HEY there should be a better place for setting this */
if ( tfx->fiberProbeItem ) {
GAGE_QUERY_ITEM_ON( tfx->query, tfx->fiberProbeItem );
}
if ( gageQuerySet( tfx->gtx, tfx->pvl, tfx->query )
|| gageUpdate( tfx->gtx ) ) {
biffMovef( TEN, GAGE, "%s: trouble with gage", me );
return 1;
}
if ( tfx->useDwi ) {
if ( !( 0 == tfx->ten2Which || 1 == tfx->ten2Which ) ) {
biffAddf( TEN, "%s: ten2Which must be 0 or 1 ( not %u )",
me, tfx->ten2Which );
return 1;
}
}
return 0;
}
/*
** exact same precautions about utility of this as with gageContextCopy!!!
** So: only after tenFiberUpdate, and don't touch anything, and don't
** call anything except tenFiberTrace and tenFiberContextNix
*/
tenFiberContext *
835 tenFiberContextCopy( tenFiberContext *oldTfx ) {
static const char me[]="tenFiberContextCopy";
tenFiberContext *tfx;
if ( oldTfx->useDwi ) {
fprintf( stderr, "!%s: sorry, can't copy DWI contexts; bye.\n", me );
exit( 1 );
}
tfx = AIR_CALLOC( 1, tenFiberContext );
memcpy( tfx, oldTfx, sizeof( tenFiberContext ) );
tfx->ksp = nrrdKernelSpecCopy( oldTfx->ksp );
tfx->gtx = gageContextCopy( oldTfx->gtx );
tfx->pvl = tfx->gtx->pvl[0]; /* HEY! gage API sucks */
tfx->gageTen = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageTensor );
tfx->gageEval = gageAnswerPointer( tfx->gtx, tfx->pvl, tenGageEval0 );
/* HEY: COPY AND PASTE */
tfx->gageEvec
= gageAnswerPointer( tfx->gtx, tfx->pvl,
( tenFiberTypeEvec0 == tfx->fiberType
? tenGageEvec0
: ( tenFiberTypeEvec1 == tfx->fiberType
? tenGageEvec1
: tenGageEvec2 ) ) );
tfx->gageAnisoStop = gageAnswerPointer( tfx->gtx, tfx->pvl,
tfx->anisoStopType );
tfx->gageAnisoSpeed = ( tfx->anisoSpeedType
? gageAnswerPointer( tfx->gtx, tfx->pvl,
tfx->anisoSpeedType )
: NULL );
return tfx;
}
tenFiberContext *
868 tenFiberContextNix( tenFiberContext *tfx ) {
if ( tfx ) {
tfx->ksp = nrrdKernelSpecNix( tfx->ksp );
tfx->gtx = gageContextNix( tfx->gtx );
free( tfx );
}
return NULL;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
tenGlyphParm *
28 tenGlyphParmNew( ) {
tenGlyphParm *parm;
parm = ( tenGlyphParm * )calloc( 1, sizeof( tenGlyphParm ) );
if ( parm ) {
parm->verbose = 0;
parm->nmask = NULL;
parm->anisoType = tenAnisoUnknown;
parm->onlyPositive = AIR_TRUE;
parm->confThresh = AIR_NAN;
parm->anisoThresh = AIR_NAN;
parm->maskThresh = AIR_NAN;
parm->glyphType = tenGlyphTypeUnknown;
parm->facetRes = 10;
parm->glyphScale = 1.0;
parm->sqdSharp = 3.0;
ELL_5V_SET( parm->edgeWidth, 0.0f, 0.0f, 0.4f, 0.2f, 0.1f );
parm->colEvec = 0; /* first */
parm->colMaxSat = 1;
parm->colGamma = 1;
parm->colIsoGray = 1;
parm->colAnisoType = tenAnisoUnknown;
parm->colAnisoModulate = 0;
ELL_4V_SET( parm->ADSP, 0, 1, 0, 30 );
parm->doSlice = AIR_FALSE;
parm->sliceAxis = 0;
parm->slicePos = 0;
parm->sliceAnisoType = tenAnisoUnknown;
parm->sliceOffset = 0.0;
parm->sliceBias = 0.05f;
parm->sliceGamma = 1.0;
}
return parm;
}
tenGlyphParm *
67 tenGlyphParmNix( tenGlyphParm *parm ) {
airFree( parm );
return NULL;
}
int
74 tenGlyphParmCheck( tenGlyphParm *parm,
const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc ) {
static const char me[]="tenGlyphParmCheck";
int duh;
size_t tenSize[3];
char stmp[5][AIR_STRLEN_SMALL];
if ( !( parm && nten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( tenAniso, parm->anisoType ) ) {
biffAddf( TEN, "%s: unset ( or invalid ) anisoType ( %d )",
me, parm->anisoType );
return 1;
}
if ( airEnumValCheck( tenAniso, parm->colAnisoType ) ) {
biffAddf( TEN, "%s: unset ( or invalid ) colAnisoType ( %d )",
me, parm->colAnisoType );
return 1;
}
if ( !( parm->facetRes >= 3 ) ) {
biffAddf( TEN, "%s: facet resolution %d not >= 3", me, parm->facetRes );
return 1;
}
if ( !( AIR_IN_OP( tenGlyphTypeUnknown, parm->glyphType,
tenGlyphTypeLast ) ) ) {
biffAddf( TEN, "%s: unset ( or invalid ) glyphType ( %d )",
me, parm->glyphType );
return 1;
}
if ( !( parm->glyphScale > 0 ) ) {
biffAddf( TEN, "%s: glyphScale must be > 0 ( not %g )", me, parm->glyphScale );
return 1;
}
if ( parm->nmask ) {
if ( npos ) {
biffAddf( TEN, "%s: can't do masking with explicit coordinate list", me );
return 1;
}
if ( !( 3 == parm->nmask->dim
&& parm->nmask->axis[0].size == nten->axis[1].size
&& parm->nmask->axis[1].size == nten->axis[2].size
&& parm->nmask->axis[2].size == nten->axis[3].size ) ) {
biffAddf( TEN, "%s: mask isn't 3-D or doesn't have sizes ( %s, %s, %s )", me,
airSprintSize_t( stmp[0], nten->axis[1].size ),
airSprintSize_t( stmp[1], nten->axis[2].size ),
airSprintSize_t( stmp[2], nten->axis[3].size ) );
return 1;
}
if ( !( AIR_EXISTS( parm->maskThresh ) ) ) {
biffAddf( TEN, "%s: maskThresh hasn't been set", me );
return 1;
}
}
if ( !( AIR_EXISTS( parm->anisoThresh )
&& AIR_EXISTS( parm->confThresh ) ) ) {
biffAddf( TEN, "%s: anisoThresh and confThresh haven't both been set", me );
return 1;
}
if ( parm->doSlice ) {
if ( npos ) {
biffAddf( TEN, "%s: can't do slice with explicit coordinate list", me );
return 1;
}
if ( !( parm->sliceAxis <=2 ) ) {
biffAddf( TEN, "%s: slice axis %d invalid", me, parm->sliceAxis );
return 1;
}
if ( !( parm->slicePos < nten->axis[1+parm->sliceAxis].size ) ) {
biffAddf( TEN, "%s: slice pos %s not in valid range [0..%s]", me,
airSprintSize_t( stmp[0], parm->slicePos ),
airSprintSize_t( stmp[1], nten->axis[1+parm->sliceAxis].size-1 ) );
return 1;
}
if ( nslc ) {
if ( 2 != nslc->dim ) {
biffAddf( TEN, "%s: explicit slice must be 2-D ( not %d )",
me, nslc->dim );
return 1;
}
tenSize[0] = nten->axis[1].size;
tenSize[1] = nten->axis[2].size;
tenSize[2] = nten->axis[3].size;
for ( duh=parm->sliceAxis; duh<2; duh++ ) {
tenSize[duh] = tenSize[duh+1];
}
if ( !( tenSize[0] == nslc->axis[0].size
&& tenSize[1] == nslc->axis[1].size ) ) {
biffAddf( TEN, "%s: axis %u slice of %sx%sx%s volume != %sx%s", me,
parm->sliceAxis,
airSprintSize_t( stmp[0], nten->axis[1].size ),
airSprintSize_t( stmp[1], nten->axis[2].size ),
airSprintSize_t( stmp[2], nten->axis[3].size ),
airSprintSize_t( stmp[3], nslc->axis[0].size ),
airSprintSize_t( stmp[4], nslc->axis[1].size ) );
return 1;
}
} else {
if ( airEnumValCheck( tenAniso, parm->sliceAnisoType ) ) {
biffAddf( TEN, "%s: unset ( or invalid ) sliceAnisoType ( %d )",
me, parm->sliceAnisoType );
return 1;
}
}
}
return 0;
}
int
184 tenGlyphGen( limnObject *glyphsLimn, echoScene *glyphsEcho,
tenGlyphParm *parm,
const Nrrd *nten, const Nrrd *npos, const Nrrd *nslc ) {
static const char me[]="tenGlyphGen";
gageShape *shape;
airArray *mop;
float *tdata, eval[3], evec[9], *cvec, rotEvec[9], mA_f[16],
absEval[3], glyphScl[3];
double pI[3], pW[3], cl, cp, sRot[16], mA[16], mB[16], msFr[9], tmpvec[3],
R, G, B, qA, qB, qC, glyphAniso, sliceGray;
unsigned int duh;
int slcCoord[3], idx, glyphIdx, axis, numGlyphs,
svRGBAfl=AIR_FALSE;
limnLook *look; int lookIdx;
echoObject *eglyph, *inst, *list=NULL, *split, *esquare;
echoPos_t eM[16], originOffset[3], edge0[3], edge1[3];
char stmp[AIR_STRLEN_SMALL];
/*
int eret;
double tmp1[3], tmp2[3];
*/
if ( !( ( glyphsLimn || glyphsEcho ) && nten && parm ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
mop = airMopNew( );
shape = gageShapeNew( );
shape->defaultCenter = nrrdCenterCell;
airMopAdd( mop, shape, ( airMopper )gageShapeNix, airMopAlways );
if ( npos ) {
if ( !( 2 == nten->dim && 7 == nten->axis[0].size ) ) {
biffAddf( TEN, "%s: nten isn't 2-D 7-by-N array", me );
airMopError( mop ); return 1;
}
if ( !( 2 == npos->dim && 3 == npos->axis[0].size
&& nten->axis[1].size == npos->axis[1].size ) ) {
biffAddf( TEN, "%s: npos isn't 2-D 3-by-%s array", me,
airSprintSize_t( stmp, nten->axis[1].size ) );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeFloat == nten->type && nrrdTypeFloat == npos->type ) ) {
biffAddf( TEN, "%s: nten and npos must be %s, not %s and %s", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nten->type ),
airEnumStr( nrrdType, npos->type ) );
airMopError( mop ); return 1;
}
} else {
if ( tenTensorCheck( nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a valid DT volume", me );
airMopError( mop ); return 1;
}
}
if ( tenGlyphParmCheck( parm, nten, npos, nslc ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
if ( !npos ) {
if ( gageShapeSet( shape, nten, tenGageKind->baseDim ) ) {
biffMovef( TEN, GAGE, "%s: trouble", me );
airMopError( mop ); return 1;
}
}
if ( parm->doSlice ) {
ELL_3V_COPY( edge0, shape->spacing );
ELL_3V_COPY( edge1, shape->spacing );
edge0[parm->sliceAxis] = edge1[parm->sliceAxis] = 0.0;
switch( parm->sliceAxis ) {
case 0:
edge0[1] = edge1[2] = 0;
ELL_4M_ROTATE_Y_SET( sRot, AIR_PI/2 );
break;
case 1:
edge0[0] = edge1[2] = 0;
ELL_4M_ROTATE_X_SET( sRot, AIR_PI/2 );
break;
case 2: default:
edge0[0] = edge1[1] = 0;
ELL_4M_IDENTITY_SET( sRot );
break;
}
ELL_3V_COPY( originOffset, shape->spacing );
ELL_3V_SCALE( originOffset, -0.5, originOffset );
originOffset[parm->sliceAxis] *= -2*parm->sliceOffset;
}
if ( glyphsLimn ) {
/* create limnLooks for diffuse and ambient-only shading */
/* ??? */
/* hack: save old value of setVertexRGBAFromLook, and set to true */
svRGBAfl = glyphsLimn->setVertexRGBAFromLook;
glyphsLimn->setVertexRGBAFromLook = AIR_TRUE;
}
if ( glyphsEcho ) {
list = echoObjectNew( glyphsEcho, echoTypeList );
}
if ( npos ) {
numGlyphs = AIR_UINT( nten->axis[1].size );
} else {
numGlyphs = shape->size[0] * shape->size[1] * shape->size[2];
}
/* find measurement frame transform */
if ( 3 == nten->spaceDim
&& AIR_EXISTS( nten->measurementFrame[0][0] ) ) {
/* msFr nten->measurementFrame
** 0 1 2 [0][0] [1][0] [2][0]
** 3 4 5 [0][1] [1][1] [2][1]
** 6 7 8 [0][2] [1][2] [2][2]
*/
msFr[0] = nten->measurementFrame[0][0];
msFr[3] = nten->measurementFrame[0][1];
msFr[6] = nten->measurementFrame[0][2];
msFr[1] = nten->measurementFrame[1][0];
msFr[4] = nten->measurementFrame[1][1];
msFr[7] = nten->measurementFrame[1][2];
msFr[2] = nten->measurementFrame[2][0];
msFr[5] = nten->measurementFrame[2][1];
msFr[8] = nten->measurementFrame[2][2];
} else {
ELL_3M_IDENTITY_SET( msFr );
}
for ( idx=0; idx<numGlyphs; idx++ ) {
tdata = ( float* )( nten->data ) + 7*idx;
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: hello %g %g %g %g %g %g %g\n",
me, idx, numGlyphs, tdata[0],
tdata[1], tdata[2], tdata[3],
tdata[4], tdata[5], tdata[6] );
}
if ( !( TEN_T_EXISTS( tdata ) ) ) {
/* there's nothing we can do here */
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: non-existent data\n",
me, idx, numGlyphs );
}
continue;
}
if ( npos ) {
ELL_3V_COPY( pW, ( float* )( npos->data ) + 3*idx );
if ( !( AIR_EXISTS( pW[0] ) && AIR_EXISTS( pW[1] ) && AIR_EXISTS( pW[2] ) ) ) {
/* position doesn't exist- perhaps because its from the push
library, which might kill points by setting coords to nan */
continue;
}
} else {
NRRD_COORD_GEN( pI, shape->size, 3, idx );
/* this does take into account full orientation */
gageShapeItoW( shape, pW, pI );
if ( parm->nmask ) {
if ( !( nrrdFLookup[parm->nmask->type]( parm->nmask->data, idx )
>= parm->maskThresh ) ) {
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: doesn't meet mask thresh\n",
me, idx, numGlyphs );
}
continue;
}
}
}
tenEigensolve_f( eval, evec, tdata );
/* transform eigenvectors by measurement frame */
ELL_3MV_MUL( tmpvec, msFr, evec + 0 );
ELL_3V_COPY_TT( evec + 0, float, tmpvec );
ELL_3MV_MUL( tmpvec, msFr, evec + 3 );
ELL_3V_COPY_TT( evec + 3, float, tmpvec );
ELL_3MV_MUL( tmpvec, msFr, evec + 6 );
ELL_3V_COPY_TT( evec + 6, float, tmpvec );
ELL_3V_CROSS( tmpvec, evec + 0, evec + 3 );
if ( 0 > ELL_3V_DOT( tmpvec, evec + 6 ) ) {
ELL_3V_SCALE( evec + 6, -1, evec + 6 );
}
ELL_3M_TRANSPOSE( rotEvec, evec );
if ( parm->doSlice
&& pI[parm->sliceAxis] == parm->slicePos ) {
/* set sliceGray */
if ( nslc ) {
/* we aren't masked by confidence, as anisotropy slice is */
for ( duh=0; duh<parm->sliceAxis; duh++ ) {
slcCoord[duh] = ( int )( pI[duh] );
}
for ( duh=duh<parm->sliceAxis; duh<2; duh++ ) {
slcCoord[duh] = ( int )( pI[duh+1] );
}
/* HEY: GLK has no idea what's going here */
slcCoord[0] = ( int )( pI[0] );
slcCoord[1] = ( int )( pI[1] );
slcCoord[2] = ( int )( pI[2] );
sliceGray =
nrrdFLookup[nslc->type]( nslc->data, slcCoord[0]
+ nslc->axis[0].size*slcCoord[1] );
} else {
if ( !( tdata[0] >= parm->confThresh ) ) {
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d ( slice ): conf %g < thresh %g\n",
me, idx, numGlyphs, tdata[0], parm->confThresh );
}
continue;
}
sliceGray = tenAnisoEval_f( eval, parm->sliceAnisoType );
}
if ( parm->sliceGamma > 0 ) {
sliceGray = AIR_AFFINE( 0, sliceGray, 1, parm->sliceBias, 1 );
sliceGray = pow( sliceGray, 1.0/parm->sliceGamma );
} else {
sliceGray = AIR_AFFINE( 0, sliceGray, 1, 0, 1-parm->sliceBias );
sliceGray = 1.0 - pow( sliceGray, -1.0/parm->sliceGamma );
}
/* make slice contribution */
/* HEY: this is *NOT* aware of shape->fromOrientation */
if ( glyphsLimn ) {
lookIdx = limnObjectLookAdd( glyphsLimn );
look = glyphsLimn->look + lookIdx;
ELL_4V_SET_TT( look->rgba, float, sliceGray, sliceGray, sliceGray, 1 );
ELL_3V_SET( look->kads, 1, 0, 0 );
look->spow = 0;
glyphIdx = limnObjectSquareAdd( glyphsLimn, lookIdx );
ELL_4M_IDENTITY_SET( mA );
ell_4m_post_mul_d( mA, sRot );
if ( !npos ) {
ELL_4M_SCALE_SET( mB,
shape->spacing[0],
shape->spacing[1],
shape->spacing[2] );
}
ell_4m_post_mul_d( mA, mB );
ELL_4M_TRANSLATE_SET( mB, pW[0], pW[1], pW[2] );
ell_4m_post_mul_d( mA, mB );
ELL_4M_TRANSLATE_SET( mB,
originOffset[0],
originOffset[1],
originOffset[2] );
ell_4m_post_mul_d( mA, mB );
ELL_4M_COPY_TT( mA_f, float, mA );
limnObjectPartTransform( glyphsLimn, glyphIdx, mA_f );
}
if ( glyphsEcho ) {
esquare = echoObjectNew( glyphsEcho, echoTypeRectangle );
ELL_3V_ADD2( ( ( echoRectangle* )esquare )->origin, pW, originOffset );
ELL_3V_COPY( ( ( echoRectangle* )esquare )->edge0, edge0 );
ELL_3V_COPY( ( ( echoRectangle* )esquare )->edge1, edge1 );
echoColorSet( esquare,
AIR_CAST( echoCol_t, sliceGray ),
AIR_CAST( echoCol_t, sliceGray ),
AIR_CAST( echoCol_t, sliceGray ), 1 );
/* this is pretty arbitrary- but I want shadows to have some effect.
Previously, the material was all ambient: ( A, D, S ) = ( 1, 0, 0 ),
which avoided all shadow effects. */
echoMatterPhongSet( glyphsEcho, esquare, 0.4f, 0.6f, 0, 40 );
echoListAdd( list, esquare );
}
}
if ( parm->onlyPositive ) {
if ( eval[2] < 0 ) {
/* didn't have all positive eigenvalues, its outta here */
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: not all evals %g %g %g > 0\n",
me, idx, numGlyphs, eval[0], eval[1], eval[2] );
}
continue;
}
}
if ( !( tdata[0] >= parm->confThresh ) ) {
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: conf %g < thresh %g\n",
me, idx, numGlyphs, tdata[0], parm->confThresh );
}
continue;
}
if ( !( tenAnisoEval_f( eval, parm->anisoType ) >= parm->anisoThresh ) ) {
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: aniso[%d] %g < thresh %g\n",
me, idx, numGlyphs, parm->anisoType,
tenAnisoEval_f( eval, parm->anisoType ), parm->anisoThresh );
}
continue;
}
glyphAniso = tenAnisoEval_f( eval, parm->colAnisoType );
/*
fprintf( stderr, "%s: eret = %d; evals = %g %g %g\n", me,
eret, eval[0], eval[1], eval[2] );
ELL_3V_CROSS( tmp1, evec+0, evec+3 ); tmp2[0] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, evec+0, evec+6 ); tmp2[1] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, evec+3, evec+6 ); tmp2[2] = ELL_3V_LEN( tmp1 );
fprintf( stderr, "%s: crosses = %g %g %g\n", me,
tmp2[0], tmp2[1], tmp2[2] );
*/
/* set transform ( in mA ) */
ELL_3V_ABS( absEval, eval );
ELL_4M_IDENTITY_SET( mA ); /* reset */
ELL_3V_SCALE( glyphScl, parm->glyphScale, absEval ); /* scale by evals */
ELL_4M_SCALE_SET( mB, glyphScl[0], glyphScl[1], glyphScl[2] );
ell_4m_post_mul_d( mA, mB );
ELL_43M_INSET( mB, rotEvec ); /* rotate by evecs */
ell_4m_post_mul_d( mA, mB );
ELL_4M_TRANSLATE_SET( mB, pW[0], pW[1], pW[2] ); /* translate */
ell_4m_post_mul_d( mA, mB );
/* set color ( in R, G, B ) */
cvec = evec + 3*( AIR_CLAMP( 0, parm->colEvec, 2 ) );
R = AIR_ABS( cvec[0] ); /* standard mapping */
G = AIR_ABS( cvec[1] );
B = AIR_ABS( cvec[2] );
/* desaturate by colMaxSat */
R = AIR_AFFINE( 0.0, parm->colMaxSat, 1.0, parm->colIsoGray, R );
G = AIR_AFFINE( 0.0, parm->colMaxSat, 1.0, parm->colIsoGray, G );
B = AIR_AFFINE( 0.0, parm->colMaxSat, 1.0, parm->colIsoGray, B );
/* desaturate some by anisotropy */
R = AIR_AFFINE( 0.0, parm->colAnisoModulate, 1.0,
R, AIR_AFFINE( 0.0, glyphAniso, 1.0, parm->colIsoGray, R ) );
G = AIR_AFFINE( 0.0, parm->colAnisoModulate, 1.0,
G, AIR_AFFINE( 0.0, glyphAniso, 1.0, parm->colIsoGray, G ) );
B = AIR_AFFINE( 0.0, parm->colAnisoModulate, 1.0,
B, AIR_AFFINE( 0.0, glyphAniso, 1.0, parm->colIsoGray, B ) );
/* clamp and do gamma */
R = AIR_CLAMP( 0.0, R, 1.0 );
G = AIR_CLAMP( 0.0, G, 1.0 );
B = AIR_CLAMP( 0.0, B, 1.0 );
R = pow( R, parm->colGamma );
G = pow( G, parm->colGamma );
B = pow( B, parm->colGamma );
/* find axis, and superquad exponents qA and qB */
if ( eval[2] > 0 ) {
/* all evals positive */
cl = AIR_MIN( 0.99, tenAnisoEval_f( eval, tenAniso_Cl1 ) );
cp = AIR_MIN( 0.99, tenAnisoEval_f( eval, tenAniso_Cp1 ) );
if ( cl > cp ) {
axis = 0;
qA = pow( 1-cp, parm->sqdSharp );
qB = pow( 1-cl, parm->sqdSharp );
} else {
axis = 2;
qA = pow( 1-cl, parm->sqdSharp );
qB = pow( 1-cp, parm->sqdSharp );
}
qC = qB;
} else if ( eval[0] < 0 ) {
/* all evals negative */
float aef[3];
aef[0] = absEval[2];
aef[1] = absEval[1];
aef[2] = absEval[0];
cl = AIR_MIN( 0.99, tenAnisoEval_f( aef, tenAniso_Cl1 ) );
cp = AIR_MIN( 0.99, tenAnisoEval_f( aef, tenAniso_Cp1 ) );
if ( cl > cp ) {
axis = 2;
qA = pow( 1-cp, parm->sqdSharp );
qB = pow( 1-cl, parm->sqdSharp );
} else {
axis = 0;
qA = pow( 1-cl, parm->sqdSharp );
qB = pow( 1-cp, parm->sqdSharp );
}
qC = qB;
} else {
#define OOSQRT2 0.70710678118654752440
#define OOSQRT3 0.57735026918962576451
/* double poleA[3]={OOSQRT3, OOSQRT3, OOSQRT3}; */
double poleB[3]={1, 0, 0};
double poleC[3]={OOSQRT2, OOSQRT2, 0};
double poleD[3]={OOSQRT3, -OOSQRT3, -OOSQRT3};
double poleE[3]={OOSQRT2, 0, -OOSQRT2};
double poleF[3]={OOSQRT3, OOSQRT3, -OOSQRT3};
double poleG[3]={0, -OOSQRT2, -OOSQRT2};
double poleH[3]={0, 0, -1};
/* double poleI[3]={-OOSQRT3, -OOSQRT3, -OOSQRT3}; */
double funk[3]={0, 4, 2}, thrn[3]={1, 4, 4};
double octa[3]={0, 2, 2}, cone[3]={1, 2, 2};
double evalN[3], tmp, bary[3];
double qq[3];
ELL_3V_NORM( evalN, eval, tmp );
if ( eval[1] >= -eval[2] ) {
/* inside B-F-C */
ell_3v_barycentric_spherical_d( bary, poleB, poleF, poleC, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], octa, bary[1], thrn, bary[2], cone );
axis = 2;
} else if ( eval[0] >= -eval[2] ) {
/* inside B-D-F */
if ( eval[1] >= 0 ) {
/* inside B-E-F */
ell_3v_barycentric_spherical_d( bary, poleB, poleE, poleF, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], octa, bary[1], funk, bary[2], thrn );
axis = 2;
} else {
/* inside B-D-E */
ell_3v_barycentric_spherical_d( bary, poleB, poleD, poleE, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], cone, bary[1], thrn, bary[2], funk );
axis = 0;
}
} else if ( eval[0] < -eval[1] ) {
/* inside D-G-H */
ell_3v_barycentric_spherical_d( bary, poleD, poleG, poleH, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], thrn, bary[1], cone, bary[2], octa );
axis = 0;
} else if ( eval[1] < 0 ) {
/* inside E-D-H */
ell_3v_barycentric_spherical_d( bary, poleE, poleD, poleH, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], funk, bary[1], thrn, bary[2], octa );
axis = 0;
} else {
/* inside F-E-H */
ell_3v_barycentric_spherical_d( bary, poleF, poleE, poleH, evalN );
ELL_3V_SCALE_ADD3( qq, bary[0], thrn, bary[1], funk, bary[2], cone );
axis = 2;
}
qA = qq[0];
qB = qq[1];
qC = qq[2];
#undef OOSQRT2
#undef OOSQRT3
}
/* add the glyph */
if ( parm->verbose >= 2 ) {
fprintf( stderr, "%s: glyph %d/%d: the glyph stays!\n",
me, idx, numGlyphs );
}
if ( glyphsLimn ) {
lookIdx = limnObjectLookAdd( glyphsLimn );
look = glyphsLimn->look + lookIdx;
ELL_4V_SET_TT( look->rgba, float, R, G, B, 1 );
ELL_3V_SET( look->kads, parm->ADSP[0], parm->ADSP[1], parm->ADSP[2] );
look->spow = 0;
switch( parm->glyphType ) {
case tenGlyphTypeBox:
glyphIdx = limnObjectCubeAdd( glyphsLimn, lookIdx );
break;
case tenGlyphTypeSphere:
glyphIdx = limnObjectPolarSphereAdd( glyphsLimn, lookIdx, axis,
2*parm->facetRes, parm->facetRes );
break;
case tenGlyphTypeCylinder:
glyphIdx = limnObjectCylinderAdd( glyphsLimn, lookIdx, axis,
parm->facetRes );
break;
case tenGlyphTypeSuperquad:
default:
glyphIdx =
limnObjectPolarSuperquadFancyAdd( glyphsLimn, lookIdx, axis,
AIR_CAST( float, qA ),
AIR_CAST( float, qB ),
AIR_CAST( float, qC ), 0,
2*parm->facetRes,
parm->facetRes );
break;
}
ELL_4M_COPY_TT( mA_f, float, mA );
limnObjectPartTransform( glyphsLimn, glyphIdx, mA_f );
}
if ( glyphsEcho ) {
switch( parm->glyphType ) {
case tenGlyphTypeBox:
eglyph = echoObjectNew( glyphsEcho, echoTypeCube );
/* nothing else to set */
break;
case tenGlyphTypeSphere:
eglyph = echoObjectNew( glyphsEcho, echoTypeSphere );
echoSphereSet( eglyph, 0, 0, 0, 1 );
break;
case tenGlyphTypeCylinder:
eglyph = echoObjectNew( glyphsEcho, echoTypeCylinder );
echoCylinderSet( eglyph, axis );
break;
case tenGlyphTypeSuperquad:
default:
eglyph = echoObjectNew( glyphsEcho, echoTypeSuperquad );
echoSuperquadSet( eglyph, axis, qA, qB );
break;
}
echoColorSet( eglyph,
AIR_CAST( echoCol_t, R ),
AIR_CAST( echoCol_t, G ),
AIR_CAST( echoCol_t, B ), 1 );
echoMatterPhongSet( glyphsEcho, eglyph,
parm->ADSP[0], parm->ADSP[1],
parm->ADSP[2], parm->ADSP[3] );
inst = echoObjectNew( glyphsEcho, echoTypeInstance );
ELL_4M_COPY( eM, mA );
echoInstanceSet( inst, eM, eglyph );
echoListAdd( list, inst );
}
}
if ( glyphsLimn ) {
glyphsLimn->setVertexRGBAFromLook = svRGBAfl;
}
if ( glyphsEcho ) {
split = echoListSplit3( glyphsEcho, list, 10 );
echoObjectAdd( glyphsEcho, split );
}
airMopOkay( mop );
return 0;
}
/*
** Zone from Eval
*/
unsigned int
685 tenGlyphBqdZoneEval( const double eval[3] ) {
double x, y, z;
unsigned int zone;
x = eval[0];
y = eval[1];
z = eval[2];
if ( y > 0 ) { /* 0 1 2 3 4 */
if ( z > 0 ) { /* 0 1 */
if ( x - y > y - z ) {
zone = 0;
} else {
zone = 1;
}
} else { /* 2 3 4 */
if ( y > -z ) {
zone = 2;
} else if ( x > -z ) {
zone = 3;
} else {
zone = 4;
}
}
} else { /* 5 6 7 8 9 */
if ( x > 0 ) { /* 5 6 7 */
if ( x > -z ) {
zone = 5;
} else if ( x > -y ) {
zone = 6;
} else {
zone = 7;
}
} else { /* 8 9 */
if ( x - y > y - z ) {
zone = 8;
} else {
zone = 9;
}
}
}
return zone;
}
/*
** UV from Eval
*/
void
732 tenGlyphBqdUvEval( double uv[2], const double eval[3] ) {
double xx, yy, zz, ax, ay, az, mm;
ax = AIR_ABS( eval[0] );
ay = AIR_ABS( eval[1] );
az = AIR_ABS( eval[2] );
mm = AIR_MAX( ax, AIR_MAX( ay, az ) );
if ( mm==0 ) { /* do not divide */
uv[0]=uv[1]=0;
return;
}
xx = eval[0]/mm;
yy = eval[1]/mm;
zz = eval[2]/mm;
uv[0] = AIR_AFFINE( -1, yy, 1, 0, 1 );
if ( xx > -zz ) {
uv[1] = AIR_AFFINE( -1, zz, 1, 0, 1 ) - uv[0] + 1;
} else {
uv[1] = AIR_AFFINE( -1, xx, 1, -1, 0 ) - uv[0] + 1;
}
return;
}
/*
** Eval from UV
*/
void
759 tenGlyphBqdEvalUv( double eval[3], const double uv[2] ) {
double xx, yy, zz, ll;
yy = AIR_AFFINE( 0, uv[0], 1, -1, 1 );
if ( uv[0] + uv[1] > 1 ) {
zz = AIR_AFFINE( 0, uv[1], 1, -1, 1 ) - 1 + yy;
xx = 1;
} else {
xx = AIR_AFFINE( 0, uv[1], 1, -1, 1 ) + yy + 1;
zz = -1;
}
ELL_3V_SET( eval, xx, yy, zz );
ELL_3V_NORM( eval, eval, ll );
return;
}
/*
** Zone from UV
*/
unsigned int
779 tenGlyphBqdZoneUv( const double uv[2] ) {
/* the use of "volatile" here, as well as additional variables for
expressions involving u and v, is based on browsing this summary of the
subtleties of IEEE 754: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
In this function, "if ( u + v > 0.5 )" returned one thing for cygwin, and
something else for other platforms. Adding volatile and more variables
for expressions brings cygwin back into line with the other platforms */
volatile double u, v, upv, tupv;
unsigned int zone;
u = uv[0];
v = uv[1];
upv = u + v;
tupv = 2*u + v;
if ( u > 0.5 ) { /* 0 1 2 3 4 */
if ( upv > 1.5 ) { /* 0 1 */
if ( u < v ) {
zone = 0;
} else {
zone = 1;
}
} else { /* 2 3 4 */
if ( tupv > 2 ) {
zone = 2;
} else if ( upv > 1 ) {
zone = 3;
} else {
zone = 4;
}
}
} else { /* 5 6 7 8 9 */
if ( upv > 0.5 ) { /* 5 6 7 */
if ( upv > 1 ) {
zone = 5;
} else if ( tupv > 1 ) {
zone = 6;
} else {
zone = 7;
}
} else { /* 8 9 */
if ( u < v ) {
zone = 8;
} else {
zone = 9;
}
}
}
return zone;
}
static void
830 baryFind( double bcoord[3], const double uvp[2],
const double uv0[2],
const double uv1[2],
const double uv2[2] ) {
double mat[9], a, a01, a02, a12;
ELL_3M_SET( mat,
uv0[0], uv0[1], 1,
uv1[0], uv1[1], 1,
uvp[0], uvp[1], 1 );
a01 = ELL_3M_DET( mat ); a01 = AIR_ABS( a01 );
ELL_3M_SET( mat,
uv0[0], uv0[1], 1,
uv2[0], uv2[1], 1,
uvp[0], uvp[1], 1 );
a02 = ELL_3M_DET( mat ); a02 = AIR_ABS( a02 );
ELL_3M_SET( mat,
uv1[0], uv1[1], 1,
uv2[0], uv2[1], 1,
uvp[0], uvp[1], 1 );
a12 = ELL_3M_DET( mat ); a12 = AIR_ABS( a12 );
a = a01 + a02 + a12;
ELL_3V_SET( bcoord, a12/a, a02/a, a01/a );
return;
}
static void
860 baryBlend( double abc[3], const double co[3],
const double abc0[3],
const double abc1[3],
const double abc2[3] ) {
unsigned int ii;
for ( ii=0; ii<3; ii++ ) {
abc[ii] = co[0]*abc0[ii] + co[1]*abc1[ii] + co[2]*abc2[ii];
}
return;
}
void
873 tenGlyphBqdAbcUv( double abc[3], const double uv[2], double betaMax ) {
static const unsigned int vertsZone[10][3] = {{0, 1, 2}, /* 0 */
{0, 2, 3}, /* 1 */
{1, 3, 4}, /* 2 */
{1, 4, 5}, /* 3 */
{4, 5, 9}, /* 4 */
{1, 5, 6}, /* 5 */
{5, 6, 9}, /* 6 */
{6, 7, 9}, /* 7 */
{7, 8, 10}, /* 8 */
{8, 9, 10}}; /* 9 */
static const double uvVert[11][2] = {{1.00, 1.00}, /* 0 */
{0.50, 1.00}, /* 1 */
{0.75, 0.75}, /* 2 */
{1.00, 0.50}, /* 3 */
{1.00, 0.00}, /* 4 */
{0.50, 0.50}, /* 5 */
{0.00, 1.00}, /* 6 */
{0.00, 0.50}, /* 7 */
{0.25, 0.25}, /* 8 */
{0.50, 0.00}, /* 9 */
{0.00, 0.00}}; /* 10 */
double abcBall[3], abcCyli[3], abcFunk[3], abcThrn[3],
abcOcta[3], abcCone[3], abcHalf[3];
/* old compile-time setting
const double *abcAll[10][11] = {
zone \ vert 0 1 2 3 4 5 6 7 8 9 10
0 {abcBall, abcCyli, abcHalf, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
1 {abcBall, NULL, abcHalf, abcCyli, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
2 { NULL, abcOcta, NULL, abcCone, abcThrn, NULL, NULL, NULL, NULL, NULL, NULL },
3 { NULL, abcOcta, NULL, NULL, abcThrn, abcFunk, NULL, NULL, NULL, NULL, NULL },
4 { NULL, NULL, NULL, NULL, abcThrn, abcFunk, NULL, NULL, NULL, abcCone, NULL },
5 { NULL, abcCone, NULL, NULL, NULL, abcFunk, abcThrn, NULL, NULL, NULL, NULL },
6 { NULL, NULL, NULL, NULL, NULL, abcFunk, abcThrn, NULL, NULL, abcOcta, NULL },
7 { NULL, NULL, NULL, NULL, NULL, NULL, abcThrn, abcCone, NULL, abcOcta, NULL },
8 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, abcCyli, abcHalf, NULL, abcBall },
9 { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, abcHalf, abcCyli, abcBall }};
*/
const double *abcAll[10][11];
unsigned int pvi[3], zone, vert;
double bcoord[3];
ELL_3V_SET( abcBall, 1, 1, 1 );
ELL_3V_SET( abcCyli, 1, 0, 0 );
ELL_3V_SET( abcFunk, 0, betaMax, 2 ); /* only one with c != b */
ELL_3V_SET( abcThrn, 1, betaMax, 3 );
ELL_3V_SET( abcOcta, 0, 2, 2 );
ELL_3V_SET( abcCone, 1, 2, 2 );
ELL_3V_SET( abcHalf, 0.5, 0.5, 0.5 ); /* alpha is half-way between alpha of
octa and cone and beta has to be
the same as alpha at for the
seam to be shape-continuous */
/* run-time setting of abcAll[][]; compile-time setting ( comments above )
gives "initializer element is not computable at load time" warnings */
for ( zone=0; zone<10; zone++ ) {
for ( vert=0; vert<11; vert++ ) {
abcAll[zone][vert]=NULL;
}
}
#define SET( zi, vi0, vi1, vi2, sh0, sh1, sh2 ) \
abcAll[zi][vi0] = abc##sh0; \
abcAll[zi][vi1] = abc##sh1; \
abcAll[zi][vi2] = abc##sh2
SET( 0, 0, 1, 2, Ball, Cyli, Half );
SET( 1, 0, 2, 3, Ball, Half, Cyli );
SET( 2, 1, 3, 4, Octa, Cone, Thrn );
SET( 3, 1, 4, 5, Octa, Thrn, Funk );
SET( 4, 4, 5, 9, Thrn, Funk, Cone );
SET( 5, 1, 5, 6, Cone, Funk, Thrn );
SET( 6, 5, 6, 9, Funk, Thrn, Octa );
SET( 7, 6, 7, 9, Thrn, Cone, Octa );
SET( 8, 7, 8, 10, Cyli, Half, Ball );
SET( 9, 8, 9, 10, Half, Cyli, Ball );
#undef SET
zone = tenGlyphBqdZoneUv( uv );
ELL_3V_COPY( pvi, vertsZone[zone] );
baryFind( bcoord, uv, uvVert[pvi[0]], uvVert[pvi[1]], uvVert[pvi[2]] );
baryBlend( abc, bcoord,
abcAll[zone][pvi[0]],
abcAll[zone][pvi[1]],
abcAll[zone][pvi[2]] );
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
tenGradientParm *
28 tenGradientParmNew( void ) {
tenGradientParm *ret;
ret = ( tenGradientParm * )calloc( 1, sizeof( tenGradientParm ) );
if ( ret ) {
ret->initStep = 1.0;
ret->jitter = 0.2;
ret->minVelocity = 0.000000001;
ret->minPotentialChange = 0.000000001;
ret->minMean = 0.0001;
ret->minMeanImprovement = 0.00005;
ret->single = AIR_FALSE;
ret->insertZeroVec = AIR_FALSE;
ret->verbose = 1;
ret->snap = 0;
ret->report = 400;
ret->expo = 1;
ret->expo_d = 0;
ret->seed = 42;
ret->maxEdgeShrink = 20;
ret->minIteration = 0;
ret->maxIteration = 1000000;
ret->step = 0;
ret->nudge = 0;
ret->itersUsed = 0;
ret->potential = 0;
ret->potentialNorm = 0;
ret->angle = 0;
ret->edge = 0;
}
return ret;
}
tenGradientParm *
62 tenGradientParmNix( tenGradientParm *tgparm ) {
airFree( tgparm );
return NULL;
}
int
69 tenGradientCheck( const Nrrd *ngrad, int type, unsigned int minnum ) {
static const char me[]="tenGradientCheck";
if ( nrrdCheck( ngrad ) ) {
biffMovef( TEN, NRRD, "%s: basic validity check failed", me );
return 1;
}
if ( !( 3 == ngrad->axis[0].size && 2 == ngrad->dim ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: need a 3xN 2-D array ( not a %sx? %u-D array )", me,
airSprintSize_t( stmp, ngrad->axis[0].size ), ngrad->dim );
return 1;
}
if ( nrrdTypeDefault != type && type != ngrad->type ) {
biffAddf( TEN, "%s: requested type %s but got type %s", me,
airEnumStr( nrrdType, type ), airEnumStr( nrrdType, ngrad->type ) );
return 1;
}
if ( nrrdTypeBlock == ngrad->type ) {
biffAddf( TEN, "%s: sorry, can't use %s type", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( minnum <= ngrad->axis[1].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: have only %s gradients, need at least %d", me,
airSprintSize_t( stmp, ngrad->axis[1].size ), minnum );
return 1;
}
return 0;
}
/*
******** tenGradientRandom
**
** generates num random unit vectors of type double
*/
int
108 tenGradientRandom( Nrrd *ngrad, unsigned int num, unsigned int seed ) {
static const char me[]="tenGradientRandom";
double *grad, len;
unsigned int gi;
if ( nrrdMaybeAlloc_va( ngrad, nrrdTypeDouble, 2,
AIR_CAST( size_t, 3 ), AIR_CAST( size_t, num ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
airSrandMT( seed );
grad = AIR_CAST( double*, ngrad->data );
for ( gi=0; gi<num; gi++ ) {
do {
grad[0] = AIR_AFFINE( 0, airDrandMT( ), 1, -1, 1 );
grad[1] = AIR_AFFINE( 0, airDrandMT( ), 1, -1, 1 );
grad[2] = AIR_AFFINE( 0, airDrandMT( ), 1, -1, 1 );
len = ELL_3V_LEN( grad );
} while ( len > 1 || !len );
ELL_3V_SCALE( grad, 1.0/len, grad );
grad += 3;
}
return 0;
}
/*
******** tenGradientIdealEdge
**
** edge length of delauney triangulation of idealized distribution of
** N gradients ( 2*N points ), but also allowing a boolean "single" flag
** saying that we actually care about N points
*/
double
141 tenGradientIdealEdge( unsigned int N, int single ) {
return sqrt( ( !single ? 4 : 8 )*AIR_PI/( sqrt( 3 )*N ) );
}
/*
******** tenGradientJitter
**
** moves all gradients by amount dist on tangent plane, in a random
** direction, and then renormalizes. The distance is a fraction
** of the ideal edge length ( via tenGradientIdealEdge )
*/
int
154 tenGradientJitter( Nrrd *nout, const Nrrd *nin, double dist ) {
static const char me[]="tenGradientJitter";
double *grad, perp0[3], perp1[3], len, theta, cc, ss, edge;
unsigned int gi, num;
if ( nrrdConvert( nout, nin, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble converting input to double", me );
return 1;
}
if ( tenGradientCheck( nout, nrrdTypeDouble, 3 ) ) {
biffAddf( TEN, "%s: didn't get valid gradients", me );
return 1;
}
grad = AIR_CAST( double*, nout->data );
num = AIR_UINT( nout->axis[1].size );
/* HEY: possible confusion between single and not */
edge = tenGradientIdealEdge( num, AIR_FALSE );
for ( gi=0; gi<num; gi++ ) {
ELL_3V_NORM( grad, grad, len );
ell_3v_perp_d( perp0, grad );
ELL_3V_CROSS( perp1, perp0, grad );
theta = AIR_AFFINE( 0, airDrandMT( ), 1, 0, 2*AIR_PI );
cc = dist*edge*cos( theta );
ss = dist*edge*sin( theta );
ELL_3V_SCALE_ADD3( grad, 1.0, grad, cc, perp0, ss, perp1 );
ELL_3V_NORM( grad, grad, len );
grad += 3;
}
return 0;
}
void
187 tenGradientMeasure( double *pot, double *minAngle, double *minEdge,
const Nrrd *npos, tenGradientParm *tgparm,
int edgeNormalize ) {
/* static const char me[]="tenGradientMeasure"; */
double diff[3], *pos, atmp=0, ptmp, edge, len;
unsigned int ii, jj, num;
/* allow minAngle NULL */
if ( !( pot && npos && tgparm ) ) {
return;
}
num = AIR_UINT( npos->axis[1].size );
pos = AIR_CAST( double *, npos->data );
edge = ( edgeNormalize
? tenGradientIdealEdge( num, tgparm->single )
: 1.0 );
*pot = 0;
if ( minAngle ) {
*minAngle = AIR_PI;
}
if ( minEdge ) {
*minEdge = 2;
}
for ( ii=0; ii<num; ii++ ) {
for ( jj=0; jj<ii; jj++ ) {
ELL_3V_SUB( diff, pos + 3*ii, pos + 3*jj );
len = ELL_3V_LEN( diff );
if ( minEdge ) {
*minEdge = AIR_MIN( *minEdge, len );
}
if ( tgparm->expo ) {
ptmp = airIntPow( edge/len, tgparm->expo );
} else {
ptmp = pow( edge/len, tgparm->expo_d );
}
*pot += ptmp;
if ( minAngle ) {
atmp = ell_3v_angle_d( pos + 3*ii, pos + 3*jj );
*minAngle = AIR_MIN( atmp, *minAngle );
}
if ( !tgparm->single ) {
*pot += ptmp;
ELL_3V_ADD2( diff, pos + 3*ii, pos + 3*jj );
len = ELL_3V_LEN( diff );
if ( minEdge ) {
*minEdge = AIR_MIN( *minEdge, len );
}
if ( tgparm->expo ) {
*pot += 2*airIntPow( edge/len, tgparm->expo );
} else {
*pot += 2*pow( edge/len, tgparm->expo_d );
}
if ( minAngle ) {
*minAngle = AIR_MIN( AIR_PI-atmp, *minAngle );
}
}
}
}
return;
}
/*
** Do asynchronous update of positions in "npos', based on force
** calculations wherein the distances are normalized "edge". Using a
** small "edge" allows forces to either underflow to zero, or be
** finite, instead of exploding to infinity, for high exponents.
**
** The smallest seen edge length is recorded in "*edgeMin", which is
** initialized to the given "edge". This allows, for example, the
** caller to try again with a smaller edge normalization.
**
** The mean velocity of the points through the update is recorded in
** "*meanVel".
**
** Based on the observation that when using large exponents, numerical
** difficulties arise from the ( force-based ) update of the positions
** of the two ( or few ) closest particles, this function puts a speed
** limit ( variable "limit" ) on the distance a particle may move during
** update, expressed as a fraction of the normalizing edge length.
** "limit" has been set heuristically, according to the exponent ( we
** have to clamp speeds more aggresively with higher exponents ), as
** well as ( even more heuristically ) according to the number of times
** the step size has been decreased. This latter factor has to be
** bounded, so that the update is not unnecessarily bounded when the
** step size gets very small at the last stages of computation.
** Without the step-size-based speed limit, the step size would
** sometimes ( e.g. num=200, expo=300 ) have to reduced to a miniscule
** value, which slows subsequent convergence terribly.
**
** this function is not static, though it could be, so that mac's
** "Sampler" app can profile this
*/
int
281 _tenGradientUpdate( double *meanVel, double *edgeMin,
Nrrd *npos, double edge, tenGradientParm *tgparm ) {
/* static const char me[]="_tenGradientUpdate"; */
double *pos, newpos[3], grad[3], ngrad[3],
dir[3], len, rep, step, diff[3], limit, expo;
int num, ii, jj, E;
E = 0;
pos = AIR_CAST( double *, npos->data );
num = AIR_UINT( npos->axis[1].size );
*meanVel = 0;
*edgeMin = edge;
expo = tgparm->expo ? tgparm->expo : tgparm->expo_d;
limit = expo*AIR_MIN( sqrt( expo ),
log( 1 + tgparm->initStep/tgparm->step ) );
for ( ii=0; ii<num; ii++ ) {
ELL_3V_SET( grad, 0, 0, 0 );
for ( jj=0; jj<num; jj++ ) {
if ( ii == jj ) {
continue;
}
ELL_3V_SUB( dir, pos + 3*ii, pos + 3*jj );
ELL_3V_NORM( dir, dir, len );
*edgeMin = AIR_MIN( *edgeMin, len );
if ( tgparm->expo ) {
rep = airIntPow( edge/len, tgparm->expo+1 );
} else {
rep = pow( edge/len, tgparm->expo_d+1 );
}
ELL_3V_SCALE_INCR( grad, rep/num, dir );
if ( !tgparm->single ) {
ELL_3V_ADD2( dir, pos + 3*ii, pos + 3*jj );
ELL_3V_NORM( dir, dir, len );
*edgeMin = AIR_MIN( *edgeMin, len );
if ( tgparm->expo ) {
rep = airIntPow( edge/len, tgparm->expo+1 );
} else {
rep = pow( edge/len, tgparm->expo_d+1 );
}
ELL_3V_SCALE_INCR( grad, rep/num, dir );
}
}
ELL_3V_NORM( ngrad, grad, len );
if ( !( AIR_EXISTS( len ) ) ) {
/* things blew up, either in incremental force
additions, or in the attempt at normalization */
E = 1;
*meanVel = AIR_NAN;
break;
}
if ( 0 == len ) {
/* if the length of grad[] underflowed to zero, we can
legitimately zero out ngrad[] */
ELL_3V_SET( ngrad, 0, 0, 0 );
}
step = AIR_MIN( len*tgparm->step, edge/limit );
ELL_3V_SCALE_ADD2( newpos,
1.0, pos + 3*ii,
step, ngrad );
ELL_3V_NORM( newpos, newpos, len );
ELL_3V_SUB( diff, pos + 3*ii, newpos );
*meanVel += ELL_3V_LEN( diff );
ELL_3V_COPY( pos + 3*ii, newpos );
}
*meanVel /= num;
return E;
}
/*
** assign random signs to the vectors and measures the length of their
** mean, as quickly as possible
*/
static double
355 party( Nrrd *npos, airRandMTState *rstate ) {
double *pos, mean[3];
unsigned int ii, num, rnd, rndBit;
pos = ( double * )( npos->data );
num = AIR_UINT( npos->axis[1].size );
rnd = airUIrandMT_r( rstate );
rndBit = 0;
ELL_3V_SET( mean, 0, 0, 0 );
for ( ii=0; ii<num; ii++ ) {
if ( 32 == rndBit ) {
rnd = airUIrandMT_r( rstate );
rndBit = 0;
}
if ( rnd & ( 1 << rndBit++ ) ) {
ELL_3V_SCALE( pos + 3*ii, -1, pos + 3*ii );
}
ELL_3V_INCR( mean, pos + 3*ii );
}
ELL_3V_SCALE( mean, 1.0/num, mean );
return ELL_3V_LEN( mean );
}
/*
** parties until the gradients settle down
*/
int
382 tenGradientBalance( Nrrd *nout, const Nrrd *nin,
tenGradientParm *tgparm ) {
static const char me[]="tenGradientBalance";
double len, lastLen, improv;
airRandMTState *rstate;
Nrrd *ncopy;
unsigned int iter, maxIter;
int done;
airArray *mop;
if ( !nout || tenGradientCheck( nin, nrrdTypeUnknown, 2 ) || !tgparm ) {
biffAddf( TEN, "%s: got NULL pointer ( %p, %p ) or invalid nin", me,
AIR_VOIDP( nout ), AIR_VOIDP( tgparm ) );
return 1;
}
if ( nrrdConvert( nout, nin, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: can't initialize output with input", me );
return 1;
}
mop = airMopNew( );
ncopy = nrrdNew( );
airMopAdd( mop, ncopy, ( airMopper )nrrdNuke, airMopAlways );
rstate = airRandMTStateNew( tgparm->seed );
airMopAdd( mop, rstate, ( airMopper )airRandMTStateNix, airMopAlways );
/* HEY: factor of 100 is an approximate hack */
maxIter = 100*tgparm->maxIteration;
lastLen = 1.0;
done = AIR_FALSE;
do {
iter = 0;
do {
iter++;
len = party( nout, rstate );
} while ( len > lastLen && iter < maxIter );
if ( iter >= maxIter ) {
if ( tgparm->verbose ) {
fprintf( stderr, "%s: stopping at max iter %u\n", me, maxIter );
}
if ( nrrdCopy( nout, ncopy ) ) {
biffMovef( TEN, NRRD, "%s: trouble copying", me );
airMopError( mop ); return 1;
}
done = AIR_TRUE;
} else {
if ( nrrdCopy( ncopy, nout ) ) {
biffMovef( TEN, NRRD, "%s: trouble copying", me );
airMopError( mop ); return 1;
}
improv = lastLen - len;
lastLen = len;
if ( tgparm->verbose ) {
fprintf( stderr, "%s: ( iter %u ) improvement: %g ( mean length = %g )\n",
me, iter, improv, len );
}
done = ( improv <= tgparm->minMeanImprovement
|| len < tgparm->minMean );
}
} while ( !done );
airMopOkay( mop );
return 0;
}
/*
******** tenGradientDistribute
**
** Takes the given list of gradients, normalizes their lengths,
** optionally jitters their positions, does point repulsion, and then
** ( optionally ) selects a combination of directions with minimum vector sum.
**
** The complicated part of this is the point repulsion, which uses a
** gradient descent with variable set size. The progress of the system
** is measured by decrease in potential ( when its measurement doesn't
** overflow to infinity ) or an increase in the minimum angle. When a
** step results in negative progress, the step size is halved, and the
** iteration is attempted again. Based on the observation that at
** some points the step size must be made very small to get progress,
** the step size is cautiously increased ( "nudged" ) at every
** iteration, to try to avoid using an overly small step. The amount
** by which the step is nudged is halved everytime the step is halved,
** to avoid endless cycling through step sizes.
*/
int
467 tenGradientDistribute( Nrrd *nout, const Nrrd *nin,
tenGradientParm *tgparm ) {
static const char me[]="tenGradientDistribute";
char filename[AIR_STRLEN_SMALL];
unsigned int ii, num, iter, oldIdx, newIdx, edgeShrink;
airArray *mop;
Nrrd *npos[2];
double *pos, len, meanVelocity, pot, potNew, potD,
edge, edgeMin, angle, angleNew;
int E;
if ( !nout || tenGradientCheck( nin, nrrdTypeUnknown, 2 ) || !tgparm ) {
biffAddf( TEN, "%s: got NULL pointer or invalid input", me );
return 1;
}
num = AIR_UINT( nin->axis[1].size );
mop = airMopNew( );
npos[0] = nrrdNew( );
npos[1] = nrrdNew( );
airMopAdd( mop, npos[0], ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, npos[1], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( npos[0], nin, nrrdTypeDouble )
|| nrrdConvert( npos[1], nin, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating temp buffers", me );
airMopError( mop ); return 1;
}
pos = ( double* )( npos[0]->data );
for ( ii=0; ii<num; ii++ ) {
ELL_3V_NORM( pos, pos, len );
pos += 3;
}
if ( tgparm->jitter ) {
if ( tenGradientJitter( npos[0], npos[0], tgparm->jitter ) ) {
biffAddf( TEN, "%s: problem jittering input", me );
airMopError( mop ); return 1;
}
}
/* initialize things prior to first iteration; have to
make sure that loop body tests pass 1st time around */
meanVelocity = 2*tgparm->minVelocity;
potD = -2*tgparm->minPotentialChange;
oldIdx = 0;
newIdx = 1;
tgparm->step = tgparm->initStep;
tgparm->nudge = 0.1;
tenGradientMeasure( &pot, &angle, NULL,
npos[oldIdx], tgparm, AIR_TRUE );
for ( iter = 0;
( ( !!tgparm->minIteration && iter < tgparm->minIteration )
||
( iter < tgparm->maxIteration
&& ( !tgparm->minPotentialChange
|| !AIR_EXISTS( potD )
|| -potD > tgparm->minPotentialChange )
&& ( !tgparm->minVelocity
|| meanVelocity > tgparm->minVelocity )
&& tgparm->step > FLT_MIN ) );
iter++ ) {
/* copy positions from old to new */
memcpy( npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof( double ) );
edge = tenGradientIdealEdge( num, tgparm->single );
edgeShrink = 0;
/* try to do a position update, which will fail if repulsion values
explode, from having an insufficiently small edge normalization,
so retry with smaller edge next time */
do {
E = _tenGradientUpdate( &meanVelocity, &edgeMin,
npos[newIdx], edge, tgparm );
if ( E ) {
if ( edgeShrink > tgparm->maxEdgeShrink ) {
biffAddf( TEN, "%s: %u > %u edge shrinks ( %g ), update still failed",
me, edgeShrink, tgparm->maxEdgeShrink, edge );
airMopError( mop ); return 1;
}
edgeShrink++;
/* re-initialize positions ( HEY ugly code logic ) */
memcpy( npos[newIdx]->data, npos[oldIdx]->data, 3*num*sizeof( double ) );
edge = edgeMin;
}
} while ( E );
tenGradientMeasure( &potNew, &angleNew, NULL,
npos[newIdx], tgparm, AIR_TRUE );
if ( ( AIR_EXISTS( pot ) && AIR_EXISTS( potNew ) && potNew <= pot )
|| angleNew >= angle ) {
/* there was progress of some kind, either through potential
decrease, or angle increase */
potD = 2*( potNew - pot )/( potNew + pot );
if ( !( iter % tgparm->report ) && tgparm->verbose ) {
fprintf( stderr, "%s( %d ): . . . . . . step = %g, edgeShrink = %u\n"
" velo = %g<>%g, phi = %g ~ %g<>%g, angle = %g ~ %g\n",
me, iter, tgparm->step, edgeShrink,
meanVelocity, tgparm->minVelocity,
pot, potD, tgparm->minPotentialChange,
angle, angleNew - angle );
}
if ( tgparm->snap && !( iter % tgparm->snap ) ) {
sprintf( filename, "%05d.nrrd", iter/tgparm->snap );
if ( tgparm->verbose ) {
fprintf( stderr, "%s( %d ): . . . . . . saving %s\n",
me, iter, filename );
}
if ( nrrdSave( filename, npos[newIdx], NULL ) ) {
char *serr;
serr = biffGetDone( NRRD );
if ( tgparm->verbose ) { /* perhaps shouldn't have this check */
fprintf( stderr, "%s: iter=%d, couldn't save snapshot:\n%s"
"continuing ...\n", me, iter, serr );
}
free( serr );
}
}
tgparm->step *= 1 + tgparm->nudge;
tgparm->step = AIR_MIN( tgparm->initStep, tgparm->step );
pot = potNew;
angle = angleNew;
/* swap buffers */
newIdx = 1 - newIdx;
oldIdx = 1 - oldIdx;
} else {
/* oops, did not make progress; back off and try again */
if ( tgparm->verbose ) {
fprintf( stderr, "%s( %d ): ######## step %g --> %g\n"
" phi = %g --> %g ~ %g, angle = %g --> %g\n",
me, iter, tgparm->step, tgparm->step/2,
pot, potNew, potD, angle, angleNew );
}
tgparm->step /= 2;
tgparm->nudge /= 2;
}
}
/* when the for-loop test fails, we stop before computing the next
iteration ( which starts with copying from npos[oldIdx] to
npos[newIdx] ) ==> the final results are in npos[oldIdx] */
if ( tgparm->verbose ) {
fprintf( stderr, "%s: .......................... done distribution:\n"
" ( %d && %d ) || ( %d \n"
" && ( %d || %d || %d ) \n"
" && ( %d || %d ) \n"
" && %d ) is false\n", me,
!!tgparm->minIteration, iter < tgparm->minIteration,
iter < tgparm->maxIteration,
!tgparm->minPotentialChange,
!AIR_EXISTS( potD ), AIR_ABS( potD ) > tgparm->minPotentialChange,
!tgparm->minVelocity, meanVelocity > tgparm->minVelocity,
tgparm->step > FLT_MIN );
fprintf( stderr, " iter=%d, velo = %g<>%g, phi = %g ~ %g<>%g;\n",
iter, meanVelocity, tgparm->minVelocity, pot,
potD, tgparm->minPotentialChange );
fprintf( stderr, " minEdge = %g; idealEdge = %g\n",
2*sin( angle/2 ), tenGradientIdealEdge( num, tgparm->single ) );
}
tenGradientMeasure( &pot, NULL, NULL, npos[oldIdx], tgparm, AIR_FALSE );
tgparm->potential = pot;
tenGradientMeasure( &pot, &angle, &edge, npos[oldIdx], tgparm, AIR_TRUE );
tgparm->potentialNorm = pot;
tgparm->angle = angle;
tgparm->edge = edge;
tgparm->itersUsed = iter;
if ( ( tgparm->minMeanImprovement || tgparm->minMean )
&& !tgparm->single ) {
if ( tgparm->verbose ) {
fprintf( stderr, "%s: optimizing balance:\n", me );
}
if ( tenGradientBalance( nout, npos[oldIdx], tgparm ) ) {
biffAddf( TEN, "%s: failed to minimize vector sum of gradients", me );
airMopError( mop ); return 1;
}
if ( tgparm->verbose ) {
fprintf( stderr, "%s: .......................... done balancing.\n", me );
}
} else {
if ( tgparm->verbose ) {
fprintf( stderr, "%s: .......................... ( no balancing )\n", me );
}
if ( nrrdConvert( nout, npos[oldIdx], nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't set output", me );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
/*
** note that if tgparm->insertZeroVec, there will be one sample more
** along axis 1 of nout than the requested #gradients "num"
*/
int
663 tenGradientGenerate( Nrrd *nout, unsigned int num, tenGradientParm *tgparm ) {
static const char me[]="tenGradientGenerate";
Nrrd *nin;
airArray *mop;
if ( !( nout && tgparm ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( num >= 3 ) ) {
biffAddf( TEN, "%s: can generate minimum of 3 gradient directions "
"( not %d )", me, num );
return 1;
}
mop = airMopNew( );
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( tenGradientRandom( nin, num, tgparm->seed )
|| tenGradientDistribute( nout, nin, tgparm ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
if ( tgparm->insertZeroVec ) {
/* this is potentially confusing: the second axis ( axis 1 )
is going to come back one longer than the requested
number of gradients! */
Nrrd *ntmp;
ptrdiff_t padMin[2] = {0, -1}, padMax[2];
padMax[0] = AIR_CAST( ptrdiff_t, nout->axis[0].size-1 );
padMax[1] = AIR_CAST( ptrdiff_t, num-1 );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdPad_nva( ntmp, nout, padMin, padMax,
nrrdBoundaryPad, 0.0 )
|| nrrdCopy( nout, ntmp ) ) {
biffMovef( TEN, NRRD, "%s: trouble adding zero vector", me );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
int
29 tenEvecRGB( Nrrd *nout, const Nrrd *nin,
const tenEvecRGBParm *rgbp ) {
static const char me[]="tenEvecRGB";
size_t size[NRRD_DIM_MAX];
float ( *lup )( const void *, size_t ), ( *ins )( void *, size_t, float );
float ten[7], eval[3], evec[9], RGB[3];
size_t II, NN;
unsigned char *odataUC;
unsigned short *odataUS;
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer ( %p, %p )",
me, AIR_CAST( void *, nout ), AIR_CVOIDP( nin ) );
return 1;
}
if ( tenEvecRGBParmCheck( rgbp ) ) {
biffAddf( TEN, "%s: RGB parm trouble", me );
return 1;
}
if ( !( 2 <= nin->dim && 7 == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: need nin->dim >= 2 ( not %u ), axis[0].size == 7 "
"( not %s )", me, nin->dim,
airSprintSize_t( stmp, nin->axis[0].size ) );
return 1;
}
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
size[0] = rgbp->genAlpha ? 4 : 3;
if ( nrrdMaybeAlloc_nva( nout, ( nrrdTypeDefault == rgbp->typeOut
? nin->type
: rgbp->typeOut ), nin->dim, size ) ) {
biffMovef( TEN, NRRD, "%s: couldn't alloc output", me );
return 1;
}
odataUC = AIR_CAST( unsigned char *, nout->data );
odataUS = AIR_CAST( unsigned short *, nout->data );
NN = nrrdElementNumber( nin )/7;
lup = nrrdFLookup[nin->type];
ins = nrrdFInsert[nout->type];
for ( II=0; II<NN; II++ ) {
TEN_T_SET( ten, lup( nin->data, 0 + 7*II ),
lup( nin->data, 1 + 7*II ), lup( nin->data, 2 + 7*II ),
lup( nin->data, 3 + 7*II ), lup( nin->data, 4 + 7*II ),
lup( nin->data, 5 + 7*II ), lup( nin->data, 6 + 7*II ) );
tenEigensolve_f( eval, evec, ten );
tenEvecRGBSingle_f( RGB, ten[0], eval, evec + 3*( rgbp->which ), rgbp );
switch ( nout->type ) {
case nrrdTypeUChar:
odataUC[0 + size[0]*II] = airIndexClamp( 0.0, RGB[0], 1.0, 256 );
odataUC[1 + size[0]*II] = airIndexClamp( 0.0, RGB[1], 1.0, 256 );
odataUC[2 + size[0]*II] = airIndexClamp( 0.0, RGB[2], 1.0, 256 );
if ( rgbp->genAlpha ) {
odataUC[3 + size[0]*II] = 255;
}
break;
case nrrdTypeUShort:
odataUS[0 + size[0]*II] = airIndexClamp( 0.0, RGB[0], 1.0, 65536 );
odataUS[1 + size[0]*II] = airIndexClamp( 0.0, RGB[1], 1.0, 65536 );
odataUS[2 + size[0]*II] = airIndexClamp( 0.0, RGB[2], 1.0, 65536 );
if ( rgbp->genAlpha ) {
odataUS[3 + size[0]*II] = 65535;
}
break;
default:
ins( nout->data, 0 + size[0]*II, RGB[0] );
ins( nout->data, 1 + size[0]*II, RGB[1] );
ins( nout->data, 2 + size[0]*II, RGB[2] );
if ( rgbp->genAlpha ) {
ins( nout->data, 3 + size[0]*II, 1.0 );
}
break;
}
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, ( NRRD_AXIS_INFO_SIZE_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis info", me );
return 1;
}
nout->axis[0].kind = nrrdKind3Color;
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
return 0;
}
#define SQR( i ) ( ( i )*( i ) )
short
121 tenEvqSingle( float vec[3], float scl ) {
static const char me[]="tenEvqSingle";
float tmp, L1;
int mi, bins, base, vi, ui;
short ret;
ELL_3V_NORM_TT( vec, float, vec, tmp );
L1 = AIR_ABS( vec[0] ) + AIR_ABS( vec[1] ) + AIR_ABS( vec[2] );
ELL_3V_SCALE( vec, 1/L1, vec );
scl = AIR_CLAMP( 0.0f, scl, 1.0f );
scl = AIR_CAST( float, pow( scl, 0.75 ) );
mi = airIndex( 0.0, scl, 1.0, 6 );
if ( mi ) {
switch ( mi ) {
case 1: bins = 16; base = 1; break;
case 2: bins = 32; base = 1+SQR( 16 ); break;
case 3: bins = 48; base = 1+SQR( 16 )+SQR( 32 ); break;
case 4: bins = 64; base = 1+SQR( 16 )+SQR( 32 )+SQR( 48 ); break;
case 5: bins = 80; base = 1+SQR( 16 )+SQR( 32 )+SQR( 48 )+SQR( 64 ); break;
default:
fprintf( stderr, "%s: PANIC: mi = %d\n", me, mi );
exit( 0 );
}
vi = airIndex( -1, vec[0]+vec[1], 1, bins );
ui = airIndex( -1, vec[0]-vec[1], 1, bins );
ret = vi*bins + ui + base;
}
else {
ret = 0;
}
return ret;
}
int
155 tenEvqVolume( Nrrd *nout,
const Nrrd *nin, int which, int aniso, int scaleByAniso ) {
static const char me[]="tenEvqVolume";
int map[3];
short *qdata;
const float *tdata;
float eval[3], evec[9], an;
size_t N, I, sx, sy, sz;
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( AIR_IN_CL( 0, which, 2 ) ) ) {
biffAddf( TEN, "%s: eigenvector index %d not in range [0..2]", me, which );
return 1;
}
if ( scaleByAniso ) {
if ( airEnumValCheck( tenAniso, aniso ) ) {
biffAddf( TEN, "%s: anisotropy metric %d not valid", me, aniso );
return 1;
}
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a valid DT volume", me );
return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeShort, 3,
sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: can't allocate output", me );
return 1;
}
N = sx*sy*sz;
tdata = ( float * )nin->data;
qdata = ( short * )nout->data;
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, evec, tdata );
if ( scaleByAniso ) {
an = tenAnisoEval_f( eval, aniso );
} else {
an = 1.0;
}
qdata[I] = tenEvqSingle( evec+ 3*which, an );
tdata += 7;
}
ELL_3V_SET( map, 1, 2, 3 );
if ( nrrdAxisInfoCopy( nout, nin, map, ( NRRD_AXIS_INFO_SIZE_BIT
| NRRD_AXIS_INFO_KIND_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
return 0;
}
int
219 tenBMatrixCheck( const Nrrd *nbmat, int type, unsigned int minnum ) {
static const char me[]="tenBMatrixCheck";
if ( nrrdCheck( nbmat ) ) {
biffMovef( TEN, NRRD, "%s: basic validity check failed", me );
return 1;
}
if ( !( 6 == nbmat->axis[0].size && 2 == nbmat->dim ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: need a 6xN 2-D array ( not a %s x? %d-D array )", me,
airSprintSize_t( stmp, nbmat->axis[0].size ), nbmat->dim );
return 1;
}
if ( nrrdTypeDefault != type && type != nbmat->type ) {
biffAddf( TEN, "%s: requested type %s but got type %s", me,
airEnumStr( nrrdType, type ), airEnumStr( nrrdType, nbmat->type ) );
return 1;
}
if ( nrrdTypeBlock == nbmat->type ) {
biffAddf( TEN, "%s: sorry, can't use %s type", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( minnum <= nbmat->axis[1].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: have only %s B-matrices, need at least %d", me,
airSprintSize_t( stmp, nbmat->axis[1].size ), minnum );
return 1;
}
return 0;
}
/*
******** _tenFindValley
**
** This is not a general purpose function, and it will take some
** work to make it that way.
**
** the tweak argument implements a cheesy heuristic: threshold should be
** on low side of histogram valley, since stdev for background is much
** narrower then stdev for brain
*/
int
263 _tenFindValley( double *valP, const Nrrd *nhist, double tweak, int save ) {
static const char me[]="_tenFindValley";
double gparm[NRRD_KERNEL_PARMS_NUM], dparm[NRRD_KERNEL_PARMS_NUM];
Nrrd *ntmpA, *ntmpB, *nhistD, *nhistDD;
float *hist, *histD, *histDD;
airArray *mop;
size_t bins, maxbb, bb;
NrrdRange *range;
/*
tenEMBimodalParm *biparm;
biparm = tenEMBimodalParmNew( );
tenEMBimodal( biparm, nhist );
biparm = tenEMBimodalParmNix( biparm );
*/
mop = airMopNew( );
airMopAdd( mop, ntmpA=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ntmpB=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nhistD=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nhistDD=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
bins = nhist->axis[0].size;
gparm[0] = bins/128; /* wacky heuristic for gaussian stdev */
gparm[1] = 3; /* how many stdevs to cut-off at */
dparm[0] = 1.0; /* unit spacing */
dparm[1] = 1.0; /* B-Spline kernel */
dparm[2] = 0.0;
if ( nrrdCheapMedian( ntmpA, nhist, AIR_TRUE, AIR_FALSE, 2, 1.0, 1024 )
|| nrrdSimpleResample( ntmpB, ntmpA,
nrrdKernelGaussian, gparm, &bins, NULL )
|| nrrdSimpleResample( nhistD, ntmpB,
nrrdKernelBCCubicD, dparm, &bins, NULL )
|| nrrdSimpleResample( nhistDD, ntmpB,
nrrdKernelBCCubicDD, dparm, &bins, NULL ) ) {
biffMovef( TEN, NRRD, "%s: trouble processing histogram", me );
airMopError( mop ); return 1;
}
if ( save ) {
nrrdSave( "tmp-histA.nrrd", ntmpA, NULL );
nrrdSave( "tmp-histB.nrrd", ntmpB, NULL );
}
hist = ( float* )( ntmpB->data );
histD = ( float* )( nhistD->data );
histDD = ( float* )( nhistDD->data );
range = nrrdRangeNewSet( ntmpB, nrrdBlind8BitRangeState );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
for ( bb=0; bb<bins-1; bb++ ) {
if ( hist[bb] == range->max ) {
/* first seek to max in histogram */
break;
}
}
maxbb = bb;
for ( ; bb<bins-1; bb++ ) {
if ( histD[bb]*histD[bb+1] < 0 && histDD[bb] > 0 ) {
/* zero-crossing in 1st deriv, positive 2nd deriv */
break;
}
}
if ( bb == bins-1 ) {
biffAddf( TEN, "%s: never saw a satisfactory zero crossing", me );
airMopError( mop ); return 1;
}
*valP = nrrdAxisInfoPos( nhist, 0, AIR_AFFINE( 0, tweak, 1, maxbb, bb ) );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
typedef struct {
double weight[3], amount, target; /* tenSizeNormalize */
/* amount: tenSizeScale */
double scale; int fixDet; int makePositive; /* tenAnisoScale */
double min, max; /* tenEigenvalueClamp */
double expo; /* tenEigenvaluePower */
double val; /* tenEigenvalueAdd */
} funcParm;
enum {
funcUnknown,
funcSizeNormalize,
funcSizeScale,
funcAnisoScale,
funcEigenvalueClamp,
funcEigenvaluePower,
funcEigenvalueAdd,
funcEigenvalueMultiply,
funcLog,
funcExp,
funcLast
};
static int
52 theFunc( Nrrd *nout, const Nrrd *nin, int func, funcParm *parm ) {
static const char me[]="theFunc";
float *tin, *tout, eval[3], evec[9], weight[3], size, mean;
size_t NN, II;
unsigned int ri;
if ( !AIR_IN_OP( funcUnknown, func, funcLast ) ) {
biffAddf( TEN, "%s: given func %d out of range [%d, %d]", me, func,
funcUnknown+1, funcLast-1 );
return 1;
}
if ( !( nout && nin && parm ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_FALSE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a tensor nrrd", me );
return 1;
}
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
}
tin = ( float* )( nin->data );
tout = ( float* )( nout->data );
NN = nrrdElementNumber( nin )/7;
switch( func ) {
case funcSizeNormalize:
ELL_3V_COPY_TT( weight, float, parm->weight );
size = weight[0] + weight[1] + weight[2];
if ( !size ) {
biffAddf( TEN, "%s: some of eigenvalue weights is zero", me );
return 1;
}
weight[0] /= size;
weight[1] /= size;
weight[2] /= size;
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
size = ( weight[0]*AIR_ABS( eval[0] )
+ weight[1]*AIR_ABS( eval[1] )
+ weight[2]*AIR_ABS( eval[2] ) );
ELL_3V_SET_TT( eval, float,
AIR_AFFINE( 0, parm->amount, 1,
eval[0], parm->target*eval[0]/size ),
AIR_AFFINE( 0, parm->amount, 1,
eval[1], parm->target*eval[1]/size ),
AIR_AFFINE( 0, parm->amount, 1,
eval[2], parm->target*eval[2]/size ) );
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcSizeScale:
for ( II=0; II<=NN-1; II++ ) {
TEN_T_SET_TT( tout, float,
tin[0],
parm->amount*tin[1],
parm->amount*tin[2],
parm->amount*tin[3],
parm->amount*tin[4],
parm->amount*tin[5],
parm->amount*tin[6] );
tin += 7;
tout += 7;
}
break;
case funcAnisoScale:
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
if ( parm->fixDet ) {
eval[0] = AIR_MAX( eval[0], 0.00001f );
eval[1] = AIR_MAX( eval[1], 0.00001f );
eval[2] = AIR_MAX( eval[2], 0.00001f );
ELL_3V_SET_TT( eval, float, log( eval[0] ), log( eval[1] ), log( eval[2] ) );
}
mean = ( eval[0] + eval[1] + eval[2] )/3.0f;
ELL_3V_SET_TT( eval, float,
AIR_LERP( parm->scale, mean, eval[0] ),
AIR_LERP( parm->scale, mean, eval[1] ),
AIR_LERP( parm->scale, mean, eval[2] ) );
if ( parm->fixDet ) {
ELL_3V_SET_TT( eval, float, exp( eval[0] ), exp( eval[1] ), exp( eval[2] ) );
}
if ( eval[2] < 0 && parm->makePositive ) {
eval[0] = AIR_MAX( eval[0], 0.0f );
eval[1] = AIR_MAX( eval[1], 0.0f );
eval[2] = AIR_MAX( eval[2], 0.0f );
}
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcEigenvalueClamp:
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
if ( AIR_EXISTS( parm->min ) ) {
ELL_3V_SET_TT( eval, float,
AIR_MAX( eval[0], parm->min ),
AIR_MAX( eval[1], parm->min ),
AIR_MAX( eval[2], parm->min ) );
}
if ( AIR_EXISTS( parm->max ) ) {
ELL_3V_SET_TT( eval, float,
AIR_MIN( eval[0], parm->max ),
AIR_MIN( eval[1], parm->max ),
AIR_MIN( eval[2], parm->max ) );
}
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcEigenvaluePower:
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
ELL_3V_SET_TT( eval, float,
pow( eval[0], parm->expo ),
pow( eval[1], parm->expo ),
pow( eval[2], parm->expo ) );
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcEigenvalueAdd:
for ( II=0; II<=NN-1; II++ ) {
/* HEY: this doesn't require eigensolve */
tenEigensolve_f( eval, evec, tin );
ELL_3V_SET_TT( eval, float,
eval[0] + parm->val,
eval[1] + parm->val,
eval[2] + parm->val );
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcEigenvalueMultiply:
for ( II=0; II<=NN-1; II++ ) {
/* HEY: this doesn't require eigensolve */
tenEigensolve_f( eval, evec, tin );
ELL_3V_SET_TT( eval, float,
eval[0]*parm->val,
eval[1]*parm->val,
eval[2]*parm->val );
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcLog:
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
for ( ri=0; ri<3; ri++ ) {
eval[ri] = AIR_CAST( float, log( eval[ri] ) );
eval[ri] = AIR_EXISTS( eval[ri] ) ? eval[ri] : -1000000;
}
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
case funcExp:
for ( II=0; II<=NN-1; II++ ) {
tenEigensolve_f( eval, evec, tin );
for ( ri=0; ri<3; ri++ ) {
eval[ri] = AIR_CAST( float, exp( eval[ri] ) );
eval[ri] = AIR_EXISTS( eval[ri] ) ? eval[ri] : 0;
}
tenMakeSingle_f( tout, tin[0], eval, evec );
tin += 7;
tout += 7;
}
break;
}
/* basic and per-axis info handled by nrrdCopy above */
return 0;
}
int
239 tenSizeNormalize( Nrrd *nout, const Nrrd *nin, double _weight[3],
double amount, double target ) {
static const char me[]="tenSizeNormalize";
funcParm parm;
ELL_3V_COPY( parm.weight, _weight );
parm.amount = amount;
parm.target = target;
if ( theFunc( nout, nin, funcSizeNormalize, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
int
255 tenSizeScale( Nrrd *nout, const Nrrd *nin, double amount ) {
static const char me[]="tenSizeScale";
funcParm parm;
parm.amount = amount;
if ( theFunc( nout, nin, funcSizeScale, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenAnisoScale
**
** scales the "deviatoric" part of a tensor up or down
*/
int
274 tenAnisoScale( Nrrd *nout, const Nrrd *nin, double scale,
int fixDet, int makePositive ) {
static const char me[]="tenAnisoScale";
funcParm parm;
parm.scale = scale;
parm.fixDet = fixDet;
parm.makePositive = makePositive;
if ( theFunc( nout, nin, funcAnisoScale, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenEigenvalueClamp
**
** enstates the given value as the lowest eigenvalue
*/
int
295 tenEigenvalueClamp( Nrrd *nout, const Nrrd *nin, double min, double max ) {
static const char me[]="tenEigenvalueClamp";
funcParm parm;
parm.min = min;
parm.max = max;
if ( theFunc( nout, nin, funcEigenvalueClamp, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenEigenvaluePower
**
** raises the eigenvalues to some power
*/
int
314 tenEigenvaluePower( Nrrd *nout, const Nrrd *nin, double expo ) {
static const char me[]="tenEigenvaluePower";
funcParm parm;
parm.expo = expo;
if ( theFunc( nout, nin, funcEigenvaluePower, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenEigenvalueAdd
**
** adds something to all eigenvalues
*/
int
332 tenEigenvalueAdd( Nrrd *nout, const Nrrd *nin, double val ) {
static const char me[]="tenEigenvalueAdd";
funcParm parm;
parm.val = val;
if ( theFunc( nout, nin, funcEigenvalueAdd, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenEigenvalueMultiply
**
** multiplies eigenvalues by something
*/
int
350 tenEigenvalueMultiply( Nrrd *nout, const Nrrd *nin, double val ) {
static const char me[]="tenEigenvalueMultiply";
funcParm parm;
parm.val = val;
if ( theFunc( nout, nin, funcEigenvalueMultiply, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenLog
**
** takes the logarithm ( by taking the log of the eigenvalues )
*/
int
368 tenLog( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="tenLog";
funcParm parm;
if ( theFunc( nout, nin, funcLog, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
******** tenExp
**
** takes the exp( ) ( by taking exp( ) of the eigenvalues )
*/
int
385 tenExp( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="tenExp";
funcParm parm;
if ( theFunc( nout, nin, funcExp, &parm ) ) {
biffAddf( TEN, "%s: trouble", me );
return 1;
}
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 6
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"length", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"radius", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0},
/* 4 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1},
/* 5 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2}
};
static void
39 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, length, radius, vec[3], ten[7],
ident[7] = {1, 1, 0, 0, 1, 0, 1};
b0 = parm[0];
length = parm[1];
radius = parm[2];
vec[0] = parm[3];
vec[1] = parm[4];
vec[2] = parm[5];
TEN_T3V_OUTER( ten, vec );
TEN_T_SCALE_ADD2( ten, length - radius, ten, radius, ident );
for ( ii=0; ii<espec->imgNum; ii++ ) {
double adc, bb;
bb = espec->bval[ii];
adc = TEN_T3V_CONTR( ten, espec->grad + 3*ii );
dwiSim[ii] = b0*exp( -bb*adc );
}
return;
}
static char *
62 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) %gX%g ( %g, %g, %g )", parm[0],
parm[1], parm[2], parm[3], parm[4], parm[5] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel1Cylinder )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel1Cylinder = {
85 TEN_MODEL_STR_1CYLINDER,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel1Cylinder = &_tenModel1Cylinder;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 5
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0},
/* 3 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1},
/* 4 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2}
};
static void
38 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, diff, vec[3];
b0 = parm[0];
diff = parm[1];
vec[0] = parm[2];
vec[1] = parm[3];
vec[2] = parm[4];
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dot;
dot = ELL_3V_DOT( vec, espec->grad + 3*ii );
dwiSim[ii] = b0*exp( -espec->bval[ii]*diff*dot*dot );
}
return;
}
static char *
56 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) %g ( %g, %g, %g )", parm[0], parm[1],
parm[2], parm[3], parm[4] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel1Stick )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel1Stick = {
79 TEN_MODEL_STR_1STICK,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel1Stick = &_tenModel1Stick;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/* NOTE: this model is a single 2nd-order tensor, not a two-tensor model */
#define PARM_NUM 7
/* 1/sqrt( 2 ) */
#define OST 0.70710678118654752440
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"Dxx", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"Dxy", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"Dxz", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0},
/* 4 */ {"Dyy", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 5 */ {"Dyz", -TEN_MODEL_DIFF_MAX*OST, TEN_MODEL_DIFF_MAX*OST, AIR_FALSE, AIR_FALSE, 0},
/* 6 */ {"Dzz", -TEN_MODEL_DIFF_MAX, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}
};
static void
44 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0;
b0 = parm[0];
for ( ii=0; ii<espec->imgNum; ii++ ) {
double adc, bb;
bb = espec->bval[ii];
/* safe because TEN_T3V_CONTR never looks at parm[0] */
adc = TEN_T3V_CONTR( parm, espec->grad + 3*ii );
dwiSim[ii] = b0*exp( -bb*adc );
}
return;
}
static char *
60 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) [%g %g %g; %g %g; %g]", parm[0],
parm[1], parm[2], parm[3],
parm[4], parm[5],
parm[6] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
static int
parmConvert( double *parmDst, const double *parmSrc,
const tenModel *modelSrc ) {
int ret;
if ( modelSrc == tenModelBall ) {
TEN_T_SET( parmDst, parmSrc[0],
parmSrc[1], 0, 0,
parmSrc[1], 0,
parmSrc[1] );
ret = 0;
} else if ( modelSrc == tenModel1Stick ) {
double ten[7];
TEN_T3V_OUTER( ten, parmSrc + 2 );
TEN_T_SCALE( parmDst, parmSrc[1], ten );
parmDst[0] = parmSrc[0];
ret = 0;
} else if ( modelSrc == tenModelBall1Stick ) {
double stick[7], ball[7], diff, frac;
diff = parmSrc[1];
frac = parmSrc[2];
TEN_T3V_OUTER( stick, parmSrc + 3 );
TEN_T_SCALE( stick, diff, stick );
TEN_T_SET( ball, 1, diff, 0, 0, diff, 0, diff );
TEN_T_LERP( parmDst, frac, ball, stick );
parmDst[0] = parmSrc[0];
ret = 1;
} else if ( modelSrc == tenModel1Cylinder ) {
double stick[7], ball[7], len, rad;
len = parmSrc[1];
rad = parmSrc[2];
TEN_T3V_OUTER( stick, parmSrc + 3 );
TEN_T_SCALE( stick, len-rad, stick );
TEN_T_SET( ball, 1, rad, 0, 0, rad, 0, rad );
TEN_T_ADD( parmDst, ball, stick );
parmDst[0] = parmSrc[0];
ret = 0;
} else if ( modelSrc == tenModel1Tensor2 ) {
parmCopy( parmDst, parmSrc );
ret = 0;
} else {
unsigned int ii;
for ( ii=0; ii<PARM_NUM; ii++ ) {
parmDst[ii] = AIR_NAN;
}
ret = 2;
}
return ret;
}
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel1Tensor2 )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel1Tensor2 = {
TEN_MODEL_STR_1TENSOR2,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel1Tensor2 = &_tenModel1Tensor2;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 2
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"th", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0}
};
static void
35 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double theta, vec[3];
/* not used: b0 = parm[0]; */
theta = parm[1];
ELL_3V_SET( vec, cos( theta ), sin( theta ), 0.0 );
for ( ii=0; ii<espec->imgNum; ii++ ) {
dwiSim[ii] = ELL_3V_DOT( vec, espec->grad + 3*ii );
}
return;
}
static char *
49 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) th=%g", parm[0], parm[1] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel1Unit2D )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel1Unit2D = {
71 TEN_MODEL_STR_1UNIT2D,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel1Unit2D = &_tenModel1Unit2D;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 3
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"r", 0.0, 5.0, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"th", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0}
};
static void
36 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double rad, theta, vec[3];
/* not used: b0 = parm[0]; */
rad = parm[1];
theta = parm[2];
ELL_3V_SET( vec, rad*cos( theta ), rad*sin( theta ), 0.0 );
for ( ii=0; ii<espec->imgNum; ii++ ) {
dwiSim[ii] = ELL_3V_DOT( vec, espec->grad + 3*ii );
}
return;
}
static char *
51 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) r=%g th=%g", parm[0], parm[1], parm[2] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel1Vector2D )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel1Vector2D = {
73 TEN_MODEL_STR_1VECTOR2D,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel1Vector2D = &_tenModel1Vector2D;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 4
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"th0", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0},
/* 2 */ {"frac", 0, 1, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"th1", 0, 2*AIR_PI, AIR_TRUE, AIR_FALSE, 0}
};
static void
37 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double th0, frac, th1, vec0[3], vec1[3];
/* not used: b0 = parm[0]; */
th0 = parm[1];
frac = parm[2];
th1 = parm[3];
ELL_3V_SET( vec0, cos( th0 ), sin( th0 ), 0.0 );
ELL_3V_SET( vec1, cos( th1 ), sin( th1 ), 0.0 );
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dot0, dot1;
dot0 = ELL_3V_DOT( vec0, espec->grad + 3*ii );
dot1 = ELL_3V_DOT( vec1, espec->grad + 3*ii );
dwiSim[ii] = AIR_LERP( frac, dot0, dot1 );
}
return;
}
static char *
57 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) ( 1-f )*th0=%g + ( f=%g )*th1=%g",
parm[0], parm[1], parm[2], parm[3] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModel2Unit2D )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModel2Unit2D = {
80 TEN_MODEL_STR_2UNIT2D,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModel2Unit2D = &_tenModel2Unit2D;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 1
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
};
static void
34 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0;
b0 = parm[0];
for ( ii=0; ii<espec->imgNum; ii++ ) {
dwiSim[ii] = b0;
}
return;
}
static char *
46 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g )", parm[0] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModelB0 )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModelB0 = {
68 TEN_MODEL_STR_B0,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModelB0 = &_tenModelB0;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 2
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0}
};
static void
35 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, diff;
b0 = parm[0];
diff = parm[1];
for ( ii=0; ii<espec->imgNum; ii++ ) {
dwiSim[ii] = b0*exp( -espec->bval[ii]*diff );
}
return;
}
static char *
48 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) %g", parm[0], parm[1] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModelBall )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModelBall = {
/*
TEN_MODEL_STR_BALL,
_TEN_MODEL_FIELDS
*/
"ball",
2,
76 parmDesc,
simulate,
parmSprint,
parmAlloc,
parmRand,
parmStep,
parmDist,
parmCopy,
parmConvert,
sqe,
sqeGrad,
sqeFit,
nll,
nllGrad,
nllFit
};
const tenModel *const tenModelBall = &_tenModelBall;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 8
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"length", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 4 */ {"radius", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 5 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0},
/* 6 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1},
/* 7 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2}
};
static void
41 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, diffBall, frac, length, radius, vec[3], ten[7],
ident[7] = {1, 1, 0, 0, 1, 0, 1};
b0 = parm[0];
diffBall = parm[1];
frac = parm[2];
length = parm[3];
radius = parm[4];
vec[0] = parm[5];
vec[1] = parm[6];
vec[2] = parm[7];
TEN_T3V_OUTER( ten, vec );
TEN_T_SCALE_ADD2( ten, length - radius, ten, radius, ident );
for ( ii=0; ii<espec->imgNum; ii++ ) {
double diffCyl, dwiBall, dwiCyl, bb;
bb = espec->bval[ii];
diffCyl = TEN_T3V_CONTR( ten, espec->grad + 3*ii );
dwiCyl = exp( -bb*diffCyl );
dwiBall = exp( -bb*diffBall );
dwiSim[ii] = b0*AIR_LERP( frac, dwiBall, dwiCyl );
}
return;
}
static char *
68 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) [( 1-f ) %g + ( f=%g ) %gX%g ( %g, %g, %g )]", parm[0],
parm[1], parm[2], parm[3], parm[4],
parm[5], parm[6], parm[7] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModelBall1Cylinder )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModelBall1Cylinder = {
92 TEN_MODEL_STR_BALL1CYLINDER,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModelBall1Cylinder = &_tenModelBall1Cylinder;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 7
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"diff_ball", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"diff_stick", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 4 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0},
/* 5 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1},
/* 6 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2}
};
static void
40 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, diffBall, diffStick, frac, vec[3];
b0 = parm[0];
diffBall = parm[1];
frac = parm[2];
diffStick = parm[3];
vec[0] = parm[4];
vec[1] = parm[5];
vec[2] = parm[6];
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dwiBall, dwiStick, dot, bb;
bb = espec->bval[ii];
dwiBall = exp( -bb*diffBall );
dot = ELL_3V_DOT( vec, espec->grad + 3*ii );
dwiStick = exp( -bb*diffStick*dot*dot );
dwiSim[ii] = b0*( AIR_LERP( frac, dwiBall, dwiStick ) );
}
return;
}
static char *
63 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) ( 1-f )*%g + ( f=%g )*( %g @ ( %g, %g, %g ) )", parm[0],
parm[1], parm[2], parm[3], parm[4], parm[5], parm[6] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModelBall1Stick )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModelBall1Stick = {
86 TEN_MODEL_STR_BALL1STICK,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModelBall1Stick = &_tenModelBall1Stick;
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 6
static const tenModelParmDesc
parmDesc[] = {
/* 0 */ {"B0", 0.0, TEN_MODEL_B0_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 1 */ {"diffusivity", 0.0, TEN_MODEL_DIFF_MAX, AIR_FALSE, AIR_FALSE, 0},
/* 2 */ {"fraction", 0.0, 1.0, AIR_FALSE, AIR_FALSE, 0},
/* 3 */ {"x", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 0},
/* 4 */ {"y", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 1},
/* 5 */ {"z", -1.0, 1.0, AIR_FALSE, AIR_TRUE, 2}
};
static void
39 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
double b0, diff, frac, vec[3];
b0 = parm[0];
diff = parm[1];
frac = parm[2];
vec[0] = parm[3];
vec[1] = parm[4];
vec[2] = parm[5];
for ( ii=0; ii<espec->imgNum; ii++ ) {
double dwiBall, dwiStck, dot;
dwiBall = exp( -espec->bval[ii]*diff );
dot = ELL_3V_DOT( vec, espec->grad + 3*ii );
dwiStck = exp( -espec->bval[ii]*diff*dot*dot );
dwiSim[ii] = b0*( ( 1-frac )*dwiBall + frac*dwiStck );
}
return;
}
static char *
60 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
sprintf( str, "( %g ) %g * ( %g + %g*( %g, %g, %g ) )", parm[0],
parm[1], 1-parm[2], parm[2], parm[3], parm[4], parm[5] );
return str;
}
_TEN_PARM_ALLOC
_TEN_PARM_RAND
_TEN_PARM_STEP
_TEN_PARM_DIST
_TEN_PARM_COPY
_TEN_PARM_CONVERT_NOOP
_TEN_SQE
_TEN_SQE_GRAD_CENTDIFF
_TEN_SQE_FIT( tenModelBall1StickEMD )
_TEN_NLL
_TEN_NLL_GRAD_STUB
_TEN_NLL_FIT_STUB
tenModel
_tenModelBall1StickEMD = {
83 TEN_MODEL_STR_BALL1STICKEMD,
_TEN_MODEL_FIELDS
};
const tenModel *const tenModelBall1StickEMD = &_tenModelBall1StickEMD;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define PARM_NUM 0
static const tenModelParmDesc
parmDesc[] = {
/* dummy to avoid compiler error */
{"dummy", 0.0, 0.0, AIR_FALSE, AIR_FALSE, 0},
};
static void
35 simulate( double *dwiSim, const double *parm, const tenExperSpec *espec ) {
unsigned int ii;
AIR_UNUSED( parm );
AIR_UNUSED( espec );
for ( ii=0; ii<espec->imgNum; ii++ ) {
dwiSim[ii] = 0;
}
return;
}
static char *
47 parmSprint( char str[AIR_STRLEN_MED], const double *parm ) {
AIR_UNUSED( parm );
sprintf( str, "constant 0" );
return str;
}
static double *
55 parmAlloc( void ) {
return NULL;
}
static void
61 parmRand( double *parm, airRandMTState *rng, int knownB0 ) {
AIR_UNUSED( parm );
AIR_UNUSED( rng );
AIR_UNUSED( knownB0 );
}
static void
68 parmStep( double *parm1, const double scl,
const double *grad, const double *parm0 ) {
AIR_UNUSED( parm1 );
AIR_UNUSED( scl );
AIR_UNUSED( grad );
AIR_UNUSED( parm0 );
}
static double
77 parmDist( const double *parmA, const double *parmB ) {
AIR_UNUSED( parmA );
AIR_UNUSED( parmB );
return 0.0;
}
static void
84 parmCopy( double *parmA, const double *parmB ) {
AIR_UNUSED( parmA );
AIR_UNUSED( parmB );
}
static int
90 parmConvert( double *parmDst, const double *parmSrc,
const tenModel *modelSrc ) {
AIR_UNUSED( parmDst );
AIR_UNUSED( parmSrc );
AIR_UNUSED( modelSrc );
return 0;
}
98 _TEN_SQE
static void
sqeGrad( double *grad, const double *parm0,
const tenExperSpec *espec,
double *dwiBuff, const double *dwiMeas,
int knownB0 ) {
AIR_UNUSED( grad );
AIR_UNUSED( parm0 );
AIR_UNUSED( espec );
AIR_UNUSED( dwiBuff );
AIR_UNUSED( dwiMeas );
AIR_UNUSED( knownB0 );
}
113 _TEN_SQE_FIT( tenModelZero )
_TEN_NLL
116 _TEN_NLL_GRAD_STUB
static double
nllFit( double *parm, const tenExperSpec *espec,
const double *dwiMeas, const double *parmInit,
int rician, double sigma, int knownB0 ) {
AIR_UNUSED( parm );
AIR_UNUSED( espec );
AIR_UNUSED( dwiMeas );
AIR_UNUSED( parmInit );
AIR_UNUSED( rician );
AIR_UNUSED( sigma );
AIR_UNUSED( knownB0 );
return AIR_NAN;
}
tenModel
133 _tenModelZero = {
TEN_MODEL_STR_ZERO,
_TEN_MODEL_FIELDS
};
137 const tenModel *const tenModelZero = &_tenModelZero;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
tenInterpParm *
29 tenInterpParmNew( void ) {
tenInterpParm *tip;
tip = AIR_CAST( tenInterpParm *, malloc( sizeof( tenInterpParm ) ) );
if ( tip ) {
tip->verbose = AIR_FALSE;
tip->convStep = 0.2;
tip->minNorm = 0.0;
tip->convEps = 0.0000000001;
tip->wghtSumEps = 0.0000001;
tip->enableRecurse = AIR_TRUE;
tip->maxIter = 20;
tip->numSteps = 100;
tip->lengthFancy = AIR_FALSE;
tip->allocLen = 0;
tip->eval = NULL;
tip->evec = NULL;
tip->rtIn = NULL;
tip->rtLog = NULL;
tip->qIn = NULL;
tip->qBuff = NULL;
tip->qInter = NULL;
tip->numIter = 0;
tip->convFinal = AIR_NAN;
tip->lengthShape = AIR_NAN;
tip->lengthOrient = AIR_NAN;
}
return tip;
}
/*
** handles allocating all the various buffers that are needed for QGL
** interpolation, so that they are repeatedly allocated and freed
** between calls
*/
int
69 tenInterpParmBufferAlloc( tenInterpParm *tip, unsigned int num ) {
static const char me[]="tenInterpParmBufferAlloc";
if ( 0 == num ) {
/* user wants to free buffers for some reason */
airFree( tip->eval ); tip->eval = NULL;
airFree( tip->evec ); tip->evec = NULL;
airFree( tip->rtIn ); tip->rtIn = NULL;
airFree( tip->rtLog ); tip->rtLog = NULL;
airFree( tip->qIn ); tip->qIn = NULL;
airFree( tip->qBuff ); tip->qBuff = NULL;
airFree( tip->qInter ); tip->qInter = NULL;
tip->allocLen = 0;
} else if ( 1 == num ) {
biffAddf( TEN, "%s: need num >= 2 ( not %u )", me, num );
return 1;
} else if ( num != tip->allocLen ) {
airFree( tip->eval ); tip->eval = NULL;
airFree( tip->evec ); tip->evec = NULL;
airFree( tip->rtIn ); tip->rtIn = NULL;
airFree( tip->rtLog ); tip->rtLog = NULL;
airFree( tip->qIn ); tip->qIn = NULL;
airFree( tip->qBuff ); tip->qBuff = NULL;
airFree( tip->qInter ); tip->qInter = NULL;
tip->eval = AIR_CALLOC( 3*num, double );
tip->evec = AIR_CALLOC( 9*num, double );
tip->rtIn = AIR_CALLOC( 3*num, double );
tip->rtLog = AIR_CALLOC( 3*num, double );
tip->qIn = AIR_CALLOC( 4*num, double );
tip->qBuff = AIR_CALLOC( 4*num, double );
tip->qInter = AIR_CALLOC( num*num, double );
if ( !( tip->eval && tip->evec &&
tip->rtIn && tip->rtLog &&
tip->qIn && tip->qBuff && tip->qInter ) ) {
biffAddf( TEN, "%s: didn't alloc buffers ( %p, %p, %p %p %p %p %p )", me,
AIR_VOIDP( tip->eval ), AIR_VOIDP( tip->evec ),
AIR_VOIDP( tip->rtIn ), AIR_VOIDP( tip->rtLog ),
AIR_VOIDP( tip->qIn ), AIR_VOIDP( tip->qBuff ),
AIR_VOIDP( tip->qInter ) );
return 1;
}
tip->allocLen = num;
}
return 0;
}
tenInterpParm *
116 tenInterpParmCopy( tenInterpParm *tip ) {
static const char me[]="tenInterpParmCopy";
tenInterpParm *newtip;
unsigned int num;
num = tip->allocLen;
newtip = tenInterpParmNew( );
if ( newtip ) {
memcpy( newtip, tip, sizeof( tenInterpParm ) );
/* manually set all pointers */
newtip->allocLen = 0;
newtip->eval = NULL;
newtip->evec = NULL;
newtip->rtIn = NULL;
newtip->rtLog = NULL;
newtip->qIn = NULL;
newtip->qBuff = NULL;
newtip->qInter = NULL;
if ( tenInterpParmBufferAlloc( newtip, num ) ) {
biffAddf( TEN, "%s: trouble allocating output", me );
return NULL;
}
memcpy( newtip->eval, tip->eval, 3*num*sizeof( double ) );
memcpy( newtip->evec, tip->evec, 9*num*sizeof( double ) );
memcpy( newtip->rtIn, tip->rtIn, 3*num*sizeof( double ) );
memcpy( newtip->rtLog, tip->rtLog, 3*num*sizeof( double ) );
memcpy( newtip->qIn, tip->qIn, 4*num*sizeof( double ) );
memcpy( newtip->qBuff, tip->qBuff, 4*num*sizeof( double ) );
memcpy( newtip->qInter, tip->qInter, num*num*sizeof( double ) );
}
return newtip;
}
tenInterpParm *
150 tenInterpParmNix( tenInterpParm *tip ) {
if ( tip ) {
airFree( tip->eval );
airFree( tip->evec );
airFree( tip->rtIn );
airFree( tip->rtLog );
airFree( tip->qIn );
airFree( tip->qBuff );
airFree( tip->qInter );
free( tip );
}
return NULL;
}
/*
******** tenInterpTwo_d
**
** interpolates between two tensors, in various ways
**
** this is really only used for demo purposes; its not useful for
** doing real work in DTI fields. So: its okay that its slow
** ( e.g. for tenInterpTypeQuatGeoLox{K, R}, it recomputes the
** eigensystems at the endpoints for every call, even though they are
** apt to be the same between calls.
**
** this
*/
void
179 tenInterpTwo_d( double oten[7],
const double tenA[7], const double tenB[7],
int ptype, double aa,
tenInterpParm *tip ) {
static const char me[]="tenInterpTwo_d";
double logA[7], logB[7], tmp1[7], tmp2[7], logMean[7],
mat1[9], mat2[9], mat3[9], sqrtA[7], isqrtA[7],
mean[7], sqrtB[7], isqrtB[7],
oeval[3], evalA[3], evalB[3], oevec[9], evecA[9], evecB[9];
if ( !( oten && tenA && tenB ) ) {
/* got NULL pointer, but not using biff */
if ( oten ) {
TEN_T_SET( oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
}
return;
}
switch ( ptype ) {
case tenInterpTypeLinear:
TEN_T_LERP( oten, aa, tenA, tenB );
break;
case tenInterpTypeLogLinear:
tenLogSingle_d( logA, tenA );
tenLogSingle_d( logB, tenB );
TEN_T_LERP( logMean, aa, logA, logB );
tenExpSingle_d( oten, logMean );
break;
case tenInterpTypeAffineInvariant:
tenSqrtSingle_d( sqrtA, tenA );
tenInv_d( isqrtA, sqrtA );
TEN_T2M( mat1, tenB );
TEN_T2M( mat2, isqrtA );
ELL_3M_MUL( mat3, mat1, mat2 ); /* B * is( A ) */
ELL_3M_MUL( mat1, mat2, mat3 ); /* is( A ) * B * is( A ) */
TEN_M2T( tmp2, mat1 );
tenPowSingle_d( tmp1, tmp2, aa ); /* m = ( is( A ) * B * is( A ) )^aa */
TEN_T2M( mat1, tmp1 );
TEN_T2M( mat2, sqrtA );
ELL_3M_MUL( mat3, mat1, mat2 ); /* m * sqrt( A ) */
ELL_3M_MUL( mat1, mat2, mat3 ); /* sqrt( A ) * m * sqrt( A ) */
TEN_M2T( oten, mat1 );
oten[0] = AIR_LERP( aa, tenA[0], tenB[0] );
if ( tip->verbose ) {
fprintf( stderr, "%s:\nA= %g %g %g %g %g %g\n"
"B = %g %g %g %g %g %g\n"
"foo = %g %g %g %g %g %g\n"
"bar( %g ) = %g %g %g %g %g %g\n", me,
tenA[1], tenA[2], tenA[3], tenA[4], tenA[5], tenA[6],
tenB[1], tenB[2], tenB[3], tenB[4], tenB[5], tenB[6],
tmp1[1], tmp1[2], tmp1[3], tmp1[4], tmp1[5], tmp1[6],
aa, oten[1], oten[2], oten[3], oten[4], oten[5], oten[6] );
}
break;
case tenInterpTypeWang:
/* HEY: this seems to be broken */
TEN_T_LERP( mean, aa, tenA, tenB ); /* "A" = mean */
tenLogSingle_d( logA, tenA );
tenLogSingle_d( logB, tenB );
TEN_T_LERP( logMean, aa, logA, logB ); /* "B" = logMean */
tenSqrtSingle_d( sqrtB, logMean );
tenInv_d( isqrtB, sqrtB );
TEN_T2M( mat1, mean );
TEN_T2M( mat2, isqrtB );
ELL_3M_MUL( mat3, mat1, mat2 );
ELL_3M_MUL( mat1, mat2, mat3 );
TEN_M2T( tmp1, mat1 );
tenSqrtSingle_d( oten, tmp1 );
oten[0] = AIR_LERP( aa, tenA[0], tenB[0] );
break;
case tenInterpTypeQuatGeoLoxK:
case tenInterpTypeQuatGeoLoxR:
tenEigensolve_d( evalA, evecA, tenA );
tenEigensolve_d( evalB, evecB, tenB );
if ( tenInterpTypeQuatGeoLoxK == ptype ) {
tenQGLInterpTwoEvalK( oeval, evalA, evalB, aa );
} else {
tenQGLInterpTwoEvalR( oeval, evalA, evalB, aa );
}
tenQGLInterpTwoEvec( oevec, evecA, evecB, aa );
tenMakeSingle_d( oten, AIR_LERP( aa, tenA[0], tenB[0] ), oeval, oevec );
break;
case tenInterpTypeGeoLoxK:
case tenInterpTypeGeoLoxR:
case tenInterpTypeLoxK:
case tenInterpTypeLoxR:
/* ( currently ) no closed-form expression for these */
TEN_T_SET( oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
break;
case tenInterpTypeRThetaPhiLinear:
if ( 1 ) {
double rtpA[3], rtpB[3], rtpM[3], eval[3], tenM[7];
tenEigensolve_d( eval, NULL, tenA );
tenTripleConvertSingle_d( rtpA, tenTripleTypeRThetaPhi,
eval, tenTripleTypeEigenvalue );
tenEigensolve_d( eval, NULL, tenB );
tenTripleConvertSingle_d( rtpB, tenTripleTypeRThetaPhi,
eval, tenTripleTypeEigenvalue );
TEN_T_LERP( tenM, aa, tenA, tenB );
tenEigensolve_d( eval, oevec, tenM );
ELL_3V_LERP( rtpM, aa, rtpA, rtpB );
tenTripleConvertSingle_d( oeval, tenTripleTypeEigenvalue,
rtpM, tenTripleTypeRThetaPhi );
}
tenMakeSingle_d( oten, AIR_LERP( aa, tenA[0], tenB[0] ), oeval, oevec );
break;
default:
/* error */
TEN_T_SET( oten, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
break;
}
return;
}
/*
** this NON-optionally uses biff
**
** for simplicity, a pre-allocated tenInterpParm MUST be passed,
** regardless of the interpolation requested
*/
int
304 tenInterpN_d( double tenOut[7],
const double *tenIn, const double *wght,
unsigned int num, int ptype, tenInterpParm *tip ) {
static const char me[]="tenInterpN_d";
unsigned int ii;
double ww, cc, tenErr[7], tmpTen[7], wghtSum, eval[3], evec[9], rtp[3];
TEN_T_SET( tenErr, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
/* wght can be NULL ==> equal 1/num weight for all */
if ( !( tenOut && tenIn && tip ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( num >= 2 ) ) {
biffAddf( TEN, "%s: need num >= 2 ( not %u )", me, num );
TEN_T_COPY( tenOut, tenErr ); return 1;
}
if ( airEnumValCheck( tenInterpType, ptype ) ) {
biffAddf( TEN, "%s: invalid %s %d", me, tenInterpType->name, ptype );
TEN_T_COPY( tenOut, tenErr ); return 1;
}
wghtSum = 0;
for ( ii=0; ii<num; ii++ ) {
ww = wght ? wght[ii] : 1.0/num;
wghtSum += ww;
}
if ( !( AIR_IN_CL( 1 - tip->wghtSumEps, wghtSum, 1 + tip->wghtSumEps ) ) ) {
biffAddf( TEN, "%s: wght sum %g not within %g of 1.0", me,
wghtSum, tip->wghtSumEps );
TEN_T_COPY( tenOut, tenErr ); return 1;
}
switch ( ptype ) {
case tenInterpTypeLinear:
TEN_T_SET( tenOut, 0, 0, 0, 0, 0, 0, 0 );
cc = 0;
for ( ii=0; ii<num; ii++ ) {
ww = wght ? wght[ii] : 1.0/num;
TEN_T_SCALE_INCR( tenOut, ww, tenIn + 7*ii );
cc += ww*( tenIn + 7*ii )[0];
}
tenOut[0] = cc;
break;
case tenInterpTypeLogLinear:
TEN_T_SET( tenOut, 0, 0, 0, 0, 0, 0, 0 );
cc = 0;
for ( ii=0; ii<num; ii++ ) {
ww = wght ? wght[ii] : 1.0/num;
tenLogSingle_d( tmpTen, tenIn + 7*ii );
TEN_T_SCALE_INCR( tenOut, ww, tmpTen );
cc += ww*( tenIn + 7*ii )[0];
}
tenOut[0] = cc;
TEN_T_COPY( tmpTen, tenOut );
tenExpSingle_d( tenOut, tmpTen );
break;
case tenInterpTypeAffineInvariant:
case tenInterpTypeWang:
biffAddf( TEN, "%s: sorry, not implemented", me );
TEN_T_COPY( tenOut, tenErr ); return 1;
break;
case tenInterpTypeGeoLoxK:
case tenInterpTypeGeoLoxR:
case tenInterpTypeLoxK:
case tenInterpTypeLoxR:
biffAddf( TEN, "%s: %s doesn't support averaging multiple tensors", me,
airEnumStr( tenInterpType, ptype ) );
TEN_T_COPY( tenOut, tenErr ); return 1;
break;
case tenInterpTypeQuatGeoLoxK:
case tenInterpTypeQuatGeoLoxR:
if ( tenInterpParmBufferAlloc( tip, num ) ) {
biffAddf( TEN, "%s: trouble getting buffers", me );
TEN_T_COPY( tenOut, tenErr ); return 1;
} else {
cc = 0;
for ( ii=0; ii<num; ii++ ) {
tenEigensolve_d( tip->eval + 3*ii, tip->evec + 9*ii, tenIn + 7*ii );
ww = wght ? wght[ii] : 1.0/num;
cc += ww*( tenIn + 7*ii )[0];
}
if ( _tenQGLInterpNEval( eval, tip->eval, wght, num, ptype, tip )
|| _tenQGLInterpNEvec( evec, tip->evec, wght, num, tip ) ) {
biffAddf( TEN, "%s: trouble computing", me );
TEN_T_COPY( tenOut, tenErr ); return 1;
}
tenMakeSingle_d( tenOut, cc, eval, evec );
}
break;
case tenInterpTypeRThetaPhiLinear:
TEN_T_SET( tmpTen, 0, 0, 0, 0, 0, 0, 0 );
ELL_3V_SET( rtp, 0, 0, 0 );
for ( ii=0; ii<num; ii++ ) {
double tmpeval[3], tmprtp[3];
tenEigensolve_d( tmpeval, NULL, tenIn + 7*ii );
tenTripleConvertSingle_d( tmprtp, tenTripleTypeRThetaPhi,
tmpeval, tenTripleTypeEigenvalue );
ww = wght ? wght[ii] : 1.0/num;
TEN_T_SCALE_INCR( tmpTen, ww, tenIn + 7*ii );
ELL_3V_SCALE_INCR( rtp, ww, tmprtp );
}
tenEigensolve_d( eval, evec, tmpTen ); /* only care about evec */
tenTripleConvertSingle_d( eval, tenTripleTypeEigenvalue,
rtp, tenTripleTypeRThetaPhi );
tenMakeSingle_d( tenOut, tmpTen[0], eval, evec );
break;
default:
biffAddf( TEN, "%s: sorry, interp type %s ( %d ) not implemented",
me, airEnumStr( tenInterpType, ptype ), ptype );
TEN_T_COPY( tenOut, tenErr );
return 1;
}
return 0;
}
int
422 _tenInterpGeoLoxRelaxOne( Nrrd *nodata, Nrrd *ntdata, Nrrd *nigrtdata,
unsigned int ii, int rotnoop, double scl,
tenInterpParm *tip ) {
static const char me[]="_tenInterpGeoLoxRelaxOne";
double *tdata, *odata, *igrtdata, *tt[5], *igrt[5][6], d02[7], d24[7],
len02, len24, tmp, tng[7], correct, update[7];
unsigned int jj;
if ( tip->verbose ) {
fprintf( stderr, "---- %u --> %u %u %u %u %u\n", ii,
2*ii - 2, 2*ii - 1, 2*ii, 2*ii + 1, 2*ii + 2 );
}
tdata = AIR_CAST( double *, ntdata->data );
odata = AIR_CAST( double *, nodata->data );
tt[0] = tdata + 7*( 2*ii - 2 );
tt[1] = tdata + 7*( 2*ii - 1 ); /* unused */
tt[2] = tdata + 7*( 2*ii + 0 );
tt[3] = tdata + 7*( 2*ii + 1 ); /* unused */
tt[4] = tdata + 7*( 2*ii + 2 );
igrtdata = AIR_CAST( double *, nigrtdata->data );
for ( jj=0; jj<6; jj++ ) {
igrt[0][jj] = igrtdata + 7*( jj + 6*( 2*ii - 2 ) ); /* unused */
igrt[1][jj] = igrtdata + 7*( jj + 6*( 2*ii - 1 ) );
igrt[2][jj] = igrtdata + 7*( jj + 6*( 2*ii + 0 ) );
igrt[3][jj] = igrtdata + 7*( jj + 6*( 2*ii + 1 ) );
igrt[4][jj] = igrtdata + 7*( jj + 6*( 2*ii + 2 ) ); /* unused */
}
/* re-align [1] and [3] bases relative to [2] */
/* HEY: should I be worrying about aligning the mode normal
when it had to be computed from eigenvectors? */
for ( jj=3; jj<6; jj++ ) {
if ( TEN_T_DOT( igrt[1][jj], igrt[2][jj] ) < 0 ) {
TEN_T_SCALE( igrt[1][jj], -1, igrt[1][jj] );
}
if ( TEN_T_DOT( igrt[3][jj], igrt[2][jj] ) < 0 ) {
TEN_T_SCALE( igrt[3][jj], -1, igrt[1][jj] );
}
}
TEN_T_SUB( tng, tt[4], tt[0] );
tmp = 1.0/TEN_T_NORM( tng );
TEN_T_SCALE( tng, tmp, tng );
TEN_T_SUB( d02, tt[2], tt[0] );
TEN_T_SUB( d24, tt[4], tt[2] );
TEN_T_SET( update, 1, 0, 0, 0, 0, 0, 0 );
for ( jj=0; jj<( rotnoop ? 3u : 6u ); jj++ ) {
len02 = TEN_T_DOT( igrt[1][jj], d02 );
len24 = TEN_T_DOT( igrt[3][jj], d24 );
correct = ( len24 - len02 )/2;
TEN_T_SCALE_INCR( update, correct*scl, igrt[2][jj] );
if ( tip->verbose ) {
fprintf( stderr, "igrt[1][%u] = %g %g %g %g %g %g\n", jj,
igrt[1][jj][1], igrt[1][jj][2], igrt[1][jj][3],
igrt[1][jj][4], igrt[1][jj][5], igrt[1][jj][6] );
fprintf( stderr, "igrt[3][%u] = %g %g %g %g %g %g\n", jj,
igrt[3][jj][1], igrt[3][jj][2], igrt[3][jj][3],
igrt[3][jj][4], igrt[3][jj][5], igrt[3][jj][6] );
fprintf( stderr, "( jj=%u ) len = %g %g --> ( d = %g ) "
"update = %g %g %g %g %g %g\n",
jj, len02, len24,
TEN_T_DOT( igrt[2][0], update ),
update[1], update[2], update[3],
update[4], update[5], update[6] );
}
}
if ( rotnoop ) {
double avg[7], diff[7], len;
TEN_T_LERP( avg, 0.5, tt[0], tt[4] );
TEN_T_SUB( diff, avg, tt[2] );
for ( jj=0; jj<3; jj++ ) {
len = TEN_T_DOT( igrt[2][jj], diff );
TEN_T_SCALE_INCR( diff, -len, igrt[2][jj] );
}
TEN_T_SCALE_INCR( update, scl*0.2, diff ); /* HEY: scaling is a hack */
if ( tip->verbose ) {
fprintf( stderr, "( rotnoop ) ( d = %g ) "
"update = %g %g %g %g %g %g\n",
TEN_T_DOT( igrt[2][0], update ),
update[1], update[2], update[3],
update[4], update[5], update[6] );
}
}
/*
TEN_T_SUB( d02, tt[2], tt[0] );
TEN_T_SUB( d24, tt[4], tt[2] );
len02 = TEN_T_DOT( tng, d02 );
len24 = TEN_T_DOT( tng, d24 );
correct = ( len24 - len02 );
TEN_T_SCALE_INCR( update, scl*correct, tng );
*/
if ( !TEN_T_EXISTS( update ) ) {
biffAddf( TEN, "%s: computed non-existent update ( step-size too big? )", me );
return 1;
}
TEN_T_ADD( odata + 7*( 2*ii + 0 ), tt[2], update );
return 0;
}
void
526 _tenInterpGeoLoxIGRT( double *igrt, double *ten, int useK, int rotNoop,
double minnorm ) {
/* static const char me[]="_tenInterpGeoLoxIGRT"; */
double eval[3], evec[9];
if ( useK ) {
tenInvariantGradientsK_d( igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm );
} else {
tenInvariantGradientsR_d( igrt + 7*0, igrt + 7*1, igrt + 7*2, ten, minnorm );
}
if ( rotNoop ) {
/* these shouldn't be used */
TEN_T_SET( igrt + 7*3, 1, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
TEN_T_SET( igrt + 7*4, 1, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
TEN_T_SET( igrt + 7*5, 1, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
} else {
tenEigensolve_d( eval, evec, ten );
tenRotationTangents_d( igrt + 7*3, igrt + 7*4, igrt + 7*5, evec );
}
return;
}
/*
** if "doubling" is non-zero, this assumes that the real
** vertices are on the even-numbered indices:
** ( 0 1 2 3 4 )
** 0 2 4 6 8 --> size=9 --> NN=4
** 1 3 5 7
*/
double
559 tenInterpPathLength( Nrrd *ntt, int doubleVerts, int fancy, int shape ) {
double *tt, len, diff[7], *tenA, *tenB;
unsigned int ii, NN;
tt = AIR_CAST( double *, ntt->data );
if ( doubleVerts ) {
NN = AIR_CAST( unsigned int, ( ntt->axis[1].size-1 )/2 );
} else {
NN = AIR_CAST( unsigned int, ntt->axis[1].size-1 );
}
len = 0;
for ( ii=0; ii<NN; ii++ ) {
if ( doubleVerts ) {
tenA = tt + 7*2*( ii + 1 );
tenB = tt + 7*2*( ii + 0 );
} else {
tenA = tt + 7*( ii + 1 );
tenB = tt + 7*( ii + 0 );
}
TEN_T_SUB( diff, tenA, tenB );
if ( fancy ) {
double mean[7], igrt[7*6], dot, incr;
unsigned int lo, hi;
TEN_T_LERP( mean, 0.5, tenA, tenB );
_tenInterpGeoLoxIGRT( igrt, mean, AIR_FALSE, AIR_FALSE, 0.0 );
if ( shape ) {
lo = 0;
hi = 2;
} else {
lo = 3;
hi = 5;
}
incr = 0;
for ( ii=lo; ii<=hi; ii++ ) {
dot = TEN_T_DOT( igrt + 7*ii, diff );
incr += dot*dot;
}
len += sqrt( incr );
} else {
len += TEN_T_NORM( diff );
}
}
return len;
}
double
606 _tenPathSpacingEqualize( Nrrd *nout, Nrrd *nin ) {
/* static const char me[]="_tenPathSpacingEqualize"; */
double *in, *out, len, diff[7],
lenTotal, /* total length of input */
lenStep, /* correct length on input polyline between output vertices */
lenIn, /* length along input processed so far */
lenHere, /* length of segment associated with current input index */
lenRmn, /* length along past input segments as yet unmapped to output */
*tenHere, *tenNext;
unsigned int idxIn, idxOut, NN;
in = AIR_CAST( double *, nin->data );
out = AIR_CAST( double *, nout->data );
NN = ( nin->axis[1].size-1 )/2;
lenTotal = tenInterpPathLength( nin, AIR_TRUE, AIR_FALSE, AIR_FALSE );
lenStep = lenTotal/NN;
/*
fprintf( stderr, "!%s: lenTotal/NN = %g/%u = %g = lenStep\n", me,
lenTotal, NN, lenStep );
*/
TEN_T_COPY( out + 7*2*( 0 + 0 ), in + 7*2*( 0 + 0 ) );
lenIn = lenRmn = 0;
idxOut = 1;
for ( idxIn=0; idxIn<NN; idxIn++ ) {
tenNext = in + 7*2*( idxIn + 1 );
tenHere = in + 7*2*( idxIn + 0 );
TEN_T_SUB( diff, tenNext, tenHere );
lenHere = TEN_T_NORM( diff );
/*
fprintf( stderr, "!%s( %u ): %g + %g >( %s )= %g\n", me, idxIn,
lenRmn, lenHere,
( lenRmn + lenHere >= lenStep ? "yes" : "no" ),
lenStep );
*/
if ( lenRmn + lenHere >= lenStep ) {
len = lenRmn + lenHere;
while ( len > lenStep ) {
len -= lenStep;
/*
fprintf( stderr, "!%s( %u ): len = %g -> %g\n", me, idxIn,
len + lenStep, len );
*/
TEN_T_AFFINE( out + 7*( 2*idxOut + 0 ),
lenHere, len, 0, tenHere, tenNext );
/*
fprintf( stderr, "!%s( %u ): out[%u] ~ %g\n", me, idxIn, idxOut,
AIR_AFFINE( lenHere, len, 0, 0, 1 ) );
*/
idxOut++;
}
lenRmn = len;
} else {
lenRmn += lenHere;
/*
fprintf( stderr, "!%s( %u ): ( ==> lenRmn = %g -> %g )\n", me, idxIn,
lenRmn - lenHere, lenRmn );
*/
}
/* now lenRmn < lenStep */
lenIn += lenHere;
}
/* copy very last one in case we didn't get to it somehow */
TEN_T_COPY( out + 7*2*( NN + 0 ), in + 7*2*( NN + 0 ) );
/* fill in vertex mid-points */
for ( idxOut=0; idxOut<NN; idxOut++ ) {
TEN_T_LERP( out + 7*( 2*idxOut + 1 ),
0.5, out + 7*( 2*idxOut + 0 ), out + 7*( 2*idxOut + 2 ) );
}
return lenTotal;
}
int
679 _tenInterpGeoLoxPolyLine( Nrrd *ngeod, unsigned int *numIter,
const double tenA[7], const double tenB[7],
unsigned int NN, int useK, int rotnoop,
tenInterpParm *tip ) {
static const char me[]="_tenInterpGeoLoxPolyLine";
Nrrd *nigrt, *ntt, *nss, *nsub;
double *igrt, *geod, *tt, len, newlen;
unsigned int ii;
airArray *mop;
if ( !( ngeod && numIter && tenA && tenB ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( NN >= 2 ) ) {
biffAddf( TEN, "%s: # steps %u too small", me, NN );
return 1;
}
mop = airMopNew( );
ntt = nrrdNew( );
airMopAdd( mop, ntt, ( airMopper )nrrdNuke, airMopAlways );
nss = nrrdNew( );
airMopAdd( mop, nss, ( airMopper )nrrdNuke, airMopAlways );
nigrt = nrrdNew( );
airMopAdd( mop, nigrt, ( airMopper )nrrdNuke, airMopAlways );
nsub = nrrdNew( );
airMopAdd( mop, nsub, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( ngeod, nrrdTypeDouble, 2,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, NN+1 ) )
|| nrrdMaybeAlloc_va( ntt, nrrdTypeDouble, 2,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, 2*NN + 1 ) )
|| nrrdMaybeAlloc_va( nigrt, nrrdTypeDouble, 3,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, 6 ),
AIR_CAST( size_t, 2*NN + 1 ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
geod = AIR_CAST( double *, ngeod->data );
tt = AIR_CAST( double *, ntt->data );
igrt = AIR_CAST( double *, nigrt->data );
*numIter = 0;
if ( NN > 14 && tip->enableRecurse ) {
unsigned int subIter;
int E;
NrrdResampleContext *rsmc;
double kparm[3] = {1.0, 0.0, 0.5};
/* recurse and find geodesic with smaller number of vertices */
if ( _tenInterpGeoLoxPolyLine( nsub, &subIter, tenA, tenB,
NN/2, useK, rotnoop, tip ) ) {
biffAddf( TEN, "%s: problem with recursive call", me );
airMopError( mop ); return 1;
}
/* upsample coarse geodesic to higher resolution */
rsmc = nrrdResampleContextNew( );
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
E = AIR_FALSE;
if ( !E ) E |= nrrdResampleDefaultCenterSet( rsmc, nrrdCenterNode );
if ( !E ) E |= nrrdResampleInputSet( rsmc, nsub );
if ( !E ) E |= nrrdResampleKernelSet( rsmc, 0, NULL, NULL );
if ( !E ) E |= nrrdResampleKernelSet( rsmc, 1, nrrdKernelTent, kparm );
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, 1, 2*NN + 1 );
if ( !E ) E |= nrrdResampleRangeFullSet( rsmc, 1 );
if ( !E ) E |= nrrdResampleBoundarySet( rsmc, nrrdBoundaryBleed );
if ( !E ) E |= nrrdResampleTypeOutSet( rsmc, nrrdTypeDefault );
if ( !E ) E |= nrrdResampleRenormalizeSet( rsmc, AIR_TRUE );
if ( !E ) E |= nrrdResampleExecute( rsmc, ntt );
if ( E ) {
biffMovef( TEN, NRRD, "%s: problem upsampling course solution", me );
airMopError( mop ); return 1;
}
*numIter += subIter;
} else {
/* initialize the path, including all the segment midpoints */
for ( ii=0; ii<=2*NN; ii++ ) {
TEN_T_AFFINE( tt + 7*ii, 0, ii, 2*NN, tenA, tenB );
}
}
for ( ii=0; ii<=2*NN; ii++ ) {
_tenInterpGeoLoxIGRT( igrt + 7*6*ii, tt + 7*ii, useK, rotnoop,
tip->minNorm );
}
nrrdCopy( nss, ntt );
newlen = tenInterpPathLength( ntt, AIR_TRUE, AIR_FALSE, AIR_FALSE );
do {
unsigned int lo, hi;
int dd;
len = newlen;
if ( 0 == *numIter % 2 ) {
lo = 1;
hi = NN;
dd = 1;
} else {
lo = NN-1;
hi = 0;
dd = -1;
}
if ( tip->verbose ) {
fprintf( stderr, "%s: ======= iter = %u ( NN=%u )\n", me, *numIter, NN );
}
for ( ii=lo; ii!=hi; ii+=dd ) {
double sclHack;
sclHack = ii*4.0/NN - ii*ii*4.0/NN/NN;
if ( _tenInterpGeoLoxRelaxOne( nss, ntt, nigrt, ii, rotnoop,
sclHack*tip->convStep, tip ) ) {
biffAddf( TEN, "%s: problem on vert %u, iter %u\n", me, ii, *numIter );
return 1;
}
}
newlen = _tenPathSpacingEqualize( ntt, nss );
/* try doing this less often */
for ( ii=0; ii<=2*NN; ii++ ) {
_tenInterpGeoLoxIGRT( igrt + 7*6*ii, tt + 7*ii, useK, rotnoop,
tip->minNorm );
}
*numIter += 1;
} while ( ( 0 == tip->maxIter || *numIter < tip->maxIter )
&& 2*AIR_ABS( newlen - len )/( newlen + len ) > tip->convEps );
/* copy final result to output */
for ( ii=0; ii<=NN; ii++ ) {
TEN_T_COPY( geod + 7*ii, tt + 7*2*ii );
}
/* values from outer-most recursion will stick */
tip->numIter = *numIter;
tip->convFinal = 2*AIR_ABS( newlen - len )/( newlen + len );
airMopOkay( mop );
return 0;
}
int
816 tenInterpTwoDiscrete_d( Nrrd *nout,
const double tenA[7], const double tenB[7],
int ptype, unsigned int num,
tenInterpParm *_tip ) {
static const char me[]="tenInterpTwoDiscrete_d";
double *out;
unsigned int ii;
airArray *mop;
tenInterpParm *tip;
if ( !nout ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( tenInterpType, ptype ) ) {
biffAddf( TEN, "%s: path type %d not a valid %s", me, ptype,
tenInterpType->name );
return 1;
}
mop = airMopNew( );
if ( _tip ) {
tip = _tip;
} else {
tip = tenInterpParmNew( );
airMopAdd( mop, tip, ( airMopper )tenInterpParmNix, airMopAlways );
}
if ( !( num >= 2 ) ) {
biffAddf( TEN, "%s: need num >= 2 ( not %u )", me, num );
airMopError( mop ); return 1;
}
if ( nrrdMaybeAlloc_va( nout, nrrdTypeDouble, 2,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, num ) ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating output", me );
airMopError( mop ); return 1;
}
out = AIR_CAST( double *, nout->data );
if ( ptype == tenInterpTypeLinear
|| ptype == tenInterpTypeLogLinear
|| ptype == tenInterpTypeAffineInvariant
|| ptype == tenInterpTypeWang
|| ptype == tenInterpTypeQuatGeoLoxK
|| ptype == tenInterpTypeQuatGeoLoxR
|| ptype == tenInterpTypeRThetaPhiLinear ) {
/* we have fast ways of doing interpolation
between two tensors for these path types */
for ( ii=0; ii<num; ii++ ) {
/* yes, this is often doing a lot of needless recomputations. */
tenInterpTwo_d( out + 7*ii, tenA, tenB,
ptype, ( double )ii/( num-1 ), tip );
}
} else if ( ptype == tenInterpTypeGeoLoxK
|| ptype == tenInterpTypeGeoLoxR
|| ptype == tenInterpTypeLoxK
|| ptype == tenInterpTypeLoxR ) {
/* we have slow iterative code for these */
unsigned int numIter;
int useK, rotnoop;
useK = ( tenInterpTypeGeoLoxK == ptype
|| tenInterpTypeLoxK == ptype );
rotnoop = ( tenInterpTypeGeoLoxK == ptype
|| tenInterpTypeGeoLoxR == ptype );
fprintf( stderr, "!%s: useK = %d, rotnoop = %d\n", me, useK, rotnoop );
if ( _tenInterpGeoLoxPolyLine( nout, &numIter,
tenA, tenB,
num, useK, rotnoop, tip ) ) {
biffAddf( TEN, "%s: trouble finding path", me );
airMopError( mop ); return 1;
}
} else {
biffAddf( TEN, "%s: sorry, interp for path %s not implemented", me,
airEnumStr( tenInterpType, ptype ) );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
double
899 tenInterpDistanceTwo_d( const double tenA[7], const double tenB[7],
int ptype, tenInterpParm *_tip ) {
static const char me[]="tenInterpDistanceTwo_d";
char *err;
tenInterpParm *tip;
airArray *mop;
double ret, diff[7], logA[7], logB[7], invA[7], det, siA[7],
mat1[9], mat2[9], mat3[9], logDiff[7];
Nrrd *npath;
if ( !( tenA && tenB && !airEnumValCheck( tenInterpType, ptype ) ) ) {
return AIR_NAN;
}
mop = airMopNew( );
switch ( ptype ) {
case tenInterpTypeLinear:
TEN_T_SUB( diff, tenA, tenB );
ret = TEN_T_NORM( diff );
break;
case tenInterpTypeLogLinear:
tenLogSingle_d( logA, tenA );
tenLogSingle_d( logB, tenB );
TEN_T_SUB( diff, logA, logB );
ret = TEN_T_NORM( diff );
break;
case tenInterpTypeAffineInvariant:
TEN_T_INV( invA, tenA, det );
tenSqrtSingle_d( siA, invA );
TEN_T2M( mat1, tenB );
TEN_T2M( mat2, siA );
ell_3m_mul_d( mat3, mat1, mat2 );
ell_3m_mul_d( mat1, mat2, mat3 );
TEN_M2T( diff, mat1 );
tenLogSingle_d( logDiff, diff );
ret = TEN_T_NORM( logDiff );
break;
case tenInterpTypeGeoLoxK:
case tenInterpTypeGeoLoxR:
case tenInterpTypeLoxK:
case tenInterpTypeLoxR:
case tenInterpTypeQuatGeoLoxK:
case tenInterpTypeQuatGeoLoxR:
npath = nrrdNew( );
airMopAdd( mop, npath, ( airMopper )nrrdNuke, airMopAlways );
if ( _tip ) {
tip = _tip;
} else {
tip = tenInterpParmNew( );
airMopAdd( mop, tip, ( airMopper )tenInterpParmNix, airMopAlways );
}
if ( tenInterpTwoDiscrete_d( npath, tenA, tenB, ptype,
tip->numSteps, tip ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble computing path:\n%s\n", me, err );
airMopError( mop ); return AIR_NAN;
}
ret = tenInterpPathLength( npath, AIR_FALSE, AIR_FALSE, AIR_FALSE );
if ( tip->lengthFancy ) {
tip->lengthShape = tenInterpPathLength( npath, AIR_FALSE,
AIR_TRUE, AIR_TRUE );
tip->lengthOrient = tenInterpPathLength( npath, AIR_FALSE,
AIR_TRUE, AIR_FALSE );
}
break;
case tenInterpTypeWang:
default:
fprintf( stderr, "%s: unimplemented %s %d!!!!\n", me,
tenInterpType->name, ptype );
ret = AIR_NAN;
break;
}
airMopOkay( mop );
return ret;
}
/*
** actually, the input nrrds don't have to be 3D ...
*/
int
980 tenInterpMulti3D( Nrrd *nout, const Nrrd *const *nin, const double *wght,
unsigned int ninLen, int ptype, tenInterpParm *_tip ) {
static const char me[]="tenInterpMulti3D";
unsigned int ninIdx;
size_t II, NN;
double ( *lup )( const void *, size_t ), ( *ins )( void *, size_t, double ),
*tbuff;
tenInterpParm *tip;
airArray *mop;
/* allow NULL wght, to signify equal weighting */
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( ninLen > 0 ) ) {
biffAddf( TEN, "%s: need at least 1 nin, not 0", me );
return 1;
}
if ( airEnumValCheck( tenInterpType, ptype ) ) {
biffAddf( TEN, "%s: invalid %s %d", me,
tenInterpType->name, ptype );
return 1;
}
if ( tenTensorCheck( nin[0], nrrdTypeDefault, AIR_FALSE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: first nrrd not a tensor array", me );
return 1;
}
if ( !( nrrdTypeFloat == nin[0]->type ||
nrrdTypeDouble == nin[0]->type ) ) {
biffAddf( TEN, "%s: need type %s or %s ( not %s ) in first nrrd", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, nin[0]->type ) );
return 1;
}
for ( ninIdx=1; ninIdx<ninLen; ninIdx++ ) {
if ( tenTensorCheck( nin[ninIdx], nrrdTypeDefault, AIR_FALSE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: nin[%u] not a tensor array", me, ninIdx );
return 1;
}
if ( !nrrdSameSize( nin[0], nin[ninIdx], AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: nin[0] doesn't match nin[%u]", me, ninIdx );
return 1;
}
if ( nin[0]->type != nin[ninIdx]->type ) {
biffAddf( TEN, "%s: nin[0] type ( %s ) != nin[%u] type ( %s )", me,
airEnumStr( nrrdType, nin[0]->type ),
ninIdx, airEnumStr( nrrdType, nin[ninIdx]->type ) );
return 1;
}
}
mop = airMopNew( );
if ( nrrdCopy( nout, nin[0] ) ) {
biffMovef( TEN, NRRD, "%s: couldn't initialize output", me );
airMopError( mop ); return 1;
}
if ( _tip ) {
tip = _tip;
} else {
tip = tenInterpParmNew( );
airMopAdd( mop, tip, ( airMopper )tenInterpParmNix, airMopAlways );
}
tbuff = AIR_CAST( double *, calloc( 7*ninLen, sizeof( double ) ) );
if ( !tbuff ) {
biffAddf( TEN, "%s: couldn't allocate tensor buff", me );
airMopError( mop ); return 1;
}
ins = nrrdDInsert[nin[0]->type];
lup = nrrdDLookup[nin[0]->type];
NN = nrrdElementNumber( nin[0] )/7;
for ( II=0; II<NN; II++ ) {
double tenOut[7];
unsigned int tt;
for ( ninIdx=0; ninIdx<ninLen; ninIdx++ ) {
for ( tt=0; tt<7; tt++ ) {
tbuff[tt + 7*ninIdx] = lup( nin[ninIdx]->data, tt + 7*II );
}
}
if ( tenInterpN_d( tenOut, tbuff, wght, ninLen, ptype, tip ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: trouble on sample %s", me,
airSprintSize_t( stmp, II ) );
airMopError( mop ); return 1;
}
for ( tt=0; tt<7; tt++ ) {
ins( nout->data, tt + 7*II, tenOut[tt] );
}
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/*
** computes ( r1 - r0 )/( log( r1 ) - log( r0 ) )
*/
double
32 _tenQGL_blah( double rr0, double rr1 ) {
double bb, ret;
if ( rr1 > rr0 ) {
/* the bb calculation below could blow up, so we recurse
with flipped order */
ret = _tenQGL_blah( rr1, rr0 );
} else {
/* rr1 <= rr0 --> rr1/rr0 <= 1 --> rr1/rr0 - 1 <= 0 --> bb <= 0 */
/* and rr1 >= 0 --> rr1/rr0 >= 0 --> rr1/rr0 - 1 >= -1 --> bb >= -1 */
bb = rr0 ? ( rr1/rr0 - 1 ) : 0;
if ( bb > -0.0001 ) {
ret = rr0*( 1 + bb*( 0.5001249976477329
- bb*( 7.0/6 + bb*( 1.0/6 - bb/720.0 ) ) ) );
} else {
/* had trouble finding a high-quality approximation for b near -1 */
bb = AIR_MAX( bb, -1 + 100*FLT_EPSILON );
ret = rr0*bb/log( bb + 1 );
}
}
return ret;
}
#define rr0 ( RThZA[0] )
#define rr1 ( RThZB[0] )
#define rr ( oRThZ[0] )
#define th0 ( RThZA[1] )
#define th1 ( RThZB[1] )
#define th ( oRThZ[1] )
#define zz0 ( RThZA[2] )
#define zz1 ( RThZB[2] )
#define zz ( oRThZ[2] )
void
66 tenQGLInterpTwoEvalK( double oeval[3],
const double evalA[3], const double evalB[3],
const double tt ) {
double RThZA[3], RThZB[3], oRThZ[3], bb;
tenTripleConvertSingle_d( RThZA, tenTripleTypeRThetaZ,
evalA, tenTripleTypeEigenvalue );
tenTripleConvertSingle_d( RThZB, tenTripleTypeRThetaZ,
evalB, tenTripleTypeEigenvalue );
if ( rr1 > rr0 ) {
/* the bb calculation below could blow up, so we recurse
with flipped order */
tenQGLInterpTwoEvalK( oeval, evalB, evalA, 1-tt );
} else {
rr = AIR_LERP( tt, rr0, rr1 );
zz = AIR_LERP( tt, zz0, zz1 );
bb = rr0 ? ( rr1/rr0 - 1 ) : 0;
/* bb can't be positive, because rr1 <= rr0 enforced above, so below
is really test for -0.001 < bb <= 0 */
if ( bb > -0.0001 ) {
double dth;
dth = th1 - th0;
/* rr0 and rr1 are similar, use stable approximation */
th = th0 + tt*( dth
+ ( 0.5 - tt/2 )*dth*bb
+ ( -1.0/12 - tt/4 + tt*tt/3 )*dth*bb*bb
+ ( 1.0/24 + tt/24 + tt*tt/6 - tt*tt*tt/4 )*dth*bb*bb*bb );
} else {
/* use original formula */
/* have to clamp value of b so that log( ) values don't explode */
bb = AIR_MAX( bb, -1 + 100*FLT_EPSILON );
th = th0 + ( th1 - th0 )*log( 1 + bb*tt )/log( 1 + bb );
}
tenTripleConvertSingle_d( oeval, tenTripleTypeEigenvalue,
oRThZ, tenTripleTypeRThetaZ );
/*
fprintf( stderr, "%s: ( b = %g ) %g %g %g <-- %g %g %g\n", "blah", bb,
oeval[0], oeval[1], oeval[2],
oRThZ[0], oRThZ[1], oRThZ[2] );
*/
}
}
double
110 _tenQGL_Kdist( const double RThZA[3], const double RThZB[3] ) {
double dr, dth, dz, bl, dist;
dr = rr1 - rr0;
bl = _tenQGL_blah( rr0, rr1 );
dth = th1 - th0;
dz = zz1 - zz0;
dist = sqrt( dr*dr + bl*bl*dth*dth + dz*dz );
return dist;
}
void
122 _tenQGL_Klog( double klog[3],
const double RThZA[3], const double RThZB[3] ) {
double dr, bl, dth, dz;
dr = rr1 - rr0;
bl = _tenQGL_blah( rr0, rr1 );
dth = th1 - th0;
dz = zz1 - zz0;
ELL_3V_SET( klog, dr, bl*dth, dz );
return;
}
void
135 _tenQGL_Kexp( double RThZB[3],
const double RThZA[3], const double klog[3] ) {
double bl;
rr1 = rr0 + klog[0];
bl = _tenQGL_blah( rr0, rr1 );
th1 = th0 + ( bl ? klog[1]/bl : 0 );
zz1 = zz0 + klog[2];
return;
}
#undef rr0
#undef rr1
#undef rr
#undef th0
#undef th1
#undef th
#undef zz0
#undef zz1
#undef zz
/*
** stable computation of ( ph1 - ph0 )/( log( tan( ph1/2 ) ) - log( tan( ph0/2 ) ) )
*/
double
160 _tenQGL_fooo( double ph1, double ph0 ) {
double ret;
if ( ph0 > ph1 ) {
ret = _tenQGL_fooo( ph0, ph1 );
} else if ( 0 == ph0/2 ) {
ret = 0;
} else {
/* ph1 >= ph0 > 0 */
if ( ph1 - ph0 < 0.0001 ) {
double dph, ss, cc;
dph = ph1 - ph0;
ss = sin( ph1 );
cc = cos( ph1 );
ret = ( ss
+ cc*dph/2
+ ( ( cos( 2*ph1 ) - 3 )/ss )*dph*dph/24
+ ( cc/( ss*ss ) )*dph*dph*dph/24 );
} else {
ret = ( ph1 - ph0 )/( log( tan( ph1/2 ) ) - log( tan( ph0/2 ) ) );
}
}
return ret;
}
#define rr0 ( RThPhA[0] )
#define rr1 ( RThPhB[0] )
#define rr ( oRThPh[0] )
#define th0 ( RThPhA[1] )
#define th1 ( RThPhB[1] )
#define th ( oRThPh[1] )
#define ph0 ( RThPhA[2] )
#define ph1 ( RThPhB[2] )
#define ph ( oRThPh[2] )
void
196 _tenQGL_Rlog( double rlog[3],
const double RThPhA[3], const double RThPhB[3] ) {
double dr, dth, dph, bl, fo;
dr = rr1 - rr0;
dth = th1 - th0;
dph = ph1 - ph0;
bl = _tenQGL_blah( rr0, rr1 );
fo = _tenQGL_fooo( ph0, ph1 );
/* rlog[0] rlog[1] rlog[2] */
ELL_3V_SET( rlog, dr, bl*dth*fo, dph*bl );
}
void
210 _tenQGL_Rexp( double RThPhB[3],
const double RThPhA[3], const double rlog[3] ) {
double bl, fo;
rr1 = rr0 + rlog[0];
bl = _tenQGL_blah( rr0, rr1 );
ph1 = ph0 + ( bl ? rlog[2]/bl : 0 );
fo = _tenQGL_fooo( ph0, ph1 );
th1 = th0 + ( bl*fo ? rlog[1]/( bl*fo ) : 0 );
return;
}
/* unlike with the K stuff, with the R stuff I seemed to have more luck
implementing pair-wise interpolation in terms of log and exp
*/
void
226 tenQGLInterpTwoEvalR( double oeval[3],
const double evalA[3], const double evalB[3],
const double tt ) {
double RThPhA[3], RThPhB[3], rlog[3], oRThPh[3];
tenTripleConvertSingle_d( RThPhA, tenTripleTypeRThetaPhi,
evalA, tenTripleTypeEigenvalue );
tenTripleConvertSingle_d( RThPhB, tenTripleTypeRThetaPhi,
evalB, tenTripleTypeEigenvalue );
_tenQGL_Rlog( rlog, RThPhA, RThPhB );
ELL_3V_SCALE( rlog, tt, rlog );
_tenQGL_Rexp( oRThPh, RThPhA, rlog );
tenTripleConvertSingle_d( oeval, tenTripleTypeEigenvalue,
oRThPh, tenTripleTypeRThetaPhi );
return;
}
double
244 _tenQGL_Rdist( const double RThPhA[3], const double RThPhB[3] ) {
double dr, dth, dph, bl, fo;
dr = rr1 - rr0;
dth = th1 - th0;
dph = ph1 - ph0;
bl = _tenQGL_blah( rr0, rr1 );
fo = _tenQGL_fooo( ph0, ph1 );
return sqrt( dr*dr + bl*bl*( dth*dth*fo*fo + dph*dph ) );
}
#undef rr0
#undef rr1
#undef rr
#undef th0
#undef th1
#undef th
#undef ph0
#undef ph1
#undef ph
/* returns the index into unitq[] of the quaternion that led to the
right alignment. If it was already aligned, this will be 0,
because unitq[0] is the identity quaternion */
int
269 _tenQGL_q_align( double qOut[4], const double qRef[4], const double qIn[4] ) {
unsigned int ii, maxDotIdx;
double unitq[8][4] = {{+1, 0, 0, 0},
{-1, 0, 0, 0},
{0, +1, 0, 0},
{0, -1, 0, 0},
{0, 0, +1, 0},
{0, 0, -1, 0},
{0, 0, 0, +1},
{0, 0, 0, -1}};
double dot[8], qInMul[8][4], maxDot;
for ( ii=0; ii<8; ii++ ) {
ell_q_mul_d( qInMul[ii], qIn, unitq[ii] );
dot[ii] = ELL_4V_DOT( qRef, qInMul[ii] );
}
maxDotIdx = 0;
maxDot = dot[maxDotIdx];
for ( ii=1; ii<8; ii++ ) {
if ( dot[ii] > maxDot ) {
maxDotIdx = ii;
maxDot = dot[maxDotIdx];
}
}
ELL_4V_COPY( qOut, qInMul[maxDotIdx] );
return maxDotIdx;
}
void
298 tenQGLInterpTwoEvec( double oevec[9],
const double evecA[9], const double evecB[9],
double tt ) {
double rotA[9], rotB[9], orot[9],
oq[4], qA[4], qB[4], _qB[4], qdiv[4], angle, axis[3], qq[4];
ELL_3M_TRANSPOSE( rotA, evecA );
ELL_3M_TRANSPOSE( rotB, evecB );
ell_3m_to_q_d( qA, rotA );
ell_3m_to_q_d( _qB, rotB );
_tenQGL_q_align( qB, qA, _qB );
/* there's probably a faster way to do this slerp qA --> qB */
ell_q_div_d( qdiv, qA, qB ); /* div = A^-1 * B */
angle = ell_q_to_aa_d( axis, qdiv );
ell_aa_to_q_d( qq, angle*tt, axis );
ell_q_mul_d( oq, qA, qq );
ell_q_to_3m_d( orot, oq );
ELL_3M_TRANSPOSE( oevec, orot );
}
void
319 tenQGLInterpTwo( double oten[7],
const double tenA[7], const double tenB[7],
int ptype, double tt, tenInterpParm *tip ) {
double oeval[3], evalA[3], evalB[3], oevec[9], evecA[9], evecB[9], cc;
AIR_UNUSED( tip );
tenEigensolve_d( evalA, evecA, tenA );
tenEigensolve_d( evalB, evecB, tenB );
cc = AIR_LERP( tt, tenA[0], tenB[0] );
if ( tenInterpTypeQuatGeoLoxK == ptype ) {
tenQGLInterpTwoEvalK( oeval, evalA, evalB, tt );
} else {
tenQGLInterpTwoEvalR( oeval, evalA, evalB, tt );
}
tenQGLInterpTwoEvec( oevec, evecA, evecB, tt );
tenMakeSingle_d( oten, cc, oeval, oevec );
return;
}
/*
** This does ( non-optionally ) use biff, to report convergence failures
**
** we do in fact require non-NULL tip, because it holds the buffers we need
*/
int
346 _tenQGLInterpNEval( double evalOut[3],
const double *evalIn, /* size 3 -by- NN */
const double *wght, /* size NN */
unsigned int NN,
int ptype, tenInterpParm *tip ) {
static const char me[]="_tenQGLInterpNEval";
double RTh_Out[3], elen;
unsigned int ii, iter;
int rttype;
void ( *llog )( double lg[3], const double RTh_A[3], const double RTh_B[3] );
void ( *lexp )( double RTh_B[3], const double RTh_A[3], const double lg[3] );
if ( !( evalOut && evalIn && tip ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
/* convert to ( R, Th, _ ) and initialize RTh_Out */
if ( tenInterpTypeQuatGeoLoxK == ptype ) {
rttype = tenTripleTypeRThetaZ;
llog = _tenQGL_Klog;
lexp = _tenQGL_Kexp;
} else {
rttype = tenTripleTypeRThetaPhi;
llog = _tenQGL_Rlog;
lexp = _tenQGL_Rexp;
}
ELL_3V_SET( RTh_Out, 0, 0, 0 );
for ( ii=0; ii<NN; ii++ ) {
double ww;
tenTripleConvertSingle_d( tip->rtIn + 3*ii, rttype,
evalIn + 3*ii, tenTripleTypeEigenvalue );
ww = wght ? wght[ii] : 1.0/NN;
ELL_3V_SCALE_INCR( RTh_Out, ww, tip->rtIn + 3*ii );
}
/* compute iterated weighted mean, stored in RTh_Out */
iter = 0;
do {
double logavg[3];
/* take log of everyone */
for ( ii=0; ii<NN; ii++ ) {
llog( tip->rtLog + 3*ii, RTh_Out, tip->rtIn + 3*ii );
}
/* average, and find length */
ELL_3V_SET( logavg, 0, 0, 0 );
for ( ii=0; ii<NN; ii++ ) {
double ww;
ww = wght ? wght[ii] : 1.0/NN;
ELL_3V_SCALE_INCR( logavg, ww, tip->rtLog + 3*ii );
}
elen = ELL_3V_LEN( logavg );
lexp( RTh_Out, RTh_Out, logavg );
iter++;
} while ( ( !tip->maxIter || iter < tip->maxIter ) && elen > tip->convEps );
if ( elen > tip->convEps ) {
ELL_3V_SET( evalOut, AIR_NAN, AIR_NAN, AIR_NAN );
biffAddf( TEN, "%s: still have error %g ( > eps %g ) after max %d iters", me,
elen, tip->convEps, tip->maxIter );
return 1;
}
/* finish, convert to eval */
tenTripleConvertSingle_d( evalOut, tenTripleTypeEigenvalue,
RTh_Out, rttype );
return 0;
}
double
416 _tenQGL_q_interdot( unsigned int *centerIdxP,
double *qq, double *inter, unsigned int NN ) {
unsigned int ii, jj;
double sum, dot, max;
for ( jj=0; jj<NN; jj++ ) {
for ( ii=0; ii<NN; ii++ ) {
inter[ii + NN*jj] = 0;
}
}
sum = 0;
for ( jj=0; jj<NN; jj++ ) {
inter[jj + NN*jj] = 1.0;
for ( ii=jj+1; ii<NN; ii++ ) {
dot = ELL_4V_DOT( qq + 4*ii, qq + 4*jj );
inter[ii + NN*jj] = dot;
inter[jj + NN*ii] = dot;
sum += dot;
}
}
for ( jj=0; jj<NN; jj++ ) {
for ( ii=1; ii<NN; ii++ ) {
inter[0 + NN*jj] += inter[ii + NN*jj];
}
}
*centerIdxP = 0;
max = inter[0 + NN*( *centerIdxP )];
for ( jj=1; jj<NN; jj++ ) {
if ( inter[0 + NN*jj] > max ) {
*centerIdxP = jj;
max = inter[0 + NN*( *centerIdxP )];
}
}
return sum;
}
/*
** This does ( non-optionally ) use biff, to report convergence failures
**
** we do in fact require non-NULL tip, because it holds the buffers we need
*/
int
458 _tenQGLInterpNEvec( double evecOut[9],
const double *evecIn, /* size 9 -by- NN */
const double *wght, /* size NN */
unsigned int NN,
tenInterpParm *tip ) {
static const char me[]="_tenQGLInterpNEvec";
double qOut[4], maxWght, len, /* odsum, */ dsum, rot[9];
unsigned int ii, centerIdx=0, fix, qiter;
if ( !( evecOut && evecIn && tip ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
/* convert to quaternions */
for ( ii=0; ii<NN; ii++ ) {
ELL_3M_TRANSPOSE( rot, evecIn + 9*ii );
ell_3m_to_q_d( tip->qIn + 4*ii, rot );
}
/* HEY: what should this be used for? variable odsum set but not used */
/* odsum = _tenQGL_q_interdot( ¢erIdx, tip->qIn, tip->qInter, NN ); */
/* find quaternion with maximal weight, use it as is ( decree that
its the right representative ), and then align rest with that.
This is actually principled; symmetry allows it */
centerIdx = 0;
if ( wght ) {
maxWght = wght[centerIdx];
for ( ii=1; ii<NN; ii++ ) {
if ( wght[ii] > maxWght ) {
centerIdx = ii;
maxWght = wght[centerIdx];
}
}
}
for ( ii=0; ii<NN; ii++ ) {
if ( ii == centerIdx ) {
continue;
}
_tenQGL_q_align( tip->qIn + 4*ii, tip->qIn + 4*centerIdx, tip->qIn + 4*ii );
}
dsum = _tenQGL_q_interdot( ¢erIdx, tip->qIn, tip->qInter, NN );
/* try to settle on tightest set of representatives */
qiter = 0;
do {
fix = 0;
for ( ii=0; ii<NN; ii++ ) {
unsigned int ff;
if ( ii == centerIdx ) {
continue;
}
ff = _tenQGL_q_align( tip->qIn + 4*ii, tip->qIn + 4*centerIdx,
tip->qIn + 4*ii );
fix = AIR_MAX( fix, ff );
}
dsum = _tenQGL_q_interdot( ¢erIdx, tip->qIn, tip->qInter, NN );
if ( tip->maxIter && qiter > tip->maxIter ) {
biffAddf( TEN, "%s: q tightening unconverged after %u iters; "
"interdot = %g -> maxfix = %u; center = %u\n",
me, tip->maxIter, dsum, fix, centerIdx );
return 1;
}
qiter++;
} while ( fix );
/*
fprintf( stderr, "!%s: dsum %g --%u--> %g\n", me, odsum, qiter, dsum );
*/
/* make sure they're normalized */
for ( ii=0; ii<NN; ii++ ) {
ELL_4V_NORM( tip->qIn + 4*ii, tip->qIn + 4*ii, len );
}
/* compute iterated weighted mean, stored in qOut */
if ( ell_q_avgN_d( qOut, &qiter, tip->qIn, tip->qBuff, wght,
NN, tip->convEps, tip->maxIter ) ) {
biffMovef( TEN, ELL, "%s: problem doing quaternion mean", me );
return 1;
}
/*
fprintf( stderr, "!%s: q avg converged in %u\n", me, qiter );
*/
/* finish, convert back to evec */
ell_q_to_3m_d( rot, qOut );
ELL_3M_TRANSPOSE( evecOut, rot );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define MAX_KMEANS_ITERATIONS 50
/* Calculate the Q-ball profile from DWIs */
void
36 _tenQball( const double b, const int gradcount, const double svals[],
const double grads[], double qvals[] ) {
/* Not an optimal Q-ball implementation! ( Taken from submission to
MICCAI 2006 ) Should be solved analytically in the future,
implemented from recent papers. */
int i, j;
double d, dist, weight, min, max;
AIR_UNUSED( b );
min = max = svals[1] / svals[0];
for( i = 0; i < gradcount; i++ ) {
d = svals[i+1] / svals[0];
if( d > max )
max = d;
else if( d < min )
min = d;
}
for( i = 0; i < gradcount; i++ ) {
qvals[i] = 0;
for( j = 0; j < gradcount; j++ ) {
d = AIR_AFFINE( min, svals[j+1] / svals[0], max, 0, 1 );
dist = ELL_3V_DOT( grads + 3*i, grads + 3*j );
dist = AIR_ABS( dist );
weight = cos( 0.5 * AIR_PI * dist );
qvals[i] += d * weight*weight*weight*weight;
}
}
return;
}
/* Segments DWIs into 2 segments based on Q-ball profiles */
void
69 _tenSegsamp2( const int gradcount, const double qvals[],
const double grads[], const double qpoints[],
unsigned int seg[], double dists[] ) {
const int segcount = 2;
int i, changed=AIR_TRUE;
double centroids[ 3*2 ]; /* 3*segcount */
AIR_UNUSED( grads );
_tenInitcent2( gradcount, qvals, qpoints, centroids );
for( i = 0; i < MAX_KMEANS_ITERATIONS && changed; i++ ) {
_tenCalcdists( segcount, centroids, gradcount, qpoints, dists );
changed = _tenCalccent2( gradcount, qpoints, dists, centroids, seg );
/*
printf( "Seg[%d]\t= { ", i );
for( j = 0; j < gradcount; j++ )
printf( "%d ", seg[j] );
printf( changed ? "}\n" : "} Convergence!\n" );
*/
}
}
/* Gives an inital choice of 2 centroids */
void
95 _tenInitcent2( const int gradcount, const double qvals[],
const double qpoints[], double centroids[6] ) {
int i, maxidx;
double max, dist;
/* Find largest peak in Q-ball */
maxidx = 0;
for( i = 0; i < gradcount; i++ )
if( qvals[maxidx] < qvals[i] )
maxidx = i;
ELL_3V_COPY( centroids, qpoints +3*maxidx );
/*
printf( "init: max=%d cent0=[%f %f %f]\n", maxidx,
centroids[0], centroids[1], centroids[2] );
*/
/* Find peak/axis from Q-ball furthest away from first peak */
max = 0;
for( i = 0; i < gradcount; i++ ) {
dist = _tenPldist( qpoints +3*i, centroids );
if ( dist > max ) {
maxidx = i;
max = dist;
}
}
ELL_3V_COPY( centroids+3, qpoints +3*maxidx );
/*
printf( "\ninit: max=%d cent1=[%f %f %f]\n", maxidx,
centroids[3], centroids[4], centroids[5] );
*/
}
/* Calculates 2 new centroids ( and a new segmentation ) from distances
between Q-balls and centroids, returns true if segmentation changed
*/
int
133 _tenCalccent2( const int gradcount, const double qpoints[],
const double dists[], double centroid[6], unsigned int seg[] ) {
#if 0
/* HEY: Attempt to implement better line-adding by adding
outerproducts of points and estimating major eigenvector
afterwards */
int i, changed=AIR_FALSE;
double sum0[9], sum1[9], mat[9], eval[3], evec[9];
ELL_3M_ZERO_SET( sum0 );
ELL_3M_ZERO_SET( sum1 );
for( i = 0; i < gradcount; i++ ) {
if( dists[i] < dists[gradcount+i] ) {
ELL_3MV_OUTER( mat, qpoints +3*i, qpoints +3*i );
ELL_3M_ADD2( sum0, sum0, mat );
changed = changed || ( seg[i] != 0 );
seg[i] = 0;
} else {
ELL_3MV_OUTER( mat, qpoints +3*i +gradcount, qpoints +3*i +gradcount );
ELL_3M_ADD2( sum1, sum1, mat );
changed = changed || ( seg[i] != 1 );
seg[i] = 1;
}
}
ell_3m_eigensolve_d( eval, evec, sum0, 0 );
ELL_3V_COPY( centroid, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) );
/* ELL_3V_SCALE( centroid, ELL_3V_LEN( centroid ), centroid ); */
ell_3m_eigensolve_d( eval, evec, sum1, 0 );
ELL_3V_COPY( centroid +3, evec + 3*ELL_MAX3_IDX( eval[0], eval[1], eval[2] ) );
/* ELL_3V_SCALE( centroid +3, ELL_3V_LEN( centroid ), centroid +3 ); Normalize */
return changed;
#endif
int i, sign, seg0count=0, seg1count=0, changed=AIR_FALSE;
double oldcentroid[6], diff[3], sum[3];
memcpy( oldcentroid, centroid, 6 * sizeof( double ) );
for( i = 0; i < gradcount; i++ ) {
if( dists[ 0*gradcount +i] < dists[1*gradcount +i] ) {
/* Try to resolve sign so that centroid do not end up as all 0 */
/* Choose signs so that the point lies "on the same side" as */
/* the previous centroid. */
diff[0] = oldcentroid[0] - qpoints[3*i +0];
diff[1] = oldcentroid[1] - qpoints[3*i +1];
diff[2] = oldcentroid[2] - qpoints[3*i +2];
sum[0] = oldcentroid[0] + qpoints[3*i +0];
sum[1] = oldcentroid[1] + qpoints[3*i +1];
sum[2] = oldcentroid[2] + qpoints[3*i +2];
sign = ( diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2] ) <
( sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2] ) ? -1 : +1;
changed = changed || ( seg[i] != 0 );
seg[i] = 0;
centroid[0] += sign * qpoints[3*i +0];
centroid[1] += sign * qpoints[3*i +1];
centroid[2] += sign * qpoints[3*i +2];
seg0count++;
} else {
diff[0] = oldcentroid[3+0] - qpoints[3*i +0];
diff[1] = oldcentroid[3+1] - qpoints[3*i +1];
diff[2] = oldcentroid[3+2] - qpoints[3*i +2];
sum[0] = oldcentroid[3+0] + qpoints[3*i +0];
sum[1] = oldcentroid[3+1] + qpoints[3*i +1];
sum[2] = oldcentroid[3+2] + qpoints[3*i +2];
sign = ( diff[0]*diff[0] + diff[1]*diff[1] + diff[2]*diff[2] ) <
( sum[0]*sum[0] + sum[1]*sum[1] + sum[2]*sum[2] ) ? -1 : +1;
changed = changed || ( seg[i] != 1 );
seg[i] = 1;
centroid[3+0] += sign * qpoints[3*i +0];
centroid[3+1] += sign * qpoints[3*i +1];
centroid[3+2] += sign * qpoints[3*i +2];
seg1count++;
}
}
centroid[0] /= seg0count;
centroid[1] /= seg0count;
centroid[2] /= seg0count;
centroid[3+0] /= seg1count;
centroid[3+1] /= seg1count;
centroid[3+2] /= seg1count;
/* printf( "cent = %f %f %f %f %f %f\n", centroid[0], centroid[1], centroid[2], centroid[3], centroid[4], centroid[5] ); */
/*
Should give error if any segment contains less than 6 elements,
i.e. if( seg0count < 6 || seg1count < 6 ), since that would
imply that a tensor cannot be computed for that segment.
*/
return changed;
}
/* Converts Q-values and gradients to points on the Q-ball surface */
void
235 _tenQvals2points( const int gradcount, const double qvals[],
const double grads[], double qpoints[] ) {
int i;
memcpy( qpoints, grads, 3 * gradcount * sizeof( double ) );
for( i = 0; i < gradcount; i++ ) {
qpoints[3*i +0] *= qvals[i];
qpoints[3*i +1] *= qvals[i];
qpoints[3*i +2] *= qvals[i];
}
}
/* Calculates the shortest distances from each centroid/axis to each
Q-ball point */
void
249 _tenCalcdists( const int centcount, const double centroid[],
const int gradcount, const double qpoints[], double dists[] ) {
int i, j;
for( j = 0; j < centcount; j++ )
for( i = 0; i < gradcount; i++ )
dists[j*gradcount +i] = _tenPldist( &qpoints[3*i], ¢roid[3*j] );
/*
printf( "dists = " );
for( i = 0; i < 2*gradcount; i++ )
printf( "%f ", dists[i] );
printf( "\n" );
*/
}
/* Estimates the shortest distance from a point to a line going
through the origin */
double
267 _tenPldist( const double point[], const double line[] ) {
double cross[3];
double negpoint[3];
negpoint[0] = -point[0];
negpoint[1] = -point[1];
negpoint[2] = -point[2];
ELL_3V_CROSS( cross, line, negpoint );
return ELL_3V_LEN( cross ) / ELL_3V_LEN( line );
}
/* Converts a segmentation into a set of 0-1 weights */
void
283 _tenSeg2weights( const int gradcount, const int seg[],
const int segcount, double weights[] ) {
int i, j;
for( j = 0; j < segcount; j++ ) {
for( i = 0; i < gradcount; i++ ) {
weights[j*gradcount +i] = ( seg[i] == j ) ? 1 : 0;
}
}
return;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#if TEEM_LEVMAR
#include <levmar.h>
#endif
/* --------------------------------------------------------------------- */
const char *
_tenDwiGageStr[] = {
"( unknown tenDwiGage )",
"all",
"b0",
"jdwi",
"adc",
"mdwi",
"tlls",
"tllserr",
"tllserrlog",
"tllslike",
"twls",
"twlserr",
"twlserrlog",
"twlslike",
"tnls",
"tnlserr",
"tnlserrlog",
"tnlslike",
"tmle",
"tmleerr",
"tmleerrlog",
"tmlelike",
"t",
"terr",
"terrlog",
"tlike",
"c",
"fa",
"adwie",
"2qs",
"2qserr",
"2qsnerr",
"2peled",
"2pelederr",
"2pelednerr",
"2peledlminfo",
};
const int
_tenDwiGageVal[] = {
tenDwiGageUnknown,
tenDwiGageAll,
tenDwiGageB0,
tenDwiGageJustDWI,
tenDwiGageADC,
tenDwiGageMeanDWIValue,
tenDwiGageTensorLLS,
tenDwiGageTensorLLSError,
tenDwiGageTensorLLSErrorLog,
tenDwiGageTensorLLSLikelihood,
tenDwiGageTensorWLS,
tenDwiGageTensorWLSError,
tenDwiGageTensorWLSErrorLog,
tenDwiGageTensorWLSLikelihood,
tenDwiGageTensorNLS,
tenDwiGageTensorNLSError,
tenDwiGageTensorNLSErrorLog,
tenDwiGageTensorNLSLikelihood,
tenDwiGageTensorMLE,
tenDwiGageTensorMLEError,
tenDwiGageTensorMLEErrorLog,
tenDwiGageTensorMLELikelihood,
tenDwiGageTensor,
tenDwiGageTensorError,
tenDwiGageTensorErrorLog,
tenDwiGageTensorLikelihood,
tenDwiGageConfidence,
tenDwiGageFA,
tenDwiGageTensorAllDWIError,
tenDwiGage2TensorQSeg,
tenDwiGage2TensorQSegError,
tenDwiGage2TensorQSegAndError,
tenDwiGage2TensorPeled,
tenDwiGage2TensorPeledError,
tenDwiGage2TensorPeledAndError,
tenDwiGage2TensorPeledLevmarInfo
};
const airEnum
_tenDwiGage = {
"tenDwiGage",
TEN_DWI_GAGE_ITEM_MAX,
_tenDwiGageStr, _tenDwiGageVal,
NULL,
NULL, NULL,
AIR_FALSE
};
122 const airEnum *const
tenDwiGage = &_tenDwiGage;
/* --------------------------------------------------------------------- */
gageItemEntry
_tenDwiGageTable[TEN_DWI_GAGE_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, parent index, needData */
{tenDwiGageUnknown, 0, 0, {0}, 0, 0, AIR_TRUE},
/* len == 0 for tenDwiGage{All, JustDWI, ADC} means "learn later at run-time" */
{tenDwiGageAll, 0, 0, {0}, 0, 0, AIR_TRUE},
{tenDwiGageB0, 1, 0, {tenDwiGageAll}, tenDwiGageAll, 0, AIR_TRUE},
{tenDwiGageJustDWI, 0, 0, {tenDwiGageAll}, tenDwiGageAll, 1, AIR_TRUE},
{tenDwiGageADC, 0, 0, {tenDwiGageB0, tenDwiGageJustDWI}, 0, 0, AIR_TRUE},
{tenDwiGageMeanDWIValue, 1, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE},
{tenDwiGageTensorLLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE},
{tenDwiGageTensorLLSError, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorLLSErrorLog, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorLLSLikelihood, 1, 0, {tenDwiGageTensorLLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorWLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE},
{tenDwiGageTensorWLSError, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorWLSErrorLog, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorWLSLikelihood, 1, 0, {tenDwiGageTensorWLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorNLS, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE},
{tenDwiGageTensorNLSError, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorNLSErrorLog, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorNLSLikelihood, 1, 0, {tenDwiGageTensorNLS}, 0, 0, AIR_TRUE},
{tenDwiGageTensorMLE, 7, 0, {tenDwiGageAll, tenDwiGageMeanDWIValue}, 0, 0, AIR_TRUE},
{tenDwiGageTensorMLEError, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE},
{tenDwiGageTensorMLEErrorLog, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE},
{tenDwiGageTensorMLELikelihood, 1, 0, {tenDwiGageTensorMLE}, 0, 0, AIR_TRUE},
/* these are NOT sub-items: they are copies, as controlled by the
kind->data, but not the query: the query can't capture the kind
of dependency implemented by having a dynamic kind */
{tenDwiGageTensor, 7, 0, {0}, /* 0 == "learn later at run time" */ 0, 0, AIR_TRUE},
{tenDwiGageTensorError, 1, 0, {0}, 0, 0, AIR_TRUE},
{tenDwiGageTensorErrorLog, 1, 0, {0}, 0, 0, AIR_TRUE},
{tenDwiGageTensorLikelihood, 1, 0, {0}, 0, 0, AIR_TRUE},
/* back to normal non-run-time items */
{tenDwiGageConfidence, 1, 0, {tenDwiGageTensor}, tenDwiGageTensor, 0, AIR_TRUE},
{tenDwiGageFA, 1, 0, {tenDwiGageTensor}, 0, 0, AIR_TRUE},
{tenDwiGageTensorAllDWIError, 0, 0, {tenDwiGageTensor, tenDwiGageJustDWI}, 0, 0, AIR_TRUE},
/* it actually doesn't make sense for tenDwiGage2TensorQSegAndError to be the parent,
because of the situations where you want the q-seg result, but don't care about error */
{tenDwiGage2TensorQSeg, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorQSegError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorQSeg}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorQSegAndError, 15, 0, {tenDwiGage2TensorQSeg, tenDwiGage2TensorQSegError}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorPeled, 14, 0, {tenDwiGageAll}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorPeledError, 1, 0, {tenDwiGageAll, tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorPeledAndError, 15, 0, {tenDwiGage2TensorPeled, tenDwiGage2TensorPeledError}, 0, 0, AIR_TRUE},
{tenDwiGage2TensorPeledLevmarInfo, 5, 0, {tenDwiGage2TensorPeled}, 0, 0, AIR_TRUE}
};
void
187 _tenDwiGageIv3Print( FILE *file, gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="_tenDwiGageIv3Print";
AIR_UNUSED( ctx );
AIR_UNUSED( pvl );
fprintf( file, "%s: sorry, unimplemented\n", me );
return;
}
void
197 _tenDwiGageFilter( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="_tenDwiGageFilter";
double *fw00, *fw11, *fw22, *dwi;
int fd, needD[3]={AIR_TRUE, AIR_FALSE, AIR_FALSE};
/* tenDwiGageKindData *kindData; */
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4,
gageScl3PFilter6, gageScl3PFilter8};
unsigned int J, dwiNum;
fd = 2*ctx->radius;
dwi = pvl->directAnswer[tenDwiGageAll];
/* kindData = AIR_CAST( tenDwiGageKindData *, pvl->kind->data ); */
dwiNum = pvl->kind->valLen;
if ( !ctx->parm.k3pack ) {
fprintf( stderr, "%s: sorry, 6pack filtering not implemented\n", me );
return;
}
fw00 = ctx->fw + fd*3*gageKernel00;
fw11 = ctx->fw + fd*3*gageKernel11;
fw22 = ctx->fw + fd*3*gageKernel22;
/* HEY: these will have to be updated if there is ever any use for
derivatives in DWIs: can't pass NULL pointers for gradient info.
The unusual use of a hard-coded local needD is because there
currently isn't allocated space in the tenDwiGage kind ( which is
unusual for its dynamic allocation ) for DWI derivatives */
if ( fd <= 8 ) {
for ( J=0; J<dwiNum; J++ ) {
filter[ctx->radius]( ctx->shape, pvl->iv3 + J*fd*fd*fd,
pvl->iv2 + J*fd*fd,
pvl->iv1 + J*fd,
fw00, fw11, fw22,
dwi + J, NULL, NULL,
needD );
}
} else {
for ( J=0; J<dwiNum; J++ ) {
gageScl3PFilterN( ctx->shape, fd, pvl->iv3 + J*fd*fd*fd,
pvl->iv2 + J*fd*fd, pvl->iv1 + J*fd,
fw00, fw11, fw22,
dwi + J, NULL, NULL,
needD );
}
}
return;
}
/* Returns the Akaike Information Criterion */
/*
** residual: is the variance
** n: number of observations: number of DWI's in our case
** k: number of parameters: number of tensor components in our case
*/
double
252 _tenComputeAIC( double residual, int n, int k ) {
double AIC = 0;
if ( residual == 0 ) {
return 0;
}
/* AIC, RSS used when doing regression */
AIC = 2*k + n*log( residual );
/* Always use bias adjustment */
/* if ( n/k < 40 ) { */
AIC = AIC + ( ( 2*k*( k + 1 ) )/( n - k - 1 ) );
/* } */
return AIC;
}
/* Form a 2D tensor from the parameters */
void
271 _tenPeledRotate2D( double ten[7], double lam1, double lam3, double phi ) {
double cc, ss, d3, d1, d2;
cc = cos( phi );
ss = sin( phi );
d1 = cc*cc*lam1 + ss*ss*lam3;
d3 = cc*ss*( lam1 - lam3 );
d2 = ss*ss*lam1 + cc*cc*lam3;
TEN_T_SET( ten, 1.0, d1, d3, 0, d2, 0, lam3 );
return;
}
/* The main callback function that is iterated during levmar */
/* vector pp of parameters is as follows:
** pp[0]: principal eigenvalue
** pp[1]: fraction of 1st tensor
** pp[2]: phi for 1st tensor
** pp[3]: phi for 2nd tensor
*/
void
293 _tenLevmarPeledCB( double *pp, double *xx, int mm, int nn, void *_pvlData ) {
/* static const char me[]="_tenLevmarPeledCB"; */
double tenA[7], tenB[7];
int ii;
tenDwiGagePvlData *pvlData;
double *egrad;
AIR_UNUSED( mm );
pvlData = AIR_CAST( tenDwiGagePvlData *, _pvlData );
/* Form the tensors using the estimated parms */
_tenPeledRotate2D( tenA, pp[0], pvlData->ten1Eval[2], pp[2] );
_tenPeledRotate2D( tenB, pp[0], pvlData->ten1Eval[2], pp[3] );
egrad = AIR_CAST( double *, pvlData->nten1EigenGrads->data );
/* skip past b0 gradient, HEY: not general purpose */
egrad += 3;
for ( ii=0; ii<nn; ii++ ) {
double argA, argB, sigA, sigB;
argA = -pvlData->tec1->bValue*TEN_T3V_CONTR( tenA, egrad + 3*ii );
argB = -pvlData->tec1->bValue*TEN_T3V_CONTR( tenB, egrad + 3*ii );
if ( pvlData->levmarUseFastExp ) {
sigA = airFastExp( argA );
sigB = airFastExp( argB );
} else {
sigA = exp( argA );
sigB = exp( argB );
}
xx[ii] = pvlData->tec1->knownB0*( pp[1]*sigA + ( 1-pp[1] )*sigB );
}
return;
}
void
327 _tenDwiGageAnswer( gageContext *ctx, gagePerVolume *pvl ) {
static const char me[]="_tenDwiGageAnswer";
unsigned int dwiIdx;
tenDwiGageKindData *kindData;
tenDwiGagePvlData *pvlData;
double *dwiAll, dwiMean=0, tentmp[7];
kindData = AIR_CAST( tenDwiGageKindData *, pvl->kind->data );
pvlData = AIR_CAST( tenDwiGagePvlData *, pvl->data );
dwiAll = pvl->directAnswer[tenDwiGageAll];
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageAll ) ) {
/* done if doV */
if ( ctx->verbose ) {
for ( dwiIdx=0; dwiIdx<pvl->kind->valLen; dwiIdx++ ) {
fprintf( stderr, "%s( %d+%g, %d+%g, %d+%g ): dwi[%u] = %g\n", me,
ctx->point.idx[0], ctx->point.frac[0],
ctx->point.idx[1], ctx->point.frac[1],
ctx->point.idx[2], ctx->point.frac[2],
dwiIdx, dwiAll[dwiIdx] );
}
fprintf( stderr, "%s: type( ngrad ) = %d = %s\n", me,
kindData->ngrad->type,
airEnumStr( nrrdType, kindData->ngrad->type ) );
}
}
/*
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageB0 ) ) {
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageJustDWI ) ) {
done if doV
}
*/
/* HEY this isn't valid for multiple b-values */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageADC ) ) {
double logdwi, logb0;
logb0 = log( AIR_MAX( kindData->valueMin,
pvl->directAnswer[tenDwiGageB0][0] ) );
for ( dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++ ) {
logdwi = log( AIR_MAX( kindData->valueMin,
pvl->directAnswer[tenDwiGageJustDWI][dwiIdx-1] ) );
pvl->directAnswer[tenDwiGageADC][dwiIdx-1]
= ( logb0 - logdwi )/kindData->bval;
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageMeanDWIValue ) ) {
dwiMean = 0;
for ( dwiIdx=1; dwiIdx<pvl->kind->valLen; dwiIdx++ ) {
dwiMean += dwiAll[dwiIdx];
}
dwiMean /= pvl->kind->valLen;
pvl->directAnswer[tenDwiGageMeanDWIValue][0] = dwiMean;
}
/* note: the gage interface to tenEstimate functionality
allows you exactly one kind of tensor estimation ( per kind ),
so the function call to do the estimation is actually
repeated over and over again; the copy into the answer
buffer is what changes... */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLS ) ) {
tenEstimate1TensorSingle_d( pvlData->tec1, tentmp, dwiAll );
TEN_T_COPY( pvl->directAnswer[tenDwiGageTensorLLS], tentmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLSError ) ) {
pvl->directAnswer[tenDwiGageTensorLLSError][0] = pvlData->tec1->errorDwi;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLSErrorLog ) ) {
pvl->directAnswer[tenDwiGageTensorLLSErrorLog][0]
= pvlData->tec1->errorLogDwi;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorWLS ) ) {
tenEstimate1TensorSingle_d( pvlData->tec1, tentmp, dwiAll );
TEN_T_COPY( pvl->directAnswer[tenDwiGageTensorWLS], tentmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorNLS ) ) {
tenEstimate1TensorSingle_d( pvlData->tec1, tentmp, dwiAll );
TEN_T_COPY( pvl->directAnswer[tenDwiGageTensorNLS], tentmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorMLE ) ) {
tenEstimate1TensorSingle_d( pvlData->tec1, tentmp, dwiAll );
TEN_T_COPY( pvl->directAnswer[tenDwiGageTensorMLE], tentmp );
}
/* HEY: have to implement all the different kinds of errors */
/* BEGIN sneakiness ........ */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensor ) ) {
gageItemEntry *item;
item = pvl->kind->table + tenDwiGageTensor;
TEN_T_COPY( pvl->directAnswer[tenDwiGageTensor],
pvl->directAnswer[item->prereq[0]] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorError ) ) {
gageItemEntry *item;
item = pvl->kind->table + tenDwiGageTensorError;
pvl->directAnswer[tenDwiGageTensorError][0]
= pvl->directAnswer[item->prereq[0]][0];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorErrorLog ) ) {
gageItemEntry *item;
item = pvl->kind->table + tenDwiGageTensorErrorLog;
pvl->directAnswer[tenDwiGageTensorErrorLog][0]
= pvl->directAnswer[item->prereq[0]][0];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLikelihood ) ) {
gageItemEntry *item;
item = pvl->kind->table + tenDwiGageTensorLikelihood;
pvl->directAnswer[tenDwiGageTensorLikelihood][0]
= pvl->directAnswer[item->prereq[0]][0];
}
/* END sneakiness ........ */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageFA ) ) {
pvl->directAnswer[tenDwiGageFA][0]
= pvl->directAnswer[tenDwiGageTensor][0]
* tenAnisoTen_d( pvl->directAnswer[tenDwiGageTensor],
tenAniso_FA );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorAllDWIError ) ) {
const double *grads;
int gradcount;
double *ten, d;
int i;
/* HEY: should switch to tenEstimate-based DWI simulation */
ten = pvl->directAnswer[tenDwiGageTensor];
gradcount = pvl->kind->valLen -1; /* Dont count b0 */
grads = ( ( const double* ) kindData->ngrad->data ) +3; /* Ignore b0 grad */
for( i=0; i < gradcount; i++ ) {
d = dwiAll[0]*exp( - pvlData->tec1->bValue
* TEN_T3V_CONTR( ten, grads + 3*i ) );
pvl->directAnswer[tenDwiGageTensorAllDWIError][i] = dwiAll[i+1] - d;
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorQSeg ) ) {
const double *grads;
int gradcount;
double *twoten;
unsigned int valIdx, E;
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg];
gradcount = pvl->kind->valLen -1; /* Dont count b0 */
grads = ( ( const double* ) kindData->ngrad->data ) +3; /* Ignore b0 grad */
if ( dwiAll[0] != 0 ) { /* S0 = 0 */
_tenQball( pvlData->tec2->bValue, gradcount, dwiAll, grads,
pvlData->qvals );
_tenQvals2points( gradcount, pvlData->qvals, grads, pvlData->qpoints );
_tenSegsamp2( gradcount, pvlData->qvals, grads, pvlData->qpoints,
pvlData->wght + 1, pvlData->dists );
} else {
/* stupid; should really return right here since data is garbage */
for ( valIdx=1; valIdx < AIR_CAST( unsigned int, gradcount+1 ); valIdx++ ) {
pvlData->wght[valIdx] = valIdx % 2;
}
}
E = 0;
for ( valIdx=1; valIdx<pvl->kind->valLen; valIdx++ ) {
if ( !E ) E |= tenEstimateSkipSet( pvlData->tec2, valIdx,
pvlData->wght[valIdx] );
}
if ( !E ) E |= tenEstimateUpdate( pvlData->tec2 );
if ( !E ) E |= tenEstimate1TensorSingle_d( pvlData->tec2,
twoten + 0, dwiAll );
for ( valIdx=1; valIdx<pvl->kind->valLen; valIdx++ ) {
if ( !E ) E |= tenEstimateSkipSet( pvlData->tec2, valIdx,
1 - pvlData->wght[valIdx] );
}
if ( !E ) E |= tenEstimateUpdate( pvlData->tec2 );
if ( !E ) E |= tenEstimate1TensorSingle_d( pvlData->tec2,
twoten + 7, dwiAll );
if ( E ) {
char *terr;
terr = biffGetDone( TEN );
fprintf( stderr, "%s: ( trouble ) %s\n", me, terr );
free( terr );
}
/* hack: confidence for two-tensor fit */
twoten[0] = ( twoten[0] + twoten[7] )/2;
twoten[7] = 0.5; /* fraction that is the first tensor ( initial value ) */
/* twoten[1 .. 6] = first tensor */
/* twoten[8 .. 13] = second tensor */
/* Compute fraction between tensors if not garbage in this voxel */
if ( twoten[0] > 0.5 ) {
double exp0, exp1, d, e=0, g=0, a=0, b=0;
int i;
for( i=0; i < gradcount; i++ ) {
exp0 = exp( -pvlData->tec2->bValue * TEN_T3V_CONTR( twoten + 0,
grads + 3*i ) );
exp1 = exp( -pvlData->tec2->bValue * TEN_T3V_CONTR( twoten + 7,
grads + 3*i ) );
d = dwiAll[i+1] / dwiAll[0];
e = exp0 - exp1;
g = d - exp1;
a += .5*e*e;
b += e*g;
}
twoten[7] = AIR_CLAMP( 0, 0.5*( b/a ), 1 );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorQSegError ) ) {
const double *grads;
int gradcount;
double *twoten, d;
int i;
/* HEY: should switch to tenEstimate-based DWI simulation */
if ( dwiAll[0] != 0 ) { /* S0 = 0 */
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg];
gradcount = pvl->kind->valLen -1; /* Dont count b0 */
grads = ( ( const double* ) kindData->ngrad->data ) +3; /* Ignore b0 grad */
pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0;
for( i=0; i < gradcount; i++ ) {
d = twoten[7]*exp( -pvlData->tec2->bValue * TEN_T3V_CONTR( twoten + 0,
grads + 3*i ) );
d += ( 1 - twoten[7] )*exp( -pvlData->tec2->bValue
*TEN_T3V_CONTR( twoten + 7, grads + 3*i ) );
d = dwiAll[i+1]/dwiAll[0] - d;
pvl->directAnswer[tenDwiGage2TensorQSegError][0] += d*d;
}
pvl->directAnswer[tenDwiGage2TensorQSegError][0] =
sqrt( pvl->directAnswer[tenDwiGage2TensorQSegError][0] );
} else {
/* HEY: COMPLETELY WRONG!! An error is not defined! */
pvl->directAnswer[tenDwiGage2TensorQSegError][0] = 0;
}
/* printf( "%f\n", pvl->directAnswer[tenDwiGage2TensorQSegError][0] ); */
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorQSegAndError ) ) {
double *twoten, *err, *twotenerr;
twoten = pvl->directAnswer[tenDwiGage2TensorQSeg];
err = pvl->directAnswer[tenDwiGage2TensorQSegError];
twotenerr = pvl->directAnswer[tenDwiGage2TensorQSegAndError];
TEN_T_COPY( twotenerr + 0, twoten + 0 );
TEN_T_COPY( twotenerr + 7, twoten + 7 );
twotenerr[14] = err[0];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorPeled ) ) {
#if TEEM_LEVMAR
#define PARAMS 4
double *twoTen, Cp /* , residual, AICSingFit, AICTwoFit */;
/* Vars for the NLLS */
double guess[PARAMS], loBnd[PARAMS], upBnd[PARAMS],
opts[LM_OPTS_SZ], *grad, *egrad, tenA[7], tenB[7],
matA[9], matB[9], matTmp[9], rott[9];
unsigned int gi;
int lmret;
/* Pointer to the location where the two tensor will be written */
twoTen = pvl->directAnswer[tenDwiGage2TensorPeled];
/* Estimate the DWI error, error is given as standard deviation */
pvlData->tec1->recordErrorDwi = AIR_FALSE;
/* Estimate the single tensor */
tenEstimate1TensorSingle_d( pvlData->tec1, pvlData->ten1, dwiAll );
/* Get the eigenValues and eigen vectors for this tensor */
tenEigensolve_d( pvlData->ten1Eval, pvlData->ten1Evec, pvlData->ten1 );
/* Get westins Cp */
Cp = tenAnisoEval_d( pvlData->ten1Eval, tenAniso_Cp1 );
/* Only do two-tensor fitting if CP is greater or equal to than a
user-defined threshold */
if ( Cp >= pvlData->levmarMinCp ) {
/* Calculate the residual, need the variance to sqr it */
/* residual = pvlData->tec1->errorDwi*pvlData->tec1->errorDwi; */
/* Calculate the AIC for single tensor fit */
/* AICSingFit = _tenComputeAIC( residual, pvlData->tec1->dwiNum, 6 ); */
/* the CP-based test is gone; caller's responsibility */
/* rotate DW gradients by inverse of eigenvector column matrix
and place into pvlData->nten1EigenGrads ( which has been
allocated by _tenDwiGagePvlDataNew( ) ) */
grad = AIR_CAST( double *, kindData->ngrad->data );
egrad = AIR_CAST( double *, pvlData->nten1EigenGrads->data );
for ( gi=0; gi<kindData->ngrad->axis[1].size; gi++ ) {
/* yes, this is also transforming some zero-length ( B0 ) gradients;
that's harmless */
ELL_3MV_MUL( egrad, pvlData->ten1Evec, grad );
grad += 3;
egrad += 3;
}
/* Lower and upper bounds for the NLLS routine */
loBnd[0] = 0.0;
loBnd[1] = 0.0;
loBnd[2] = -AIR_PI/2;
loBnd[3] = -AIR_PI/2;
upBnd[0] = pvlData->ten1Eval[0]*5;
upBnd[1] = 1.0;
upBnd[2] = AIR_PI/2;
upBnd[3] = AIR_PI/2;
/* Starting point for the NLLS */
guess[0] = pvlData->ten1Eval[0];
guess[1] = 0.5;
guess[2] = AIR_PI/4;
guess[3] = -AIR_PI/4;
/*
guess[2] = AIR_AFFINE( 0, airDrandMT_r( pvlData->randState ), 1,
AIR_PI/6, AIR_PI/3 );
guess[3] = AIR_AFFINE( 0, airDrandMT_r( pvlData->randState ), 1,
-AIR_PI/6, -AIR_PI/3 );
*/
/* Fill in the constraints for the LM optimization,
the threshold of error difference */
opts[0] = pvlData->levmarTau;
opts[1] = pvlData->levmarEps1;
opts[2] = pvlData->levmarEps2;
opts[3] = pvlData->levmarEps3;
/* Very imp to set this opt, note that only forward
differences are used to approx Jacobian */
opts[4] = pvlData->levmarDelta;
/* run NLLS, results are stored back into guess[] */
pvlData->levmarUseFastExp = AIR_FALSE;
lmret = dlevmar_bc_dif( _tenLevmarPeledCB, guess, pvlData->tec1->dwi,
PARAMS, pvlData->tec1->dwiNum, loBnd, upBnd,
NULL, pvlData->levmarMaxIter, opts,
pvlData->levmarInfo,
NULL, NULL, pvlData );
if ( -1 == lmret ) {
ctx->errNum = 1;
sprintf( ctx->errStr, "%s: dlevmar_bc_dif( ) failed!", me );
} else {
/* Get the AIC for the two tensor fit, use the levmarinfo
to get the residual */
/*
residual = pvlData->levmarInfo[1]/pvlData->tec1->dwiNum;
AICTwoFit = _tenComputeAIC( residual, pvlData->tec1->dwiNum, 12 );
*/
/* Form the tensors using the estimated pp, returned in guess */
_tenPeledRotate2D( tenA, guess[0], pvlData->ten1Eval[2], guess[2] );
_tenPeledRotate2D( tenB, guess[0], pvlData->ten1Eval[2], guess[3] );
TEN_T2M( matA, tenA );
TEN_T2M( matB, tenB );
ELL_3M_TRANSPOSE( rott, pvlData->ten1Evec );
ELL_3M_MUL( matTmp, matA, pvlData->ten1Evec );
ELL_3M_MUL( matA, rott, matTmp );
ELL_3M_MUL( matTmp, matB, pvlData->ten1Evec );
ELL_3M_MUL( matB, rott, matTmp );
/* Copy two two tensors */
/* guess[1] is population fraction of first tensor */
if ( guess[1] > 0.5 ) {
twoTen[7] = guess[1];
TEN_M2T( twoTen + 0, matA );
TEN_M2T( twoTen + 7, matB );
} else {
twoTen[7] = 1 - guess[1];
TEN_M2T( twoTen + 0, matB );
TEN_M2T( twoTen + 7, matA );
}
twoTen[0] = 1;
}
} else {
/* its too planar- just do single tensor fit */
TEN_T_COPY( twoTen, pvlData->ten1 );
TEN_T_SET( twoTen + 7, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 );
}
#undef PARAMS
#else
double *twoTen;
twoTen = pvl->directAnswer[tenDwiGage2TensorPeled];
TEN_T_SET( twoTen + 0, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
TEN_T_SET( twoTen + 7, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
fprintf( stderr, "%s: sorry, not compiled with TEEM_LEVMAR\n", me );
#endif
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorPeledError ) ) {
double *info;
info = pvlData->levmarInfo;
pvl->directAnswer[tenDwiGage2TensorPeledError][0] = 0;
if ( info[1] > 0 ) {
/* Returning the standard deviation */
pvl->directAnswer[tenDwiGage2TensorPeledError][0] =
sqrt( info[1]/pvlData->tec1->dwiNum );
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorPeledAndError ) ) {
double *twoten, *err, *twotenerr;
/* HEY cut and paste */
twoten = pvl->directAnswer[tenDwiGage2TensorPeled];
err = pvl->directAnswer[tenDwiGage2TensorPeledError];
twotenerr = pvl->directAnswer[tenDwiGage2TensorPeledAndError];
TEN_T_COPY( twotenerr + 0, twoten + 0 );
TEN_T_COPY( twotenerr + 7, twoten + 7 );
twotenerr[14] = err[0];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGage2TensorPeledLevmarInfo ) ) {
double *info;
unsigned int ii, alen;
alen = gageKindAnswerLength( pvl->kind, tenDwiGage2TensorPeledLevmarInfo );
info = pvl->directAnswer[tenDwiGage2TensorPeledLevmarInfo];
for ( ii=0; ii<alen; ii++ ) {
info[ii] = pvlData->levmarInfo[ii];
}
}
return;
}
/* --------------------- pvlData */
/* note use of the GAGE biff key */
void *
748 _tenDwiGagePvlDataNew( const gageKind *kind ) {
static const char me[]="_tenDwiGagePvlDataNew";
tenDwiGagePvlData *pvlData;
tenDwiGageKindData *kindData;
const int segcount = 2;
unsigned int num;
int E;
if ( tenDwiGageKindCheck( kind ) ) {
biffMovef( GAGE, TEN, "%s: kindData not ready for use", me );
return NULL;
}
kindData = AIR_CAST( tenDwiGageKindData *, kind->data );
pvlData = AIR_CALLOC( 1, tenDwiGagePvlData );
if ( !pvlData ) {
biffAddf( GAGE, "%s: couldn't allocate pvl data!", me );
return NULL;
}
pvlData->tec1 = tenEstimateContextNew( );
pvlData->tec2 = tenEstimateContextNew( );
for ( num=1; num<=2; num++ ) {
tenEstimateContext *tec;
tec = ( 1 == num ? pvlData->tec1 : pvlData->tec2 );
E = 0;
if ( !E ) tenEstimateVerboseSet( tec, 0 );
if ( !E ) tenEstimateNegEvalShiftSet( tec, AIR_FALSE );
if ( !E ) E |= tenEstimateMethodSet( tec, 1 == num
? kindData->est1Method
: kindData->est2Method );
if ( !E ) E |= tenEstimateValueMinSet( tec, kindData->valueMin );
if ( kindData->ngrad->data ) {
if ( !E ) E |= tenEstimateGradientsSet( tec, kindData->ngrad,
kindData->bval, AIR_FALSE );
} else {
if ( !E ) E |= tenEstimateBMatricesSet( tec, kindData->nbmat,
kindData->bval, AIR_FALSE );
}
if ( !E ) E |= tenEstimateThresholdSet( tec,
kindData->thresh, kindData->soft );
if ( !E ) E |= tenEstimateUpdate( tec );
if ( E ) {
biffMovef( GAGE, TEN, "%s: trouble setting %u estimation", me, num );
return NULL;
}
}
pvlData->vbuf = AIR_CALLOC( kind->valLen, double );
pvlData->wght = AIR_CALLOC( kind->valLen, unsigned int );
/* HEY: this is where we act on the the assumption about
having val[0] be T2 baseline and all subsequent val[i] be DWIs */
pvlData->wght[0] = 1;
pvlData->qvals = AIR_CALLOC( kind->valLen-1, double );
pvlData->qpoints = AIR_CALLOC( 3*( kind->valLen-1 ), double );
pvlData->dists = AIR_CALLOC( segcount*( kind->valLen-1 ), double );
pvlData->weights = AIR_CALLOC( segcount*( kind->valLen-1 ), double );
if ( kindData->ngrad->data ) {
pvlData->nten1EigenGrads = nrrdNew( );
/* this is for allocation only; values will get over-written */
nrrdCopy( pvlData->nten1EigenGrads, kindData->ngrad );
} else {
/* HEY: currently don't handle general B-matrices here */
pvlData->nten1EigenGrads = NULL;
}
pvlData->randSeed = kindData->randSeed;
pvlData->randState = airRandMTStateNew( pvlData->randSeed );
/* initialize single-tensor info to all NaNs */
TEN_T_SET( pvlData->ten1, AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN,
AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pvlData->ten1Evec + 0, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pvlData->ten1Evec + 3, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pvlData->ten1Evec + 6, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( pvlData->ten1Eval, AIR_NAN, AIR_NAN, AIR_NAN );
/* here's an okay spot to check our compile-time assumptions
about the levmar library */
#if TEEM_LEVMAR
/* this is needed to make sure that the tenDwiGage2TensorPeledLevmarInfo
item definition above is valid */
if ( 5 != LM_OPTS_SZ ) {
biffAddf( GAGE, "%s: LM_OPTS_SZ ( %d ) != expected 5\n", me, LM_OPTS_SZ );
return NULL;
}
#endif
pvlData->levmarUseFastExp = AIR_FALSE;
pvlData->levmarMaxIter = 200;
pvlData->levmarTau = 1E-03; /* LM_INIT_MU; */
pvlData->levmarEps1 = 1E-8;
pvlData->levmarEps2 = 1E-8;
pvlData->levmarEps3 = 1E-8;
pvlData->levmarDelta = 1E-8;
pvlData->levmarMinCp = 0.1;
/* pvlData->levmarInfo[] is output; not initialized */
return AIR_CAST( void *, pvlData );
}
void *
849 _tenDwiGagePvlDataCopy( const gageKind *kind, const void *_pvlDataOld ) {
tenDwiGagePvlData *pvlDataOld, *pvlDataNew;
pvlDataOld = AIR_CAST( tenDwiGagePvlData *, _pvlDataOld );
pvlDataNew = AIR_CAST( tenDwiGagePvlData *, _tenDwiGagePvlDataNew( kind ) );
/* HEY: no error checking? */
if ( pvlDataOld->nten1EigenGrads ) {
nrrdCopy( pvlDataNew->nten1EigenGrads, pvlDataOld->nten1EigenGrads );
}
/* need to copy randState or randSeed? */
TEN_T_COPY( pvlDataNew->ten1, pvlDataOld->ten1 );
ELL_3M_COPY( pvlDataNew->ten1Evec, pvlDataOld->ten1Evec );
ELL_3V_COPY( pvlDataNew->ten1Eval, pvlDataOld->ten1Eval );
pvlDataNew->levmarUseFastExp = pvlDataOld->levmarUseFastExp;
pvlDataNew->levmarMaxIter = pvlDataOld->levmarMaxIter;
pvlDataNew->levmarTau = pvlDataOld->levmarTau;
pvlDataNew->levmarEps1 = pvlDataOld->levmarEps1;
pvlDataNew->levmarEps2 = pvlDataOld->levmarEps2;
pvlDataNew->levmarEps3 = pvlDataOld->levmarEps3;
pvlDataNew->levmarDelta = pvlDataOld->levmarDelta;
pvlDataNew->levmarMinCp = pvlDataOld->levmarMinCp;
/* pvlData->levmarInfo[] is output; not copied */
return pvlDataNew;
}
int
879 _tenDwiGagePvlDataUpdate( const gageKind *kind,
const gageContext *ctx,
const gagePerVolume *pvl, const void *_pvlData ) {
/* static const char me[]="_tenDwiGagePvlDataUpdate"; */
tenDwiGagePvlData *pvlData;
AIR_UNUSED( ctx );
pvlData = AIR_CAST( tenDwiGagePvlData *, _pvlData );
AIR_UNUSED( kind );
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLSError )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorWLSError )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorNLSError )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorMLEError ) ) {
pvlData->tec1->recordErrorDwi = AIR_TRUE;
} else {
pvlData->tec1->recordErrorDwi = AIR_FALSE;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLSErrorLog )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorWLSErrorLog )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorNLSErrorLog )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorMLEErrorLog ) ) {
pvlData->tec1->recordErrorLogDwi = AIR_TRUE;
} else {
pvlData->tec1->recordErrorLogDwi = AIR_FALSE;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorLLSLikelihood )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorWLSLikelihood )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorNLSLikelihood )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenDwiGageTensorMLELikelihood ) ) {
pvlData->tec1->recordLikelihoodDwi = AIR_TRUE;
} else {
pvlData->tec1->recordLikelihoodDwi = AIR_FALSE;
}
/*
fprintf( stderr, "%s: record %d %d %d\n", me,
pvlData->tec1->recordErrorDwi,
pvlData->tec1->recordErrorLogDwi,
pvlData->tec1->recordLikelihoodDwi );
*/
return 0;
}
void *
922 _tenDwiGagePvlDataNix( const gageKind *kind, void *_pvlData ) {
tenDwiGagePvlData *pvlData;
AIR_UNUSED( kind );
pvlData = AIR_CAST( tenDwiGagePvlData *, _pvlData );
if ( pvlData ) {
tenEstimateContextNix( pvlData->tec1 );
tenEstimateContextNix( pvlData->tec2 );
airFree( pvlData->vbuf );
airFree( pvlData->wght );
airFree( pvlData->qvals );
airFree( pvlData->qpoints );
airFree( pvlData->dists );
airFree( pvlData->weights );
nrrdNuke( pvlData->nten1EigenGrads );
airRandMTStateNix( pvlData->randState );
airFree( pvlData );
}
return NULL;
}
/* --------------------- kindData */
tenDwiGageKindData*
946 tenDwiGageKindDataNew( void ) {
tenDwiGageKindData *ret;
ret = AIR_CALLOC( 1, tenDwiGageKindData );
if ( ret ) {
/* it may be that only one of these is actually filled */
ret->ngrad = nrrdNew( );
ret->nbmat = nrrdNew( );
ret->thresh = ret->soft = ret->bval = AIR_NAN;
ret->est1Method = tenEstimate1MethodUnknown;
ret->est2Method = tenEstimate2MethodUnknown;
ret->randSeed = 42;
}
return ret;
}
tenDwiGageKindData*
963 tenDwiGageKindDataNix( tenDwiGageKindData *kindData ) {
if ( kindData ) {
nrrdNuke( kindData->ngrad );
nrrdNuke( kindData->nbmat );
airFree( kindData );
}
return NULL;
}
/* --------------------- dwiKind, and dwiKind->data setting*/
/*
** Because this kind has to be dynamically allocated,
** this is not the kind, but just the template for it
** HEY: having a const public version of this could be a
** nice way of having a way of referring to the dwiKind
** without having to allocate it each time
*/
gageKind
_tenDwiGageKindTmpl = {
AIR_TRUE, /* dynamically allocated */
TEN_DWI_GAGE_KIND_NAME,
&_tenDwiGage,
1,
0, /* NOT: set later by tenDwiGageKindSet( ) */
TEN_DWI_GAGE_ITEM_MAX,
NULL, /* NOT: modified copy of _tenDwiGageTable,
allocated by tenDwiGageKindNew( ), and
set by _tenDwiGageKindSet( ) */
_tenDwiGageIv3Print,
_tenDwiGageFilter,
_tenDwiGageAnswer,
_tenDwiGagePvlDataNew,
_tenDwiGagePvlDataCopy,
_tenDwiGagePvlDataNix,
_tenDwiGagePvlDataUpdate,
NULL /* NOT: allocated by tenDwiGageKindNew( ),
insides set by tenDwiGageKindSet( ) */
};
gageKind *
1005 tenDwiGageKindNew( ) {
gageKind *kind;
kind = AIR_CALLOC( 1, gageKind );
if ( kind ) {
memcpy( kind, &_tenDwiGageKindTmpl, sizeof( gageKind ) );
kind->valLen = 0; /* still has to be set later */
kind->table = AIR_CAST( gageItemEntry *,
malloc( sizeof( _tenDwiGageTable ) ) );
memcpy( kind->table, _tenDwiGageTable, sizeof( _tenDwiGageTable ) );
kind->data = AIR_CAST( void *, tenDwiGageKindDataNew( ) );
}
return kind;
}
gageKind *
1021 tenDwiGageKindNix( gageKind *kind ) {
if ( kind ) {
airFree( kind->table );
tenDwiGageKindDataNix( AIR_CAST( tenDwiGageKindData *, kind->data ) );
airFree( kind );
}
return NULL;
}
/*
** NOTE: this sets information in both the kind and kindData
*/
int
1035 tenDwiGageKindSet( gageKind *dwiKind,
double thresh, double soft, double bval, double valueMin,
const Nrrd *ngrad,
const Nrrd *nbmat,
int e1method, int e2method,
unsigned int randSeed ) {
static const char me[]="tenDwiGageKindSet";
tenDwiGageKindData *kindData;
double grad[3], ( *lup )( const void *, size_t );
unsigned int gi;
if ( !dwiKind ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 0;
}
if ( !( !!( ngrad ) ^ !!( nbmat ) ) ) {
biffAddf( TEN, "%s: need exactly one non-NULL in {ngrad, nbmat}", me );
return 1;
}
if ( nbmat ) {
biffAddf( TEN, "%s: sorry, B-matrices temporarily disabled", me );
return 1;
}
/* ( used for detecting errors in losslessly writing/reading
a gradient set )
{
fprintf( stderr, "!%s: saving ngrad.nrrd\n", me );
if ( ngrad ) {
nrrdSave( "ngrad.nrrd", ngrad, NULL );
}
}
*/
if ( tenGradientCheck( ngrad, nrrdTypeDefault, 7 ) ) {
biffAddf( TEN, "%s: problem with given gradients", me );
return 1;
}
/* make sure that gradient lengths are as expected */
lup = nrrdDLookup[ngrad->type];
grad[0] = lup( ngrad->data, 0 );
grad[1] = lup( ngrad->data, 1 );
grad[2] = lup( ngrad->data, 2 );
if ( 0.0 != ELL_3V_LEN( grad ) ) {
biffAddf( TEN, "%s: sorry, currently need grad[0] to be len 0 ( not %g )",
me, ELL_3V_LEN( grad ) );
return 1;
}
for ( gi=1; gi<ngrad->axis[1].size; gi++ ) {
grad[0] = lup( ngrad->data, 0 + 3*gi );
grad[1] = lup( ngrad->data, 1 + 3*gi );
grad[2] = lup( ngrad->data, 2 + 3*gi );
if ( 0.0 == ELL_3V_LEN( grad ) ) {
biffAddf( TEN, "%s: sorry, all but first gradient must be non-zero "
"( %u is zero )", me, gi );
return 1;
}
}
if ( airEnumValCheck( tenEstimate1Method, e1method ) ) {
biffAddf( TEN, "%s: e1method %d is not a valid %s", me,
e1method, tenEstimate1Method->name );
return 1;
}
if ( airEnumValCheck( tenEstimate2Method, e2method ) ) {
biffAddf( TEN, "%s: emethod %d is not a valid %s", me,
e2method, tenEstimate2Method->name );
return 1;
}
kindData = AIR_CAST( tenDwiGageKindData *, dwiKind->data );
if ( nrrdConvert( kindData->ngrad, ngrad, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: trouble converting", me );
return 1;
}
dwiKind->valLen = kindData->ngrad->axis[1].size;
/* fixing up the item table ... */
dwiKind->table[tenDwiGageAll].answerLength = dwiKind->valLen;
dwiKind->table[tenDwiGageJustDWI].answerLength = dwiKind->valLen - 1;
dwiKind->table[tenDwiGageADC].answerLength = dwiKind->valLen - 1;
dwiKind->table[tenDwiGageTensorAllDWIError].answerLength =
dwiKind->valLen - 1;
switch ( e1method ) {
case tenEstimate1MethodLLS:
dwiKind->table[tenDwiGageTensor].prereq[0]
= tenDwiGageTensorLLS;
dwiKind->table[tenDwiGageTensorError].prereq[0]
= tenDwiGageTensorLLSError;
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0]
= tenDwiGageTensorLLSErrorLog;
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0]
= tenDwiGageTensorLLSLikelihood;
break;
case tenEstimate1MethodWLS:
dwiKind->table[tenDwiGageTensor].prereq[0]
= tenDwiGageTensorWLS;
dwiKind->table[tenDwiGageTensorError].prereq[0]
= tenDwiGageTensorWLSError;
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0]
= tenDwiGageTensorWLSErrorLog;
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0]
= tenDwiGageTensorWLSLikelihood;
break;
case tenEstimate1MethodNLS:
dwiKind->table[tenDwiGageTensor].prereq[0]
= tenDwiGageTensorNLS;
dwiKind->table[tenDwiGageTensorError].prereq[0]
= tenDwiGageTensorNLSError;
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0]
= tenDwiGageTensorNLSErrorLog;
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0]
= tenDwiGageTensorNLSLikelihood;
break;
case tenEstimate1MethodMLE:
dwiKind->table[tenDwiGageTensor].prereq[0]
= tenDwiGageTensorMLE;
dwiKind->table[tenDwiGageTensorError].prereq[0]
= tenDwiGageTensorMLEError;
dwiKind->table[tenDwiGageTensorErrorLog].prereq[0]
= tenDwiGageTensorMLEErrorLog;
dwiKind->table[tenDwiGageTensorLikelihood].prereq[0]
= tenDwiGageTensorMLELikelihood;
break;
default:
biffAddf( TEN, "%s: unimplemented %s: %s ( %d )", me,
tenEstimate1Method->name,
airEnumStr( tenEstimate1Method, e1method ), e1method );
return 1;
break;
}
kindData->thresh = thresh;
kindData->soft = soft;
kindData->bval = bval;
kindData->valueMin = valueMin;
kindData->est1Method = e1method;
kindData->est2Method = e2method;
kindData->randSeed = randSeed;
return 0;
}
int
1175 tenDwiGageKindCheck( const gageKind *kind ) {
static const char me[]="tenDwiGageKindCheck";
if ( !kind ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( strcmp( kind->name, TEN_DWI_GAGE_KIND_NAME ) ) {
biffAddf( TEN, "%s: got \"%s\" kind, not \"%s\"", me,
kind->name, TEN_DWI_GAGE_KIND_NAME );
return 1;
}
if ( 0 == kind->valLen ) {
biffAddf( TEN, "%s: don't yet know valLen", me );
return 1;
}
if ( !kind->data ) {
biffAddf( TEN, "%s: kind->data is NULL", me );
return 1;
}
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
typedef struct {
double *buffTen, *buffWght;
tenInterpParm *tip; /* sneakiness: using tip->allocLen to record
allocation sizes of buffTen and buffWght, too */
} _tenGagePvlData;
gageItemEntry
_tenGageTable[TEN_GAGE_ITEM_MAX+1] = {
/* enum value len, deriv, prereqs, parent item, parent index, needData */
{tenGageUnknown, 0, 0, {0}, 0, 0, AIR_FALSE},
{tenGageTensor, 7, 0, {0}, 0, 0, AIR_FALSE},
{tenGageConfidence, 1, 0, {tenGageTensor}, tenGageTensor, 0, AIR_FALSE},
{tenGageTrace, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageNorm, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageB, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageDet, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageS, 1, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageQ, 1, 0, {tenGageS, tenGageB}, 0, 0, AIR_FALSE},
{tenGageFA, 1, 0, {tenGageQ, tenGageS}, 0, 0, AIR_FALSE},
{tenGageR, 1, 0, {tenGageTrace, tenGageB, tenGageDet, tenGageS}, 0, 0, AIR_FALSE},
{tenGageMode, 1, 0, {tenGageR, tenGageQ}, 0, 0, AIR_FALSE},
{tenGageTheta, 1, 0, {tenGageMode}, 0, 0, AIR_FALSE},
{tenGageModeWarp, 1, 0, {tenGageMode}, 0, 0, AIR_FALSE},
{tenGageOmega, 1, 0, {tenGageFA, tenGageMode}, 0, 0, AIR_FALSE},
{tenGageEval, 3, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageEval0, 1, 0, {tenGageEval}, tenGageEval, 0, AIR_FALSE},
{tenGageEval1, 1, 0, {tenGageEval}, tenGageEval, 1, AIR_FALSE},
{tenGageEval2, 1, 0, {tenGageEval}, tenGageEval, 2, AIR_FALSE},
{tenGageEvec, 9, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageEvec0, 3, 0, {tenGageEvec}, tenGageEvec, 0, AIR_FALSE},
{tenGageEvec1, 3, 0, {tenGageEvec}, tenGageEvec, 3, AIR_FALSE},
{tenGageEvec2, 3, 0, {tenGageEvec}, tenGageEvec, 6, AIR_FALSE},
{tenGageDelNormK2, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageDelNormK3, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageDelNormR1, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageDelNormR2, 7, 0, {tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageDelNormPhi1, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE},
{tenGageDelNormPhi2, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE},
{tenGageDelNormPhi3, 7, 0, {tenGageEvec}, 0, 0, AIR_FALSE},
{tenGageTensorGrad, 21, 1, {0}, 0, 0, AIR_FALSE},
{tenGageTensorGradMag, 3, 1, {tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageTensorGradMagMag, 1, 1, {tenGageTensorGradMag}, 0, 0, AIR_FALSE},
{tenGageTraceGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageTraceGradMag, 1, 1, {tenGageTraceGradVec}, 0, 0, AIR_FALSE},
{tenGageTraceNormal, 3, 1, {tenGageTraceGradVec, tenGageTraceGradMag}, 0, 0, AIR_FALSE},
{tenGageNormGradVec, 3, 1, {tenGageNorm, tenGageSGradVec}, 0, 0, AIR_FALSE},
{tenGageNormGradMag, 1, 1, {tenGageNormGradVec}, 0, 0, AIR_FALSE},
{tenGageNormNormal, 3, 1, {tenGageNormGradVec, tenGageNormGradMag}, 0, 0, AIR_FALSE},
{tenGageBGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageBGradMag, 1, 1, {tenGageBGradVec}, 0, 0, AIR_FALSE},
{tenGageBNormal, 3, 1, {tenGageBGradVec, tenGageBGradMag}, 0, 0, AIR_FALSE},
{tenGageDetGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageDetGradMag, 1, 1, {tenGageDetGradVec}, 0, 0, AIR_FALSE},
{tenGageDetNormal, 3, 1, {tenGageDetGradVec, tenGageDetGradMag}, 0, 0, AIR_FALSE},
{tenGageSGradVec, 3, 1, {tenGageTensor, tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageSGradMag, 1, 1, {tenGageSGradVec}, 0, 0, AIR_FALSE},
{tenGageSNormal, 3, 1, {tenGageSGradVec, tenGageSGradMag}, 0, 0, AIR_FALSE},
{tenGageQGradVec, 3, 1, {tenGageSGradVec, tenGageBGradVec}, 0, 0, AIR_FALSE},
{tenGageQGradMag, 1, 1, {tenGageQGradVec}, 0, 0, AIR_FALSE},
{tenGageQNormal, 3, 1, {tenGageQGradVec, tenGageQGradMag}, 0, 0, AIR_FALSE},
{tenGageFAGradVec, 3, 1, {tenGageQGradVec, tenGageSGradVec, tenGageFA}, 0, 0, AIR_FALSE},
{tenGageFAGradMag, 1, 1, {tenGageFAGradVec}, 0, 0, AIR_FALSE},
{tenGageFANormal, 3, 1, {tenGageFAGradVec, tenGageFAGradMag}, 0, 0, AIR_FALSE},
{tenGageRGradVec, 3, 1, {tenGageR, tenGageTraceGradVec, tenGageBGradVec,
tenGageDetGradVec, tenGageSGradVec}, 0, 0, AIR_FALSE},
{tenGageRGradMag, 1, 1, {tenGageRGradVec}, 0, 0, AIR_FALSE},
{tenGageRNormal, 3, 1, {tenGageRGradVec, tenGageRGradMag}, 0, 0, AIR_FALSE},
{tenGageModeGradVec, 3, 1, {tenGageRGradVec, tenGageQGradVec, tenGageMode}, 0, 0, AIR_FALSE},
{tenGageModeGradMag, 1, 1, {tenGageModeGradVec}, 0, 0, AIR_FALSE},
{tenGageModeNormal, 3, 1, {tenGageModeGradVec, tenGageModeGradMag}, 0, 0, AIR_FALSE},
{tenGageThetaGradVec, 3, 1, {tenGageRGradVec, tenGageQGradVec, tenGageTheta}, 0, 0, AIR_FALSE},
{tenGageThetaGradMag, 1, 1, {tenGageThetaGradVec}, 0, 0, AIR_FALSE},
{tenGageThetaNormal, 3, 1, {tenGageThetaGradVec, tenGageThetaGradMag}, 0, 0, AIR_FALSE},
{tenGageOmegaGradVec, 3, 1, {tenGageFA, tenGageMode, tenGageFAGradVec, tenGageModeGradVec}, 0, 0, AIR_FALSE},
{tenGageOmegaGradMag, 1, 1, {tenGageOmegaGradVec}, 0, 0, AIR_FALSE},
{tenGageOmegaNormal, 3, 1, {tenGageOmegaGradVec, tenGageOmegaGradMag}, 0, 0, AIR_FALSE},
{tenGageInvarKGrads, 9, 1, {tenGageDelNormK2, tenGageDelNormK3, tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageInvarKGradMags, 3, 1, {tenGageInvarKGrads}, 0, 0, AIR_FALSE},
{tenGageInvarRGrads, 9, 1, {tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormK3,
tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageInvarRGradMags, 3, 1, {tenGageInvarRGrads}, 0, 0, AIR_FALSE},
{tenGageRotTans, 9, 1, {tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3,
tenGageTensorGrad}, 0, 0, AIR_FALSE},
{tenGageRotTanMags, 3, 1, {tenGageRotTans}, 0, 0, AIR_FALSE},
{tenGageEvalGrads, 9, 1, {tenGageTensorGrad, tenGageEval, tenGageEvec}, 0, 0, AIR_FALSE},
{tenGageCl1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageCp1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageCa1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageClpmin1, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageCl2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageCp2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageCa2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageClpmin2, 1, 0, {tenGageTensor, tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE},
{tenGageHessian, 63, 2, {0}, 0, 0, AIR_FALSE},
{tenGageTraceHessian, 9, 2, {tenGageHessian}, 0, 0, AIR_FALSE},
{tenGageTraceHessianEval, 3, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE},
{tenGageTraceHessianEval0, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 0, AIR_FALSE},
{tenGageTraceHessianEval1, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 1, AIR_FALSE},
{tenGageTraceHessianEval2, 1, 2, {tenGageTraceHessianEval}, tenGageTraceHessianEval, 2, AIR_FALSE},
{tenGageTraceHessianEvec, 9, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE},
{tenGageTraceHessianEvec0, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 0, AIR_FALSE},
{tenGageTraceHessianEvec1, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 3, AIR_FALSE},
{tenGageTraceHessianEvec2, 3, 2, {tenGageTraceHessianEvec}, tenGageTraceHessianEvec, 6, AIR_FALSE},
{tenGageTraceHessianFrob, 1, 2, {tenGageTraceHessian}, 0, 0, AIR_FALSE},
{tenGageBHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE},
{tenGageDetHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE},
{tenGageSHessian, 9, 2, {tenGageTensor, tenGageTensorGrad, tenGageHessian}, 0, 0, AIR_FALSE},
{tenGageQHessian, 9, 2, {tenGageBHessian, tenGageSHessian}, 0, 0, AIR_FALSE},
{tenGageFAHessian, 9, 2, {tenGageSHessian, tenGageQHessian,
tenGageSGradVec, tenGageQGradVec, tenGageFA}, 0, 0, AIR_FALSE},
{tenGageFAHessianEval, 3, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE},
{tenGageFAHessianEval0, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 0, AIR_FALSE},
{tenGageFAHessianEval1, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 1, AIR_FALSE},
{tenGageFAHessianEval2, 1, 2, {tenGageFAHessianEval}, tenGageFAHessianEval, 2, AIR_FALSE},
{tenGageFAHessianEvec, 9, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE},
{tenGageFAHessianEvec0, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 0, AIR_FALSE},
{tenGageFAHessianEvec1, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 3, AIR_FALSE},
{tenGageFAHessianEvec2, 3, 2, {tenGageFAHessianEvec}, tenGageFAHessianEvec, 6, AIR_FALSE},
{tenGageFAHessianFrob, 1, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE},
{tenGageFARidgeSurfaceStrength, 1, 2, {tenGageConfidence, tenGageFAHessianEval}, 0, 0, AIR_FALSE},
{tenGageFAValleySurfaceStrength, 1, 2, {tenGageConfidence, tenGageFAHessianEval}, 0, 0, AIR_FALSE},
{tenGageFALaplacian, 1, 2, {tenGageFAHessian}, 0, 0, AIR_FALSE},
{tenGageFAHessianEvalMode, 1, 2, {tenGageFAHessianEval}, 0, 0, AIR_FALSE},
{tenGageFARidgeLineAlignment, 1, 2, {tenGageEvec0, tenGageFAHessianEvec0, tenGageFAHessianEvalMode}, 0, 0, AIR_FALSE},
{tenGageFARidgeSurfaceAlignment, 1, 2, {tenGageEvec0, tenGageFAHessianEvec2, tenGageFAHessianEvalMode}, 0, 0, AIR_FALSE},
{tenGageFA2ndDD, 1, 2, {tenGageFAHessian, tenGageFANormal}, 0, 0, AIR_FALSE},
{tenGageFAGeomTens, 9, 2, {tenGageFAHessian, tenGageFAGradMag, tenGageFANormal}, 0, 0, AIR_FALSE},
{tenGageFAKappa1, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE},
{tenGageFAKappa2, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE},
{tenGageFATotalCurv, 1, 2, {tenGageFAGeomTens}, 0, 0, AIR_FALSE},
{tenGageFAShapeIndex, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE},
{tenGageFAMeanCurv, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE},
{tenGageFAGaussCurv, 1, 2, {tenGageFAKappa1, tenGageFAKappa2}, 0, 0, AIR_FALSE},
{tenGageFACurvDir1, 3, 2, {tenGageFAGeomTens, tenGageFAKappa2}, 0, 0, AIR_FALSE},
{tenGageFACurvDir2, 3, 2, {tenGageFAGeomTens, tenGageFAKappa1}, 0, 0, AIR_FALSE},
{tenGageFAFlowlineCurv, 1, 2, {tenGageFANormal, tenGageFAHessian, tenGageFAGradMag}, 0, 0, AIR_FALSE},
{tenGageRHessian, 9, 2, {tenGageR, tenGageRGradVec, tenGageTraceHessian,
tenGageBHessian, tenGageDetHessian, tenGageSHessian}, 0, 0, AIR_FALSE},
{tenGageModeHessian, 9, 2, {tenGageR, tenGageQ, tenGageRGradVec, tenGageQGradVec,
tenGageRHessian, tenGageQHessian}, 0, 0, AIR_FALSE},
{tenGageModeHessianEval, 3, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE},
{tenGageModeHessianEval0, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 0, AIR_FALSE},
{tenGageModeHessianEval1, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 1, AIR_FALSE},
{tenGageModeHessianEval2, 1, 2, {tenGageModeHessianEval}, tenGageModeHessianEval, 2, AIR_FALSE},
{tenGageModeHessianEvec, 9, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE},
{tenGageModeHessianEvec0, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 0, AIR_FALSE},
{tenGageModeHessianEvec1, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 3, AIR_FALSE},
{tenGageModeHessianEvec2, 3, 2, {tenGageModeHessianEvec}, tenGageModeHessianEvec, 6, AIR_FALSE},
{tenGageModeHessianFrob, 1, 2, {tenGageModeHessian}, 0, 0, AIR_FALSE},
{tenGageOmegaHessian, 9, 2, {tenGageFA, tenGageMode, tenGageFAGradVec, tenGageModeGradVec,
tenGageFAHessian, tenGageModeHessian}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianEval, 3, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianEval0, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 0, AIR_FALSE},
{tenGageOmegaHessianEval1, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 1, AIR_FALSE},
{tenGageOmegaHessianEval2, 1, 2, {tenGageOmegaHessianEval}, tenGageOmegaHessianEval, 2, AIR_FALSE},
{tenGageOmegaHessianEvec, 9, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianEvec0, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 0, AIR_FALSE},
{tenGageOmegaHessianEvec1, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 3, AIR_FALSE},
{tenGageOmegaHessianEvec2, 3, 2, {tenGageOmegaHessianEvec}, tenGageOmegaHessianEvec, 6, AIR_FALSE},
{tenGageOmegaLaplacian, 1, 2, {tenGageOmegaHessian}, 0, 0, AIR_FALSE},
{tenGageOmega2ndDD, 1, 2, {tenGageOmegaHessian, tenGageOmegaNormal}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianContrTenEvec0, 1, 2, {tenGageOmegaHessian, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianContrTenEvec1, 1, 2, {tenGageOmegaHessian, tenGageEvec1}, 0, 0, AIR_FALSE},
{tenGageOmegaHessianContrTenEvec2, 1, 2, {tenGageOmegaHessian, tenGageEvec2}, 0, 0, AIR_FALSE},
{tenGageTraceGradVecDotEvec0, 1, 1, {tenGageTraceGradVec, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageTraceDiffusionAlign, 1, 1, {tenGageTraceNormal, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageTraceDiffusionFraction, 1, 1, {tenGageTraceNormal, tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageFAGradVecDotEvec0, 1, 1, {tenGageFAGradVec, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageFADiffusionAlign, 1, 1, {tenGageFANormal, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageFADiffusionFraction, 1, 1, {tenGageFANormal, tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageOmegaGradVecDotEvec0, 1, 1, {tenGageOmegaGradVec, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageOmegaDiffusionAlign, 1, 1, {tenGageOmegaNormal, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageOmegaDiffusionFraction, 1, 1, {tenGageOmegaNormal, tenGageTensor}, 0, 0, AIR_FALSE},
/* currently don't have tenGageConfGradVec */
{tenGageConfGradVecDotEvec0, 1, 1, {tenGageTensorGrad, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageConfDiffusionAlign, 1, 1, {tenGageTensorGrad, tenGageEvec0}, 0, 0, AIR_FALSE},
{tenGageConfDiffusionFraction, 1, 1, {tenGageTensorGrad, tenGageTensor}, 0, 0, AIR_FALSE},
{tenGageCovariance, 21, 0, {tenGageTensor}, /* and all the values in iv3 */ 0, 0, AIR_FALSE},
{tenGageCovarianceRGRT, 21, 0, {tenGageCovariance,
tenGageDelNormR1, tenGageDelNormR2, tenGageDelNormK3,
tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3}, 0, 0, AIR_FALSE},
{tenGageCovarianceKGRT, 21, 0, {tenGageCovariance,
tenGageDelNormK2, tenGageDelNormK3,
tenGageDelNormPhi1, tenGageDelNormPhi2, tenGageDelNormPhi3}, 0, 0, AIR_FALSE},
{tenGageTensorLogEuclidean, 7, 0, {0}, 0, 0, AIR_FALSE},
{tenGageTensorQuatGeoLoxK, 7, 0, {0}, 0, 0, AIR_FALSE},
{tenGageTensorQuatGeoLoxR, 7, 0, {0}, 0, 0, AIR_FALSE},
{tenGageTensorRThetaPhiLinear, 7, 0, {0}, 0, 0, AIR_FALSE},
{tenGageCl1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE},
{tenGageCl1GradMag, 1, 1, {tenGageCl1GradVec}, 0, 0, AIR_FALSE},
{tenGageCl1Normal, 3, 1, {tenGageCl1GradVec, tenGageCl1GradMag}, 0, 0, AIR_FALSE},
{tenGageCp1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE},
{tenGageCp1GradMag, 1, 1, {tenGageCp1GradVec}, 0, 0, AIR_FALSE},
{tenGageCp1Normal, 3, 1, {tenGageCp1GradVec, tenGageCp1GradMag}, 0, 0, AIR_FALSE},
{tenGageCa1GradVec, 3, 1, {tenGageTrace, tenGageEval, tenGageEvalGrads}, 0, 0, AIR_FALSE},
{tenGageCa1GradMag, 1, 1, {tenGageCa1GradVec}, 0, 0, AIR_FALSE},
{tenGageCa1Normal, 3, 1, {tenGageCa1GradVec, tenGageCa1GradMag}, 0, 0, AIR_FALSE},
{tenGageTensorGradRotE, 21, 1, {tenGageTensorGrad, tenGageEval, tenGageEvec}, 0, 0, AIR_FALSE},
{tenGageEvalHessian, 27, 2, {tenGageTensorGradRotE, tenGageHessian, tenGageEval}, 0, 0, AIR_FALSE },
{tenGageCl1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE },
{tenGageCl1HessianEval, 3, 2, {tenGageCl1Hessian}, 0, 0, AIR_FALSE },
{tenGageCl1HessianEval0, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 0, AIR_FALSE},
{tenGageCl1HessianEval1, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 1, AIR_FALSE},
{tenGageCl1HessianEval2, 1, 2, {tenGageCl1HessianEval}, tenGageCl1HessianEval, 2, AIR_FALSE},
{tenGageCl1HessianEvec, 9, 2, {tenGageCl1Hessian}, 0, 0, AIR_FALSE },
{tenGageCl1HessianEvec0, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 0, AIR_FALSE},
{tenGageCl1HessianEvec1, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 3, AIR_FALSE},
{tenGageCl1HessianEvec2, 3, 2, {tenGageCl1HessianEvec}, tenGageCl1HessianEvec, 6, AIR_FALSE},
{tenGageCp1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE },
{tenGageCp1HessianEval, 3, 2, {tenGageCp1Hessian}, 0, 0, AIR_FALSE },
{tenGageCp1HessianEval0, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 0, AIR_FALSE},
{tenGageCp1HessianEval1, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 1, AIR_FALSE},
{tenGageCp1HessianEval2, 1, 2, {tenGageCp1HessianEval}, tenGageCp1HessianEval, 2, AIR_FALSE},
{tenGageCp1HessianEvec, 9, 2, {tenGageCp1Hessian}, 0, 0, AIR_FALSE },
{tenGageCp1HessianEvec0, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 0, AIR_FALSE},
{tenGageCp1HessianEvec1, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 3, AIR_FALSE},
{tenGageCp1HessianEvec2, 3, 2, {tenGageCp1HessianEvec}, tenGageCp1HessianEvec, 6, AIR_FALSE},
{tenGageCa1Hessian, 9, 2, {tenGageTensorGradRotE, tenGageEvalHessian}, 0, 0, AIR_FALSE },
{tenGageCa1HessianEval, 3, 2, {tenGageCa1Hessian}, 0, 0, AIR_FALSE },
{tenGageCa1HessianEval0, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 0, AIR_FALSE},
{tenGageCa1HessianEval1, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 1, AIR_FALSE},
{tenGageCa1HessianEval2, 1, 2, {tenGageCa1HessianEval}, tenGageCa1HessianEval, 2, AIR_FALSE},
{tenGageCa1HessianEvec, 9, 2, {tenGageCa1Hessian}, 0, 0, AIR_FALSE },
{tenGageCa1HessianEvec0, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 0, AIR_FALSE},
{tenGageCa1HessianEvec1, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 3, AIR_FALSE},
{tenGageCa1HessianEvec2, 3, 2, {tenGageCa1HessianEvec}, tenGageCa1HessianEvec, 6, AIR_FALSE},
{tenGageFiberCurving, 1, 1, {tenGageRotTans, tenGageEvec}, 0, 0, AIR_FALSE },
{tenGageFiberDispersion, 1, 1, {tenGageRotTans, tenGageEvec}, 0, 0, AIR_FALSE },
{tenGageAniso, TEN_ANISO_MAX+1, 0, {tenGageEval0, tenGageEval1, tenGageEval2}, 0, 0, AIR_FALSE}
};
void
292 _tenGageIv3Print( FILE *file, gageContext *ctx, gagePerVolume *pvl ) {
double *iv3;
int i, fd;
fd = 2*ctx->radius;
iv3 = pvl->iv3 + fd*fd*fd;
fprintf( file, "iv3[]'s *Dxx* component:\n" );
switch( fd ) {
case 2:
fprintf( file, "% 10.4f % 10.4f\n", ( float )iv3[6], ( float )iv3[7] );
fprintf( file, " % 10.4f % 10.4f\n\n", ( float )iv3[4], ( float )iv3[5] );
fprintf( file, "% 10.4f % 10.4f\n", ( float )iv3[2], ( float )iv3[3] );
fprintf( file, " % 10.4f % 10.4f\n", ( float )iv3[0], ( float )iv3[1] );
break;
case 4:
for ( i=3; i>=0; i-- ) {
fprintf( file, "% 10.4f % 10.4f % 10.4f % 10.4f\n",
( float )iv3[12+16*i], ( float )iv3[13+16*i],
( float )iv3[14+16*i], ( float )iv3[15+16*i] );
fprintf( file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n",
( float )iv3[ 8+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 9+16*i], ( float )iv3[10+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[11+16*i] );
fprintf( file, " % 10.4f %c% 10.4f % 10.4f%c % 10.4f\n",
( float )iv3[ 4+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 5+16*i], ( float )iv3[ 6+16*i], ( i==1||i==2 )?'\\':' ',
( float )iv3[ 7+16*i] );
fprintf( file, " % 10.4f % 10.4f % 10.4f % 10.4f\n",
( float )iv3[ 0+16*i], ( float )iv3[ 1+16*i],
( float )iv3[ 2+16*i], ( float )iv3[ 3+16*i] );
if ( i ) fprintf( file, "\n" );
}
break;
default:
for ( i=0; i<fd*fd*fd; i++ ) {
fprintf( file, " iv3[% 3d, % 3d, % 3d] = % 10.4f\n",
i%fd, ( i/fd )%fd, i/( fd*fd ), ( float )iv3[i] );
}
break;
}
return;
}
void
336 _tenGageFilter( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_tenGageFilter";
double *fw00, *fw11, *fw22, *ten, *tgrad, *thess;
int fd;
gageScl3PFilter_t *filter[5] = {NULL, gageScl3PFilter2, gageScl3PFilter4,
gageScl3PFilter6, gageScl3PFilter8};
unsigned int valIdx;
fd = 2*ctx->radius;
ten = pvl->directAnswer[tenGageTensor];
tgrad = pvl->directAnswer[tenGageTensorGrad];
thess = pvl->directAnswer[tenGageHessian];
if ( !ctx->parm.k3pack ) {
fprintf( stderr, "!%s: sorry, 6pack filtering not implemented\n", me );
return;
}
fw00 = ctx->fw + fd*3*gageKernel00;
fw11 = ctx->fw + fd*3*gageKernel11;
fw22 = ctx->fw + fd*3*gageKernel22;
/* perform the filtering */
/* HEY: we still want trilinear interpolation of confidence, no? */
if ( fd <= 8 ) {
for ( valIdx=0; valIdx<7; valIdx++ ) {
filter[ctx->radius]( ctx->shape,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
ten + valIdx, tgrad + valIdx*3, thess + valIdx*9,
pvl->needD );
}
} else {
for ( valIdx=0; valIdx<7; valIdx++ ) {
gageScl3PFilterN( ctx->shape, fd,
pvl->iv3 + valIdx*fd*fd*fd,
pvl->iv2 + valIdx*fd*fd,
pvl->iv1 + valIdx*fd,
fw00, fw11, fw22,
ten + valIdx, tgrad + valIdx*3, thess + valIdx*9,
pvl->needD );
}
}
return;
}
void
383 _tenGageAnswer( gageContext *ctx, gagePerVolume *pvl ) {
char me[]="_tenGageAnswer";
double *tenAns, *evalAns, *evecAns, *vecTmp=NULL, *matTmp=NULL,
*gradDtA=NULL, *gradDtB=NULL, *gradDtC=NULL,
*gradDtD=NULL, *gradDtE=NULL, *gradDtF=NULL,
*hessDtA=NULL, *hessDtB=NULL, *hessDtC=NULL,
*hessDtD=NULL, *hessDtE=NULL, *hessDtF=NULL,
*gradCbS=NULL, *gradCbB=NULL, *gradCbQ=NULL, *gradCbR=NULL,
*hessCbS=NULL, *hessCbB=NULL, *hessCbQ=NULL, *hessCbR=NULL,
gradDdXYZ[21]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
double tmp0, tmp1, tmp3, magTmp=0,
dtA=0, dtB=0, dtC=0, dtD=0, dtE=0, dtF=0,
cbQ=0, cbR=0, cbA=0, cbB=0, cbC=0, cbS=0,
gradCbA[3]={0, 0, 0}, gradCbC[3]={0, 0, 0};
double hessCbA[9]={0, 0, 0, 0, 0, 0, 0, 0, 0},
hessCbC[9]={0, 0, 0, 0, 0, 0, 0, 0, 0};
int ci;
tenAns = pvl->directAnswer[tenGageTensor];
evalAns = pvl->directAnswer[tenGageEval];
evecAns = pvl->directAnswer[tenGageEvec];
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensor ) ) {
/* done if doV */
/* HEY: this was prohibiting a Deft-related hack
tenAns[0] = AIR_CLAMP( 0, tenAns[0], 1 );
*/
/* HEY: and this was botching using 1-conf as potential energy for push
tenAns[0] = AIR_MAX( 0, tenAns[0] );
*/
dtA = tenAns[1];
dtB = tenAns[2];
dtC = tenAns[3];
dtD = tenAns[4];
dtE = tenAns[5];
dtF = tenAns[6];
if ( ctx->verbose ) {
fprintf( stderr, "!%s: tensor = ( %g ) %g %g %g %g %g %g\n", me,
tenAns[0], dtA, dtB, dtC, dtD, dtE, dtF );
}
}
/* done if doV
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageConfidence ) ) {
}
*/
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTrace ) ) {
cbA = -( pvl->directAnswer[tenGageTrace][0] = dtA + dtD + dtF );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageNorm ) ) {
pvl->directAnswer[tenGageNorm][0] =
sqrt( dtA*dtA + dtD*dtD + dtF*dtF + 2*dtB*dtB + 2*dtC*dtC + 2*dtE*dtE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageB ) ) {
cbB = pvl->directAnswer[tenGageB][0] =
dtA*dtD + dtA*dtF + dtD*dtF - dtB*dtB - dtC*dtC - dtE*dtE;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDet ) ) {
cbC = -( pvl->directAnswer[tenGageDet][0] =
2*dtB*dtC*dtE + dtA*dtD*dtF
- dtC*dtC*dtD - dtA*dtE*dtE - dtB*dtB*dtF );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageS ) ) {
cbS = ( pvl->directAnswer[tenGageS][0] =
dtA*dtA + dtD*dtD + dtF*dtF
+ 2*dtB*dtB + 2*dtC*dtC + 2*dtE*dtE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageQ ) ) {
cbQ = pvl->directAnswer[tenGageQ][0] = ( cbS - cbB )/9;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFA ) ) {
tmp0 = ( cbS
? cbQ/cbS
: 0 );
tmp0 = AIR_MAX( 0, tmp0 );
pvl->directAnswer[tenGageFA][0] = 3*sqrt( tmp0 );
/*
if ( !AIR_EXISTS( pvl->directAnswer[tenGageFA][0] ) ) {
fprintf( stderr, "!%s: cbS = %g, cbQ = %g, cbQ/( epsilon + cbS ) = %g\n"
"tmp0 = max( 0, cbQ/( epsilon + cbS ) ) = %g\n"
"sqrt( tmp0 ) = %g --> %g\n", me,
cbS, cbQ, cbQ/( epsilon + cbS ),
tmp0, sqrt( tmp0 ), pvl->directAnswer[tenGageFA][0] );
}
*/
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageR ) ) {
cbR = pvl->directAnswer[tenGageR][0] =
( 5*cbA*cbB - 27*cbC - 2*cbA*cbS )/54;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageMode ) ) {
double cbQQQ;
cbQQQ = 2*AIR_MAX( 0, cbQ*cbQ*cbQ );
tmp0 = 1.41421356237309504880*( cbQQQ ? cbR/( sqrt( cbQQQ ) ) : 0 );
pvl->directAnswer[tenGageMode][0] = AIR_CLAMP( -1, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmega ) ) {
pvl->directAnswer[tenGageOmega][0] =
pvl->directAnswer[tenGageFA][0]*( 1+pvl->directAnswer[tenGageMode][0] )/2;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTheta ) ) {
pvl->directAnswer[tenGageTheta][0] =
acos( -pvl->directAnswer[tenGageMode][0] )/AIR_PI;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeWarp ) ) {
pvl->directAnswer[tenGageModeWarp][0] =
cos( ( 1-pvl->directAnswer[tenGageMode][0] )*AIR_PI/2 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageEvec ) ) {
/* we do the longer process to get eigenvectors, and in the process
we always find the eigenvalues, whether or not they were asked for */
tenEigensolve_d( evalAns, evecAns, tenAns );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageEval ) ) {
/* else eigenvectors are NOT needed, but eigenvalues ARE needed */
tenEigensolve_d( evalAns, NULL, tenAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormK2 )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormK3 ) ) {
double tmp[7];
tenInvariantGradientsK_d( tmp,
pvl->directAnswer[tenGageDelNormK2],
pvl->directAnswer[tenGageDelNormK3],
tenAns, 0.0000001 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormR1 )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormR2 ) ) {
tenInvariantGradientsR_d( pvl->directAnswer[tenGageDelNormR1],
pvl->directAnswer[tenGageDelNormR2],
pvl->directAnswer[tenGageDelNormK3],
tenAns, 0.0000001 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormPhi1 )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormPhi2 )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDelNormPhi3 ) ) {
tenRotationTangents_d( pvl->directAnswer[tenGageDelNormPhi1],
pvl->directAnswer[tenGageDelNormPhi2],
pvl->directAnswer[tenGageDelNormPhi3],
evecAns );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorGrad ) ) {
/* done if doD1 */
/* still have to set up pointer variables that item answers
below will rely on as short-cuts */
vecTmp = pvl->directAnswer[tenGageTensorGrad];
gradDtA = vecTmp + 1*3;
gradDtB = vecTmp + 2*3;
gradDtC = vecTmp + 3*3;
gradDtD = vecTmp + 4*3;
gradDtE = vecTmp + 5*3;
gradDtF = vecTmp + 6*3;
TEN_T_SET( gradDdXYZ + 0*7, tenAns[0],
gradDtA[0], gradDtB[0], gradDtC[0],
gradDtD[0], gradDtE[0],
gradDtF[0] );
TEN_T_SET( gradDdXYZ + 1*7, tenAns[0],
gradDtA[1], gradDtB[1], gradDtC[1],
gradDtD[1], gradDtE[1],
gradDtF[1] );
TEN_T_SET( gradDdXYZ + 2*7, tenAns[0],
gradDtA[2], gradDtB[2], gradDtC[2],
gradDtD[2], gradDtE[2],
gradDtF[2] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorGradMag ) ) {
vecTmp = pvl->directAnswer[tenGageTensorGradMag];
vecTmp[0] = sqrt( TEN_T_DOT( gradDdXYZ + 0*7, gradDdXYZ + 0*7 ) );
vecTmp[1] = sqrt( TEN_T_DOT( gradDdXYZ + 1*7, gradDdXYZ + 1*7 ) );
vecTmp[2] = sqrt( TEN_T_DOT( gradDdXYZ + 2*7, gradDdXYZ + 2*7 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorGradMag ) ) {
pvl->directAnswer[tenGageTensorGradMagMag][0] = ELL_3V_LEN( vecTmp );
}
/* --- Trace --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceGradVec ) ) {
vecTmp = pvl->directAnswer[tenGageTraceGradVec];
ELL_3V_ADD3( vecTmp, gradDtA, gradDtD, gradDtF );
ELL_3V_SCALE( gradCbA, -1, vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceGradMag ) ) {
magTmp = pvl->directAnswer[tenGageTraceGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageTraceNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* ---- Norm stuff handled after S */
/* --- B --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageBGradVec ) ) {
gradCbB = vecTmp = pvl->directAnswer[tenGageBGradVec];
ELL_3V_SCALE_ADD6( vecTmp,
dtD + dtF, gradDtA,
dtA + dtF, gradDtD,
dtA + dtD, gradDtF,
-2*dtB, gradDtB,
-2*dtC, gradDtC,
-2*dtE, gradDtE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageBGradMag ) ) {
magTmp = pvl->directAnswer[tenGageBGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageBNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageBNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Det --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDetGradVec ) ) {
vecTmp = pvl->directAnswer[tenGageDetGradVec];
ELL_3V_SCALE_ADD6( vecTmp,
dtD*dtF - dtE*dtE, gradDtA,
2*( dtC*dtE - dtB*dtF ), gradDtB,
2*( dtB*dtE - dtC*dtD ), gradDtC,
dtA*dtF - dtC*dtC, gradDtD,
2*( dtB*dtC - dtA*dtE ), gradDtE,
dtA*dtD - dtB*dtB, gradDtF );
ELL_3V_SCALE( gradCbC, -1, vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDetGradMag ) ) {
magTmp = pvl->directAnswer[tenGageDetGradMag][0] =
AIR_CAST( float, ELL_3V_LEN( vecTmp ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDetNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageDetNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- S --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageSGradVec ) ) {
gradCbS = vecTmp = pvl->directAnswer[tenGageSGradVec];
ELL_3V_SCALE_ADD6( vecTmp,
2*dtA, gradDtA,
2*dtD, gradDtD,
2*dtF, gradDtF,
4*dtB, gradDtB,
4*dtC, gradDtC,
4*dtE, gradDtE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageSGradMag ) ) {
magTmp = pvl->directAnswer[tenGageSGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageSNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageSNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Norm --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageNormGradVec ) ) {
double nslc;
nslc = pvl->directAnswer[tenGageNorm][0];
nslc = nslc ? 1/( 2*nslc ) : 0.0;
vecTmp = pvl->directAnswer[tenGageNormGradVec];
ELL_3V_SCALE( vecTmp, nslc, pvl->directAnswer[tenGageSGradVec] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageNormGradMag ) ) {
magTmp = pvl->directAnswer[tenGageNormGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageNormNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageNormNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Q --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageQGradVec ) ) {
gradCbQ = vecTmp = pvl->directAnswer[tenGageQGradVec];
ELL_3V_SCALE_ADD2( vecTmp,
1.0/9, gradCbS,
-1.0/9, gradCbB );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageQGradMag ) ) {
magTmp = pvl->directAnswer[tenGageQGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageQNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageQNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- FA --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAGradVec ) ) {
vecTmp = pvl->directAnswer[tenGageFAGradVec];
tmp3 = AIR_MAX( 0, pvl->directAnswer[tenGageFA][0] );
tmp0 = cbQ ? tmp3/( 2*cbQ ) : 0;
tmp1 = cbS ? -tmp3/( 2*cbS ) : 0;
ELL_3V_SCALE_ADD2( vecTmp,
tmp0, gradCbQ,
tmp1, gradCbS );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAGradMag ) ) {
magTmp = pvl->directAnswer[tenGageFAGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFANormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageFANormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- R --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRGradVec ) ) {
gradCbR = vecTmp = pvl->directAnswer[tenGageRGradVec];
ELL_3V_SCALE_ADD4( vecTmp,
( 5*cbB - 2*cbS )/54, gradCbA,
5*cbA/54, gradCbB,
-0.5, gradCbC,
-cbA/27, gradCbS );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRGradMag ) ) {
magTmp = pvl->directAnswer[tenGageRGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageRNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Mode --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeGradVec ) ) {
vecTmp = pvl->directAnswer[tenGageModeGradVec];
tmp1 = AIR_MAX( 0, cbQ*cbQ*cbQ );
tmp1 = tmp1 ? sqrt( 1/tmp1 ) : 0;
tmp0 = cbQ ? -tmp1*3*cbR/( 2*cbQ ) : 0;
ELL_3V_SCALE_ADD2( vecTmp,
tmp0, gradCbQ,
tmp1, gradCbR );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeGradMag ) ) {
magTmp = pvl->directAnswer[tenGageModeGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageModeNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Theta --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageThetaGradVec ) ) {
vecTmp = pvl->directAnswer[tenGageThetaGradVec];
tmp1 = AIR_MAX( 0, cbQ*cbQ*cbQ );
tmp0 = tmp1 ? cbR*cbR/tmp1 : 0;
tmp1 = sqrt( tmp1 )*sqrt( 1.0 - tmp0 );
tmp1 = tmp1 ? 1/( AIR_PI*tmp1 ) : 0.0;
tmp0 = cbQ ? -tmp1*3*cbR/( 2*cbQ ) : 0.0;
ELL_3V_SCALE_ADD2( vecTmp,
tmp0, gradCbQ,
tmp1, gradCbR );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageThetaGradMag ) ) {
magTmp = pvl->directAnswer[tenGageThetaGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageThetaNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageThetaNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- Omega --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaGradVec ) ) {
double fa, mode, *faGrad, *modeGrad;
vecTmp = pvl->directAnswer[tenGageOmegaGradVec];
fa = pvl->directAnswer[tenGageFA][0];
mode = pvl->directAnswer[tenGageMode][0];
faGrad = pvl->directAnswer[tenGageFAGradVec];
modeGrad = pvl->directAnswer[tenGageModeGradVec];
ELL_3V_SCALE_ADD2( vecTmp,
( 1+mode )/2, faGrad,
fa/2, modeGrad );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaGradMag ) ) {
magTmp = pvl->directAnswer[tenGageOmegaGradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaNormal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageOmegaNormal],
magTmp ? 1/magTmp : 0, vecTmp );
}
#define SQRT_1_OVER_3 0.57735026918962576450
/* --- Invariant gradients + rotation tangents --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageInvarKGrads ) ) {
double mu1Grad[7], *mu2Grad, *skwGrad;
TEN_T_SET( mu1Grad, 1,
SQRT_1_OVER_3, 0, 0,
SQRT_1_OVER_3, 0,
SQRT_1_OVER_3 );
mu2Grad = pvl->directAnswer[tenGageDelNormK2];
skwGrad = pvl->directAnswer[tenGageDelNormK3];
ELL_3V_SET( pvl->directAnswer[tenGageInvarKGrads] + 0*3,
TEN_T_DOT( mu1Grad, gradDdXYZ + 0*7 ),
TEN_T_DOT( mu1Grad, gradDdXYZ + 1*7 ),
TEN_T_DOT( mu1Grad, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageInvarKGrads] + 1*3,
TEN_T_DOT( mu2Grad, gradDdXYZ + 0*7 ),
TEN_T_DOT( mu2Grad, gradDdXYZ + 1*7 ),
TEN_T_DOT( mu2Grad, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageInvarKGrads] + 2*3,
TEN_T_DOT( skwGrad, gradDdXYZ + 0*7 ),
TEN_T_DOT( skwGrad, gradDdXYZ + 1*7 ),
TEN_T_DOT( skwGrad, gradDdXYZ + 2*7 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageInvarKGradMags ) ) {
ELL_3V_SET( pvl->directAnswer[tenGageInvarKGradMags],
ELL_3V_LEN( pvl->directAnswer[tenGageInvarKGrads] + 0*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageInvarKGrads] + 1*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageInvarKGrads] + 2*3 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageInvarRGrads ) ) {
double *R1Grad, *R2Grad, *R3Grad;
R1Grad = pvl->directAnswer[tenGageDelNormR1];
R2Grad = pvl->directAnswer[tenGageDelNormR2];
R3Grad = pvl->directAnswer[tenGageDelNormK3];
ELL_3V_SET( pvl->directAnswer[tenGageInvarRGrads] + 0*3,
TEN_T_DOT( R1Grad, gradDdXYZ + 0*7 ),
TEN_T_DOT( R1Grad, gradDdXYZ + 1*7 ),
TEN_T_DOT( R1Grad, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageInvarRGrads] + 1*3,
TEN_T_DOT( R2Grad, gradDdXYZ + 0*7 ),
TEN_T_DOT( R2Grad, gradDdXYZ + 1*7 ),
TEN_T_DOT( R2Grad, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageInvarRGrads] + 2*3,
TEN_T_DOT( R3Grad, gradDdXYZ + 0*7 ),
TEN_T_DOT( R3Grad, gradDdXYZ + 1*7 ),
TEN_T_DOT( R3Grad, gradDdXYZ + 2*7 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageInvarRGradMags ) ) {
ELL_3V_SET( pvl->directAnswer[tenGageInvarRGradMags],
ELL_3V_LEN( pvl->directAnswer[tenGageInvarRGrads] + 0*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageInvarRGrads] + 1*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageInvarRGrads] + 2*3 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageEvalGrads ) ) {
double matOut[9], tenOut[9], tmpRes[9],
rel1, rel2, w1, w2, eps;
unsigned int evi;
for ( evi=0; evi<=2; evi++ ) {
ELL_3MV_OUTER( matOut, evecAns + evi*3, evecAns + evi*3 );
TEN_M2T( tenOut, matOut );
ELL_3V_SET( tmpRes + evi*3,
TEN_T_DOT( tenOut, gradDdXYZ + 0*7 ),
TEN_T_DOT( tenOut, gradDdXYZ + 1*7 ),
TEN_T_DOT( tenOut, gradDdXYZ + 2*7 ) );
}
/* Added 2008-06-27: In case there are duplicate eigenvalues,
* average their derivatives to avoid visible artifacs in edge
* maps. Provide a smooth transition to the ill-defined case */
eps=0.05; /* threshold at which we start the transition */
/* interpolation weights from relative eigenvalue distance */
rel1=( evalAns[0]-evalAns[1] )/( fabs( evalAns[0] )+fabs( evalAns[1] ) );
rel2=( evalAns[1]-evalAns[2] )/( fabs( evalAns[1] )+fabs( evalAns[2] ) );
w1=rel1/eps-1;
w1*=w1;
w2=rel2/eps-1;
w2*=w2;
if ( rel1>eps ) {
ELL_3V_COPY( pvl->directAnswer[tenGageEvalGrads], tmpRes );
} else {
if ( rel2>eps ) {
ELL_3V_SCALE_ADD2( pvl->directAnswer[tenGageEvalGrads],
1-0.5*w1, tmpRes, 0.5*w1, tmpRes+3 );
} else {
ELL_3V_SCALE_ADD3( pvl->directAnswer[tenGageEvalGrads],
1-0.5*w1-w1*w2/6.0, tmpRes,
0.5*w1-w1*w2/6.0, tmpRes+3,
w1*w2/3.0, tmpRes+6 );
}
}
if ( rel2>eps ) {
ELL_3V_COPY( pvl->directAnswer[tenGageEvalGrads]+6, tmpRes+6 );
} else {
if ( rel1>eps ) {
ELL_3V_SCALE_ADD2( pvl->directAnswer[tenGageEvalGrads]+6,
1-0.5*w2, tmpRes+6, 0.5*w2, tmpRes+3 );
} else {
ELL_3V_SCALE_ADD3( pvl->directAnswer[tenGageEvalGrads]+6,
1-0.5*w2-w1*w2/6.0, tmpRes+6,
0.5*w2-w1*w2/6.0, tmpRes+3,
w1*w2/3.0, tmpRes );
}
}
ELL_3V_ADD3( pvl->directAnswer[tenGageEvalGrads]+3,
tmpRes, tmpRes+3, tmpRes+6 );
ELL_3V_ADD2( tmpRes, pvl->directAnswer[tenGageEvalGrads],
pvl->directAnswer[tenGageEvalGrads]+6 );
ELL_3V_SUB( pvl->directAnswer[tenGageEvalGrads]+3,
pvl->directAnswer[tenGageEvalGrads]+3,
tmpRes ); /* l2 = trace - l1 - l3 */
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRotTans ) ) {
double phi1[7], phi2[7], phi3[7];
tenRotationTangents_d( phi1, phi2, phi3, evecAns );
ELL_3V_SET( pvl->directAnswer[tenGageRotTans] + 0*3,
TEN_T_DOT( phi1, gradDdXYZ + 0*7 ),
TEN_T_DOT( phi1, gradDdXYZ + 1*7 ),
TEN_T_DOT( phi1, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageRotTans] + 1*3,
TEN_T_DOT( phi2, gradDdXYZ + 0*7 ),
TEN_T_DOT( phi2, gradDdXYZ + 1*7 ),
TEN_T_DOT( phi2, gradDdXYZ + 2*7 ) );
ELL_3V_SET( pvl->directAnswer[tenGageRotTans] + 2*3,
TEN_T_DOT( phi3, gradDdXYZ + 0*7 ),
TEN_T_DOT( phi3, gradDdXYZ + 1*7 ),
TEN_T_DOT( phi3, gradDdXYZ + 2*7 ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRotTanMags ) ) {
ELL_3V_SET( pvl->directAnswer[tenGageRotTanMags],
ELL_3V_LEN( pvl->directAnswer[tenGageRotTans] + 0*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageRotTans] + 1*3 ),
ELL_3V_LEN( pvl->directAnswer[tenGageRotTans] + 2*3 ) );
}
/* --- C{l, p, a, lpmin}1 --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Cl1 );
pvl->directAnswer[tenGageCl1][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Cp1 );
pvl->directAnswer[tenGageCp1][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Ca1 );
pvl->directAnswer[tenGageCa1][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageClpmin1 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Clpmin1 );
pvl->directAnswer[tenGageClpmin1][0] = AIR_CLAMP( 0, tmp0, 1 );
}
/* --- C{l, p, a, lpmin}2 --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl2 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Cl2 );
pvl->directAnswer[tenGageCl2][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp2 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Cp2 );
pvl->directAnswer[tenGageCp2][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa2 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Ca2 );
pvl->directAnswer[tenGageCa2][0] = AIR_CLAMP( 0, tmp0, 1 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageClpmin2 ) ) {
tmp0 = tenAnisoEval_d( evalAns, tenAniso_Clpmin2 );
pvl->directAnswer[tenGageClpmin2][0] = AIR_CLAMP( 0, tmp0, 1 );
}
/* --- Hessian madness ( the derivative, not the soldier ) --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageHessian ) ) {
/* done if doD2; still have to set up pointers */
matTmp = pvl->directAnswer[tenGageHessian];
hessDtA = matTmp + 1*9;
hessDtB = matTmp + 2*9;
hessDtC = matTmp + 3*9;
hessDtD = matTmp + 4*9;
hessDtE = matTmp + 5*9;
hessDtF = matTmp + 6*9;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceHessian ) ) {
ELL_3M_SCALE_ADD3( pvl->directAnswer[tenGageTraceHessian],
1.0, hessDtA,
1.0, hessDtD,
1.0, hessDtF );
ELL_3M_SCALE( hessCbA, -1, pvl->directAnswer[tenGageTraceHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceHessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageTraceHessianEval],
pvl->directAnswer[tenGageTraceHessianEvec],
pvl->directAnswer[tenGageTraceHessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceHessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageTraceHessianEval],
pvl->directAnswer[tenGageTraceHessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceHessianFrob ) ) {
pvl->directAnswer[tenGageTraceHessianFrob][0]
= ELL_3M_FROB( pvl->directAnswer[tenGageTraceHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageBHessian ) ) {
hessCbB = matTmp = pvl->directAnswer[tenGageBHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, dtB, hessDtB );
ELL_3M_SCALE_INCR( matTmp, dtC, hessDtC );
ELL_3M_SCALE_INCR( matTmp, dtE, hessDtE );
ELL_3MV_OUTER_INCR( matTmp, gradDtB, gradDtB );
ELL_3MV_OUTER_INCR( matTmp, gradDtC, gradDtC );
ELL_3MV_OUTER_INCR( matTmp, gradDtE, gradDtE );
ELL_3M_SCALE( matTmp, -2, matTmp );
ELL_3MV_OUTER_INCR( matTmp, gradDtD, gradDtA );
ELL_3MV_OUTER_INCR( matTmp, gradDtF, gradDtA );
ELL_3MV_OUTER_INCR( matTmp, gradDtA, gradDtD );
ELL_3MV_OUTER_INCR( matTmp, gradDtF, gradDtD );
ELL_3MV_OUTER_INCR( matTmp, gradDtA, gradDtF );
ELL_3MV_OUTER_INCR( matTmp, gradDtD, gradDtF );
ELL_3M_SCALE_INCR( matTmp, dtD + dtF, hessDtA );
ELL_3M_SCALE_INCR( matTmp, dtA + dtF, hessDtD );
ELL_3M_SCALE_INCR( matTmp, dtA + dtD, hessDtF );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageDetHessian ) ) {
double tmp[3];
matTmp = pvl->directAnswer[tenGageDetHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3V_SCALE_ADD3( tmp, dtD, gradDtF,
dtF, gradDtD,
-2*dtE, gradDtE );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtA );
ELL_3M_SCALE_INCR( matTmp, dtD*dtF - dtE*dtE, hessDtA );
ELL_3V_SCALE_ADD4( tmp, 2*dtC, gradDtE,
2*dtE, gradDtC,
-2*dtB, gradDtF,
-2*dtF, gradDtB );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtB );
ELL_3M_SCALE_INCR( matTmp, 2*( dtC*dtE - dtB*dtF ), hessDtB );
ELL_3V_SCALE_ADD4( tmp, 2*dtB, gradDtE,
2*dtE, gradDtB,
-2*dtC, gradDtD,
-2*dtD, gradDtC );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtC );
ELL_3M_SCALE_INCR( matTmp, 2*( dtB*dtE - dtC*dtD ), hessDtC );
ELL_3V_SCALE_ADD3( tmp, dtA, gradDtF,
dtF, gradDtA,
-2*dtC, gradDtC );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtD );
ELL_3M_SCALE_INCR( matTmp, dtA*dtF - dtC*dtC, hessDtD );
ELL_3V_SCALE_ADD4( tmp, 2*dtB, gradDtC,
2*dtC, gradDtB,
-2*dtA, gradDtE,
-2*dtE, gradDtA );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtE );
ELL_3M_SCALE_INCR( matTmp, 2*( dtB*dtC - dtA*dtE ), hessDtE );
ELL_3V_SCALE_ADD3( tmp, dtA, gradDtD,
dtD, gradDtA,
-2*dtB, gradDtB );
ELL_3MV_OUTER_INCR( matTmp, tmp, gradDtF );
ELL_3M_SCALE_INCR( matTmp, dtA*dtD - dtB*dtB, hessDtF );
ELL_3M_SCALE( hessCbC, -1, pvl->directAnswer[tenGageDetHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageSHessian ) ) {
hessCbS = matTmp = pvl->directAnswer[tenGageSHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, dtB, hessDtB );
ELL_3MV_OUTER_INCR( matTmp, gradDtB, gradDtB );
ELL_3M_SCALE_INCR( matTmp, dtC, hessDtC );
ELL_3MV_OUTER_INCR( matTmp, gradDtC, gradDtC );
ELL_3M_SCALE_INCR( matTmp, dtE, hessDtE );
ELL_3MV_OUTER_INCR( matTmp, gradDtE, gradDtE );
ELL_3M_SCALE( matTmp, 2, matTmp );
ELL_3M_SCALE_INCR( matTmp, dtA, hessDtA );
ELL_3MV_OUTER_INCR( matTmp, gradDtA, gradDtA );
ELL_3M_SCALE_INCR( matTmp, dtD, hessDtD );
ELL_3MV_OUTER_INCR( matTmp, gradDtD, gradDtD );
ELL_3M_SCALE_INCR( matTmp, dtF, hessDtF );
ELL_3MV_OUTER_INCR( matTmp, gradDtF, gradDtF );
ELL_3M_SCALE( matTmp, 2, matTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageQHessian ) ) {
hessCbQ = pvl->directAnswer[tenGageQHessian];
ELL_3M_SCALE_ADD2( hessCbQ,
1.0/9, hessCbS,
-1.0/9, hessCbB );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAHessian ) ) {
double tmpQ, rQ, orQ, oQ, tmpS, rS, orS, oS;
tmpQ = AIR_MAX( 0, cbQ );
tmpS = AIR_MAX( 0, cbS );
oQ = tmpQ ? 1/tmpQ : 0;
oS = tmpS ? 1/tmpS : 0;
rQ = sqrt( tmpQ );
rS = sqrt( tmpS );
orQ = rQ ? 1/rQ : 0;
orS = rS ? 1/rS : 0;
matTmp = pvl->directAnswer[tenGageFAHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, orS*orQ, hessCbQ );
ELL_3M_SCALE_INCR( matTmp, -rQ*orS*oS, hessCbS );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -orS*orQ*oQ/2, gradCbQ, gradCbQ );
ELL_3MV_SCALE_OUTER_INCR( matTmp, 3*rQ*orS*oS*oS/2, gradCbS, gradCbS );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -orS*oS*orQ/2, gradCbS, gradCbQ );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -orQ*orS*oS/2, gradCbQ, gradCbS );
ELL_3M_SCALE( matTmp, 3.0/2, matTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAHessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageFAHessianEval],
pvl->directAnswer[tenGageFAHessianEvec],
pvl->directAnswer[tenGageFAHessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAHessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageFAHessianEval],
pvl->directAnswer[tenGageFAHessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAHessianFrob ) ) {
pvl->directAnswer[tenGageFAHessianFrob][0]
= ELL_3M_FROB( pvl->directAnswer[tenGageFAHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFARidgeSurfaceStrength ) ) {
double ev;
ev = -pvl->directAnswer[tenGageFAHessianEval][2];
ev = AIR_MAX( 0, ev );
pvl->directAnswer[tenGageFARidgeSurfaceStrength][0] = tenAns[0]*ev;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAValleySurfaceStrength ) ) {
double ev;
ev = pvl->directAnswer[tenGageFAHessianEval][0];
ev = AIR_MAX( 0, ev );
pvl->directAnswer[tenGageFAValleySurfaceStrength][0] = tenAns[0]*ev;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFALaplacian ) ) {
double *hess;
hess = pvl->directAnswer[tenGageFAHessian];
pvl->directAnswer[tenGageFALaplacian][0] = hess[0] + hess[4] + hess[8];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAHessianEvalMode ) ) {
double *heval;
heval = pvl->directAnswer[tenGageFAHessianEval];
pvl->directAnswer[tenGageFAHessianEvalMode][0] = airMode3_d( heval );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFARidgeLineAlignment ) ) {
double *hev0, *dev0, dot, mde;
hev0 = pvl->directAnswer[tenGageFAHessianEvec0];
dev0 = pvl->directAnswer[tenGageEvec0];
dot = ELL_3V_DOT( hev0, dev0 );
mde = pvl->directAnswer[tenGageFAHessianEvalMode][0];
mde = AIR_AFFINE( -1, mde, 1, 0, 1 );
pvl->directAnswer[tenGageFARidgeLineAlignment][0] = mde*dot*dot;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFARidgeSurfaceAlignment ) ) {
double *hev2, *dev0, dot, mde;
hev2 = pvl->directAnswer[tenGageFAHessianEvec2];
dev0 = pvl->directAnswer[tenGageEvec0];
dot = ELL_3V_DOT( hev2, dev0 );
mde = pvl->directAnswer[tenGageFAHessianEvalMode][0];
mde = AIR_AFFINE( -1, mde, 1, 1, 0 );
pvl->directAnswer[tenGageFARidgeSurfaceAlignment][0]= mde*( 1-dot*dot );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFA2ndDD ) ) {
double *hess, *norm, tmpv[3];
hess = pvl->directAnswer[tenGageFAHessian];
norm = pvl->directAnswer[tenGageFANormal];
ELL_3MV_MUL( tmpv, hess, norm );
pvl->directAnswer[tenGageFA2ndDD][0] = ELL_3V_DOT( norm, tmpv );
}
/* HEY: lots of this is copy/paste from gage/sclanswer.c */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAGeomTens ) ) {
double denom, *fahess, *fagmag, tmpMat[9], *fnorm, nPerp[9], sHess[9];
fahess = pvl->directAnswer[tenGageFAHessian];
fagmag = pvl->directAnswer[tenGageFAGradMag];
fnorm = pvl->directAnswer[tenGageFANormal];
denom = ( *fagmag ) ? 1.0/( *fagmag ) : 0.0;
ELL_3M_SCALE( sHess, denom, fahess );
ELL_3M_IDENTITY_SET( nPerp );
ELL_3MV_SCALE_OUTER_INCR( nPerp, -1, fnorm, fnorm );
/* gten = nPerp * sHess * nPerp */
ELL_3M_MUL( tmpMat, sHess, nPerp );
ELL_3M_MUL( pvl->directAnswer[tenGageFAGeomTens], nPerp, tmpMat );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFATotalCurv ) ) {
pvl->directAnswer[tenGageFATotalCurv][0] =
ELL_3M_FROB( pvl->directAnswer[tenGageFAGeomTens] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAKappa1 ) ||
GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAKappa2 ) ) {
double *k1, *k2, T, N, D;
k1 = pvl->directAnswer[tenGageFAKappa1];
k2 = pvl->directAnswer[tenGageFAKappa2];
T = ELL_3M_TRACE( pvl->directAnswer[tenGageFAGeomTens] );
N = pvl->directAnswer[tenGageFATotalCurv][0];
D = 2*N*N - T*T;
D = AIR_MAX( D, 0 );
D = sqrt( D );
k1[0] = 0.5*( T + D );
k2[0] = 0.5*( T - D );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAMeanCurv ) ) {
double k1, k2;
k1 = pvl->directAnswer[tenGageFAKappa1][0];
k2 = pvl->directAnswer[tenGageFAKappa2][0];
pvl->directAnswer[tenGageFAMeanCurv][0] = ( k1 + k2 )/2;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAShapeIndex ) ) {
double k1, k2;
k1 = pvl->directAnswer[tenGageFAKappa1][0];
k2 = pvl->directAnswer[tenGageFAKappa2][0];
pvl->directAnswer[tenGageFAShapeIndex][0] =
-( 2/AIR_PI )*atan2( k1 + k2, k1 - k2 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAGaussCurv ) ) {
double k1, k2;
k1 = pvl->directAnswer[tenGageFAKappa1][0];
k2 = pvl->directAnswer[tenGageFAKappa2][0];
pvl->directAnswer[tenGageFAGaussCurv][0] = k1*k2;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFACurvDir1 ) ) {
double kk, tmpMat[9], tmpVec[3];
kk = pvl->directAnswer[tenGageFAKappa2][0];
ELL_3M_COPY( tmpMat, pvl->directAnswer[tenGageFAGeomTens] );
tmpMat[0] -= kk; tmpMat[4] -= kk; tmpMat[8] -= kk;
ell_3m_1d_nullspace_d( tmpVec, tmpMat );
ELL_3V_COPY( pvl->directAnswer[tenGageFACurvDir1], tmpVec );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFACurvDir2 ) ) {
double kk, tmpMat[9], tmpVec[3];
kk = pvl->directAnswer[tenGageFAKappa1][0];
ELL_3M_COPY( tmpMat, pvl->directAnswer[tenGageFAGeomTens] );
tmpMat[0] -= kk; tmpMat[4] -= kk; tmpMat[8] -= kk;
ell_3m_1d_nullspace_d( tmpVec, tmpMat );
ELL_3V_COPY( pvl->directAnswer[tenGageFACurvDir1], tmpVec );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAFlowlineCurv ) ) {
double *fahess, *fnorm, *fagmag, denom, nPerp[9], nProj[9],
ncTen[9], sHess[9], tmpMat[9];
fnorm = pvl->directAnswer[tenGageFANormal];
fahess = pvl->directAnswer[tenGageFAHessian];
fagmag = pvl->directAnswer[tenGageFAGradMag];
ELL_3MV_OUTER( nProj, fnorm, fnorm );
ELL_3M_IDENTITY_SET( nPerp );
ELL_3M_SCALE_INCR( nPerp, -1, nProj );
denom = ( *fagmag ) ? 1.0/( *fagmag ) : 0.0;
ELL_3M_SCALE( sHess, denom, fahess );
/* ncTen = nPerp * sHess * nProj */
ELL_3M_MUL( tmpMat, sHess, nProj );
ELL_3M_MUL( ncTen, nPerp, tmpMat );
pvl->directAnswer[gageSclFlowlineCurv][0] = ELL_3M_FROB( ncTen );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageRHessian ) ) {
hessCbR = matTmp = pvl->directAnswer[tenGageRHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, 5*cbB - 2*cbS, hessCbA );
ELL_3MV_SCALE_OUTER_INCR( matTmp, 5, gradCbB, gradCbA );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -2, gradCbS, gradCbA );
ELL_3M_SCALE_INCR( matTmp, 5*cbA, hessCbB );
ELL_3MV_SCALE_OUTER_INCR( matTmp, 5, gradCbA, gradCbB );
ELL_3M_SCALE_INCR( matTmp, -27, hessCbC );
ELL_3M_SCALE_INCR( matTmp, -2*cbA, hessCbS );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -2, gradCbA, gradCbS );
ELL_3M_SCALE( matTmp, 1.0/54, matTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeHessian ) ) {
double tmpQ, oQ, rQ;
tmpQ = AIR_MAX( 0, cbQ );
rQ = sqrt( tmpQ );
oQ = tmpQ ? 1/tmpQ : 0;
matTmp = pvl->directAnswer[tenGageModeHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, -( 3.0/2 )*cbR, hessCbQ );
ELL_3MV_SCALE_OUTER_INCR( matTmp, ( 15.0/4 )*cbR*oQ, gradCbQ, gradCbQ );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -( 3.0/2 ), gradCbR, gradCbQ );
ELL_3M_SCALE_INCR( matTmp, cbQ, hessCbR );
ELL_3MV_SCALE_OUTER_INCR( matTmp, -( 3.0/2 ), gradCbQ, gradCbR );
tmp0 = ( tmpQ && rQ ) ? 1/( tmpQ*tmpQ*rQ ) : 0.0;
ELL_3M_SCALE( matTmp, tmp0, matTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeHessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageModeHessianEval],
pvl->directAnswer[tenGageModeHessianEvec],
pvl->directAnswer[tenGageModeHessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeHessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageModeHessianEval],
pvl->directAnswer[tenGageModeHessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageModeHessianFrob ) ) {
pvl->directAnswer[tenGageModeHessianFrob][0]
= ELL_3M_FROB( pvl->directAnswer[tenGageModeHessian] );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessian ) ) {
double fa, mode, *modeGrad, *faGrad, *modeHess, *faHess;
fa = pvl->directAnswer[tenGageFA][0];
mode = pvl->directAnswer[tenGageMode][0];
faGrad = pvl->directAnswer[tenGageFAGradVec];
modeGrad = pvl->directAnswer[tenGageModeGradVec];
faHess = pvl->directAnswer[tenGageFAHessian];
modeHess = pvl->directAnswer[tenGageModeHessian];
matTmp = pvl->directAnswer[tenGageOmegaHessian];
ELL_3M_ZERO_SET( matTmp );
ELL_3M_SCALE_INCR( matTmp, ( 1+mode )/2, faHess );
ELL_3M_SCALE_INCR( matTmp, fa/2, modeHess );
ELL_3MV_SCALE_OUTER_INCR( matTmp, 0.5, modeGrad, faGrad );
ELL_3MV_SCALE_OUTER_INCR( matTmp, 0.5, faGrad, modeGrad );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageOmegaHessianEval],
pvl->directAnswer[tenGageOmegaHessianEvec],
pvl->directAnswer[tenGageOmegaHessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageOmegaHessianEval],
pvl->directAnswer[tenGageOmegaHessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaLaplacian ) ) {
double *hess;
hess = pvl->directAnswer[tenGageOmegaHessian];
pvl->directAnswer[tenGageOmegaLaplacian][0] = hess[0] + hess[4] + hess[8];
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmega2ndDD ) ) {
double *hess, *norm, tmpv[3];
hess = pvl->directAnswer[tenGageOmegaHessian];
norm = pvl->directAnswer[tenGageOmegaNormal];
ELL_3MV_MUL( tmpv, hess, norm );
pvl->directAnswer[tenGageOmega2ndDD][0] = ELL_3V_DOT( norm, tmpv );
}
/* the copy-and-paste nature of this is really getting out of control ... */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessianContrTenEvec0 ) ) {
double *hess, *evec, tmpv[3];
hess = pvl->directAnswer[tenGageOmegaHessian];
evec = pvl->directAnswer[tenGageEvec0];
ELL_3MV_MUL( tmpv, hess, evec );
pvl->directAnswer[tenGageOmegaHessianContrTenEvec0][0] = ELL_3V_DOT( evec, tmpv );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessianContrTenEvec1 ) ) {
double *hess, *evec, tmpv[3];
hess = pvl->directAnswer[tenGageOmegaHessian];
evec = pvl->directAnswer[tenGageEvec1];
ELL_3MV_MUL( tmpv, hess, evec );
pvl->directAnswer[tenGageOmegaHessianContrTenEvec1][0] = ELL_3V_DOT( evec, tmpv );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaHessianContrTenEvec2 ) ) {
double *hess, *evec, tmpv[3];
hess = pvl->directAnswer[tenGageOmegaHessian];
evec = pvl->directAnswer[tenGageEvec2];
ELL_3MV_MUL( tmpv, hess, evec );
pvl->directAnswer[tenGageOmegaHessianContrTenEvec2][0] = ELL_3V_DOT( evec, tmpv );
}
/* --- evec0 dot products */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceGradVecDotEvec0 ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageTraceGradVec] );
pvl->directAnswer[tenGageTraceGradVecDotEvec0][0] = AIR_ABS( tmp0 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceDiffusionAlign ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageTraceNormal] );
tmp0 = 1 - 2*acos( AIR_ABS( tmp0 ) )/AIR_PI;
pvl->directAnswer[tenGageTraceDiffusionAlign][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTraceDiffusionFraction ) ) {
double tmpv[3];
TEN_TV_MUL( tmpv, tenAns, pvl->directAnswer[tenGageTraceNormal] );
tmp0 = ELL_3V_DOT( tmpv, pvl->directAnswer[tenGageTraceNormal] );
tmp0 /= TEN_T_TRACE( tenAns ) ? TEN_T_TRACE( tenAns ) : 1;
pvl->directAnswer[tenGageTraceDiffusionFraction][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFAGradVecDotEvec0 ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageFAGradVec] );
pvl->directAnswer[tenGageFAGradVecDotEvec0][0] = AIR_ABS( tmp0 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFADiffusionAlign ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageFANormal] );
tmp0 = 1 - 2*acos( AIR_ABS( tmp0 ) )/AIR_PI;
pvl->directAnswer[tenGageFADiffusionAlign][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFADiffusionFraction ) ) {
double tmpv[3];
TEN_TV_MUL( tmpv, tenAns, pvl->directAnswer[tenGageFANormal] );
tmp0 = ELL_3V_DOT( tmpv, pvl->directAnswer[tenGageFANormal] );
tmp0 /= TEN_T_TRACE( tenAns ) ? TEN_T_TRACE( tenAns ) : 1;
pvl->directAnswer[tenGageFADiffusionFraction][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaGradVecDotEvec0 ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageOmegaGradVec] );
pvl->directAnswer[tenGageOmegaGradVecDotEvec0][0] = AIR_ABS( tmp0 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaDiffusionAlign ) ) {
tmp0 = ELL_3V_DOT( evecAns + 0*3, pvl->directAnswer[tenGageOmegaNormal] );
tmp0 = 1 - 2*acos( AIR_ABS( tmp0 ) )/AIR_PI;
pvl->directAnswer[tenGageOmegaDiffusionAlign][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageOmegaDiffusionFraction ) ) {
double tmpv[3];
TEN_TV_MUL( tmpv, tenAns, pvl->directAnswer[tenGageOmegaNormal] );
tmp0 = ELL_3V_DOT( tmpv, pvl->directAnswer[tenGageOmegaNormal] );
tmp0 /= TEN_T_TRACE( tenAns ) ? TEN_T_TRACE( tenAns ) : 1;
pvl->directAnswer[tenGageOmegaDiffusionFraction][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageConfGradVecDotEvec0 ) ) {
double *confGrad;
confGrad = pvl->directAnswer[tenGageTensorGrad];
tmp0 = ELL_3V_DOT( evecAns + 0*3, confGrad );
pvl->directAnswer[tenGageConfGradVecDotEvec0][0] = AIR_ABS( tmp0 );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageConfDiffusionAlign ) ) {
double *confGrad, confNorm[3], tmp;
confGrad = pvl->directAnswer[tenGageTensorGrad];
ELL_3V_NORM( confNorm, confGrad, tmp );
tmp0 = ELL_3V_DOT( evecAns + 0*3, confNorm );
tmp0 = 1 - 2*acos( AIR_ABS( tmp0 ) )/AIR_PI;
pvl->directAnswer[tenGageConfDiffusionAlign][0] = tmp0;
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageConfDiffusionFraction ) ) {
double *confGrad, confNorm[3], tmp, tmpv[3];
confGrad = pvl->directAnswer[tenGageTensorGrad];
ELL_3V_NORM( confNorm, confGrad, tmp );
TEN_TV_MUL( tmpv, tenAns, confNorm );
tmp0 = ELL_3V_DOT( tmpv, confNorm );
tmp0 /= TEN_T_TRACE( tenAns ) ? TEN_T_TRACE( tenAns ) : 1;
pvl->directAnswer[tenGageConfDiffusionFraction][0] = tmp0;
}
/* --- Covariance --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCovariance ) ) {
unsigned int cc, tt, taa, tbb,
vijk, vii, vjj, vkk, fd, fddd;
double *cov, ww, wxx, wyy, wzz, ten[7];
cov = pvl->directAnswer[tenGageCovariance];
/* HEY: casting because radius signed ( shouldn't be ) */
fd = AIR_CAST( unsigned int, 2*ctx->radius );
fddd = fd*fd*fd;
/* reset answer */
for ( cc=0; cc<21; cc++ ) {
cov[cc] = 0;
}
ten[0] = 1; /* never used anyway */
for ( vijk=0; vijk<fddd; vijk++ ) {
vii = vijk % fd;
vjj = ( vijk/fd ) % fd;
vkk = vijk/fd/fd;
wxx = ctx->fw[vii + fd*( 0 + 3*gageKernel00 )];
wyy = ctx->fw[vjj + fd*( 1 + 3*gageKernel00 )];
wzz = ctx->fw[vkk + fd*( 2 + 3*gageKernel00 )];
ww = wxx*wyy*wzz;
for ( tt=1; tt<7; tt++ ) {
ten[tt] = ww*( pvl->iv3[vijk + fddd*tt] - tenAns[tt] );
}
cc = 0;
for ( taa=0; taa<6; taa++ ) {
for ( tbb=taa; tbb<6; tbb++ ) {
/* HEY: do I really mean to have this factor in here? */
/* it probably meant that the units in the IGRT TMI paper were
wrong by this factor ... */
cov[cc] += 100000*ten[taa+1]*ten[tbb+1];
cc++;
}
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCovarianceRGRT ) ) {
double *cov, *covr, *igrt[6];
unsigned int taa, tbb, cc;
cov = pvl->directAnswer[tenGageCovariance];
covr = pvl->directAnswer[tenGageCovarianceRGRT];
igrt[0] = pvl->directAnswer[tenGageDelNormR1];
igrt[1] = pvl->directAnswer[tenGageDelNormR2];
igrt[2] = pvl->directAnswer[tenGageDelNormK3];
igrt[3] = pvl->directAnswer[tenGageDelNormPhi1];
igrt[4] = pvl->directAnswer[tenGageDelNormPhi2];
igrt[5] = pvl->directAnswer[tenGageDelNormPhi3];
cc = 0;
for ( taa=0; taa<6; taa++ ) {
for ( tbb=taa; tbb<6; tbb++ ) {
covr[cc] = tenDoubleContract_d( igrt[tbb], cov, igrt[taa] );
cc++;
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCovarianceKGRT ) ) {
double *cov, *covk, *igrt[6], delnormk1[7];
unsigned int taa, tbb, cc;
cov = pvl->directAnswer[tenGageCovariance];
covk = pvl->directAnswer[tenGageCovarianceKGRT];
TEN_T_SET( delnormk1, 1, 0.57735026, 0, 0, 0.57735026, 0, 0.57735026 );
igrt[0] = delnormk1;
igrt[1] = pvl->directAnswer[tenGageDelNormK2];
igrt[2] = pvl->directAnswer[tenGageDelNormK3];
igrt[3] = pvl->directAnswer[tenGageDelNormPhi1];
igrt[4] = pvl->directAnswer[tenGageDelNormPhi2];
igrt[5] = pvl->directAnswer[tenGageDelNormPhi3];
cc = 0;
for ( taa=0; taa<6; taa++ ) {
for ( tbb=taa; tbb<6; tbb++ ) {
covk[cc] = tenDoubleContract_d( igrt[tbb], cov, igrt[taa] );
cc++;
}
}
}
/* these are items that somewhat bypass the convolution result
( in tenGageTensor ) because it has to do something else fancy
with the constituent tensors. This is young and hacky code;
and it may be that facilitating this kind of processing should
be better supported by the gage API ... */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorLogEuclidean ) ||
GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorQuatGeoLoxK ) ||
GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorQuatGeoLoxR ) ||
GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorRThetaPhiLinear ) ) {
unsigned int vijk, vii, vjj, vkk, fd, fddd;
_tenGagePvlData *pvlData;
double *ans;
int qret;
pvlData = AIR_CAST( _tenGagePvlData *, pvl->data );
/* HEY: casting because radius is signed ( shouldn't be ) */
fd = AIR_CAST( unsigned int, 2*ctx->radius );
fddd = fd*fd*fd;
for ( vijk=0; vijk<fddd; vijk++ ) {
double wxx, wyy, wzz;
unsigned int tt;
vii = vijk % fd;
vjj = ( vijk/fd ) % fd;
vkk = vijk/fd/fd;
wxx = ctx->fw[vii + fd*( 0 + 3*gageKernel00 )];
wyy = ctx->fw[vjj + fd*( 1 + 3*gageKernel00 )];
wzz = ctx->fw[vkk + fd*( 2 + 3*gageKernel00 )];
pvlData->buffWght[vijk] = wxx*wyy*wzz;
for ( tt=0; tt<7; tt++ ) {
pvlData->buffTen[tt + 7*vijk] = pvl->iv3[vijk + fddd*tt];
}
}
qret = 0;
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorLogEuclidean ) ) {
ans = pvl->directAnswer[tenGageTensorLogEuclidean];
qret = tenInterpN_d( ans, pvlData->buffTen, pvlData->buffWght, fddd,
tenInterpTypeLogLinear, pvlData->tip );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorQuatGeoLoxK ) ) {
ans = pvl->directAnswer[tenGageTensorQuatGeoLoxK];
qret = tenInterpN_d( ans, pvlData->buffTen, pvlData->buffWght, fddd,
tenInterpTypeQuatGeoLoxK, pvlData->tip );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorQuatGeoLoxR ) ) {
ans = pvl->directAnswer[tenGageTensorQuatGeoLoxR];
qret= tenInterpN_d( ans, pvlData->buffTen, pvlData->buffWght, fddd,
tenInterpTypeQuatGeoLoxR, pvlData->tip );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorRThetaPhiLinear ) ) {
ans = pvl->directAnswer[tenGageTensorRThetaPhiLinear];
qret= tenInterpN_d( ans, pvlData->buffTen, pvlData->buffWght, fddd,
tenInterpTypeRThetaPhiLinear, pvlData->tip );
}
if ( qret ) {
char *lerr;
fprintf( stderr, "!%s: problem!!!\n %s", me, lerr = biffGet( TEN ) );
free( lerr );
}
}
/* --- cl/cp/ca gradients --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1GradVec ) ) {
vecTmp = pvl->directAnswer[tenGageCl1GradVec];
ELL_3V_SET( vecTmp,
( evalAns[0]*( -2*pvl->directAnswer[tenGageEvalGrads][3]-pvl->directAnswer[tenGageEvalGrads][6] )
+evalAns[1]*( 2*pvl->directAnswer[tenGageEvalGrads][0]+pvl->directAnswer[tenGageEvalGrads][6] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][0]-pvl->directAnswer[tenGageEvalGrads][3] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
( evalAns[0]*( -2*pvl->directAnswer[tenGageEvalGrads][4]-pvl->directAnswer[tenGageEvalGrads][7] )
+evalAns[1]*( 2*pvl->directAnswer[tenGageEvalGrads][1]+pvl->directAnswer[tenGageEvalGrads][7] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][1]-pvl->directAnswer[tenGageEvalGrads][4] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
( evalAns[0]*( -2*pvl->directAnswer[tenGageEvalGrads][5]-pvl->directAnswer[tenGageEvalGrads][8] )
+evalAns[1]*( 2*pvl->directAnswer[tenGageEvalGrads][2]+pvl->directAnswer[tenGageEvalGrads][8] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][2]-pvl->directAnswer[tenGageEvalGrads][5] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1GradMag ) ) {
magTmp = pvl->directAnswer[tenGageCl1GradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1Normal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageCl1Normal],
magTmp ? 1/magTmp : 0, vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1GradVec ) ) {
vecTmp = pvl->directAnswer[tenGageCp1GradVec];
ELL_3V_SET( vecTmp,
2*( evalAns[0]*( pvl->directAnswer[tenGageEvalGrads][3]-pvl->directAnswer[tenGageEvalGrads][6] )
+evalAns[1]*( -pvl->directAnswer[tenGageEvalGrads][0]-2*pvl->directAnswer[tenGageEvalGrads][6] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][0]+2*pvl->directAnswer[tenGageEvalGrads][3] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
2*( evalAns[0]*( pvl->directAnswer[tenGageEvalGrads][4]-pvl->directAnswer[tenGageEvalGrads][7] )
+evalAns[1]*( -pvl->directAnswer[tenGageEvalGrads][1]-2*pvl->directAnswer[tenGageEvalGrads][7] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][1]+2*pvl->directAnswer[tenGageEvalGrads][4] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
2*( evalAns[0]*( pvl->directAnswer[tenGageEvalGrads][5]-pvl->directAnswer[tenGageEvalGrads][8] )
+evalAns[1]*( -pvl->directAnswer[tenGageEvalGrads][2]-2*pvl->directAnswer[tenGageEvalGrads][8] )
+evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][2]+2*pvl->directAnswer[tenGageEvalGrads][5] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1GradMag ) ) {
magTmp = pvl->directAnswer[tenGageCp1GradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1Normal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageCp1Normal],
magTmp ? 1/magTmp : 0, vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1GradVec ) ) {
vecTmp = pvl->directAnswer[tenGageCa1GradVec];
ELL_3V_SET( vecTmp,
-3*( ( evalAns[0]+evalAns[1] )*pvl->directAnswer[tenGageEvalGrads][6]
-evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][0]+pvl->directAnswer[tenGageEvalGrads][3] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
-3*( ( evalAns[0]+evalAns[1] )*pvl->directAnswer[tenGageEvalGrads][7]
-evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][1]+pvl->directAnswer[tenGageEvalGrads][4] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ),
-3*( ( evalAns[0]+evalAns[1] )*pvl->directAnswer[tenGageEvalGrads][8]
-evalAns[2]*( pvl->directAnswer[tenGageEvalGrads][2]+pvl->directAnswer[tenGageEvalGrads][5] ) )
/( pvl->directAnswer[tenGageTrace][0]*pvl->directAnswer[tenGageTrace][0] ) );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1GradMag ) ) {
magTmp = pvl->directAnswer[tenGageCa1GradMag][0] = ELL_3V_LEN( vecTmp );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1Normal ) ) {
ELL_3V_SCALE( pvl->directAnswer[tenGageCa1Normal],
magTmp ? 1/magTmp : 0, vecTmp );
}
/* --- tensor gradient, rotated into eigenframe of the tensor itself --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageTensorGradRotE ) ) {
/* confidence not affected by rotation */
double evecsT[9], evecs[9], tmp[9], tmp2[9];
double diff0, diff1, diff2, diffthresh;
double rdiff0, rdiff1, rdiff2;
unsigned int evi, tci;
ELL_3V_COPY( pvl->directAnswer[tenGageTensorGradRotE],
pvl->directAnswer[tenGageTensorGrad] );
/* pre-compute relative eval diffs to detect ill-conditioned case */
diff0=( evalAns[0]-evalAns[1] )/( fabs( evalAns[0] )+fabs( evalAns[1] ) );
diff1=( evalAns[1]-evalAns[2] )/( fabs( evalAns[1] )+fabs( evalAns[2] ) );
diff2=( evalAns[0]-evalAns[2] )/( fabs( evalAns[0] )+fabs( evalAns[2] ) );
diffthresh=0.05;
if ( diff2>diffthresh ) rdiff2=1.0; else rdiff2=diff2/diffthresh;
if ( diff1>diffthresh ) rdiff1=1.0; else rdiff1=diff1/diffthresh;
if ( diff0>diffthresh ) rdiff0=1.0; else rdiff0=diff0/diffthresh;
ELL_3M_COPY( evecs, evecAns );
ELL_3M_TRANSPOSE( evecsT, evecs );
for ( evi=0; evi<3; evi++ ) {
char sign;
/* first, simply rotate derivatives into eigenframe of value */
TEN_T2M( tmp, gradDdXYZ + evi*7 );
ell_3m_mul_d( tmp2, tmp, evecsT );
ell_3m_mul_d( tmp, evecs, tmp2 );
/* If necessary, perform a number of additional rotations to
* distribute eigenvalue derivatives equally in ill-defined
* cases. Explanation in Schultz and Seidel, "Using Eigenvalue
* Derivatives for Edge Detection in DT-MRI Data", DAGM 2008*/
if ( rdiff0<1.0 ) {
/* the goal is to find the smallest angle phi such that
* rotation by phi around z will result in tmp[0]=tmp[4], i.e.:
*
* cos( phi )^2*tmp[0]-2*sin( phi )*cos( phi )*tmp[1]+sin( phi )^2*tmp[4] =
* sin( phi )^2*tmp[0]+2*sin( phi )*cos( phi )*tmp[1]+cos( phi )^2*tmp[4]
* =>
* tan( 2*phi )=( tmp[0]-tmp[4] )/( 2*tmp[1] )
*
* we use atan2 to avoid potential problems with tmp[1]==0,
* but manipulate the signs of the arguments s.t. the result
* is always in [-pi/2, pi/2] ( i.e., the smallest solution of
* the above equality )
*/
/* rotate around z axis */
double phi, R[9], RT[9];
sign = ( tmp[0]-tmp[4] )*tmp[1]>0?1:-1;
phi=0.5*atan2( sign*fabs( tmp[0]-tmp[4] ), fabs( 2*tmp[1] ) );
ELL_3M_ROTATE_Z_SET( R, ( 1.0-rdiff0 )*phi );
ELL_3M_TRANSPOSE( RT, R );
ell_3m_mul_d( tmp2, tmp, RT );
ell_3m_mul_d( tmp, R, tmp2 );
}
if ( rdiff1<1.0 ) {
/* rotate around x axis */
double phi, R[9], RT[9];
sign = ( tmp[4]-tmp[8] )*tmp[5]>0?1:-1;
phi=0.5*atan2( sign*fabs( tmp[4]-tmp[8] ), fabs( 2*tmp[5] ) );
ELL_3M_ROTATE_X_SET( R, ( 1.0-rdiff1 )*phi );
ELL_3M_TRANSPOSE( RT, R );
ell_3m_mul_d( tmp2, tmp, RT );
ell_3m_mul_d( tmp, R, tmp2 );
}
if ( rdiff2<1.0 ) {
double mean, submatrix[3], isoPhi, _gamma, beta, A, C, R[9], RT[9];
int axis, midaxis, smallest;
mean=( tmp[0]+tmp[4]+tmp[8] )/3.0;
/* what's the median? */
midaxis=0;
if ( ( tmp[0]>tmp[4] && tmp[4]>tmp[8] )||
( tmp[0]<tmp[4] && tmp[4]<tmp[8] ) )
midaxis=1;
else if ( ( tmp[4]>tmp[8] && tmp[8]>tmp[0] )||
( tmp[4]<tmp[8] && tmp[8]<tmp[0] ) )
midaxis=2;
/* do we first rotate around smallest or largest? */
smallest = 0;
if ( mean>tmp[4*midaxis] ) {
smallest=1;
}
sign=1;
if ( ( smallest && ( tmp[0]<tmp[4] && tmp[0]<tmp[8] ) ) ||
( !smallest && ( tmp[0]>tmp[4] && tmp[0]>tmp[8] ) ) ) {
axis=0;
submatrix[0]=tmp[4];
submatrix[1]=tmp[5];
submatrix[2]=tmp[8];
if ( midaxis!=1 ) sign=-1;
} else if ( ( smallest && ( tmp[4]<tmp[0] && tmp[4]<tmp[8] ) ) ||
( !smallest && ( tmp[4]>tmp[0] && tmp[4]>tmp[8] ) ) ) {
axis=1;
submatrix[0]=tmp[8];
submatrix[1]=tmp[2];
submatrix[2]=tmp[0];
if ( midaxis!=2 ) sign=-1;
} else {
axis=2;
submatrix[0]=tmp[0];
submatrix[1]=tmp[1];
submatrix[2]=tmp[4];
if ( midaxis!=0 ) sign=-1;
}
isoPhi=0.0f;
_gamma=sign*( submatrix[0]-submatrix[2] );
beta=-sign*2*submatrix[1];
A=sqrt( _gamma*_gamma+beta*beta );
C=atan2( _gamma, beta );
isoPhi=0.5*( asin( 2.0/A*( mean-0.5*( submatrix[0]+submatrix[2] ) ) )-C );
/* make sure we use the minimal rotation */
isoPhi=asin( 2.0/A*( mean-0.5*( submatrix[0]+submatrix[2] ) ) );
if ( isoPhi>0 ) {
if ( fabs( AIR_PI-isoPhi-C )<fabs( isoPhi-C ) )
isoPhi=0.5*( AIR_PI-isoPhi-C );
else
isoPhi=0.5*( isoPhi-C );
} else if ( isoPhi<0 ) {
if ( fabs( -AIR_PI-isoPhi-C )<fabs( isoPhi-C ) )
isoPhi=0.5*( -AIR_PI-isoPhi-C );
else
isoPhi=0.5*( isoPhi-C );
}
/* perform the rotation */
switch ( axis ) {
case 0:
ELL_3M_ROTATE_X_SET( R, ( 1.0-rdiff2 )*isoPhi );
break;
case 1:
ELL_3M_ROTATE_Y_SET( R, ( 1.0-rdiff2 )*isoPhi );
break;
default:
ELL_3M_ROTATE_Z_SET( R, ( 1.0-rdiff2 )*isoPhi );
break;
}
ELL_3M_TRANSPOSE( RT, R );
ell_3m_mul_d( tmp2, tmp, RT );
ell_3m_mul_d( tmp, R, tmp2 );
/* rotate around the now corrected evec */
axis=midaxis;
switch ( midaxis ) {
case 0:
sign = ( tmp[0]-tmp[4] )*tmp[1]>0?1:-1;
ELL_3M_ROTATE_X_SET( R, ( 1.0-rdiff2 )*0.5*atan2( sign*fabs( tmp[0]-tmp[4] ), fabs( 2*tmp[1] ) ) );
break;
case 1:
sign = ( tmp[8]-tmp[0] )*tmp[2]>0?1:-1;
ELL_3M_ROTATE_Y_SET( R, ( 1.0-rdiff2 )*0.5*atan2( sign*fabs( tmp[8]-tmp[0] ), fabs( 2*tmp[2] ) ) );
break;
case 2:
sign = ( tmp[4]-tmp[8] )*tmp[5]>0?1:-1;
ELL_3M_ROTATE_Z_SET( R, ( 1.0-rdiff2 )*0.5*atan2( sign*fabs( tmp[4]-tmp[8] ), fabs( 2*tmp[5] ) ) );
break;
}
ELL_3M_TRANSPOSE( RT, R );
ell_3m_mul_d( tmp2, tmp, RT );
ell_3m_mul_d( tmp, R, tmp2 );
}
/* Now, we can set the answer */
TEN_M2T( tmp2, tmp );
for ( tci=1; tci<7; tci++ ) {
pvl->directAnswer[tenGageTensorGradRotE][3*tci+evi] = tmp2[tci];
}
}
}
/* --- Eigenvalue Hessians: rotate partial second derivatives into
* eigenframe and take into account correction factor based on
* eigenvector derivatives --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageEvalHessian ) ) {
/* some aliases for convenience: */
double *thess = pvl->directAnswer[tenGageHessian];
double *evhess = pvl->directAnswer[tenGageEvalHessian];
double *tgradE = pvl->directAnswer[tenGageTensorGradRotE];
int dira, dirb; /* directions of first/second partial derivative */
int k; /* number of the eigenvalue */
double evecsT[9], tmp[9];
/* HEY: some copy-paste from tenGageEvalGrads */
double eps=0.05; /* treshold for handling degeneracies */
double rel1, rel2, rel3, w1, w2;
/* interpolation weights from relative eigenvalue distance */
rel1=evalAns[0]*evalAns[0]+evalAns[1]*evalAns[1];
if ( rel1>1e-10 )
rel1=( evalAns[0]-evalAns[1] )*( evalAns[0]-evalAns[1] )/rel1;
rel2=evalAns[0]*evalAns[0]+evalAns[2]*evalAns[2];
if ( rel2>1e-10 )
rel2=( evalAns[0]-evalAns[2] )*( evalAns[0]-evalAns[2] )/rel2;
rel3=evalAns[1]*evalAns[1]+evalAns[2]*evalAns[2];
if ( rel3>1e-10 )
rel3=( evalAns[1]-evalAns[2] )*( evalAns[1]-evalAns[2] )/rel3;
w1=rel1/eps-1;
w1*=w1;
w2=rel2/eps-1;
w2*=w2;
ELL_3M_TRANSPOSE( evecsT, evecAns );
for ( dira=0; dira<3; dira++ ) {
for ( dirb=0; dirb<=dira; dirb++ ) { /* exploit symmetry of Hessian */
double rdiff1, rdiff2;
double l1res, l2res, l3res;
/* collect second partial derivatives in dira, dirb */
double H[9];
ELL_3V_SET( H, thess[9+3*dirb+dira], thess[18+3*dirb+dira],
thess[27+3*dirb+dira] );
ELL_3V_SET( H+3, thess[18+3*dirb+dira], thess[36+3*dirb+dira],
thess[45+3*dirb+dira] );
ELL_3V_SET( H+6, thess[27+3*dirb+dira], thess[45+3*dirb+dira],
thess[54+3*dirb+dira] );
/* rotate into eigenframe of value */
ell_3m_mul_d( tmp, H, evecsT );
ell_3m_mul_d( H, evecAns, tmp );
/* we have to divide by rdiff=lambda_1-lambda_2; the following
* is a heuristic to avoid numerical problems in case rdiff is
* near zero */
if ( rel1>eps ) rdiff1=1.0/( evalAns[0]-evalAns[1] );
else if ( rel1>1e-10 ) rdiff1=( rel1/eps )/( evalAns[0]-evalAns[1] );
else rdiff1=0;
if ( rel2>eps ) rdiff2=1.0/( evalAns[0]-evalAns[2] );
else if ( rel2>1e-10 ) rdiff2=( rel2/eps )/( evalAns[0]-evalAns[2] );
else rdiff2=0;
l1res=H[0]+2*( tgradE[6+dira]*tgradE[6+dirb]*rdiff1+
tgradE[9+dira]*tgradE[9+dirb]*rdiff2 );
if ( rel1>eps ) rdiff1=1.0/( evalAns[1]-evalAns[0] );
else if ( rel1>1e-10 ) rdiff1=( rel1/eps )/( evalAns[1]-evalAns[0] );
else rdiff1=0;
if ( rel3>eps ) rdiff2=1.0/( evalAns[1]-evalAns[2] );
else if ( rel3>1e-10 ) rdiff2=( rel3/eps )/( evalAns[1]-evalAns[2] );
else rdiff2=0;
l2res=H[4]+2*( tgradE[6+dira]*tgradE[6+dirb]*rdiff1+
tgradE[15+dira]*tgradE[15+dirb]*rdiff2 );
if ( rel2>eps ) rdiff1=1.0/( evalAns[2]-evalAns[0] );
else if ( rel2>1e-10 ) rdiff1=( rel2/eps )/( evalAns[2]-evalAns[0] );
else rdiff1=0;
if ( rel3>eps ) rdiff2=1.0/( evalAns[2]-evalAns[1] );
else if ( rel3>1e-10 ) rdiff2=( rel3/eps )/( evalAns[2]-evalAns[1] );
else rdiff2=0;
l3res=H[8]+2*( tgradE[9+dira]*tgradE[9+dirb]*rdiff1+
tgradE[15+dira]*tgradE[15+dirb]*rdiff2 );
if ( rel1>eps )
evhess[3*dirb+dira]=l1res;
else {
if ( rel2>eps )
evhess[3*dirb+dira]=( 1-0.5*w1 )*l1res+0.5*w1*l2res;
else
evhess[3*dirb+dira]=( 1-0.5*w1-w1*w2/6.0 )*l1res+
( 0.5*w1-w1*w2/6.0 )*l2res+
w1*w2/3.0*l3res;
}
if ( rel2>eps )
evhess[18+3*dirb+dira]=l3res;
else {
if ( rel1>eps )
evhess[18+3*dirb+dira]=( 1-0.5*w2 )*l3res+0.5*w2*l2res;
else
evhess[18+3*dirb+dira]=( 1-0.5*w2-w1*w2/6.0 )*l3res+
( 0.5*w2-w1*w2/6.0 )*l2res+
w1*w2/3.0*l1res;
}
evhess[9+3*dirb+dira]=l1res+l2res+l3res - evhess[3*dirb+dira] -
evhess[18+3*dirb+dira];
}
}
for ( dira=0; dira<2; dira++ ) {
for ( dirb=dira+1; dirb<3; dirb++ ) { /* copy over symmetric values */
for ( k=0; k<3; k++ ) {
evhess[9*k+3*dirb+dira]=evhess[9*k+3*dira+dirb];
}
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1Hessian ) ) {
int dira, dirb;
double *cl1hess = pvl->directAnswer[tenGageCl1Hessian];
double *tgradE = pvl->directAnswer[tenGageTensorGradRotE];
double *evhess = pvl->directAnswer[tenGageEvalHessian];
/* A and B come out of the quotient rule; cf. appendix of Schultz
* et al., TVCG 2010 for more details */
double B = ( evalAns[0]+evalAns[1]+evalAns[2] )*
( evalAns[0]+evalAns[1]+evalAns[2] );
for ( dira=0; dira<3; dira++ ) {
for ( dirb=0; dirb<=dira; dirb++ ) { /* again, exploit Hessian symmetry */
double A = evalAns[0]*( -2*tgradE[12+dira]-tgradE[18+dira] )+
evalAns[1]*( 2*tgradE[3+dira]+tgradE[18+dira] )+
evalAns[2]*( tgradE[3+dira]-tgradE[12+dira] );
double Ad = tgradE[3+dirb]*( -2*tgradE[12+dira]-tgradE[18+dira] )+
evalAns[0]*( -2*evhess[9+3*dirb+dira]-evhess[18+3*dirb+dira] )+
tgradE[12+dirb]*( 2*tgradE[3+dira]+tgradE[18+dira] )+
evalAns[1]*( 2*evhess[3*dirb+dira]+evhess[18+3*dirb+dira] )+
tgradE[18+dirb]*( tgradE[3+dira]-tgradE[12+dira] )+
evalAns[2]*( evhess[3*dirb+dira]-evhess[9+3*dirb+dira] );
double Bd = 2*( evalAns[0]+evalAns[1]+evalAns[2] )*
( tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb] );
cl1hess[3*dirb+dira]=Ad/B-A/B*Bd/B;
}
}
for ( dira=0; dira<2; dira++ ) {
for ( dirb=dira+1; dirb<3; dirb++ ) { /* copy over symmetric values */
cl1hess[3*dirb+dira]=cl1hess[3*dira+dirb];
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1HessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageCl1HessianEval],
pvl->directAnswer[tenGageCl1HessianEvec],
pvl->directAnswer[tenGageCl1Hessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCl1HessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageCl1HessianEval],
pvl->directAnswer[tenGageCl1Hessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1Hessian ) ) {
int dira, dirb;
double *cp1hess = pvl->directAnswer[tenGageCp1Hessian];
double *tgradE = pvl->directAnswer[tenGageTensorGradRotE];
double *evhess = pvl->directAnswer[tenGageEvalHessian];
double B = ( evalAns[0]+evalAns[1]+evalAns[2] )*
( evalAns[0]+evalAns[1]+evalAns[2] );
for ( dira=0; dira<3; dira++ ) {
for ( dirb=0; dirb<=dira; dirb++ ) { /* again, exploit Hessian symmetry */
double A = 2*( evalAns[0]*( tgradE[12+dira]-tgradE[18+dira] )+
evalAns[1]*( -tgradE[3+dira]-2*tgradE[18+dira] )+
evalAns[2]*( tgradE[3+dira]+2*tgradE[12+dira] ) );
double Ad=2*( evalAns[0]*( evhess[9+3*dirb+dira]-evhess[18+3*dirb+dira] )+
evalAns[1]*( -evhess[3*dirb+dira]-2*evhess[18+3*dirb+dira] )+
evalAns[2]*( evhess[3*dirb+dira]+2*evhess[9+3*dirb+dira] )+
tgradE[3+dirb]*( tgradE[12+dira]-tgradE[18+dira] )+
tgradE[12+dirb]*( -tgradE[3+dira]-2*tgradE[18+dira] )+
tgradE[18+dirb]*( tgradE[3+dira]+2*tgradE[12+dira] ) );
double Bd = 2*( evalAns[0]+evalAns[1]+evalAns[2] )*
( tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb] );
cp1hess[3*dirb+dira]=Ad/B-A/B*Bd/B;
}
}
for ( dira=0; dira<2; dira++ ) {
for ( dirb=dira+1; dirb<3; dirb++ ) { /* copy over symmetric values */
cp1hess[3*dirb+dira]=cp1hess[3*dira+dirb];
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1HessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageCp1HessianEval],
pvl->directAnswer[tenGageCp1HessianEvec],
pvl->directAnswer[tenGageCp1Hessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCp1HessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageCp1HessianEval],
pvl->directAnswer[tenGageCp1Hessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1Hessian ) ) {
int dira, dirb;
double *ca1hess = pvl->directAnswer[tenGageCa1Hessian];
double *tgradE = pvl->directAnswer[tenGageTensorGradRotE];
double *evhess = pvl->directAnswer[tenGageEvalHessian];
double B = ( evalAns[0]+evalAns[1]+evalAns[2] )*
( evalAns[0]+evalAns[1]+evalAns[2] );
for ( dira=0; dira<3; dira++ ) {
for ( dirb=0; dirb<=dira; dirb++ ) { /* again, exploit Hessian symmetry */
double A = 3*( evalAns[0]*tgradE[18+dira]+evalAns[1]*tgradE[18+dira]+
evalAns[2]*( -tgradE[3+dira]-tgradE[12+dira] ) );
double Ad = 3*( tgradE[3+dirb]*tgradE[18+dira]+
tgradE[12+dirb]*tgradE[18+dira]+
tgradE[18+dirb]*( -tgradE[3+dira]-tgradE[12+dira] )+
evalAns[0]*evhess[18+3*dirb+dira]+
evalAns[1]*evhess[18+3*dirb+dira]+
evalAns[2]*( -evhess[3*dirb+dira]-evhess[9+3*dirb+dira] ) );
double Bd = 2*( evalAns[0]+evalAns[1]+evalAns[2] )*
( tgradE[3+dirb]+tgradE[12+dirb]+tgradE[18+dirb] );
/* above formulas are true for cs, so flip sign here */
ca1hess[3*dirb+dira]=-Ad/B+A/B*Bd/B;
}
}
for ( dira=0; dira<2; dira++ ) {
for ( dirb=dira+1; dirb<3; dirb++ ) { /* copy over symmetric values */
ca1hess[3*dirb+dira]=ca1hess[3*dira+dirb];
}
}
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1HessianEvec ) ) {
ell_3m_eigensolve_d( pvl->directAnswer[tenGageCa1HessianEval],
pvl->directAnswer[tenGageCa1HessianEvec],
pvl->directAnswer[tenGageCa1Hessian], AIR_TRUE );
} else if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageCa1HessianEval ) ) {
ell_3m_eigenvalues_d( pvl->directAnswer[tenGageCa1HessianEval],
pvl->directAnswer[tenGageCa1Hessian], AIR_TRUE );
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFiberCurving )
|| GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFiberDispersion ) ) {
double rtout[7], evout[7];
TEN_T3V_OUTER( rtout, pvl->directAnswer[tenGageRotTans] + 1*3 );
TEN_T3V_OUTER_INCR( rtout, pvl->directAnswer[tenGageRotTans] + 2*3 );
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFiberCurving ) ) {
TEN_T3V_OUTER( evout, pvl->directAnswer[tenGageEvec0] );
pvl->directAnswer[tenGageFiberCurving][0] = TEN_T_DOT( rtout, evout );
/* pvl->directAnswer[tenGageFiberCurving][0] *= 100000; */
}
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageFiberDispersion ) ) {
TEN_T3V_OUTER( evout, pvl->directAnswer[tenGageEvec1] );
TEN_T3V_OUTER_INCR( evout, pvl->directAnswer[tenGageEvec2] );
pvl->directAnswer[tenGageFiberDispersion][0] = TEN_T_DOT( rtout, evout );
/* pvl->directAnswer[tenGageFiberDispersion][0] *= 100000; */
}
}
/* --- Aniso --- */
if ( GAGE_QUERY_ITEM_TEST( pvl->query, tenGageAniso ) ) {
for ( ci=tenAnisoUnknown+1; ci<=TEN_ANISO_MAX; ci++ ) {
pvl->directAnswer[tenGageAniso][ci] = tenAnisoEval_d( evalAns, ci );
}
}
return;
}
void *
2037 _tenGagePvlDataNew( const struct gageKind_t *kind ) {
_tenGagePvlData *pvlData;
AIR_UNUSED( kind );
pvlData = AIR_CALLOC( 1, _tenGagePvlData );
if ( pvlData ) {
pvlData->buffTen = NULL;
pvlData->buffWght = NULL;
pvlData->tip = tenInterpParmNew( );
}
return pvlData;
}
void *
2051 _tenGagePvlDataCopy( const struct gageKind_t *kind,
const void *_pvlDataOld ) {
_tenGagePvlData *pvlDataNew, *pvlDataOld;
unsigned int num;
AIR_UNUSED( kind );
pvlDataOld = AIR_CAST( _tenGagePvlData *, _pvlDataOld );
num = pvlDataOld->tip->allocLen;
pvlDataNew = AIR_CALLOC( 1, _tenGagePvlData );
if ( pvlDataNew ) {
pvlDataNew->buffTen = AIR_CALLOC( 7*num, double );
pvlDataNew->buffWght = AIR_CALLOC( num, double );
pvlDataNew->tip = tenInterpParmCopy( pvlDataOld->tip );
}
return pvlDataNew;
}
void *
2069 _tenGagePvlDataNix( const struct gageKind_t *kind,
void *_pvlData ) {
_tenGagePvlData *pvlData;
AIR_UNUSED( kind );
pvlData = AIR_CAST( _tenGagePvlData *, _pvlData );
airFree( pvlData->buffTen );
airFree( pvlData->buffWght );
tenInterpParmNix( pvlData->tip );
airFree( pvlData );
return NULL;
}
int
2083 _tenGagePvlDataUpdate( const struct gageKind_t *kind,
const gageContext *ctx, const gagePerVolume *pvl,
const void *_pvlData ) {
_tenGagePvlData *pvlData;
unsigned int fd, num;
AIR_UNUSED( kind );
AIR_UNUSED( pvl );
pvlData = AIR_CAST( _tenGagePvlData *, _pvlData );
fd = AIR_CAST( unsigned int, 2*ctx->radius );
num = fd*fd*fd;
if ( num != pvlData->tip->allocLen ) {
/* HEY: no error checking */
airFree( pvlData->buffTen );
pvlData->buffTen = NULL;
airFree( pvlData->buffWght );
pvlData->buffWght = NULL;
pvlData->buffTen = AIR_CALLOC( 7*num, double );
pvlData->buffWght = AIR_CALLOC( num, double );
tenInterpParmBufferAlloc( pvlData->tip, num );
}
return 0;
}
gageKind
_tenGageKind = {
AIR_FALSE, /* statically allocated */
"tensor",
&_tenGage,
1,
7,
TEN_GAGE_ITEM_MAX,
_tenGageTable,
_tenGageIv3Print,
_tenGageFilter,
_tenGageAnswer,
_tenGagePvlDataNew,
_tenGagePvlDataCopy,
_tenGagePvlDataNix,
_tenGagePvlDataUpdate,
NULL
};
gageKind *
tenGageKind = &_tenGageKind;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
const char *
tenModelPrefixStr = "DWMRI_model:";
static const tenModel *
31 str2model( const char *str ) {
const tenModel *ret;
if ( !strcmp( str, TEN_MODEL_STR_ZERO ) ) {
ret = tenModelZero;
} else if ( !strcmp( str, TEN_MODEL_STR_B0 ) ) {
ret = tenModelB0;
} else if ( !strcmp( str, TEN_MODEL_STR_BALL ) ) {
ret = tenModelBall;
} else if ( !strcmp( str, TEN_MODEL_STR_1STICK ) ) {
ret = tenModel1Stick;
} else if ( !strcmp( str, TEN_MODEL_STR_1VECTOR2D ) ) {
ret = tenModel1Vector2D;
} else if ( !strcmp( str, TEN_MODEL_STR_1UNIT2D ) ) {
ret = tenModel1Unit2D;
} else if ( !strcmp( str, TEN_MODEL_STR_2UNIT2D ) ) {
ret = tenModel2Unit2D;
} else if ( !strcmp( str, TEN_MODEL_STR_BALL1STICKEMD ) ) {
ret = tenModelBall1StickEMD;
} else if ( !strcmp( str, TEN_MODEL_STR_BALL1STICK ) ) {
ret = tenModelBall1Stick;
} else if ( !strcmp( str, TEN_MODEL_STR_BALL1CYLINDER ) ) {
ret = tenModelBall1Cylinder;
} else if ( !strcmp( str, TEN_MODEL_STR_1CYLINDER ) ) {
ret = tenModel1Cylinder;
} else if ( !strcmp( str, TEN_MODEL_STR_1TENSOR2 ) ) {
ret = tenModel1Tensor2;
} else {
/* we don't currently have a tenModelUnknown */
ret = NULL;
}
return ret;
}
int
66 tenModelParse( const tenModel **model, int *plusB0,
int requirePrefix, const char *_str ) {
static const char me[]="tenModelParse";
char *str, *modstr, *pre;
airArray *mop;
if ( !( model && plusB0 && _str ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
str = airStrdup( _str );
if ( !str ) {
biffAddf( TEN, "%s: couldn't strdup", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, str, airFree, airMopAlways );
pre = strstr( str, tenModelPrefixStr );
if ( pre ) {
str += strlen( tenModelPrefixStr );
} else {
if ( requirePrefix ) {
biffAddf( TEN, "%s: didn't see prefix \"%s\" in \"%s\"", me,
tenModelPrefixStr, _str );
airMopError( mop ); return 1;
}
}
airToLower( str ); /* for sake of "b0" and str2model below */
if ( ( modstr = strchr( str, '+' ) ) ) {
*modstr = '\0';
++modstr;
if ( !strcmp( str, "b0" ) ) {
*plusB0 = AIR_TRUE;
} else {
biffAddf( TEN, "%s: string ( \"%s\" ) prior to \"+\" not \"b0\"", me, str );
airMopError( mop ); return 1;
}
} else {
*plusB0 = AIR_FALSE;
modstr = str;
}
if ( !( *model = str2model( modstr ) ) ) {
biffAddf( TEN, "%s: didn't recognize \"%s\" as model", me, modstr );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
int
117 tenModelFromAxisLearnPossible( const NrrdAxisInfo *axinfo ) {
/* HEY keep in synch with nrrdKind* code below */
return ( nrrdKind3DSymMatrix == axinfo->kind
|| nrrdKind3DMaskedSymMatrix == axinfo->kind
|| airStrlen( axinfo->label ) );
}
int
126 tenModelFromAxisLearn( const tenModel **modelP,
int *plusB0,
const NrrdAxisInfo *axinfo ) {
static const char me[]="tenModelFromAxisLearn";
if ( !( modelP && plusB0 && axinfo ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
*plusB0 = AIR_FALSE;
/* first try to learn model from axis "kind" */
/* HEY should probably also support 3 vector for stick? */
/* HEY keep in synch with nrrdKind* code above */
if ( nrrdKind3DSymMatrix == axinfo->kind
|| nrrdKind3DMaskedSymMatrix == axinfo->kind ) {
*modelP = tenModel1Tensor2;
} else if ( airStrlen( axinfo->label ) ) {
/* try to parse from label */
if ( tenModelParse( modelP, plusB0, AIR_TRUE, axinfo->label ) ) {
biffAddf( TEN, "%s: couldn't parse label \"%s\"", me, axinfo->label );
*modelP = NULL;
return 1;
}
} else {
biffAddf( TEN, "%s: don't have kind or label info to learn model", me );
*modelP = NULL;
return 1;
}
return 0;
}
/*
** If nB0 is given, then those B0 image values will be used.
** In this case, either the parm vector can be short by one ( seems to be
** missing B0 ), or the parm vector includes B0, but these will be ignored
** and over-written with the B0 values from nB0.
**
** basic and axis info is derived from _nparm
*/
int
167 tenModelSimulate( Nrrd *ndwi, int typeOut,
tenExperSpec *espec,
const tenModel *model,
const Nrrd *_nB0,
const Nrrd *_nparm,
int keyValueSet ) {
static const char me[]="tenModelSimulate";
double *ddwi, *parm, ( *ins )( void *v, size_t I, double d );
char *dwi;
size_t szOut[NRRD_DIM_MAX], II, numSamp;
const Nrrd *nB0, /* B0 as doubles */
*ndparm, /* parm as doubles */
*ndpparm, /* parm as doubles, padded */
*nparm; /* final parm as doubles, padded, w/ correct B0 values */
Nrrd *ntmp; /* non-const pointer for working */
airArray *mop;
unsigned int gpsze, /* given parm size */
ii;
int useB0Img, needPad, axmap[NRRD_DIM_MAX];
if ( !( ndwi && espec && model /* _nB0 can be NULL */ && _nparm ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !espec->imgNum ) {
biffAddf( TEN, "%s: given espec wants 0 images, unset?", me );
return 1;
}
gpsze = _nparm->axis[0].size;
if ( model->parmNum - 1 == gpsze ) {
/* got one less than needed parm #, see if we got B0 */
if ( !_nB0 ) {
biffAddf( TEN, "%s: got %u parms, need %u ( for %s ), "
"but didn't get B0 vol",
me, gpsze, model->parmNum, model->name );
return 1;
}
useB0Img = AIR_TRUE;
needPad = AIR_TRUE;
} else if ( model->parmNum != gpsze ) {
biffAddf( TEN, "%s: mismatch between getting %u parms, "
"needing %u ( for %s )\n",
me, gpsze, model->parmNum, model->name );
return 1;
} else {
/* model->parmNum == gpsze */
needPad = AIR_FALSE;
useB0Img = !!_nB0;
}
mop = airMopNew( );
/* get parms as doubles */
if ( nrrdTypeDouble == _nparm->type ) {
ndparm = _nparm;
} else {
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ntmp, _nparm, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert parm to %s", me,
airEnumStr( nrrdType, nrrdTypeDouble ) );
airMopError( mop ); return 1;
}
ndparm = ntmp;
}
/* get parms the right length */
if ( !needPad ) {
ndpparm = ndparm;
} else {
ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
unsigned int ax;
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
for ( ax=0; ax<ndparm->dim; ax++ ) {
min[ax] = ( !ax ? -1 : 0 );
max[ax] = ndparm->axis[ax].size-1;
}
if ( nrrdPad_nva( ntmp, ndparm, min, max, nrrdBoundaryBleed, 0.0 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't pad", me );
airMopError( mop ); return 1;
}
ndpparm = ntmp;
}
/* put in B0 values if needed */
if ( !useB0Img ) {
nparm = ndpparm;
} else {
if ( nrrdTypeDouble == _nB0->type ) {
nB0 = _nB0;
} else {
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ntmp, _nB0, nrrdTypeDouble ) ) {
biffMovef( TEN, NRRD, "%s: couldn't convert B0 to %s", me,
airEnumStr( nrrdType, nrrdTypeDouble ) );
airMopError( mop ); return 1;
}
nB0 = ntmp;
}
/* HEY: this is mostly likely a waste of memory,
but its all complicated by const-correctness */
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSplice( ntmp, ndpparm, nB0, 0, 0 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't splice in B0", me );
airMopError( mop ); return 1;
}
nparm = ntmp;
}
/* allocate output ( and set axmap ) */
for ( ii=0; ii<nparm->dim; ii++ ) {
szOut[ii] = ( !ii
? espec->imgNum
: nparm->axis[ii].size );
axmap[ii] = ( !ii
? -1
: AIR_CAST( int, ii ) );
}
if ( nrrdMaybeAlloc_nva( ndwi, typeOut, nparm->dim, szOut ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
if ( !( ddwi = AIR_CALLOC( espec->imgNum, double ) ) ) {
biffAddf( TEN, "%s: couldn't allocate dwi buffer", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, ddwi, airFree, airMopAlways );
numSamp = nrrdElementNumber( nparm )/nparm->axis[0].size;
/* set output */
ins = nrrdDInsert[typeOut];
parm = AIR_CAST( double *, nparm->data );
dwi = AIR_CAST( char *, ndwi->data );
for ( II=0; II<numSamp; II++ ) {
model->simulate( ddwi, parm, espec );
for ( ii=0; ii<espec->imgNum; ii++ ) {
ins( dwi, ii, ddwi[ii] );
}
parm += model->parmNum;
dwi += espec->imgNum*nrrdTypeSize[typeOut];
}
if ( keyValueSet ) {
if ( tenDWMRIKeyValueFromExperSpecSet( ndwi, espec ) ) {
biffAddf( TEN, "%s: trouble", me );
airMopError( mop ); return 1;
}
}
if ( nrrdAxisInfoCopy( ndwi, _nparm, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( ndwi, _nparm,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis or basic info", me );
airMopError( mop ); return 1;
}
ndwi->axis[0].kind = nrrdKindList;
airMopOkay( mop );
return 0;
}
/*
** _tenModelSqeFitSingle
**
** callable function ( as opposed to tenModel method ) for doing
** sqe fitting. Returns the sqe at the converged fit location
** Requires PARM_NUM length buffers testParm and grad
*/
double
346 _tenModelSqeFitSingle( const tenModel *model,
double *testParm, double *grad,
double *parm, double *convFrac, unsigned int *itersTaken,
const tenExperSpec *espec,
double *dwiBuff, const double *dwiMeas,
const double *parmInit, int knownB0,
unsigned int minIter, unsigned int maxIter,
double convEps, int verbose ) {
static const char me[]="_tenModelSqeFitSingle";
unsigned int iter, subIter;
double step, bak, opp, val, testval, dist, td;
int done;
char pstr[AIR_STRLEN_MED];
step = 1;
model->copy( parm, parmInit );
val = model->sqe( parm, espec, dwiBuff, dwiMeas, knownB0 );
model->sqeGrad( grad, parm, espec, dwiBuff, dwiMeas, knownB0 );
if ( verbose > 1 ) {
model->sprint( pstr, parm );
fprintf( stderr, "\n" );
fprintf( stderr, "%s( %s ): minIter = %u, maxIter = %u\n", me, model->name,
minIter, maxIter );
fprintf( stderr, "%s( %s ): starting at %s -> %g ( step %g )\n", me,
model->name, pstr, val, step );
}
opp = 1.2; /* opportunistic step size increase */
bak = 0.5; /* scaling back because of bad step */
iter = 0;
dist = convEps*8;
do {
subIter = 0;
do {
model->step( testParm, -step, grad, parm );
testval = model->sqe( testParm, espec, dwiBuff, dwiMeas, knownB0 );
if ( verbose > 1 ) {
model->sprint( pstr, testParm );
fprintf( stderr, "%s( %s ): ( iter %u/%u ) tried %s -> %g ( step %g )\n",
me, model->name, iter, subIter, pstr, testval, step );
}
if ( testval > val ) {
step *= bak;
}
subIter++;
} while ( testval > val && subIter <= maxIter );
if ( subIter > maxIter ) {
/* something went wrong with merely trying to find a downhill step;
this has occurred previously when ( because of a bug ) the
per-parameter bounds put the test location inside the bounding
box while the initial location was outside => could never converge.
Not using biff, so this is one way of trying to signal the problem */
model->copy( parm, parmInit );
*convFrac = AIR_POS_INF;
*itersTaken = maxIter;
return AIR_POS_INF;
}
td = model->dist( testParm, parm );
dist = ( td + dist )/2;
val = testval;
model->copy( parm, testParm );
model->sqeGrad( grad, parm, espec, dwiBuff, dwiMeas, knownB0 );
step *= opp;
iter++;
done = ( iter < minIter
? AIR_FALSE
: ( iter > maxIter ) || dist < convEps );
} while ( !done );
*convFrac = dist/convEps;
*itersTaken = iter;
return val;
}
int
420 tenModelSqeFit( Nrrd *nparm,
Nrrd **nsqeP, Nrrd **nconvP, Nrrd **niterP,
const tenModel *model,
const tenExperSpec *espec, const Nrrd *ndwi,
int knownB0, int saveB0, int typeOut,
unsigned int minIter, unsigned int maxIter,
unsigned int starts, double convEps,
airRandMTState *_rng, int verbose ) {
static const char me[]="tenModelSqeFit";
char doneStr[13];
double *ddwi, *dwibuff, sqe, sqeBest,
*dparm, *dparmBest,
( *ins )( void *v, size_t I, double d ),
( *lup )( const void *v, size_t I );
airArray *mop;
unsigned int saveParmNum, dwiNum, ii, lablen, itersTaken;
size_t szOut[NRRD_DIM_MAX], II, numSamp;
int axmap[NRRD_DIM_MAX], erraxmap[NRRD_DIM_MAX], fitVerbose;
const char *dwi;
char *parm;
airRandMTState *rng;
Nrrd *nsqe, *nconv, *niter;
/* nsqeP, nconvP, niterP can be NULL */
if ( !( nparm && model && espec && ndwi ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !( starts > 0 ) ) {
biffAddf( TEN, "%s: need non-zero starts", me );
return 1;
}
if ( !( nrrdTypeFloat == typeOut || nrrdTypeDouble == typeOut ) ) {
biffAddf( TEN, "%s: typeOut must be %s or %s, not %s", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeDouble ),
airEnumStr( nrrdType, typeOut ) );
return 1;
}
dwiNum = ndwi->axis[0].size;
if ( espec->imgNum != dwiNum ) {
biffAddf( TEN, "%s: espec expects %u images but dwi has %u on axis 0",
me, espec->imgNum, AIR_CAST( unsigned int, dwiNum ) );
return 1;
}
/* allocate output ( and set axmap ) */
dparm = model->alloc( );
dparmBest = model->alloc( );
if ( !( dparm && dparmBest ) ) {
biffAddf( TEN, "%s: couldn't allocate parm vecs", me );
return 1;
}
mop = airMopNew( );
airMopAdd( mop, dparm, airFree, airMopAlways );
airMopAdd( mop, dparmBest, airFree, airMopAlways );
saveParmNum = saveB0 ? model->parmNum : model->parmNum-1;
for ( ii=0; ii<ndwi->dim; ii++ ) {
szOut[ii] = ( !ii
? saveParmNum
: ndwi->axis[ii].size );
axmap[ii] = ( !ii
? -1
: AIR_CAST( int, ii ) );
if ( ii ) {
erraxmap[ii-1] = AIR_CAST( int, ii );
}
}
if ( nrrdMaybeAlloc_nva( nparm, typeOut, ndwi->dim, szOut ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output "
"( saveB0 %d, knownB0 %d )", me, saveB0, knownB0 );
airMopError( mop ); return 1;
}
if ( nsqeP ) {
nsqe = *nsqeP;
if ( !nsqe ) {
nsqe = nrrdNew( );
*nsqeP = nsqe;
}
if ( nrrdMaybeAlloc_nva( nsqe, typeOut, ndwi->dim-1, szOut+1 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate error output", me );
airMopError( mop ); return 1;
}
} else {
nsqe = NULL;
}
if ( nconvP ) {
nconv = *nconvP;
if ( !nconv ) {
nconv = nrrdNew( );
*nconvP = nconv;
}
if ( nrrdMaybeAlloc_nva( nconv, nrrdTypeDouble, ndwi->dim-1, szOut+1 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate conv output", me );
airMopError( mop ); return 1;
}
} else {
nconv = NULL;
}
if ( niterP ) {
niter = *niterP;
if ( !niter ) {
niter = nrrdNew( );
*niterP = niter;
}
if ( nrrdMaybeAlloc_nva( niter, nrrdTypeUInt, ndwi->dim-1, szOut+1 ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate iter output", me );
airMopError( mop ); return 1;
}
} else {
niter = NULL;
}
ddwi = AIR_CALLOC( espec->imgNum, double );
dwibuff = AIR_CALLOC( espec->imgNum, double );
if ( !( ddwi && dwibuff ) ) {
biffAddf( TEN, "%s: couldn't allocate dwi buffers", me );
airMopError( mop ); return 1;
}
airMopAdd( mop, ddwi, airFree, airMopAlways );
airMopAdd( mop, dwibuff, airFree, airMopAlways );
/* set output */
if ( _rng ) {
rng = _rng;
} else {
airRandMTStateGlobalInit( );
rng = airRandMTStateGlobal;
}
numSamp = nrrdElementNumber( ndwi )/ndwi->axis[0].size;
lup = nrrdDLookup[ndwi->type];
ins = nrrdDInsert[typeOut];
parm = AIR_CAST( char *, nparm->data );
dwi = AIR_CAST( char *, ndwi->data );
itersTaken = 0;
if ( verbose ) {
fprintf( stderr, "%s: fitting ... ", me );
fflush( stderr );
}
for ( II=0; II<numSamp; II++ ) {
double cvf, convFrac=0;
unsigned int ss, itak;
if ( verbose ) {
fprintf( stderr, "%s", airDoneStr( 0, II, numSamp, doneStr ) );
fflush( stderr );
}
for ( ii=0; ii<dwiNum; ii++ ) {
ddwi[ii] = lup( dwi, ii );
}
sqeBest = DBL_MAX; /* forces at least one improvement */
for ( ss=0; ss<starts; ss++ ) {
/* can add other debugging conditions here */
fitVerbose = verbose;
if ( knownB0 ) {
dparm[0] = tenExperSpecKnownB0Get( espec, ddwi );
}
model->rand( dparm, rng, knownB0 );
sqe = model->sqeFit( dparm, &cvf, &itak,
espec, dwibuff, ddwi,
dparm, knownB0, minIter, maxIter,
convEps, fitVerbose );
if ( sqe <= sqeBest ) {
sqeBest = sqe;
model->copy( dparmBest, dparm );
itersTaken = itak;
convFrac = cvf;
}
}
for ( ii=0; ii<saveParmNum; ii++ ) {
ins( parm, ii, saveB0 ? dparmBest[ii] : dparmBest[ii+1] );
}
/* save things about fitting into nrrds */
if ( nsqeP ) {
ins( nsqe->data, II, sqeBest );
}
if ( nconvP ) {
nrrdDInsert[nrrdTypeDouble]( nconv->data, II, convFrac );
}
if ( niterP ) {
nrrdDInsert[nrrdTypeUInt]( niter->data, II, itersTaken );
}
parm += saveParmNum*nrrdTypeSize[typeOut];
dwi += espec->imgNum*nrrdTypeSize[ndwi->type];
}
if ( verbose ) {
fprintf( stderr, "%s\n", airDoneStr( 0, II, numSamp, doneStr ) );
}
if ( nrrdAxisInfoCopy( nparm, ndwi, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( nparm, ndwi,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis or basic info", me );
airMopError( mop ); return 1;
}
if ( nsqeP ) {
if ( nrrdAxisInfoCopy( nsqe, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( nsqe, ndwi,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD,
"%s: couldn't copy axis or basic info to error out", me );
airMopError( mop ); return 1;
}
}
if ( nconvP ) {
if ( nrrdAxisInfoCopy( nconv, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( nconv, ndwi,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD,
"%s: couldn't copy axis or basic info to conv out", me );
airMopError( mop ); return 1;
}
}
if ( niterP ) {
if ( nrrdAxisInfoCopy( niter, ndwi, erraxmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( niter, ndwi,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD,
"%s: couldn't copy axis or basic info to iter out", me );
airMopError( mop ); return 1;
}
}
lablen = ( strlen( tenModelPrefixStr )
+ ( saveB0 ? strlen( "B0+" ) : 0 )
+ strlen( model->name )
+ 1 );
nparm->axis[0].label = AIR_CALLOC( lablen, char );
sprintf( nparm->axis[0].label, "%s%s%s",
tenModelPrefixStr,
saveB0 ? "B0+" : "",
model->name );
airMopOkay( mop );
return 0;
}
int
687 tenModelNllFit( Nrrd *nparm, Nrrd **nnllP,
const tenModel *model,
const tenExperSpec *espec, const Nrrd *ndwi,
int rician, double sigma, int knownB0 ) {
AIR_UNUSED( nparm );
AIR_UNUSED( nnllP );
AIR_UNUSED( model );
AIR_UNUSED( espec );
AIR_UNUSED( ndwi );
AIR_UNUSED( rician );
AIR_UNUSED( sigma );
AIR_UNUSED( knownB0 );
return 0;
}
/*
** copy the B0 info if we have it
** use the same type on the way out.
*/
int
709 tenModelConvert( Nrrd *nparmDst, int *convRetP, const tenModel *modelDst,
const Nrrd *nparmSrc, const tenModel *_modelSrc ) {
static char me[]="tenModelConvert";
const tenModel *modelSrc;
double *dpdst, *dpsrc, ( *lup )( const void *v, size_t I ),
( *ins )( void *v, size_t I, double d );
size_t szOut[NRRD_DIM_MAX], II, NN, tsize;
airArray *mop;
int withB0, axmap[NRRD_DIM_MAX], convRet=0;
unsigned int parmNumDst, parmNumSrc, ii, lablen;
const char *parmSrc;
char *parmDst;
if ( !( nparmDst && modelDst && nparmSrc ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( !_modelSrc ) {
/* we have to try to learn the source model from the nrrd */
if ( tenModelFromAxisLearn( &modelSrc, &withB0, nparmSrc->axis + 0 ) ) {
biffAddf( TEN, "%s: couldn't learn model from src nparm", me );
return 1;
}
} else {
modelSrc = _modelSrc;
if ( modelSrc->parmNum == nparmSrc->axis[0].size ) {
withB0 = AIR_TRUE;
} if ( modelSrc->parmNum-1 == nparmSrc->axis[0].size ) {
withB0 = AIR_FALSE;
} else {
biffAddf( TEN, "%s: axis[0].size %u is not \"%s\" parmnum %u or 1 less",
me, AIR_CAST( unsigned int, nparmSrc->axis[0].size ),
modelSrc->name, modelSrc->parmNum );
return 1;
}
}
mop = airMopNew( );
dpdst = modelDst->alloc( );
airMopAdd( mop, dpdst, airFree, airMopAlways );
dpsrc = modelSrc->alloc( );
airMopAdd( mop, dpsrc, airFree, airMopAlways );
lup = nrrdDLookup[nparmSrc->type];
ins = nrrdDInsert[nparmSrc->type];
parmNumDst = withB0 ? modelDst->parmNum : modelDst->parmNum-1;
parmNumSrc = nparmSrc->axis[0].size;
for ( ii=0; ii<nparmSrc->dim; ii++ ) {
szOut[ii] = ( !ii
? parmNumDst
: nparmSrc->axis[ii].size );
axmap[ii] = ( !ii
? -1
: AIR_CAST( int, ii ) );
}
if ( nrrdMaybeAlloc_nva( nparmDst, nparmSrc->type, nparmSrc->dim, szOut ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
airMopError( mop ); return 1;
}
NN = nrrdElementNumber( nparmSrc )/nparmSrc->axis[0].size;
tsize = nrrdTypeSize[nparmSrc->type];
parmSrc = AIR_CAST( char *, nparmSrc->data );
parmDst = AIR_CAST( char *, nparmDst->data );
if ( !withB0 ) {
dpsrc[0] = 0;
}
for ( II=0; II<NN; II++ ) {
for ( ii=0; ii<parmNumSrc; ii++ ) {
dpsrc[withB0 ? ii : ii+1] = lup( parmSrc, ii );
}
convRet = modelDst->convert( dpdst, dpsrc, modelSrc );
if ( 2 == convRet ) { /* HEY should be enum for this value */
biffAddf( TEN, "%s: error converting from \"%s\" to \"%s\"", me,
modelSrc->name, modelDst->name );
airMopError( mop ); return 1;
}
for ( ii=0; ii<parmNumDst; ii++ ) {
ins( parmDst, ii, dpdst[withB0 ? ii : ii+1] );
}
parmSrc += parmNumSrc*tsize;
parmDst += parmNumDst*tsize;
}
if ( convRetP ) {
*convRetP = convRet;
}
if ( nrrdAxisInfoCopy( nparmDst, nparmSrc, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( nparmDst, nparmSrc,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis or basic info", me );
airMopError( mop ); return 1;
}
/* HEY: COPY AND PASTE! from above. perhaps make helper functions? */
lablen = ( strlen( tenModelPrefixStr )
+ ( withB0 ? strlen( "B0+" ) : 0 )
+ strlen( modelDst->name )
+ 1 );
nparmDst->axis[0].label = AIR_CALLOC( lablen, char );
sprintf( nparmDst->axis[0].label, "%s%s%s",
tenModelPrefixStr,
withB0 ? "B0+" : "",
modelDst->name );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Information about this program and its use"
int
30 tend_aboutMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_MED];
char par1[] = "\t\t\t\t"
"\"tend\" is a command-line interface to much of the functionality "
"in \"ten\", a C library for diffusion image processing. Ten is one "
"library in the \"Teem\" collection of libraries. More information "
"about Teem is at <http://teem.sf.net>. A checkout of Teem source "
"is available via:\n "
"svn co http://svn.code.sf.net/p/teem/code/teem/trunk teem\n ";
/* "svn co http://teem.svn.sf.net/svnroot/teem/teem/trunk teem\n "; */
char par2[] = "\t\t\t\t"
"Long-term maintenance of this software depends on funding, and "
"funding depends on being able to document who is using it for what. "
"If tend or Ten has helped in your research, including for simple one-off "
"experiments or mundane data hacking, the developers of Teem would love "
"to know. There are multiple ways of communicating this. "
"In your publications, consider adding a line such as this "
"in the Acknowledgments: "
"\"Data processing performed with the tend tool, "
"part of the Teem toolkit available at "
"http://teem.sf.net\". "
"Alternatively, please email glk@uchicago.edu and briefly describe "
"how Teem software has helped in your work. "
"Please also consider joining the teem-users mailing list: "
"<http://lists.sourceforge.net/lists/listinfo/teem-users>. This is "
"the primary forum for feedback, questions, and feature requests.\n ";
char par3[] = "\t\t\t\t"
"Like \"unu\", another Teem command-line binary, it is often useful "
"to chain together invocations of tend with pipes, as in the "
"following, which estimates tensors from DWIs, takes a slice of the "
"tensor volume, computes the standard RGB colormap of the principal "
"eigenvector, and then quantizes it to an 8-bit PNG:\n";
char par4[] = "\ttend estim -i dwi.nhdr -B kvp -knownB0 true \\\n "
" | tend slice -a 2 -p 30 \\\n "
" | tend evecrgb -c 0 -a cl2 -gam 1.2 \\\n "
" | unu quantize -b 8 -min 0 -max 1 -o z30-rgb.png\n";
AIR_UNUSED( argc );
AIR_UNUSED( argv );
AIR_UNUSED( me );
fprintf( stdout, "\n" );
sprintf( buff, "--- %s ---", tendTitle );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stdout, fmt, buff );
airTeemVersionSprint( buff );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stdout, fmt, buff );
fprintf( stdout, "\n" );
_hestPrintStr( stdout, 1, 0, 78, par1, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par2, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par3, AIR_FALSE );
_hestPrintStr( stdout, 2, 0, 78, par4, AIR_FALSE );
return 0;
}
91 TEND_CMD( about, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Generate barycentric histograms of anisotropy"
static const char *_tend_anhistInfoL =
( INFO
". The barycentric space used is either one of Westin's "
"triple of spherical, linear, and planar anisotropy. The bin "
"counts in the histogram are weighted by the confidence value." );
int
35 tend_anhistMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int version, res, right;
Nrrd *nin, *nout, *nwght;
char *outS;
hestOptAdd( &hopt, "v", "westin version", airTypeInt, 1, 1, &version, "1",
"Which version of Westin's anisotropy metric triple "
"to use, either \"1\" or \"2\"" );
hestOptAdd( &hopt, "w", "nweight", airTypeOther, 1, 1, &nwght, "",
"how to weigh contributions to histogram. By default "
"( not using this option ), the increment is one bin count per "
"sample, but by giving a nrrd, the value in the nrrd at the "
"corresponding location will be the bin count increment ",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "r", "res", airTypeInt, 1, 1, &res, NULL,
"resolution of anisotropy plot" );
hestOptAdd( &hopt, "right", NULL, airTypeInt, 0, 0, &right, NULL,
"sample a right-triangle-shaped region, instead of "
"a roughly equilateral triangle. " );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_anhistInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenAnisoHistogram( nout, nin, nwght, right, version, res ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making histogram:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
88 TEND_CMD( anhist, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Graph anisotropy metric in barycentric coords"
static const char *_tend_anplotInfoL =
( INFO
". The metrics all vary from 0.0 to 1.0, and will be sampled "
"in the lower right half of the image. The plane on which they are "
"sampled is a surface of constant trace. You may want to use "
"\"unu resample -s = x0.57735 -k tent\" to transform the triangle into "
"a 30-60-90 triangle, and \"ilk -t 1, -0.5, 0, 0, 0.866, 0 -k tent "
"-0 u:0, 1 -b pad -bg 0\" ( possibly followed by "
"teem/src/limntest/triimg ) to transform the domain into an equilateral "
"triangle." );
int
40 tend_anplotMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int res, aniso, whole, nanout, hflip;
Nrrd *nout;
char *outS;
hestOptAdd( &hopt, "r", "res", airTypeInt, 1, 1, &res, "256",
"resolution of anisotropy plot" );
hestOptAdd( &hopt, "w", NULL, airTypeInt, 0, 0, &whole, NULL,
"sample the whole triangle of constant trace, "
"instead of just the "
"sixth of it in which the eigenvalues have the "
"traditional sorted order. " );
hestOptAdd( &hopt, "hflip", NULL, airTypeInt, 0, 0, &hflip, NULL,
"flip the two bottom corners ( swapping the place of "
"linear and planar )" );
hestOptAdd( &hopt, "nan", NULL, airTypeInt, 0, 0, &nanout, NULL,
"set the pixel values outside the triangle to be NaN, "
"instead of 0" );
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL,
"Which anisotropy metric to plot. " TEN_ANISO_DESC,
NULL, tenAniso );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_anplotInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenAnisoPlot( nout, aniso, res, hflip, whole, nanout ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making plot:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
93 TEND_CMD( anplot, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Scale the anisotropic component of the tensors"
static const char *_tend_anscaleInfoL =
( INFO
". This maintains the isotropic component of the tensor, and fixes "
"either the trace or determinant, "
"while scaling up ( or down ) the \"deviatoric\" component "
"of the tensor. Good for exaggerating the shape of nearly isotropic "
"tensors." );
int
37 tend_anscaleMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float scale;
int fixDet, makePositive;
hestOptAdd( &hopt, "s", "scale", airTypeFloat, 1, 1, &scale, NULL,
"Amount by which to scale deviatoric component of tensor." );
hestOptAdd( &hopt, "fd", NULL, airTypeInt, 0, 0, &fixDet, NULL,
"instead of fixing the per-sample trace ( the default ), fix the "
"determinant ( ellipsoid volume )" );
hestOptAdd( &hopt, "mp", NULL, airTypeInt, 0, 0, &makePositive, NULL,
"after changing the eigenvalues of the tensor, enforce their "
"non-negative-ness. By default, no such constraint is imposed." );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_anscaleInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenAnisoScale( nout, nin, scale, fixDet, makePositive ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
84 TEND_CMD( anscale, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Apply an anisotropy metric to a DT volume"
static const char *_tend_anvolInfoL =
( INFO
". The anisotropy value will be zero in the locations which "
"don't meet the given confidence threshold." );
int
34 tend_anvolMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int aniso;
Nrrd *nin, *nout;
char *outS;
float thresh;
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL,
"Which anisotropy metric to plot. " TEN_ANISO_DESC,
NULL, tenAniso );
hestOptAdd( &hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5",
"confidence threshold" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_anvolInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenAnisoVolume( nout, nin, aniso, thresh ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making aniso volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
78 TEND_CMD( anvol, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Average across tensor volumes"
static const char *_tend_avgInfoL =
( INFO
". The output is the same size as the any one of the inputs. "
"The individual tensors may be averaged in various ways." );
int
34 tend_avgMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int ninLen, itype;
Nrrd **nin, *nout;
char *outS;
hestOptAdd( &hopt, "i", "nin1 nin2", airTypeOther, 2, -1, &nin, NULL,
"list of input diffusion tensor volumes",
&ninLen, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &itype, "linear",
"averaging method",
NULL, tenInterpType );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_avgInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenInterpMulti3D( nout, AIR_CAST( const Nrrd*const*, nin ), NULL,
ninLen, itype, NULL ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
79 TEND_CMD( avg, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Non-linear least-squares fitting of b-value curves"
static const char *_tend_bfitInfoL =
( INFO
". Axis 0 is replaced by three values: amp, dec, err, based on a "
"non-linear least-squares fit of amp*exp( -b*dec ) to the range of DWI "
"values along input axis 0, as a function of changing b values. " );
int
35 tend_bfitMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
double *bb, *ww, *_ww, eps;
unsigned int ii, bbLen, _wwLen;
int iterMax;
char *outS;
hparm->respFileEnable = AIR_TRUE;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"Input nrrd. List of DWIs from different b-values must "
"be along axis 0", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b", "b1 b2", airTypeDouble, 2, -1, &bb, NULL,
"b values across axis 0 of input nrrd", &bbLen );
hestOptAdd( &hopt, "w", "w1 w2", airTypeDouble, 2, -1, &_ww, "nan nan",
"weights for samples in non-linear fitting", &_wwLen );
hestOptAdd( &hopt, "imax", "# iter", airTypeInt, 1, 1, &iterMax, "10",
"max number of iterations to use in non-linear fitting, or, "
"use 0 to do only initial linear fit" );
hestOptAdd( &hopt, "eps", "epsilon", airTypeDouble, 1, 1, &eps, "1",
"epsilon convergence threshold for non-linear fitting" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output tensor volume" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_bfitInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( bbLen == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: got %d b-values but axis 0 size is %s\n", me,
bbLen, airSprintSize_t( stmp, nin->axis[0].size ) );
airMopError( mop ); return 1;
}
if ( AIR_EXISTS( _ww[0] ) ) {
if ( !( _wwLen == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: got %d weights but axis 0 size is %s\n", me,
_wwLen, airSprintSize_t( stmp, nin->axis[0].size ) );
airMopError( mop ); return 1;
}
ww = _ww;
} else {
/* no explicit weights specified */
ww = ( double* )calloc( nin->axis[0].size, sizeof( double ) );
airMopAdd( mop, ww, airFree, airMopAlways );
for ( ii=0; ii<nin->axis[0].size; ii++ ) {
ww[ii] = 1.0;
}
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenBVecNonLinearFit( nout, nin, bb, ww, iterMax, eps ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
112 TEND_CMD( bfit, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculate B-matrix given gradient directions"
static const char *_tend_bmatInfoL =
( INFO
", assuming no diffusion weighting from the other imaging gradients. "
"The input is a 3-by-N array of floats or doubles, each row being "
"one of the gradient directions used for diffusion-weighted imaging. "
"A plain text file with one gradient per line, no punctuation, is an "
"easy way to specify this information. "
"The gradient vector coefficients are used as is, without normalization "
"( since different gradient strengths are sometimes desired ). "
"The output has one row of the B-matrix per line, with coefficient "
"ordering Bxx, Bxy, Bxz, Byy, Byz, Bzz, and with the off-diagonal "
"elements NOT pre-multiplied by 2." );
int
42 tend_bmatMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *ngrad, *nout;
char *outS;
hestOptAdd( &hopt, "i", "grads", airTypeOther, 1, 1, &ngrad, NULL,
"array of gradient directions", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output B matrix" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_bmatInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenBMatrixCalc( nout, ngrad ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making B matrix:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
79 TEND_CMD( bmat, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Generate postscript renderings of 2D glyphs"
static const char *_tend_ellipseInfoL =
( INFO
". Not much to look at here." );
int
33 tend_ellipseDoit( FILE *file, Nrrd *nten, Nrrd *npos, Nrrd *nstn,
float min[2], float max[2],
float gscale, float dotRad, float lineWidth, float cthresh,
int invert ) {
size_t sx=0, sy=0, ti, nt;
int x, y, vi, *sdata;
double aspect, minX, minY, maxX, maxY, conf, Dxx, Dxy, Dyy, px, py, spx, spy;
float *tdata, *pdata;
if ( npos ) {
nt = npos->axis[1].size;
aspect = ( max[0] - min[0] )/( max[1] - min[1] );
} else {
spx = ( AIR_EXISTS( nten->axis[1].spacing )
? nten->axis[1].spacing
: 1 );
spy = ( AIR_EXISTS( nten->axis[2].spacing )
? nten->axis[2].spacing
: 1 );
sx = nten->axis[1].size;
sy = nten->axis[2].size;
nt = sx*sy;
aspect = sx*spx/( sy*spy );
}
if ( aspect > 7.5/10 ) {
/* image has a wider aspect ratio than safely printable page area */
minX = 0.5;
maxX = 8.0;
minY = 5.50 - 7.5/2/aspect;
maxY = 5.50 + 7.5/2/aspect;
} else {
/* image is taller ... */
minX = 4.25 - 10.0/2*aspect;
maxX = 4.25 + 10.0/2*aspect;
minY = 0.5;
maxY = 10.5;
}
minX *= 72; minY *= 72;
maxX *= 72; maxY *= 72;
if ( npos ) {
gscale *= AIR_CAST( float, ( maxX - minX )/( max[0] - min[0] ) );
dotRad *= AIR_CAST( float, ( maxX - minX )/( max[0] - min[0] ) );
lineWidth *= AIR_CAST( float, ( maxX - minX )/( max[0] - min[0] ) );
}
fprintf( file, "%%!PS-Adobe-3.0 EPSF-3.0\n" );
fprintf( file, "%%%%Creator: tend ellipse\n" );
fprintf( file, "%%%%Title: blah blah blah\n" );
fprintf( file, "%%%%Pages: 1\n" );
fprintf( file, "%%%%BoundingBox: %d %d %d %d\n",
AIR_CAST( int, floor( minX ) ), AIR_CAST( int, floor( minY ) ),
AIR_CAST( int, ceil( maxX ) ), AIR_CAST( int, ceil( maxY ) ) );
fprintf( file, "%%%%HiResBoundingBox: %g %g %g %g\n",
minX, minY, maxX, maxY );
fprintf( file, "%%%%EndComments\n" );
fprintf( file, "%%%%BeginProlog\n" );
fprintf( file, "%%%%EndProlog\n" );
fprintf( file, "%%%%Page: 1 1\n" );
fprintf( file, "gsave\n" );
if ( invert ) {
fprintf( file, "0 setgray\n" );
fprintf( file, "%g %g moveto\n", minX, minY );
fprintf( file, "%g %g lineto\n", maxX, minY );
fprintf( file, "%g %g lineto\n", maxX, maxY );
fprintf( file, "%g %g lineto\n", minX, maxY );
fprintf( file, "closepath fill\n" );
}
fprintf( file, "gsave\n" );
fprintf( file, "0.5 setgray\n" );
tdata = ( float* )nten->data;
pdata = npos ? ( float* )npos->data : NULL;
for ( ti=0; ti<nt; ti++ ) {
if ( npos ) {
px = AIR_AFFINE( min[0], pdata[0], max[0], minX, maxX );
py = AIR_AFFINE( min[1], pdata[1], max[1], maxY, minY );
pdata += 2;
} else {
x = ti % sx;
y = ti / sx;
px = NRRD_CELL_POS( minX, maxX, sx, x );
py = NRRD_CELL_POS( minY, maxY, sy, sy-1-y );
}
conf = tdata[0];
if ( conf > cthresh ) {
double eval0, eval1, dd;
Dxx = tdata[1];
Dxy = tdata[2];
Dyy = tdata[3];
dd = Dxx - Dyy;
eval0 = 0.5*( -Dxx + sqrt( 4*Dxy*Dxy + dd*dd ) - Dyy );
eval1 = 0.5*( -Dxx - sqrt( 4*Dxy*Dxy + dd*dd ) - Dyy );
fprintf( file, "gsave\n" );
fprintf( file, "matrix currentmatrix\n" );
fprintf( file, "[%g %g %g %g %g %g] concat\n",
Dxx, -Dxy, -Dxy, Dyy, px, py );
fprintf( file, "0 0 %g 0 360 arc closepath\n", gscale );
fprintf( file, "setmatrix\n" );
if ( eval0 * eval1 < 0 ) {
fprintf( file, "gsave\n" );
fprintf( file, "0.15 setgray\n" );
fprintf( file, "fill\n" );
fprintf( file, "grestore\n" );
} else {
fprintf( file, "fill\n" );
}
fprintf( file, "grestore\n" );
}
tdata += 4;
}
fprintf( file, "grestore\n" );
if ( dotRad && !nstn ) {
fprintf( file, "gsave\n" );
tdata = ( float* )nten->data;
pdata = npos ? ( float* )npos->data : NULL;
fprintf( file, "%g setgray\n", invert ? 1.0 : 0.0 );
for ( ti=0; ti<nt; ti++ ) {
if ( npos ) {
px = AIR_AFFINE( min[0], pdata[0], max[0], minX, maxX );
py = AIR_AFFINE( min[1], pdata[1], max[1], maxY, minY );
pdata += 2;
} else {
x = ti % sx;
y = ti / sx;
px = NRRD_CELL_POS( minX, maxX, sx, x );
py = NRRD_CELL_POS( minY, maxY, sy, sy-1-y );
}
conf = tdata[0];
if ( conf > cthresh ) {
fprintf( file, "%g %g %g 0 360 arc closepath fill\n", px, py, dotRad );
}
tdata += 4;
}
fprintf( file, "grestore\n" );
}
if ( ( dotRad || lineWidth ) && npos && nstn ) {
fprintf( file, "gsave\n" );
tdata = ( float* )nten->data;
pdata = npos ? ( float* )npos->data : NULL;
sdata = nstn ? ( int* )nstn->data : NULL;
fprintf( file, "%g setlinewidth\n", lineWidth );
fprintf( file, "%g setgray\n", invert ? 1.0 : 0.0 );
fprintf( file, "1 setlinecap\n" );
fprintf( file, "1 setlinejoin\n" );
for ( ti=0; ti<nstn->axis[1].size; ti++ ) {
if ( 1 == sdata[1 + 3*ti] ) {
vi = sdata[0 + 3*ti];
px = AIR_AFFINE( min[0], pdata[0 + 2*vi], max[0], minX, maxX );
py = AIR_AFFINE( min[1], pdata[1 + 2*vi], max[1], maxY, minY );
if ( tdata[0 + 4*vi] > cthresh ) {
fprintf( file, "%g %g %g 0 360 arc closepath fill\n", px, py, dotRad );
}
} else {
fprintf( file, "newpath\n" );
for ( vi = sdata[0 + 3*ti];
vi < sdata[0 + 3*ti] + sdata[1 + 3*ti];
vi++ ) {
px = AIR_AFFINE( min[0], pdata[0 + 2*vi], max[0], minX, maxX );
py = AIR_AFFINE( min[1], pdata[1 + 2*vi], max[1], maxY, minY );
fprintf( file, "%g %g %s\n", px, py,
vi == sdata[0 + 3*ti] ? "moveto" : "lineto" );
}
fprintf( file, "stroke\n" );
vi = sdata[0 + 3*ti] + sdata[2 + 3*ti];
px = AIR_AFFINE( min[0], pdata[0 + 2*vi], max[0], minX, maxX );
py = AIR_AFFINE( min[1], pdata[1 + 2*vi], max[1], maxY, minY );
fprintf( file, "%g %g %g 0 360 arc closepath fill\n",
px, py, dotRad + lineWidth );
}
}
fprintf( file, "grestore\n" );
}
fprintf( file, "grestore\n" );
return 0;
}
int
217 tend_ellipseMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr;
airArray *mop;
Nrrd *nten, *npos, *nstn;
char *outS;
float gscale, dotRad, lineWidth, cthresh, min[2], max[2];
FILE *fout;
int invert;
mop = airMopNew( );
hestOptAdd( &hopt, "ctr", "conf thresh", airTypeFloat, 1, 1, &cthresh, "0.5",
"Glyphs will be drawn only for tensors with confidence "
"values greater than this threshold" );
hestOptAdd( &hopt, "gsc", "scale", airTypeFloat, 1, 1, &gscale, "1",
"over-all glyph size" );
hestOptAdd( &hopt, "dot", "radius", airTypeFloat, 1, 1, &dotRad, "0.0",
"radius of little dot to put in middle of ellipse, or \"0\" "
"for no such dot" );
hestOptAdd( &hopt, "wid", "width", airTypeFloat, 1, 1, &lineWidth, "0.0",
"with of lines for tractlets" );
hestOptAdd( &hopt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL,
"use white ellipses on black background, instead of reverse" );
hestOptAdd( &hopt, "min", "minX minY", airTypeFloat, 2, 2, min, "-1 -1",
"when using \"-p\", minimum corner" );
hestOptAdd( &hopt, "max", "maxX maxY", airTypeFloat, 2, 2, max, "1 1",
"when using \"-p\", maximum corner" );
/* input/output */
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nten, "-",
"image of 2D tensors", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "",
"Instead of being on a grid, tensors are at arbitrary locations, "
"as defined by this 2-by-N array of floats", NULL, NULL,
nrrdHestNrrd );
hestOptAdd( &hopt, "s", "stn array", airTypeOther, 1, 1, &nstn, "",
"Locations given by \"-p\" have this connectivity", NULL, NULL,
nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output PostScript file" );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_ellipseInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( npos ) {
if ( !( 2 == nten->dim && 4 == nten->axis[0].size
&& 2 == npos->dim && 2 == npos->axis[0].size
&& nten->axis[1].size == npos->axis[1].size ) ) {
fprintf( stderr, "%s: didn't get matching lists of tensors and pos's\n",
me );
airMopError( mop ); return 1;
}
if ( !( nrrdTypeFloat == npos->type ) ) {
fprintf( stderr, "%s: didn't get float type positions\n", me );
airMopError( mop ); return 1;
}
} else {
if ( !( 3 == nten->dim && 4 == nten->axis[0].size ) ) {
fprintf( stderr, "%s: didn't get a 3-D 4-by-X-by-Y 2D tensor array\n",
me );
airMopError( mop ); return 1;
}
}
if ( !( nrrdTypeFloat == nten->type ) ) {
fprintf( stderr, "%s: didn't get float type tensors\n", me );
airMopError( mop ); return 1;
}
if ( nstn ) {
if ( !( nrrdTypeUInt == nstn->type
&& 2 == nstn->dim
&& 3 == nstn->axis[0].size ) ) {
fprintf( stderr, "%s: connectivity isn't 2-D 3-by-N array of %ss\n",
me, airEnumStr( nrrdType, nrrdTypeInt ) );
airMopError( mop ); return 1;
}
}
if ( !( fout = airFopen( outS, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing\n", me, outS );
airMopError( mop ); return 1;
}
airMopAdd( mop, fout, ( airMopper )airFclose, airMopAlways );
tend_ellipseDoit( fout, nten, npos, nstn, min, max,
gscale, dotRad, lineWidth, cthresh, invert );
airMopOkay( mop );
return 0;
}
311 TEND_CMD( ellipse, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Register diffusion-weighted echo-planar images"
static const char *_tend_epiregInfoL =
( INFO
". This registration corrects the shear, scale, and translate along "
"the phase encoding direction ( assumed to be the Y ( second ) axis of "
"the image ) caused by eddy currents from the diffusion-encoding "
"gradients with echo-planar imaging. The method is based on calculating "
"moments of segmented images, where the segmentation is a simple "
"procedure based on blurring ( optional ), thresholding and "
"connected component analysis. "
"The registered DWIs are resampled with the "
"chosen kernel, with the separate DWIs stacked along axis 0." );
int
41 tend_epiregMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret, rret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
char *outS, *buff;
char *gradS;
NrrdKernelSpec *ksp;
Nrrd **nin, **nout3D, *nout4D, *ngrad, *ngradKVP, *nbmatKVP;
unsigned int ni, ninLen, *skip, skipNum;
int ref, noverbose, progress, nocc, baseNum;
float bw[2], thr, fitFrac;
double bvalue;
hestOptAdd( &hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, NULL,
"all the diffusion-weighted images ( DWIs ), as separate 3D nrrds, "
"**OR**: one 4D nrrd of all DWIs stacked along axis 0",
&ninLen, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "g", "grads", airTypeString, 1, 1, &gradS, NULL,
"array of gradient directions, in the same order as the "
"associated DWIs were given to \"-i\", "
"**OR** \"-g kvp\" signifies that gradient directions should "
"be read from the key/value pairs of the DWI",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "r", "reference", airTypeInt, 1, 1, &ref, "-1",
"which of the DW volumes ( zero-based numbering ) should be used "
"as the standard, to which all other images are transformed. "
"Using -1 ( the default ) means that 9 intrinsic parameters "
"governing the relationship between the gradient direction "
"and the resulting distortion are estimated and fitted, "
"ensuring good registration with the non-diffusion-weighted "
"T2 image ( which is never explicitly used in registration ). "
"Otherwise, by picking a specific DWI, no distortion parameter "
"estimation is done. " );
hestOptAdd( &hopt, "nv", NULL, airTypeInt, 0, 0, &noverbose, NULL,
"turn OFF verbose mode, and "
"have no idea what stage processing is at." );
hestOptAdd( &hopt, "p", NULL, airTypeInt, 0, 0, &progress, NULL,
"save out intermediate steps of processing" );
hestOptAdd( &hopt, "bw", "x, y blur", airTypeFloat, 2, 2, bw, "1.0 2.0",
"standard devs in X and Y directions of gaussian filter used "
"to blur the DWIs prior to doing segmentation. This blurring "
"does not effect the final resampling of registered DWIs. "
"Use \"0.0 0.0\" to say \"no blurring\"" );
hestOptAdd( &hopt, "t", "DWI thresh", airTypeFloat, 1, 1, &thr, "nan",
"Threshold value to use on DWIs, "
"to do initial separation of brain and non-brain. By default, "
"the threshold is determined automatically by histogram "
"analysis. " );
hestOptAdd( &hopt, "ncc", NULL, airTypeInt, 0, 0, &nocc, NULL,
"do *NOT* do connected component ( CC ) analysis, after "
"thresholding and before moment calculation. Doing CC analysis "
"usually gives better results because it converts the "
"thresholding output into something much closer to a "
"real segmentation" );
hestOptAdd( &hopt, "f", "fit frac", airTypeFloat, 1, 1, &fitFrac, "0.70",
"( only meaningful with \"-r -1\" ) When doing linear fitting "
"of the intrinsic distortion parameters, it is good "
"to ignore the slices for which the segmentation was poor. A "
"heuristic is used to rank the slices according to segmentation "
"quality. This option controls how many of the ( best ) slices "
"contribute to the fitting. Use \"0\" to disable distortion "
"parameter fitting. " );
hestOptAdd( &hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "cubic:0, 0.5",
"kernel for resampling DWIs along the phase-encoding "
"direction during final registration stage",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "s", "start #", airTypeInt, 1, 1, &baseNum, "1",
"first number to use in numbered sequence of output files." );
hestOptAdd( &hopt, "o", "output/prefix", airTypeString, 1, 1, &outS, "-",
"For separate 3D DWI volume inputs: prefix for output filenames; "
"will save out one ( registered ) "
"DWI for each input DWI, using the same type as the input. "
"**OR**: For single 4D DWI input: output file name. " );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_epiregInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( strcmp( "kvp", gradS ) ) {
/* they're NOT coming from key/value pairs */
if ( nrrdLoad( ngrad=nrrdNew( ), gradS, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble loading gradient list:\n%s\n", me, err );
airMopError( mop ); return 1;
}
} else {
if ( 1 != ninLen ) {
fprintf( stderr, "%s: can do key/value pairs only from single nrrd", me );
airMopError( mop ); return 1;
}
/* they are coming from key/value pairs */
if ( tenDWMRIKeyValueParse( &ngradKVP, &nbmatKVP, &bvalue,
&skip, &skipNum, nin[0] ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing gradient list:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nbmatKVP ) {
fprintf( stderr, "%s: sorry, can only use gradients, not b-matrices", me );
airMopError( mop ); return 1;
}
ngrad = ngradKVP;
}
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopAlways );
nout3D = AIR_CALLOC( ninLen, Nrrd * );
airMopAdd( mop, nout3D, airFree, airMopAlways );
nout4D = nrrdNew( );
airMopAdd( mop, nout4D, ( airMopper )nrrdNuke, airMopAlways );
buff = AIR_CALLOC( airStrlen( outS ) + 10, char );
airMopAdd( mop, buff, airFree, airMopAlways );
if ( !( nout3D && nout4D && buff ) ) {
fprintf( stderr, "%s: couldn't allocate buffers", me );
airMopError( mop ); return 1;
}
for ( ni=0; ni<ninLen; ni++ ) {
nout3D[ni]=nrrdNew( );
airMopAdd( mop, nout3D[ni], ( airMopper )nrrdNuke, airMopAlways );
}
if ( 1 == ninLen ) {
rret = tenEpiRegister4D( nout4D, nin[0], ngrad,
ref,
bw[0], bw[1], fitFrac, thr, !nocc,
ksp->kernel, ksp->parm,
progress, !noverbose );
} else {
rret = tenEpiRegister3D( nout3D, nin, ninLen, ngrad,
ref,
bw[0], bw[1], fitFrac, thr, !nocc,
ksp->kernel, ksp->parm,
progress, !noverbose );
}
if ( rret ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble doing epireg:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( 1 == ninLen ) {
if ( nrrdSave( outS, nout4D, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing \"%s\":\n%s\n", me, outS, err );
airMopError( mop ); return 1;
}
} else {
for ( ni=0; ni<ninLen; ni++ ) {
if ( ninLen+baseNum > 99 ) {
sprintf( buff, "%s%05d.nrrd", outS, ni+baseNum );
} else if ( ninLen+baseNum > 9 ) {
sprintf( buff, "%s%02d.nrrd", outS, ni+baseNum );
} else {
sprintf( buff, "%s%d.nrrd", outS, ni+baseNum );
}
if ( nrrdSave( buff, nout3D[ni], NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing \"%s\":\n%s\n", me, buff, err );
airMopError( mop ); return 1;
}
}
}
airMopOkay( mop );
return 0;
}
210 TEND_CMD( epireg, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Estimate tensors from a set of DW images"
static const char *_tend_estimInfoL =
( INFO
". The tensor coefficient weightings associated with "
"each of the DWIs, the B-matrix, is given either as a separate array, "
"( see \"tend bmat\" usage info for details ), or by the key-value pairs "
"in the DWI nrrd header. A \"confidence\" value is computed with the "
"tensor, based on a soft thresholding of the sum of all the DWIs, "
"according to the threshold and softness parameters. " );
int
38 tend_estimMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd **nin, *nin4d, *nbmat, *nterr, *nB0, *nout;
char *outS, *terrS, *bmatS, *eb0S;
float soft, scale, sigma;
int dwiax, EE, knownB0, oldstuff, estmeth, verbose, fixneg;
unsigned int ninLen, axmap[4], wlsi, *skip, skipNum, skipIdx;
double valueMin, thresh;
Nrrd *ngradKVP=NULL, *nbmatKVP=NULL;
double bKVP, bval;
tenEstimateContext *tec;
hestOptAdd( &hopt, "old", NULL, airTypeInt, 0, 0, &oldstuff, NULL,
"instead of the new tenEstimateContext code, use "
"the old tenEstimateLinear code" );
hestOptAdd( &hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "nan",
"Rician noise parameter" );
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0",
"verbosity level" );
hestOptAdd( &hopt, "est", "estimate method", airTypeEnum, 1, 1, &estmeth,
"lls",
"estimation method to use. \"lls\": linear-least squares",
NULL, tenEstimate1Method );
hestOptAdd( &hopt, "wlsi", "WLS iters", airTypeUInt, 1, 1, &wlsi, "1",
"when using weighted-least-squares ( \"-est wls\" ), how "
"many iterations to do after the initial weighted fit." );
hestOptAdd( &hopt, "fixneg", NULL, airTypeInt, 0, 0, &fixneg, NULL,
"after estimating the tensor, ensure that there are no negative "
"eigenvalues by adding ( to all eigenvalues ) the amount by which "
"the smallest is negative ( corresponding to increasing the "
"non-DWI image value )." );
hestOptAdd( &hopt, "ee", "filename", airTypeString, 1, 1, &terrS, "",
"Giving a filename here allows you to save out the tensor "
"estimation error: a value which measures how much error there "
"is between the tensor model and the given diffusion weighted "
"measurements for each sample. By default, no such error "
"calculation is saved." );
hestOptAdd( &hopt, "eb", "filename", airTypeString, 1, 1, &eb0S, "",
"In those cases where there is no B=0 reference image given "
"( \"-knownB0 false\" ), "
"giving a filename here allows you to save out the B=0 image "
"which is estimated from the data. By default, this image value "
"is estimated but not saved." );
hestOptAdd( &hopt, "t", "thresh", airTypeDouble, 1, 1, &thresh, "nan",
"value at which to threshold the mean DWI value per pixel "
"in order to generate the \"confidence\" mask. By default, "
"the threshold value is calculated automatically, based on "
"histogram analysis." );
hestOptAdd( &hopt, "soft", "soft", airTypeFloat, 1, 1, &soft, "0",
"how fuzzy the confidence boundary should be. By default, "
"confidence boundary is perfectly sharp" );
hestOptAdd( &hopt, "scale", "scale", airTypeFloat, 1, 1, &scale, "1",
"After estimating the tensor, scale all of its elements "
"( but not the confidence value ) by this amount. Can help with "
"downstream numerical precision if values are very large "
"or small." );
hestOptAdd( &hopt, "mv", "min val", airTypeDouble, 1, 1, &valueMin, "1.0",
"minimum plausible value ( especially important for linear "
"least squares estimation )" );
hestOptAdd( &hopt, "B", "B-list", airTypeString, 1, 1, &bmatS, NULL,
"6-by-N list of B-matrices characterizing "
"the diffusion weighting for each "
"image. \"tend bmat\" is one source for such a matrix; see "
"its usage info for specifics on how the coefficients of "
"the B-matrix are ordered. "
"An unadorned plain text file is a great way to "
"specify the B-matrix.\n **OR**\n "
"Can say just \"-B kvp\" to try to learn B matrices from "
"key/value pair information in input images." );
hestOptAdd( &hopt, "b", "b", airTypeDouble, 1, 1, &bval, "nan",
"\"b\" diffusion-weighting factor ( units of sec/mm^2 )" );
hestOptAdd( &hopt, "knownB0", "bool", airTypeBool, 1, 1, &knownB0, NULL,
"Indicates if the B=0 non-diffusion-weighted reference image "
"is known, or if it has to be estimated along with the tensor "
"elements.\n "
"\b\bo if \"true\": in the given list of diffusion gradients or "
"B-matrices, there are one or more with zero norm, which are "
"simply averaged to find the B=0 reference image value\n "
"\b\bo if \"false\": there may or may not be diffusion-weighted "
"images among the input; the B=0 image value is going to be "
"estimated along with the diffusion model" );
hestOptAdd( &hopt, "i", "dwi0 dwi1", airTypeOther, 1, -1, &nin, "-",
"all the diffusion-weighted images ( DWIs ), as separate 3D nrrds, "
"**OR**: One 4D nrrd of all DWIs stacked along axis 0",
&ninLen, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output tensor volume" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_estimInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
nbmat = nrrdNew( );
airMopAdd( mop, nbmat, ( airMopper )nrrdNuke, airMopAlways );
/* figure out B-matrix */
if ( strcmp( "kvp", airToLower( bmatS ) ) ) {
/* its NOT coming from key/value pairs */
if ( !AIR_EXISTS( bval ) ) {
fprintf( stderr, "%s: need to specify scalar b-value\n", me );
airMopError( mop ); return 1;
}
if ( nrrdLoad( nbmat, bmatS, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble loading B-matrix:\n%s\n", me, err );
airMopError( mop ); return 1;
}
nin4d = nin[0];
skip = NULL;
skipNum = 0;
} else {
/* it IS coming from key/value pairs */
if ( 1 != ninLen ) {
fprintf( stderr, "%s: require a single 4-D DWI volume for "
"key/value pair based calculation of B-matrix\n", me );
airMopError( mop ); return 1;
}
if ( oldstuff ) {
if ( knownB0 ) {
fprintf( stderr, "%s: sorry, key/value-based DWI info not compatible "
"with older implementation of knownB0\n", me );
airMopError( mop ); return 1;
}
}
if ( tenDWMRIKeyValueParse( &ngradKVP, &nbmatKVP, &bKVP,
&skip, &skipNum, nin[0] ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing DWI info:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( AIR_EXISTS( bval ) ) {
fprintf( stderr, "%s: WARNING: key/value pair derived b-value %g "
"over-riding %g from command-line", me, bKVP, bval );
}
bval = bKVP;
if ( ngradKVP ) {
airMopAdd( mop, ngradKVP, ( airMopper )nrrdNuke, airMopAlways );
if ( tenBMatrixCalc( nbmat, ngradKVP ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble finding B-matrix:\n%s\n", me, err );
airMopError( mop ); return 1;
}
} else {
airMopAdd( mop, nbmatKVP, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nbmat, nbmatKVP, nrrdTypeDouble ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting B-matrix:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
/* this will work because of the impositions of tenDWMRIKeyValueParse */
dwiax = ( ( nrrdKindList == nin[0]->axis[0].kind ||
nrrdKindVector == nin[0]->axis[0].kind )
? 0
: ( ( nrrdKindList == nin[0]->axis[1].kind ||
nrrdKindVector == nin[0]->axis[1].kind )
? 1
: ( ( nrrdKindList == nin[0]->axis[2].kind ||
nrrdKindVector == nin[0]->axis[2].kind )
? 2
: 3 ) ) );
if ( 0 == dwiax ) {
nin4d = nin[0];
} else {
axmap[0] = dwiax;
axmap[1] = 1 > dwiax ? 1 : 0;
axmap[2] = 2 > dwiax ? 2 : 1;
axmap[3] = 3 > dwiax ? 3 : 2;
nin4d = nrrdNew( );
airMopAdd( mop, nin4d, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesPermute( nin4d, nin[0], axmap ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble creating DWI volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
}
nterr = NULL;
nB0 = NULL;
if ( !oldstuff ) {
if ( 1 != ninLen ) {
fprintf( stderr, "%s: sorry, currently need single 4D volume "
"for new implementation\n", me );
airMopError( mop ); return 1;
}
if ( !AIR_EXISTS( thresh ) ) {
unsigned char *isB0 = NULL;
double bten[7], bnorm, *bmat;
unsigned int sl;
/* from nbmat, create an array that indicates B0 images */
if ( tenBMatrixCheck( nbmat, nrrdTypeDouble, 6 ) ) {
biffAddf( TEN, "%s: problem within given b-matrix", me );
airMopError( mop ); return 1;
}
isB0 = AIR_CAST( unsigned char *, malloc( nbmat->axis[1].size ) );
airMopAdd( mop, isB0, airFree, airMopAlways );
bmat = ( double* ) nbmat->data;
for ( sl=0; sl<nbmat->axis[1].size; sl++ ) {
TEN_T_SET( bten, 1.0,
bmat[0], bmat[1], bmat[2],
bmat[3], bmat[4],
bmat[5] );
bnorm = TEN_T_NORM( bten );
isB0[sl]=( bnorm==0.0 );
bmat+=6;
}
if ( tenEstimateThresholdFind( &thresh, isB0, nin4d ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble finding threshold:\n%s\n", me, err );
airMopError( mop ); return 1;
}
/* HACK to lower threshold a titch */
thresh *= 0.93;
fprintf( stderr, "%s: using mean DWI threshold %g\n", me, thresh );
}
tec = tenEstimateContextNew( );
tec->progress = AIR_TRUE;
airMopAdd( mop, tec, ( airMopper )tenEstimateContextNix, airMopAlways );
EE = 0;
if ( !EE ) tenEstimateVerboseSet( tec, verbose );
if ( !EE ) tenEstimateNegEvalShiftSet( tec, fixneg );
if ( !EE ) EE |= tenEstimateMethodSet( tec, estmeth );
if ( !EE ) EE |= tenEstimateBMatricesSet( tec, nbmat, bval, !knownB0 );
if ( !EE ) EE |= tenEstimateValueMinSet( tec, valueMin );
for ( skipIdx=0; skipIdx<skipNum; skipIdx++ ) {
/* fprintf( stderr, "%s: skipping %u\n", me, skip[skipIdx] ); */
if ( !EE ) EE |= tenEstimateSkipSet( tec, skip[skipIdx], AIR_TRUE );
}
switch( estmeth ) {
case tenEstimate1MethodLLS:
if ( airStrlen( terrS ) ) {
tec->recordErrorLogDwi = AIR_TRUE;
/* tec->recordErrorDwi = AIR_TRUE; */
}
break;
case tenEstimate1MethodNLS:
if ( airStrlen( terrS ) ) {
tec->recordErrorDwi = AIR_TRUE;
}
break;
case tenEstimate1MethodWLS:
if ( !EE ) tec->WLSIterNum = wlsi;
if ( airStrlen( terrS ) ) {
tec->recordErrorDwi = AIR_TRUE;
}
break;
case tenEstimate1MethodMLE:
if ( !( AIR_EXISTS( sigma ) && sigma > 0.0 ) ) {
fprintf( stderr, "%s: can't do %s w/out sigma > 0 ( not %g )\n",
me, airEnumStr( tenEstimate1Method, tenEstimate1MethodMLE ),
sigma );
airMopError( mop ); return 1;
}
if ( !EE ) EE |= tenEstimateSigmaSet( tec, sigma );
if ( airStrlen( terrS ) ) {
tec->recordLikelihoodDwi = AIR_TRUE;
}
break;
}
if ( !EE ) EE |= tenEstimateThresholdSet( tec, thresh, soft );
if ( !EE ) EE |= tenEstimateUpdate( tec );
if ( EE ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up estimation:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( tenEstimate1TensorVolume4D( tec, nout, &nB0,
airStrlen( terrS )
? &nterr
: NULL,
nin4d, nrrdTypeFloat ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble doing estimation:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( airStrlen( terrS ) ) {
airMopAdd( mop, nterr, ( airMopper )nrrdNuke, airMopAlways );
}
} else {
EE = 0;
if ( 1 == ninLen ) {
EE = tenEstimateLinear4D( nout, airStrlen( terrS ) ? &nterr : NULL, &nB0,
nin4d, nbmat, knownB0, thresh, soft, bval );
} else {
EE = tenEstimateLinear3D( nout, airStrlen( terrS ) ? &nterr : NULL, &nB0,
( const Nrrd*const* )nin, ninLen, nbmat,
knownB0, thresh, soft, bval );
}
if ( EE ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making tensor volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( nterr ) {
/* it was allocated by tenEstimate*, we have to clean it up */
airMopAdd( mop, nterr, ( airMopper )nrrdNuke, airMopAlways );
}
if ( nB0 ) {
/* it was allocated by tenEstimate*, we have to clean it up */
airMopAdd( mop, nB0, ( airMopper )nrrdNuke, airMopAlways );
}
if ( 1 != scale ) {
if ( tenSizeScale( nout, nout, scale ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble doing scaling:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( nterr ) {
if ( nrrdSave( terrS, nterr, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing error image:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( !knownB0 && airStrlen( eb0S ) ) {
if ( nrrdSave( eb0S, nB0, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing estimated B=0 image:\n%s\n",
me, err );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
384 TEND_CMD( estim, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculate one or more eigenvalues in a DT volume"
static const char *_tend_evalInfoL =
( INFO
". " );
int
33 tend_evalMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret, map[4];
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int ret, *comp, compLen, cc;
Nrrd *nin, *nout;
char *outS;
float thresh, *edata, *tdata, eval[3], evec[9];
size_t N, I, sx, sy, sz;
hestOptAdd( &hopt, "c", "c0 ", airTypeInt, 1, 3, &comp, NULL,
"which eigenvalues should be saved out. \"0\" for the "
"largest, \"1\" for the middle, \"2\" for the smallest, "
"\"0 1\", \"1 2\", \"0 1 2\" or similar for more than one",
&compLen );
hestOptAdd( &hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5",
"confidence threshold" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evalInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
for ( cc=0; cc<compLen; cc++ ) {
if ( !AIR_IN_CL( 0, comp[cc], 2 ) ) {
fprintf( stderr, "%s: requested component %d ( %d of 3 ) not in [0..2]\n",
me, comp[cc], cc+1 );
airMopError( mop ); return 1;
}
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get a valid DT volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( 1 == compLen ) {
ret = nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3,
sx, sy, sz );
} else {
ret = nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, compLen ), sx, sy, sz );
}
if ( ret ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
N = sx*sy*sz;
edata = ( float * )nout->data;
tdata = ( float * )nin->data;
if ( 1 == compLen ) {
ELL_3V_SET( map, 1, 2, 3 );
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, evec, tdata );
edata[I] = ( tdata[0] >= thresh )*eval[comp[0]];
tdata += 7;
}
} else {
ELL_4V_SET( map, 0, 1, 2, 3 );
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, evec, tdata );
for ( cc=0; cc<compLen; cc++ )
edata[cc] = ( tdata[0] >= thresh )*eval[comp[cc]];
edata += compLen;
tdata += 7;
}
}
if ( nrrdAxisInfoCopy( nout, nin, map, NRRD_AXIS_INFO_SIZE_BIT ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( 1 != compLen ) {
nout->axis[0].label = ( char * )airFree( nout->axis[0].label );
nout->axis[0].kind = nrrdKindUnknown;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
141 TEND_CMD( eval, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Modify shape by adding a constant to all eigenvalues"
static const char *_tend_evaladdInfoL =
( INFO
". The orientation of the tensor is unchanged. Note that unlike "
"\"tend anscale\", this operation can completely change the shape "
"of the tensor." );
int
35 tend_evaladdMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float val;
hestOptAdd( &hopt, "v", "value", airTypeFloat, 1, 1, &val, NULL,
"Value to add to all eigenvalues" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evaladdInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEigenvalueAdd( nout, nin, val ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
75 TEND_CMD( evaladd, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Modify shape by clamping eigenvalues in some range"
static const char *_tend_evalclampInfoL =
( INFO
". The orientation of the tensor is unchanged. Note that unlike "
"\"tend anscale\", this operation can completely change the shape "
"of the tensor." );
int
35 tend_evalclampMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float min, max;
hestOptAdd( &hopt, "min", "min", airTypeFloat, 1, 1, &min, NULL,
"Eigenvalues are clamped from below by this ( the minimum "
"output eigenvalue ). Use \"nan\" to signify that no "
"minimum clamping should be done." );
hestOptAdd( &hopt, "max", "max", airTypeFloat, 1, 1, &max, "nan",
"Eigenvalues are clamped from above by this ( the maximum "
"output eigenvalue ). Use \"nan\" to signify that no "
"maximum clamping should be done." );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evalclampInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEigenvalueClamp( nout, nin, min, max ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
81 TEND_CMD( evalclamp, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Modify shape by multiplying eigenvalues by a constant"
static const char *_tend_evalmultInfoL =
( INFO
". The orientation of the tensor is unchanged." );
int
33 tend_evalmultMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float val;
hestOptAdd( &hopt, "v", "value", airTypeFloat, 1, 1, &val, NULL,
"Value to multiply eigenvalues by" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evalmultInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEigenvalueMultiply( nout, nin, val ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
73 TEND_CMD( evalmult, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Modify shape by raising eigenvalues to some power"
static const char *_tend_evalpowInfoL =
( INFO
". The orientation of the tensor is unchanged." );
int
33 tend_evalpowMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float expo;
hestOptAdd( &hopt, "p", "power", airTypeFloat, 1, 1, &expo, NULL,
"Power to which to raise all the eigenvalues." );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output tensor volume" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evalpowInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEigenvaluePower( nout, nin, expo ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
73 TEND_CMD( evalpow, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculate one or more eigenvectors in a DT volume"
static const char *_tend_evecInfoL =
( INFO
". " );
int
33 tend_evecMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int ret, *comp, compLen, cc;
Nrrd *nin, *nout;
char *outS;
float thresh, *edata, *tdata, eval[3], evec[9], scl;
size_t N, I, sx, sy, sz;
hestOptAdd( &hopt, "c", "c0 ", airTypeInt, 1, 3, &comp, NULL,
"which eigenvalues should be saved out. \"0\" for the "
"largest, \"1\" for the middle, \"2\" for the smallest, "
"\"0 1\", \"1 2\", \"0 1 2\" or similar for more than one",
&compLen );
hestOptAdd( &hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5",
"confidence threshold" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evecInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
for ( cc=0; cc<compLen; cc++ ) {
if ( !AIR_IN_CL( 0, comp[cc], 2 ) ) {
fprintf( stderr, "%s: requested component %d ( %d of 3 ) not in [0..2]\n",
me, comp[cc], cc+1 );
airMopError( mop ); return 1;
}
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get a valid DT volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
ret = nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 3*compLen ), sx, sy, sz );
if ( ret ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
N = sx*sy*sz;
edata = ( float * )nout->data;
tdata = ( float * )nin->data;
if ( 1 == compLen ) {
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, evec, tdata );
scl = AIR_CAST( float, tdata[0] >= thresh );
ELL_3V_SCALE( edata, scl, evec+3*comp[0] );
edata += 3;
tdata += 7;
}
} else {
for ( I=0; I<N; I++ ) {
tenEigensolve_f( eval, evec, tdata );
scl = AIR_CAST( float, tdata[0] >= thresh );
for ( cc=0; cc<compLen; cc++ ) {
ELL_3V_SCALE( edata+3*cc, scl, evec+3*comp[cc] );
}
edata += 3*compLen;
tdata += 7;
}
}
if ( nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_SIZE_BIT ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
nout->axis[0].label = ( char * )airFree( nout->axis[0].label );
nout->axis[0].kind = nrrdKindUnknown;
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
136 TEND_CMD( evec, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Make an RGB volume from an eigenvector and an anisotropy"
static const char *_tend_evecrgbInfoL =
( INFO
". " );
int
33 tend_evecrgbMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
tenEvecRGBParm *rgbp;
Nrrd *nin, *nout;
char *outS;
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
rgbp = tenEvecRGBParmNew( );
airMopAdd( mop, rgbp, AIR_CAST( airMopper, tenEvecRGBParmNix ), airMopAlways );
hestOptAdd( &hopt, "c", "evec index", airTypeUInt, 1, 1, &( rgbp->which ), NULL,
"which eigenvector will be colored. \"0\" for the "
"principal, \"1\" for the middle, \"2\" for the minor" );
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1, &( rgbp->aniso ), NULL,
"Which anisotropy to use for modulating the saturation "
"of the colors. " TEN_ANISO_DESC,
NULL, tenAniso );
hestOptAdd( &hopt, "t", "thresh", airTypeDouble, 1, 1, &( rgbp->confThresh ),
"0.5", "confidence threshold" );
hestOptAdd( &hopt, "bg", "background", airTypeDouble, 1, 1, &( rgbp->bgGray ),
"0", "gray level to use for voxels who's confidence is zero " );
hestOptAdd( &hopt, "gr", "gray", airTypeDouble, 1, 1, &( rgbp->isoGray ), "0",
"the gray level to desaturate towards as anisotropy "
"decreases ( while confidence remains 1.0 )" );
hestOptAdd( &hopt, "gam", "gamma", airTypeDouble, 1, 1, &( rgbp->gamma ), "1",
"gamma to use on color components" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evecrgbInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEvecRGB( nout, nin, rgbp ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble doing colormapping:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
92 TEND_CMD( evecrgb, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Quantize directions of diffusion"
static const char *_tend_evqInfoL =
( INFO
". Because VTK doesn't do multi-dimensional colormaps, we have to "
"quantize directions of diffusion ( usually just the principal eigenvector ) "
"in order to create the usual XYZ<->RGB coloring. Because "
"eigenvector directions are poorly defined in regions of low "
"anisotropy, the length of the vector ( pre-quantization ) is modulated "
"by anisotropy, requiring the selection of some anisotropy metric." );
int
38 tend_evqMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int which, aniso, dontScaleByAniso;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "c", "evec index", airTypeInt, 1, 1, &which, "0",
"Which eigenvector should be quantized: \"0\" for the "
"direction of fastest diffusion ( eigenvector associated "
"with largest eigenvalue ), \"1\" or \"2\" for other two "
"eigenvectors ( associated with middle and smallest eigenvalue )" );
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1, &aniso, NULL,
"Which anisotropy metric to scale the eigenvector "
"with. " TEN_ANISO_DESC,
NULL, tenAniso );
hestOptAdd( &hopt, "ns", NULL, airTypeInt, 0, 0, &dontScaleByAniso, NULL,
"Don't attenuate the color by anisotropy. By default ( not "
"using this option ), regions with low or no anisotropy are "
"very dark colors or black" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_evqInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenEvqVolume( nout, nin, which, aniso, !dontScaleByAniso ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble quantizing eigenvectors:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
90 TEND_CMD( evq, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculates exp( ) of the tensor"
static const char *_tend_expInfoL =
( INFO
", which is based on exp( ) of the eigenvalues." );
int
33 tend_expMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output image" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_expInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenExp( nout, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
70 TEND_CMD( exp, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Converts masked non-redundant tensor images to redundant"
static const char *_tend_expandInfoL =
( INFO
". For images of 3D tensors, this converts from a 7-value tensor "
"starting with the confidence/mask value "
"( conf, Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ) to "
"a 9-value tensor with the full matrix "
"( Dxx, Dxy, Dxz, Dxy, Dyy, Dyz, Dxz, Dyz, Dzz ). "
"This is set to all zeros when the confidence is below the given "
"threshold. For images of 2D tensors, the conversion is from "
"( conf, Dxx, Dxy, Dyy ) to ( Dxx, Dxy, Dxy, Dyy ). " );
int
40 tend_expandMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
int orientRed, orientRedWithOrigin, mfRed;
float scale, thresh;
hestOptAdd( &hopt, "t", "thresh", airTypeFloat, 1, 1, &thresh, "0.5",
"confidence level to threshold output tensors at. Should "
"be between 0.0 and 1.0." );
hestOptAdd( &hopt, "s", "scale", airTypeFloat, 1, 1, &scale, "1.0",
"how to scale values before saving as 9-value tensor. Useful "
"for visualization tools which assume certain characteristic "
"ranges of eigenvalues" );
hestOptAdd( &hopt, "unmf", NULL, airTypeInt, 0, 0, &mfRed, NULL,
"apply and remove the measurement frame, if it exists" );
hestOptAdd( &hopt, "ro", NULL, airTypeInt, 0, 0, &orientRed, NULL,
"reduce general image orientation to axis-aligned spacings" );
hestOptAdd( &hopt, "roo", NULL, airTypeInt, 0, 0,
&orientRedWithOrigin, NULL,
"reduce general image orientation to axis-aligned spacings, "
"while also making some effort to set axis mins from "
"space origin" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume, with 7 values per sample",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output tensor volume, with the 9 matrix components per sample" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_expandInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( mfRed
&& 3 == nin->spaceDim
&& AIR_EXISTS( nin->measurementFrame[0][0] ) ) {
if ( tenMeasurementFrameReduce( nin, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with measurement frame:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( 4 == nin->axis[0].size
? tenExpand2D( nout, nin, scale, thresh )
: tenExpand( nout, nin, scale, thresh ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble expanding tensors:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( orientRedWithOrigin || orientRed ) {
if ( nrrdOrientationReduce( nout, nout,
orientRedWithOrigin
? AIR_TRUE
: AIR_FALSE ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble unorienting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
118 TEND_CMD( expand, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Fiber tractography, from one or more seeds"
static const char *_tend_fiberInfoL =
( INFO
". A fairly complete command-line interface to the tenFiber API." );
int
33 tend_fiberMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
char *outS;
tenFiberContext *tfx;
tenFiberSingle *tfbs;
NrrdKernelSpec *ksp;
double start[3], step, *_stop, *stop;
const airEnum *ftypeEnum;
char *ftypeS;
int E, intg, useDwi, allPaths, verbose, worldSpace, worldSpaceOut,
ftype, ftypeDef;
Nrrd *nin, *nseed, *nmat, *_nmat;
unsigned int si, stopLen, whichPath;
double matx[16]={1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
tenFiberMulti *tfml;
limnPolyData *fiberPld;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "dwi", NULL, airTypeInt, 0, 0, &useDwi, NULL,
"input volume is a DWI volume, not a single tensor volume" );
hestOptAdd( &hopt, "s", "seed point", airTypeDouble, 3, 3, start, "0 0 0",
"seed point for fiber; it will propogate in two opposite "
"directions starting from here" );
hestOptAdd( &hopt, "ns", "seed nrrd", airTypeOther, 1, 1, &nseed, "",
"3-by-N nrrd of seedpoints", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "wsp", NULL, airTypeInt, 0, 0, &worldSpace, NULL,
"define seedpoint and output path in worldspace. Otherwise, "
"( without using this option ) everything is in index space" );
hestOptAdd( &hopt, "t", "type", airTypeString, 1, 1, &ftypeS, "",
"fiber type; defaults to something" );
hestOptAdd( &hopt, "n", "intg", airTypeEnum, 1, 1, &intg, "rk4",
"integration method for fiber tracking",
NULL, tenFiberIntg );
hestOptAdd( &hopt, "k", "kernel", airTypeOther, 1, 1, &ksp, "tent",
"kernel for reconstructing tensor field",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &hopt, "wp", "which", airTypeUInt, 1, 1, &whichPath, "0",
"when doing multi-tensor tracking, index of path to follow "
"( made moot by \"-ap\" )" );
hestOptAdd( &hopt, "ap", "allpaths", airTypeInt, 0, 0, &allPaths, NULL,
"follow all paths from ( all ) seedpoint( s ), output will be "
"polydata, rather than a single 3-by-N nrrd, even if only "
"a single path is generated" );
hestOptAdd( &hopt, "wspo", NULL, airTypeInt, 0, 0, &worldSpaceOut, NULL,
"output should be in worldspace, even if input is not "
"( this feature is unstable and/or confusing )" );
hestOptAdd( &hopt, "step", "step size", airTypeDouble, 1, 1, &step, "0.01",
"stepsize along fiber, in world space" );
hestOptAdd( &hopt, "stop", "stop1", airTypeOther, 1, -1, &_stop, NULL,
"the conditions that should signify the end of a fiber, or "
"when to discard a fiber that is done propagating. "
"Multiple stopping criteria are logically OR-ed and tested at "
"every point along the fiber. Possibilities include:\n "
"\b\bo \"aniso:<type>, <thresh>\": require anisotropy to be "
"above the given threshold. Which anisotropy type is given "
"as with \"tend anvol\" ( see its usage info )\n "
"\b\bo \"len:<length>\": limits the length, in world space, "
"of each fiber half\n "
"\b\bo \"steps:<N>\": the number of steps in each fiber half "
"is capped at N\n "
"\b\bo \"conf:<thresh>\": requires the tensor confidence value "
"to be above the given thresh\n "
"\b\bo \"radius:<thresh>\": requires that the radius of "
"curvature of the fiber stay above given thr\n "
"\b\bo \"frac:<F>\": in multi-tensor tracking, the fraction "
"of the tracked component must stay above F\n "
"\b\bo \"minlen:<len>\": discard fiber if its final whole "
"length is below len ( not really a termination criterion )\n "
"\b\bo \"minsteps:<N>\": discard fiber if its final number of "
"steps is below N ( not really a termination criterion )",
&stopLen, NULL, tendFiberStopCB );
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0",
"verbosity level" );
hestOptAdd( &hopt, "nmat", "transform", airTypeOther, 1, 1, &_nmat, "",
"a 4x4 homogenous transform matrix ( as a nrrd, or just a text "
"file ) given with this option will be applied to the output "
"tractography vertices just prior to output",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "out", airTypeString, 1, 1, &outS, "-",
"output fiber( s )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_fiberInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
tfbs = tenFiberSingleNew( );
airMopAdd( mop, tfbs, ( airMopper )tenFiberSingleNix, airMopAlways );
if ( _nmat ) {
if ( !( 2 == _nmat->dim
&& 4 == _nmat->axis[0].size
&& 4 == _nmat->axis[1].size ) ) {
fprintf( stderr, "%s: transform matrix must be 2-D 4-by-4 array "
"not %u-D %u-by-?\n", me,
AIR_CAST( unsigned int, _nmat->dim ),
AIR_CAST( unsigned int, _nmat->axis[0].size ) );
airMopError( mop ); return 1;
}
nmat = nrrdNew( );
airMopAdd( mop, nmat, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nmat, _nmat, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem with transform matrix\n%s\n", me, err );
airMopError( mop ); return 1;
}
ELL_4M_COPY( matx, AIR_CAST( double *, nmat->data ) );
fprintf( stderr, "%s: transforming output by:\n", me );
ell_4m_print_d( stderr, matx );
}
if ( useDwi ) {
tfx = tenFiberContextDwiNew( nin, 50, 1, 1,
tenEstimate1MethodLLS,
tenEstimate2MethodPeled );
} else {
tfx = tenFiberContextNew( nin );
}
if ( !tfx ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: failed to create the fiber context:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopAdd( mop, tfx, ( airMopper )tenFiberContextNix, airMopAlways );
E = 0;
for ( si=0, stop=_stop; si<stopLen; si++, stop+=3 ) {
int istop; /* not from Apple */
istop = AIR_CAST( int, stop[0] );
switch( istop ) {
case tenFiberStopAniso:
if ( !E ) E |= tenFiberStopSet( tfx, istop,
AIR_CAST( int, stop[1] ), stop[2] );
break;
case tenFiberStopNumSteps:
case tenFiberStopMinNumSteps:
if ( !E ) E |= tenFiberStopSet( tfx, istop,
AIR_CAST( unsigned int, stop[1] ) );
break;
case tenFiberStopLength:
case tenFiberStopConfidence:
case tenFiberStopFraction:
case tenFiberStopRadius:
case tenFiberStopMinLength:
if ( !E ) E |= tenFiberStopSet( tfx, istop, stop[1] );
break;
case tenFiberStopBounds:
/* nothing to actually do */
break;
default:
fprintf( stderr, "%s: stop method %d not supported\n", me,
istop );
airMopError( mop ); return 1;
break;
}
}
if ( !E ) {
if ( useDwi ) {
ftypeEnum = tenDwiFiberType;
ftypeDef = tenDwiFiberType2Evec0;
} else {
ftypeEnum = tenFiberType;
ftypeDef = tenFiberTypeEvec0;
}
if ( airStrlen( ftypeS ) ) {
ftype = airEnumVal( ftypeEnum, ftypeS );
if ( airEnumUnknown( ftypeEnum ) == ftype ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a %s\n", me,
ftypeS, ftypeEnum->name );
airMopError( mop ); return 1;
}
} else {
ftype = ftypeDef;
fprintf( stderr, "%s: ( defaulting %s to %s )\n", me,
ftypeEnum->name, airEnumStr( ftypeEnum, ftype ) );
}
E |= tenFiberTypeSet( tfx, ftype );
}
if ( !E ) E |= tenFiberKernelSet( tfx, ksp->kernel, ksp->parm );
if ( !E ) E |= tenFiberIntgSet( tfx, intg );
if ( !E ) E |= tenFiberParmSet( tfx, tenFiberParmStepSize, step );
if ( !E ) E |= tenFiberParmSet( tfx, tenFiberParmUseIndexSpace,
worldSpace ? AIR_FALSE: AIR_TRUE );
if ( !E ) E |= tenFiberUpdate( tfx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
tenFiberVerboseSet( tfx, verbose );
if ( !allPaths ) {
if ( tenFiberSingleTrace( tfx, tfbs, start, whichPath ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( tenFiberStopUnknown == tfx->whyNowhere ) {
fprintf( stderr, "%s: steps[back, forw] = %u, %u; seedIdx = %u\n", me,
tfbs->stepNum[0], tfbs->stepNum[1], tfbs->seedIdx );
fprintf( stderr, "%s: whyStop[back, forw] = %s, %s\n", me,
airEnumStr( tenFiberStop, tfbs->whyStop[0] ),
airEnumStr( tenFiberStop, tfbs->whyStop[1] ) );
if ( worldSpaceOut && !worldSpace ) {
/* have to convert output to worldspace */
fprintf( stderr, "%s: WARNING!!! output conversion "
"to worldspace not done!!!\n", me );
}
if ( _nmat ) {
fprintf( stderr, "%s: WARNING!!! output transform "
"not done!!!\n", me );
}
if ( nrrdSave( outS, tfbs->nvert, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
} else {
fprintf( stderr, "%s: fiber failed to start: %s.\n",
me, airEnumDesc( tenFiberStop, tfx->whyNowhere ) );
}
} else {
if ( !nseed ) {
fprintf( stderr, "%s: didn't get seed nrrd via \"-ns\"\n", me );
airMopError( mop ); return 1;
}
tfml = tenFiberMultiNew( );
airMopAdd( mop, tfml, ( airMopper )tenFiberMultiNix, airMopAlways );
/*
fiberArr = airArrayNew( AIR_CAST( void **, &fiber ), NULL,
sizeof( tenFiberSingle ), 1024 );
airArrayStructCB( fiberArr,
AIR_CAST( void ( * )( void * ), tenFiberSingleInit ),
AIR_CAST( void ( * )( void * ), tenFiberSingleDone ) );
airMopAdd( mop, fiberArr, ( airMopper )airArrayNuke, airMopAlways );
*/
fiberPld = limnPolyDataNew( );
airMopAdd( mop, fiberPld, ( airMopper )limnPolyDataNix, airMopAlways );
if ( tenFiberMultiTrace( tfx, tfml, nseed )
|| tenFiberMultiPolyData( tfx, fiberPld, tfml ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( worldSpaceOut && !worldSpace ) {
/* have to convert output to worldspace */
unsigned int ii;
double indx[4], world[3];
for ( ii=0; ii<fiberPld->xyzwNum; ii++ ) {
ELL_4V_COPY( indx, fiberPld->xyzw + 4*ii );
ELL_4V_HOMOG( indx, indx );
gageShapeItoW( tfx->gtx->shape, world, indx );
ELL_3V_COPY_TT( fiberPld->xyzw + 4*ii, float, world );
( fiberPld->xyzw + 4*ii )[3] = 1.0;
}
}
if ( _nmat ) {
/* have to further transform output by given matrix */
unsigned int ii;
double xxx[4], yyy[4];
for ( ii=0; ii<fiberPld->xyzwNum; ii++ ) {
ELL_4V_COPY( xxx, fiberPld->xyzw + 4*ii );
ELL_4MV_MUL( yyy, matx, xxx );
ELL_4V_HOMOG( yyy, yyy );
ELL_4V_COPY_TT( fiberPld->xyzw + 4*ii, float, yyy );
}
}
if ( limnPolyDataSave( outS, fiberPld ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
315 TEND_CMD( fiber, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
/*
******** tendCmdList[]
**
** NULL-terminated array of unrrduCmd pointers, as ordered by
** TEN_MAP macro
*/
unrrduCmd *
tendCmdList[] = {
TEND_MAP( TEND_LIST )
NULL
};
const char *tendTitle = "tend: Diffusion Image Processing and Analysis";
/*
******** tendFiberStopParse
**
** for parsing the different ways in which a fiber should be stopped
** For the sake of laziness and uniformity, the stop information is
** stored in an array of 3 ( three ) doubles:
** info[0]: int value from tenFiberStop* enum
** info[1]: 1st parameter associated with stop method ( always used )
** info[2]: 2nd parameter, used occasionally
*/
int
52 tendFiberStopParse( void *ptr, char *_str, char err[AIR_STRLEN_HUGE] ) {
char me[]="tenFiberStopParse", *str, *opt, *opt2;
double *info;
airArray *mop;
int integer;
if ( !( ptr && _str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
info = ( double * )ptr;
mop = airMopNew( );
str = airStrdup( _str );
airMopMem( mop, &str, airMopAlways );
opt = strchr( str, ':' );
if ( !opt ) {
/* couldn't parse string as nrrdEncoding, but there wasn't a colon */
sprintf( err, "%s: didn't see a colon in \"%s\"", me, str );
airMopError( mop ); return 1;
}
*opt = '\0';
opt++;
info[0] = AIR_CAST( int, airEnumVal( tenFiberStop, str ) );
if ( tenFiberStopUnknown == AIR_CAST( int, info[0] ) ) {
sprintf( err, "%s: didn't recognize \"%s\" as %s",
me, str, tenFiberStop->name );
airMopError( mop ); return 1;
}
switch( AIR_CAST( int, info[0] ) ) {
case tenFiberStopAniso:
/* <aniso>, <level> : tenAniso, double */
opt2 = strchr( opt, ', ' );
if ( !opt2 ) {
sprintf( err, "%s: didn't see comma between aniso and level in \"%s\"",
me, opt );
airMopError( mop ); return 1;
}
*opt2 = '\0';
opt2++;
info[1] = AIR_CAST( int, airEnumVal( tenAniso, opt ) );
if ( tenAnisoUnknown == AIR_CAST( int, info[1] ) ) {
sprintf( err, "%s: didn't recognize \"%s\" as %s",
me, opt, tenAniso->name );
airMopError( mop ); return 1;
}
if ( 1 != sscanf( opt2, "%lg", info+2 ) ) {
sprintf( err, "%s: couldn't parse aniso level \"%s\" as double",
me, opt2 );
airMopError( mop ); return 1;
}
/*
fprintf( stderr, "!%s: parsed aniso:%s, %g\n", me,
airEnumStr( tenAniso, AIR_CAST( int, info[1] ) ), info[2] );
*/
break;
case tenFiberStopFraction:
case tenFiberStopLength:
case tenFiberStopRadius:
case tenFiberStopConfidence:
case tenFiberStopMinLength:
/* all of these take a single double */
if ( 1 != sscanf( opt, "%lg", info+1 ) ) {
sprintf( err, "%s: couldn't parse %s \"%s\" as double", me,
airEnumStr( tenFiberStop, AIR_CAST( int, info[0] ) ), opt );
airMopError( mop ); return 1;
}
/*
fprintf( stderr, "!%s: parse %s:%g\n", me,
airEnumStr( tenFiberStop, AIR_CAST( int, info[0] ) ),
info[1] );
*/
break;
case tenFiberStopNumSteps:
case tenFiberStopMinNumSteps:
/* <#steps> : int */
if ( 1 != sscanf( opt, "%d", &integer ) ) {
sprintf( err, "%s: couldn't parse \"%s\" as int", me, opt );
airMopError( mop ); return 1;
}
info[1] = integer;
/* fprintf( stderr, "!%s: parse steps:%d\n", me, integer ); */
break;
case tenFiberStopBounds:
/* moron */
break;
default:
sprintf( err, "%s: stop method %d not supported", me,
AIR_CAST( int, info[0] ) );
airMopError( mop ); return 1;
break;
}
airMopOkay( mop );
return 0;
}
hestCB
_tendFiberStopCB = {
3*sizeof( double ),
"fiber stop",
tendFiberStopParse,
NULL
};
hestCB *
tendFiberStopCB = &_tendFiberStopCB;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Generate postscript or ray-traced renderings of 3D glyphs"
static const char *_tend_glyphInfoL =
( INFO
". Whether the output is postscript or a ray-traced image is controlled "
"by the initial \"rt\" flag ( by default, the output is postscript ). "
"Because this is doing viz/graphics, many parameters need to be set. "
"Use a response file to simplify giving the command-line options which "
"aren't changing between invocations. "
"The postscript output is an EPS file, suitable for including as a figure "
"in LaTeX, or viewing with ghostview, or distilling into PDF. "
"The ray-traced output is a 5 channel ( R, G, B, A, T ) float nrrd, suitable for "
"\"unu crop -min 0 0 0 -max 2 M M \" followed by "
"\"unu gamma\" and/or \"unu quantize -b 8\"." );
#define _LIMNMAGIC "LIMN0000"
int
44 _tendGlyphReadCams( int imgSize[2], limnCamera **camP,
unsigned int *numCamsP, FILE *fin ) {
static const char me[]="_tendGlyphReadCams";
char line[AIR_STRLEN_HUGE];
int ki;
double di, dn, df, fr[3], at[3], up[3], va, dwell;
airArray *mop, *camA;
if ( !( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE )
&& !strcmp( _LIMNMAGIC, line ) ) ) {
biffAddf( TEN, "%s: couldn't read first line or it wasn't \"%s\"",
me, _LIMNMAGIC );
return 1;
}
if ( !( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE )
&& 2 == ( airStrtrans( airStrtrans( line, '{', ' ' ), '}', ' ' ),
sscanf( line, "imgSize %d %d", imgSize+0, imgSize+1 ) ) ) ) {
biffAddf( TEN, "%s: couldn't read second line or it wasn't "
"\"imgSize <sizeX> <sizeY>\"", me );
return 1;
}
mop = airMopNew( );
camA = airArrayNew( ( void ** )camP, numCamsP, sizeof( limnCamera ), 1 );
airMopAdd( mop, camA, ( airMopper )airArrayNix, airMopAlways );
while ( 0 < airOneLine( fin, line, AIR_STRLEN_HUGE ) ) {
airStrtrans( airStrtrans( line, '{', ' ' ), '}', ' ' );
ki = airArrayLenIncr( camA, 1 );
if ( 14 != sscanf( line, "cam.di %lg cam.at %lg %lg %lg "
"cam.up %lg %lg %lg cam.dn %lg cam.df %lg cam.va %lg "
"relDwell %lg cam.fr %lg %lg %lg",
&di, at+0, at+1, at+2,
up+0, up+1, up+2, &dn, &df, &va,
&dwell, fr+0, fr+1, fr+2 ) ) {
biffAddf( TEN, "%s: trouble parsing line %d: \"%s\"", me, ki, line );
airMopError( mop ); return 1;
}
( *camP )[ki].neer = dn;
( *camP )[ki].faar = df;
( *camP )[ki].dist = di;
ELL_3V_COPY( ( *camP )[ki].from, fr );
ELL_3V_COPY( ( *camP )[ki].at, at );
ELL_3V_COPY( ( *camP )[ki].up, up );
( *camP )[ki].fov = va;
( *camP )[ki].aspect = ( double )imgSize[0]/imgSize[1];
( *camP )[ki].atRelative = AIR_FALSE;
( *camP )[ki].orthographic = AIR_FALSE;
( *camP )[ki].rightHanded = AIR_TRUE;
}
airMopOkay( mop );
return 0;
}
int
100 tend_glyphMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret, doRT = AIR_FALSE;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *emap, *nraw, *npos, *nslc;
char *outS;
limnCamera *cam, *hackcams;
limnObject *glyph;
limnWindow *win;
echoObject *rect=NULL;
echoScene *scene;
echoRTParm *eparm;
echoGlobalState *gstate;
tenGlyphParm *gparm;
float bg[3], edgeColor[3], buvne[5], shadow, creaseAngle;
int ires[2], slice[2], nobg, ambocc, concave;
unsigned int hackci, hacknumcam;
size_t hackmin[3]={0, 0, 0}, hackmax[3]={2, 0, 0};
char *hackFN, hackoutFN[AIR_STRLEN_SMALL];
FILE *hackF;
Nrrd *hacknpng, *hacknrgb;
NrrdRange *hackrange;
double v2w[9], ldir[3], edir[3], fdir[3], corn[3], len;
/* so that command-line options can be read from file */
hparm->respFileEnable = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
mop = airMopNew( );
cam = limnCameraNew( );
airMopAdd( mop, cam, ( airMopper )limnCameraNix, airMopAlways );
glyph = limnObjectNew( 1000, AIR_TRUE );
airMopAdd( mop, glyph, ( airMopper )limnObjectNix, airMopAlways );
scene = echoSceneNew( );
airMopAdd( mop, scene, ( airMopper )echoSceneNix, airMopAlways );
win = limnWindowNew( limnDevicePS );
airMopAdd( mop, win, ( airMopper )limnWindowNix, airMopAlways );
gparm = tenGlyphParmNew( );
airMopAdd( mop, gparm, ( airMopper )tenGlyphParmNix, airMopAlways );
eparm = echoRTParmNew( );
airMopAdd( mop, eparm, ( airMopper )echoRTParmNix, airMopAlways );
/* do postscript or ray-traced? */
hestOptAdd( &hopt, "rt", NULL, airTypeFloat, 0, 0, &doRT, NULL,
"generate ray-traced output. By default ( not using this "
"option ), postscript output is generated." );
hestOptAdd( &hopt, "v", "level", airTypeInt, 1, 1, &( gparm->verbose ), "0",
"verbosity level" );
/* which points will rendered */
hestOptAdd( &hopt, "ctr", "conf thresh", airTypeFloat, 1, 1,
&( gparm->confThresh ), "0.5",
"Glyphs will be drawn only for tensors with confidence "
"values greater than this threshold" );
hestOptAdd( &hopt, "a", "aniso", airTypeEnum, 1, 1,
&( gparm->anisoType ), "fa",
"Which anisotropy metric to use for thresholding the data "
"points to be drawn", NULL, tenAniso );
hestOptAdd( &hopt, "atr", "aniso thresh", airTypeFloat, 1, 1,
&( gparm->anisoThresh ), "0.5",
"Glyphs will be drawn only for tensors with anisotropy "
"greater than this threshold" );
hestOptAdd( &hopt, "p", "pos array", airTypeOther, 1, 1, &npos, "",
"Instead of being on a grid, tensors are at arbitrary locations, "
"as defined by this 3-by-N array of floats. Doing this makes "
"various other options moot", NULL, NULL,
nrrdHestNrrd );
hestOptAdd( &hopt, "m", "mask vol", airTypeOther, 1, 1, &( gparm->nmask ), "",
"Scalar volume ( if any ) for masking region in which glyphs are "
"drawn, in conjunction with \"mtr\" flag. ", NULL, NULL,
nrrdHestNrrd );
hestOptAdd( &hopt, "mtr", "mask thresh", airTypeFloat, 1, 1,
&( gparm->maskThresh ),
"0.5", "Glyphs will be drawn only for tensors with mask "
"value greater than this threshold" );
/* how glyphs will be shaped */
hestOptAdd( &hopt, "g", "glyph shape", airTypeEnum, 1, 1,
&( gparm->glyphType ), "box",
"shape of glyph to use for display. Possibilities "
"include \"box\", \"sphere\", \"cylinder\", and "
"\"superquad\"", NULL, tenGlyphType );
hestOptAdd( &hopt, "sh", "sharpness", airTypeFloat, 1, 1,
&( gparm->sqdSharp ), "3.0",
"for superquadric glyphs, how much to sharp edges form as a "
"function of differences between eigenvalues. Higher values "
"mean that edges form more easily" );
hestOptAdd( &hopt, "gsc", "scale", airTypeFloat, 1, 1, &( gparm->glyphScale ),
"0.01", "over-all glyph size in world-space" );
/* how glyphs will be colored */
hestOptAdd( &hopt, "c", "evector #", airTypeInt, 1, 1, &( gparm->colEvec ), "0",
"which eigenvector should determine coloring. "
"( formally \"v\" ) "
"\"0\", \"1\", \"2\" are principal, medium, and minor" );
hestOptAdd( &hopt, "sat", "saturation", airTypeFloat, 1, 1,
&( gparm->colMaxSat ), "1.0",
"maximal saturation to use on glyph colors ( use 0.0 to "
"create a black and white image )" );
hestOptAdd( &hopt, "ga", "aniso", airTypeEnum, 1, 1,
&( gparm->colAnisoType ), "fa",
"Which anisotropy metric to use for modulating the "
"saturation of the glyph color", NULL, tenAniso );
hestOptAdd( &hopt, "am", "aniso mod", airTypeFloat, 1, 1,
&( gparm->colAnisoModulate ),
"0.0", "How much to modulate glyph color saturation by "
"anisotropy ( as chosen by \"-ga\" ). "
"If 1.0, then glyphs for zero anisotropy "
"data points will have no hue. " );
hestOptAdd( &hopt, "gg", "gray", airTypeFloat, 1, 1, &( gparm->colIsoGray ),
"1.0", "desaturating glyph color due to low anisotropy "
"tends towards this gray level" );
hestOptAdd( &hopt, "gam", "gamma", airTypeFloat, 1, 1, &( gparm->colGamma ),
"0.7", "gamma to use on color components ( after saturation )" );
hestOptAdd( &hopt, "emap", "env map", airTypeOther, 1, 1, &emap, "",
"environment map to use for shading glyphs. By default, "
"there is no shading", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "adsp", "phong", airTypeFloat, 4, 4, &( gparm->ADSP ),
"0 1 0 30", "phong ambient, diffuse, specular components, "
"and specular power" );
hestOptAdd( &hopt, "bg", "background", airTypeFloat, 3, 3, bg, "1 1 1",
"background RGB color; each component in range [0.0, 1.0]" );
hestOptAdd( &hopt, "ec", "edge rgb", airTypeFloat, 3, 3, edgeColor, "0 0 0",
"edge RGB color; each component in range [0.0, 1.0]" );
/* parameters for showing a dataset slice */
hestOptAdd( &hopt, "slc", "axis pos", airTypeInt, 2, 2, slice, "-1 -1",
"For showing a gray-scale slice of anisotropy: the axis "
"and position along which to slice. Use \"-1 -1\" to signify "
"that no slice should be shown" );
hestOptAdd( &hopt, "si", "slice image", airTypeOther, 1, 1, &nslc, "",
"Instead of showing a slice of the anisotropy used to cull "
"glyphs, show something else. ", NULL, NULL,
nrrdHestNrrd );
hestOptAdd( &hopt, "off", "slice offset", airTypeFloat, 1, 1,
&( gparm->sliceOffset ), "0.0",
"Offset from slice position to render slice at ( so that it "
"doesn't occlude glyphs )." );
hestOptAdd( &hopt, "sg", "slice gamma", airTypeFloat, 1, 1,
&( gparm->sliceGamma ), "1.7",
"Gamma to apply to values on slice." );
hestOptAdd( &hopt, "sb", "slice bias", airTypeFloat, 1, 1,
&( gparm->sliceBias ), "0.05",
"amount by which to bump up slice gray values prior to gamma." );
/* camera */
hestOptAdd( &hopt, "fr", "from point", airTypeDouble, 3, 3, cam->from, NULL,
"position of camera, used to determine view vector" );
hestOptAdd( &hopt, "at", "at point", airTypeDouble, 3, 3, cam->at, "0 0 0",
"camera look-at point, used to determine view vector" );
hestOptAdd( &hopt, "up", "up vector", airTypeDouble, 3, 3, cam->up, "0 0 1",
"camera pseudo-up vector, used to determine view coordinates" );
hestOptAdd( &hopt, "rh", NULL, airTypeInt, 0, 0, &( cam->rightHanded ), NULL,
"use a right-handed UVN frame ( V points down )" );
hestOptAdd( &hopt, "dn", "near clip", airTypeDouble, 1, 1, &( cam->neer ), "-2",
"position of near clipping plane, relative to look-at point" );
hestOptAdd( &hopt, "df", "far clip", airTypeDouble, 1, 1, &( cam->faar ), "2",
"position of far clipping plane, relative to look-at point" );
hestOptAdd( &hopt, "or", NULL, airTypeInt, 0, 0, &( cam->orthographic ), NULL,
"use orthogonal projection" );
hestOptAdd( &hopt, "ur", "uMin uMax", airTypeDouble, 2, 2, cam->uRange,
"-1 1", "range in U direction of image plane" );
hestOptAdd( &hopt, "vr", "vMin vMax", airTypeDouble, 2, 2, cam->vRange,
"-1 1", "range in V direction of image plane" );
hestOptAdd( &hopt, "fv", "fov", airTypeDouble, 1, 1, &( cam->fov ), "nan",
"if not NaN, vertical field-of-view, in degrees" );
/* postscript-specific options */
hestOptAdd( &hopt, "gr", "glyph res", airTypeInt, 1, 1, &( gparm->facetRes ),
"10", "( * postscript only * ) "
"resolution of polygonalization of glyphs ( all glyphs "
"other than the default box )" );
hestOptAdd( &hopt, "wd", "3 widths", airTypeFloat, 3, 3, gparm->edgeWidth,
"0.8 0.4 0.0", "( * postscript only * ) "
"width of edges drawn for three kinds of glyph "
"edges: silohuette, crease, non-crease" );
hestOptAdd( &hopt, "psc", "scale", airTypeFloat, 1, 1, &( win->scale ), "300",
"( * postscript only * ) "
"scaling from screen space units to postscript units "
"( in points )" );
hestOptAdd( &hopt, "ca", "angle", airTypeFloat, 1, 1, &creaseAngle, "70",
"( * postscript only * ) "
"minimum crease angle" );
hestOptAdd( &hopt, "nobg", NULL, airTypeInt, 0, 0, &nobg, NULL,
"( * postscript only * ) "
"don't initially fill with background color" );
hestOptAdd( &hopt, "concave", NULL, airTypeInt, 0, 0, &concave, NULL,
"use slightly buggy rendering method suitable for "
"concave or self-occluding objects" );
/* ray-traced-specific options */
hestOptAdd( &hopt, "is", "nx ny", airTypeInt, 2, 2, ires, "256 256",
"( * ray-traced only * ) "
"image size ( resolution ) to render" );
hestOptAdd( &hopt, "ns", "# samp", airTypeInt, 1, 1, &( eparm->numSamples ), "4",
"( * ray-traced only * ) "
"number of samples per pixel ( must be a square number )" );
if ( airThreadCapable ) {
hestOptAdd( &hopt, "nt", "# threads", airTypeInt, 1, 1,
&( eparm->numThreads ), "1",
"( * ray-traced only * ) "
"number of threads to be used for rendering" );
}
hestOptAdd( &hopt, "al", "B U V N E", airTypeFloat, 5, 5, buvne,
"0 -1 -1 -4 0.7",
"( * ray-traced only * ) "
"brightness ( B ), view-space location ( U V N ), "
"and length of edge ( E ) "
"of a square area light source, for getting soft shadows. "
"Requires lots more samples \"-ns\" to converge. Use "
"brightness 0 ( the default ) to turn this off, and use "
"environment map-based shading ( \"-emap\" ) instead. " );
hestOptAdd( &hopt, "ao", NULL, airTypeInt, 0, 0, &ambocc, NULL,
"set up 6 area lights in a box to approximate "
"ambient occlusion" );
hestOptAdd( &hopt, "shadow", "s", airTypeFloat, 1, 1, &shadow, "1.0",
"the extent to which shadowing occurs" );
hestOptAdd( &hopt, "hack", "hack", airTypeString, 1, 1, &hackFN, "",
"don't mind me" );
/* input/output */
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file" );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_glyphInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
/* set up slicing stuff */
if ( !( -1 == slice[0] && -1 == slice[1] ) ) {
gparm->doSlice = AIR_TRUE;
gparm->sliceAxis = slice[0];
gparm->slicePos = slice[1];
gparm->sliceAnisoType = gparm->anisoType;
/* gparm->sliceOffset set by hest */
}
if ( npos ) {
fprintf( stderr, "!%s: have npos --> turning off onlyPositive \n", me );
gparm->onlyPositive = AIR_FALSE;
}
if ( gparm->verbose ) {
fprintf( stderr, "%s: verbose = %d\n", me, gparm->verbose );
}
if ( tenGlyphGen( doRT ? NULL : glyph,
doRT ? scene : NULL,
gparm,
nin, npos, nslc ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble generating glyphs:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( AIR_EXISTS( cam->fov ) ) {
if ( limnCameraAspectSet( cam, ires[0], ires[1], nrrdCenterCell ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with camera:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
cam->dist = 0;
cam->atRelative = AIR_TRUE;
if ( limnCameraUpdate( cam ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with camera:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( doRT ) {
nraw = nrrdNew( );
airMopAdd( mop, nraw, ( airMopper )nrrdNuke, airMopAlways );
gstate = echoGlobalStateNew( );
airMopAdd( mop, gstate, ( airMopper )echoGlobalStateNix, airMopAlways );
eparm->shadow = shadow;
if ( buvne[0] > 0 ) {
ELL_34M_EXTRACT( v2w, cam->V2W );
ELL_3MV_MUL( ldir, v2w, buvne+1 );
ell_3v_perp_d( edir, ldir );
ELL_3V_NORM( edir, edir, len );
ELL_3V_CROSS( fdir, ldir, edir );
ELL_3V_NORM( fdir, fdir, len );
ELL_3V_SCALE( edir, buvne[4]/2, edir );
ELL_3V_SCALE( fdir, buvne[4]/2, fdir );
ELL_3V_ADD4( corn, cam->at, ldir, edir, fdir );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect,
corn[0], corn[1], corn[2],
-edir[0]*2, -edir[1]*2, -edir[2]*2,
-fdir[0]*2, -fdir[1]*2, -fdir[2]*2 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, buvne[0], 0 );
echoObjectAdd( scene, rect );
}
if ( ambocc ) {
double eye[3], llen;
ELL_3V_SUB( eye, cam->from, cam->at );
llen = 4*ELL_3V_LEN( eye );
ELL_3V_COPY( corn, cam->at );
corn[0] -= llen/2;
corn[1] -= llen/2;
corn[2] -= llen/2;
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
llen, 0, 0, 0, llen, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
0, 0, llen, llen, 0, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
0, llen, 0, 0, 0, llen );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
corn[0] += llen/2;
corn[1] += llen/2;
corn[2] += llen/2;
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
0, -llen, 0, -llen, 0, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
-llen, 0, 0, 0, 0, -llen );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
rect = echoObjectNew( scene, echoTypeRectangle );
echoRectangleSet( rect, corn[0], corn[1], corn[2],
0, 0, -llen, 0, -llen, 0 );
echoColorSet( rect, 1, 1, 1, 1 );
echoMatterLightSet( scene, rect, 1, AIR_CAST( echoCol_t, llen ) );
echoObjectAdd( scene, rect );
}
eparm->imgResU = ires[0];
eparm->imgResV = ires[1];
eparm->jitterType = ( eparm->numSamples > 1
? echoJitterJitter
: echoJitterNone );
eparm->aperture = 0;
eparm->renderBoxes = AIR_FALSE;
eparm->seedRand = AIR_FALSE;
eparm->renderLights = AIR_FALSE;
ELL_3V_COPY( scene->bkgr, bg );
scene->envmap = emap;
if ( !airStrlen( hackFN ) ) {
/* normal operation: one ray-tracing for one invocation */
if ( echoRTRender( nraw, cam, scene, eparm, gstate ) ) {
airMopAdd( mop, err = biffGetDone( ECHO ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble ray-tracing %s\n", me, err );
airMopError( mop );
return 1;
}
if ( nrrdSave( outS, nraw, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving ray-tracing output %s\n", me, err );
airMopError( mop );
return 1;
}
} else {
/* hack: multiple renderings per invocation */
if ( !( hackF = airFopen( hackFN, stdin, "rb" ) ) ) {
fprintf( stderr, "%s: couldn't fopen( \"%s\", \"rb\" ): %s\n",
me, hackFN, strerror( errno ) );
airMopError( mop ); return 1;
}
if ( _tendGlyphReadCams( ires, &hackcams, &hacknumcam, hackF ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble reading frames %s\n", me, err );
airMopError( mop );
return 1;
}
eparm->imgResU = ires[0];
eparm->imgResV = ires[1];
hackmax[1] = ires[0]-1;
hackmax[2] = ires[1]-1;
hacknrgb = nrrdNew( );
hacknpng = nrrdNew( );
airMopAdd( mop, hacknrgb, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, hacknpng, ( airMopper )nrrdNuke, airMopAlways );
hackrange = nrrdRangeNew( 0.0, 1.0 );
airMopAdd( mop, hackrange, ( airMopper )nrrdRangeNix, airMopAlways );
for ( hackci=0; hackci<hacknumcam; hackci++ ) {
memcpy( cam, hackcams + hackci, sizeof( limnCamera ) );
/* rightHanded and orthographic not handled nicely */
if ( rect ) {
if ( limnCameraUpdate( cam ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with camera:\n%s\n", me, err );
airMopError( mop ); return 1;
}
ELL_34M_EXTRACT( v2w, cam->V2W );
ELL_3MV_MUL( ldir, v2w, buvne+1 );
ell_3v_perp_d( edir, ldir );
ELL_3V_NORM( edir, edir, len );
ELL_3V_CROSS( fdir, ldir, edir );
ELL_3V_NORM( fdir, fdir, len );
ELL_3V_SCALE( edir, buvne[4]/2, edir );
ELL_3V_SCALE( fdir, buvne[4]/2, fdir );
ELL_3V_ADD4( corn, cam->at, ldir, edir, fdir );
echoRectangleSet( rect,
corn[0], corn[1], corn[2],
edir[0]*2, edir[1]*2, edir[2]*2,
fdir[0]*2, fdir[1]*2, fdir[2]*2 );
}
if ( echoRTRender( nraw, cam, scene, eparm, gstate ) ) {
airMopAdd( mop, err = biffGetDone( ECHO ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble ray-tracing %s\n", me, err );
airMopError( mop );
return 1;
}
sprintf( hackoutFN, "%04d.png", hackci );
if ( nrrdCrop( hacknrgb, nraw, hackmin, hackmax )
|| nrrdQuantize( hacknpng, hacknrgb, hackrange, 8 )
|| nrrdSave( hackoutFN, hacknpng, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output %s\n", me, err );
airMopError( mop );
return 1;
}
}
}
} else {
if ( !( win->file = airFopen( outS, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't fopen( \"%s\", \"wb\" ): %s\n",
me, outS, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, win->file, ( airMopper )airFclose, airMopAlways );
cam->neer = -0.000000001;
cam->faar = 0.0000000001;
win->ps.lineWidth[limnEdgeTypeBackFacet] = 0;
win->ps.lineWidth[limnEdgeTypeBackCrease] = 0;
win->ps.lineWidth[limnEdgeTypeContour] = gparm->edgeWidth[0];
win->ps.lineWidth[limnEdgeTypeFrontCrease] = gparm->edgeWidth[1];
win->ps.lineWidth[limnEdgeTypeFrontFacet] = gparm->edgeWidth[2];
win->ps.lineWidth[limnEdgeTypeBorder] = 0;
/* win->ps.lineWidth[limnEdgeTypeFrontCrease]; */
win->ps.creaseAngle = creaseAngle;
win->ps.noBackground = nobg;
ELL_3V_COPY( win->ps.bg, bg );
ELL_3V_COPY( win->ps.edgeColor, edgeColor );
if ( limnObjectRender( glyph, cam, win )
|| ( concave
? limnObjectPSDrawConcave( glyph, cam, emap, win )
: limnObjectPSDraw( glyph, cam, emap, win ) ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble drawing glyphs:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
airMopOkay( mop );
return 0;
}
574 TEND_CMD( glyph, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculate balanced gradient directions for DWI acquisition"
static const char *_tend_gradsInfoL =
( INFO
", based on a simulation of anti-podal point pairs repelling each other "
"on the unit sphere surface. This can either distribute more uniformly "
"a given set of gradients, or it can make a new distribution from scratch. "
"A more clever implementation could decrease drag with time, as the "
"solution converges, to get closer to the minimum energy configuration "
"faster. In the mean time, you can run a second pass on the output of "
"the first pass, using lower drag. A second phase of the algorithm "
"tries sign changes in gradient directions in trying to find an optimally "
"balanced set of directions. This uses a randomized search, so if it "
"doesn't seem to be finishing in a reasonable amount of time, try "
"restarting with a different \"-seed\"." );
int
43 tend_gradsMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int num, E;
Nrrd *nin, *nout;
char *outS;
tenGradientParm *tgparm;
unsigned int seed;
mop = airMopNew( );
tgparm = tenGradientParmNew( );
airMopAdd( mop, tgparm, ( airMopper )tenGradientParmNix, airMopAlways );
hestOptAdd( &hopt, "n", "# dir", airTypeInt, 1, 1, &num, "6",
"desired number of diffusion gradient directions" );
hestOptAdd( &hopt, "i", "grads", airTypeOther, 1, 1, &nin, "",
"initial gradient directions to start with, instead "
"of default random initial directions ( overrides \"-n\" )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "seed", "value", airTypeUInt, 1, 1, &seed, "42",
"seed value to used with airSrandMT( )" );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1, &( tgparm->initStep ),
"1.0",
"time increment in solver" );
hestOptAdd( &hopt, "single", NULL, airTypeInt, 0, 0, &( tgparm->single ), NULL,
"instead of the default behavior of tracking a pair of "
"antipodal points ( appropriate for determining DWI gradients ), "
"use only single points ( appropriate for who knows what )." );
hestOptAdd( &hopt, "snap", "interval", airTypeInt, 1, 1, &( tgparm->snap ), "0",
"specifies an interval between which snapshots of the point "
"positions should be saved out. By default ( not using this "
"option ), there is no such snapshot behavior" );
hestOptAdd( &hopt, "jitter", "jitter", airTypeDouble, 1, 1,
&( tgparm->jitter ), "0.1",
"amount by which to perturb points when given an input nrrd" );
hestOptAdd( &hopt, "miniter", "# iters", airTypeInt, 1, 1,
&( tgparm->minIteration ), "0",
"max number of iterations for which to run the simulation" );
hestOptAdd( &hopt, "maxiter", "# iters", airTypeInt, 1, 1,
&( tgparm->maxIteration ), "1000000",
"max number of iterations for which to run the simulation" );
hestOptAdd( &hopt, "minvelo", "vel", airTypeDouble, 1, 1,
&( tgparm->minVelocity ), "0.00001",
"low threshold on mean velocity of repelling points, "
"at which point repulsion phase of algorithm terminates." );
hestOptAdd( &hopt, "exp", "exponent", airTypeDouble, 1, 1,
&( tgparm->expo_d ), "1",
"the exponent n that determines the potential energy 1/r^n." );
hestOptAdd( &hopt, "dp", "potential change", airTypeDouble, 1, 1,
&( tgparm->minPotentialChange ), "0.000000001",
"low threshold on fractional change of potential at "
"which point repulsion phase of algorithm terminates." );
hestOptAdd( &hopt, "minimprov", "delta", airTypeDouble, 1, 1,
&( tgparm->minMeanImprovement ), "0.00005",
"in the second phase of the algorithm, "
"when stochastically balancing the sign of the gradients, "
"the ( small ) improvement in length of mean gradient "
"which triggers termination ( as further improvements "
"are unlikely." );
hestOptAdd( &hopt, "minmean", "len", airTypeDouble, 1, 1,
&( tgparm->minMean ), "0.0001",
"if length of mean gradient falls below this, finish "
"the balancing phase" );
hestOptAdd( &hopt, "izv", "insert", airTypeBool, 1, 1,
&( tgparm->insertZeroVec ), "false",
"adding zero vector at beginning of grads" );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_gradsInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* see if it was an integral exponent */
tgparm->expo = AIR_CAST( unsigned int, tgparm->expo_d );
if ( tgparm->expo == tgparm->expo_d ) {
/* ooo, it was */
tgparm->expo_d = 0;
} else {
/* no, its non-integral, indicate this as follows */
tgparm->expo = 0;
}
tgparm->seed = seed;
if ( tgparm->snap ) {
tgparm->report = tgparm->snap;
}
E = ( nin
? tenGradientDistribute( nout, nin, tgparm )
: tenGradientGenerate( nout, num, tgparm ) );
if ( E ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making distribution:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
153 TEND_CMD( grads, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Generate twisting helical tensor field"
static const char *_tend_helixInfoL =
( INFO
". The main utility of such a field is to debug handling of coordinate "
"systems in tensor field visualization. The \"space directions\" and "
"\"space origin\" fields of the NRRD header determines the mapping from "
"coordinates in the index space of the image to coordinates in the "
"world space in which the image is "
"sampled. The \"measurement frame\" field determines the mapping from "
"the coordinates of the tensor itself, to coordinates of the world space. "
"When these are correctly handled, the "
"region of high anisotropy is a right-handed helix ( same as DNA ). "
"Using differing axes sizes ( via \"-s\" ) helps make sure that the "
"raster ordering of axes is correct. In addition, the tensors twist "
"relative to the helix, which exposes handling of the measurement frame. "
"If you trace paths guided by the principal eigenvector of the tensors, "
"along the surface of the helical cylinder, you get another "
"right-handed helix, as if the the tensor field is modeling the result "
"if twisting a set of fibers into single-stranded helical bundle. " );
void
48 tend_helixDoit( Nrrd *nout, double bnd,
double orig[3], double i2w[9], double mf[9],
double r, double R, double S, double angle, int incrtwist,
double ev[3], double bgEval, int verbose ) {
int sx, sy, sz, xi, yi, zi;
double th, t0, t1, t2, t3, v1, v2,
wpos[3], vpos[3], mfT[9],
W2H[9], H2W[9], H2C[9], C2H[9], fv[3], rv[3], uv[3], mA[9], mB[9], inside,
tmp[3], len;
float *out;
sx = nout->axis[1].size;
sy = nout->axis[2].size;
sz = nout->axis[3].size;
out = ( float* )nout->data;
ELL_3M_TRANSPOSE( mfT, mf );
for ( zi=0; zi<sz; zi++ ) {
if ( verbose ) {
fprintf( stderr, "zi = %d/%d\n", zi, sz );
}
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
ELL_3V_SET( tmp, xi, yi, zi );
ELL_3MV_MUL( vpos, i2w, tmp );
ELL_3V_INCR( vpos, orig );
#define WPOS( pos, th ) ELL_3V_SET( ( pos ), R*cos( th ), R*sin( th ), S*( th )/( 2*AIR_PI ) )
#define VAL( th ) ( WPOS( wpos, th ), ELL_3V_DIST( wpos, vpos ) )
#define RR 0.61803399
#define CC ( 1.0-RR )
#define SHIFT3( a, b, c, d ) ( a )=( b ); ( b )=( c ); ( c )=( d )
#define SHIFT2( a, b, c ) ( a )=( b ); ( b )=( c )
th = atan2( vpos[1], vpos[0] );
th += 2*AIR_PI*floor( 0.5 + vpos[2]/S - th/( 2*AIR_PI ) );
if ( S*th/( 2*AIR_PI ) > vpos[2] ) {
t0 = th - AIR_PI; t3 = th;
} else {
t0 = th; t3 = th + AIR_PI;
}
t1 = RR*t0 + CC*t3;
t2 = CC*t0 + RR*t3;
v1 = VAL( t1 );
v2 = VAL( t2 );
while ( t3-t0 > 0.000001*( AIR_ABS( t1 )+AIR_ABS( t2 ) ) ) {
if ( v1 < v2 ) {
SHIFT3( t3, t2, t1, CC*t0 + RR*t2 );
SHIFT2( v2, v1, VAL( t1 ) );
} else {
SHIFT3( t0, t1, t2, RR*t1 + CC*t3 );
SHIFT2( v1, v2, VAL( t2 ) );
}
}
/* t1 ( and t2 ) are now the th for which the point on the helix
( R*cos( th ), R*sin( th ), S*( th )/( 2*AIR_PI ) ) is closest to vpos */
WPOS( wpos, t1 );
ELL_3V_SUB( wpos, vpos, wpos );
ELL_3V_SET( fv, -R*sin( t1 ), R*cos( t1 ), S/AIR_PI ); /* helix tangent */
ELL_3V_NORM( fv, fv, len );
ELL_3V_COPY( rv, wpos );
ELL_3V_NORM( rv, rv, len );
len = ELL_3V_DOT( rv, fv );
ELL_3V_SCALE( tmp, -len, fv );
ELL_3V_ADD2( rv, rv, tmp );
ELL_3V_NORM( rv, rv, len ); /* rv now normal to helix, closest to
pointing to vpos */
ELL_3V_CROSS( uv, rv, fv );
ELL_3V_NORM( uv, uv, len ); /* ( rv, fv, uv ) now right-handed frame */
ELL_3MV_ROW0_SET( W2H, uv ); /* as is ( uv, rv, fv ) */
ELL_3MV_ROW1_SET( W2H, rv );
ELL_3MV_ROW2_SET( W2H, fv );
ELL_3M_TRANSPOSE( H2W, W2H );
inside = 0.5 - 0.5*airErf( ( ELL_3V_LEN( wpos )-r )/( bnd + 0.0001 ) );
if ( incrtwist ) {
th = angle*ELL_3V_LEN( wpos )/r;
} else {
th = angle;
}
ELL_3M_ROTATE_Y_SET( H2C, th );
ELL_3M_TRANSPOSE( C2H, H2C );
ELL_3M_SCALE_SET( mA,
AIR_LERP( inside, bgEval, ev[1] ),
AIR_LERP( inside, bgEval, ev[2] ),
AIR_LERP( inside, bgEval, ev[0] ) );
ELL_3M_MUL( mB, mA, H2C );
ELL_3M_MUL( mA, mB, W2H );
ELL_3M_MUL( mB, mA, mf );
ELL_3M_MUL( mA, C2H, mB );
ELL_3M_MUL( mB, H2W, mA );
ELL_3M_MUL( mA, mfT, mB );
TEN_M2T_TT( out, float, mA );
out[0] = 1.0;
out += 7;
}
}
}
return;
}
int
150 tend_helixMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int size[3], nit, verbose;
Nrrd *nout;
double R, r, S, bnd, angle, ev[3], ip[3], iq[4], mp[3], mq[4], tmp[9],
orig[3], i2w[9], rot[9], mf[9], spd[4][3], bge;
char *outS;
hestOptAdd( &hopt, "s", "size", airTypeInt, 3, 3, size, NULL,
"sizes along fast, medium, and slow axes of the sampled volume, "
"often called \"X\", \"Y\", and \"Z\". It is best to use "
"slightly different sizes here, to expose errors in interpreting "
"axis ordering ( e.g. \"-s 39 40 41\" )" );
hestOptAdd( &hopt, "ip", "image orientation", airTypeDouble, 3, 3, ip,
"0 0 0",
"quaternion quotient space orientation of image" );
hestOptAdd( &hopt, "mp", "measurement orientation", airTypeDouble, 3, 3, mp,
"0 0 0",
"quaternion quotient space orientation of measurement frame" );
hestOptAdd( &hopt, "b", "boundary", airTypeDouble, 1, 1, &bnd, "10",
"parameter governing how fuzzy the boundary between high and "
"low anisotropy is. Use \"-b 0\" for no fuzziness" );
hestOptAdd( &hopt, "r", "little radius", airTypeDouble, 1, 1, &r, "30",
"( minor ) radius of cylinder tracing helix" );
hestOptAdd( &hopt, "R", "big radius", airTypeDouble, 1, 1, &R, "50",
"( major ) radius of helical turns" );
hestOptAdd( &hopt, "S", "spacing", airTypeDouble, 1, 1, &S, "100",
"spacing between turns of helix ( along its axis )" );
hestOptAdd( &hopt, "a", "angle", airTypeDouble, 1, 1, &angle, "60",
"maximal angle of twist of tensors along path. There is no "
"twist at helical core of path, and twist increases linearly "
"with radius around this path. Positive twist angle with "
"positive spacing resulting in a right-handed twist around a "
"right-handed helix. " );
hestOptAdd( &hopt, "nit", NULL, airTypeInt, 0, 0, &nit, NULL,
"changes behavior of twist angle as function of distance from "
"center of helical core: instead of increasing linearly as "
"describe above, be at a constant angle" );
hestOptAdd( &hopt, "ev", "eigenvalues", airTypeDouble, 3, 3, ev,
"0.006 0.002 0.001",
"eigenvalues of tensors ( in order ) along direction of coil, "
"circumferential around coil, and radial around coil. " );
hestOptAdd( &hopt, "bg", "background", airTypeDouble, 1, 1, &bge, "0.5",
"eigenvalue of isotropic background" );
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "1",
"verbose output" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_helixInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, size[0] ),
AIR_CAST( size_t, size[1] ),
AIR_CAST( size_t, size[2] ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
ELL_4V_SET( iq, 1.0, ip[0], ip[1], ip[2] );
ell_q_to_3m_d( rot, iq );
ELL_3V_SET( orig,
-2*R + 2*R/size[0],
-2*R + 2*R/size[1],
-2*R + 2*R/size[2] );
ELL_3M_ZERO_SET( i2w );
ELL_3M_DIAG_SET( i2w, 4*R/size[0], 4*R/size[1], 4*R/size[2] );
ELL_3MV_MUL( tmp, rot, orig );
ELL_3V_COPY( orig, tmp );
ELL_3M_MUL( tmp, rot, i2w );
ELL_3M_COPY( i2w, tmp );
ELL_4V_SET( mq, 1.0, mp[0], mp[1], mp[2] );
ell_q_to_3m_d( mf, mq );
tend_helixDoit( nout, bnd,
orig, i2w, mf,
r, R, S, angle*AIR_PI/180, !nit, ev, bge,
verbose );
nrrdSpaceSet( nout, nrrdSpaceRightAnteriorSuperior );
nrrdSpaceOriginSet( nout, orig );
ELL_3V_SET( spd[0], AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3MV_COL0_GET( spd[1], i2w );
ELL_3MV_COL1_GET( spd[2], i2w );
ELL_3MV_COL2_GET( spd[3], i2w );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoSpaceDirection,
spd[0], spd[1], spd[2], spd[3] );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoCenter,
nrrdCenterUnknown, nrrdCenterCell,
nrrdCenterCell, nrrdCenterCell );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoKind,
nrrdKind3DMaskedSymMatrix, nrrdKindSpace,
nrrdKindSpace, nrrdKindSpace );
nout->measurementFrame[0][0] = mf[0];
nout->measurementFrame[1][0] = mf[1];
nout->measurementFrame[2][0] = mf[2];
nout->measurementFrame[0][1] = mf[3];
nout->measurementFrame[1][1] = mf[4];
nout->measurementFrame[2][1] = mf[5];
nout->measurementFrame[0][2] = mf[6];
nout->measurementFrame[1][2] = mf[7];
nout->measurementFrame[2][2] = mf[8];
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
273 TEND_CMD( helix, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculates logarithm of the tensor"
static const char *_tend_logInfoL =
( INFO
", which is based on finding the log of the eigenvalues." );
int
33 tend_logMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output image" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_logInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenLog( nout, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
70 TEND_CMD( log, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Create DT volume from confidence and eigensystem"
static const char *_tend_makeInfoL =
( INFO
". The input is in the form of three nrrds, one for confidence "
"values ( 3D ), one for eigenvalues ( 4D, three evals per voxel ), and "
"one for eigenvectors ( 4D, nine evec components per voxel )." );
int
35 tend_makeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin[3], *nout;
char *outS;
hestOptAdd( &hopt, "i", "conf evals evecs", airTypeOther, 3, 3, nin, NULL,
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_makeInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenMake( nout, nin[0], nin[1], nin[2] ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making tensor volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
73 TEND_CMD( make, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "convert from one model to another"
static const char *_tend_mconvInfoL =
( INFO
". More docs here." );
int
33 tend_mconvMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS, *modelSrcS, *modelDstS;
const tenModel *modelDst, *modelSrc;
int saveB0;
hestOptAdd( &hopt, "mo", "model", airTypeString, 1, 1, &modelDstS, NULL,
"which model to convert to" );
hestOptAdd( &hopt, "mi", "model", airTypeString, 1, 1, &modelSrcS, "",
"model converting from; if not set, will try to determine "
"from input nrrd" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input nrrd of model parms",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output nrrd of model parms" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_mconvInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenModelParse( &modelDst, &saveB0, AIR_FALSE, modelDstS ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing model \"%s\":\n%s\n", me,
modelDstS, err );
airMopError( mop ); return 1;
}
if ( saveB0 ) {
printf( "%s: warning: saving B0 is determined by input nrrd "
"having B0 info.\n", me );
}
if ( airStrlen( modelSrcS ) ) {
if ( tenModelParse( &modelSrc, &saveB0, AIR_FALSE, modelSrcS ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing model \"%s\":\n%s\n", me,
modelSrcS, err );
airMopError( mop ); return 1;
}
} else {
modelSrc = NULL;
}
if ( tenModelConvert( nout, NULL, modelDst, nin, modelSrc ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
98 TEND_CMD( mconv, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Estimate models from a set of DW images"
static const char *_tend_mfitInfoL =
( INFO
". More docs here." );
int
33 tend_mfitMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout, *nterr, *nconv, *niter;
char *outS, *terrS, *convS, *iterS, *modS;
int knownB0, saveB0, verbose, mlfit, typeOut;
unsigned int maxIter, minIter, starts;
double sigma, eps;
const tenModel *model;
tenExperSpec *espec;
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &verbose, "0",
"verbosity level" );
hestOptAdd( &hopt, "m", "model", airTypeString, 1, 1, &modS, NULL,
"which model to fit. Use optional \"b0+\" prefix to "
"indicate that the B0 image should also be saved "
"( independent of whether it was known or had to be "
"estimated, according to \"-knownB0\" )." );
hestOptAdd( &hopt, "ns", "# starts", airTypeUInt, 1, 1, &starts, "1",
"number of random starting points at which to initialize "
"fitting" );
hestOptAdd( &hopt, "ml", NULL, airTypeInt, 0, 0, &mlfit, NULL,
"do ML fitting, rather than least-squares, which also "
"requires setting \"-sigma\"" );
hestOptAdd( &hopt, "sigma", "sigma", airTypeDouble, 1, 1, &sigma, "nan",
"Gaussian/Rician noise parameter" );
hestOptAdd( &hopt, "eps", "eps", airTypeDouble, 1, 1, &eps, "0.01",
"convergence epsilon" );
hestOptAdd( &hopt, "mini", "min iters", airTypeUInt, 1, 1, &minIter, "3",
"minimum required # iterations for fitting." );
hestOptAdd( &hopt, "maxi", "max iters", airTypeUInt, 1, 1, &maxIter, "100",
"maximum allowable # iterations for fitting." );
hestOptAdd( &hopt, "knownB0", "bool", airTypeBool, 1, 1, &knownB0, NULL,
"Indicates if the B=0 non-diffusion-weighted reference image "
"is known ( \"true\" ) because it appears one or more times "
"amongst the DWIs, or, if it has to be estimated along with "
"the other model parameters ( \"false\" )" );
/* ( this is now specified as part of the "-m" model description )
hestOptAdd( &hopt, "saveB0", "bool", airTypeBool, 1, 1, &saveB0, NULL,
"Indicates if the B=0 non-diffusion-weighted value "
"should be saved in output, regardless of whether it was "
"known or had to be esimated" );
*/
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &typeOut, "float",
"output type of model parameters",
NULL, nrrdType );
hestOptAdd( &hopt, "i", "dwi", airTypeOther, 1, 1, &nin, "-",
"all the diffusion-weighted images in one 4D nrrd",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output parameter vector image" );
hestOptAdd( &hopt, "eo", "filename", airTypeString, 1, 1, &terrS, "",
"Giving a filename here allows you to save out the per-sample "
"fitting error. By default, no such error is saved." );
hestOptAdd( &hopt, "co", "filename", airTypeString, 1, 1, &convS, "",
"Giving a filename here allows you to save out the per-sample "
"convergence fraction. By default, no such error is saved." );
hestOptAdd( &hopt, "io", "filename", airTypeString, 1, 1, &iterS, "",
"Giving a filename here allows you to save out the per-sample "
"number of iterations needed for fitting. "
"By default, no such error is saved." );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_mfitInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nterr = NULL;
nconv = NULL;
niter = NULL;
espec = tenExperSpecNew( );
airMopAdd( mop, espec, ( airMopper )tenExperSpecNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenModelParse( &model, &saveB0, AIR_FALSE, modS ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing model \"%s\":\n%s\n", me, modS, err );
airMopError( mop ); return 1;
}
if ( tenExperSpecFromKeyValueSet( espec, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble getting exper from kvp:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( tenModelSqeFit( nout,
airStrlen( terrS ) ? &nterr : NULL,
airStrlen( convS ) ? &nconv : NULL,
airStrlen( iterS ) ? &niter : NULL,
model, espec, nin,
knownB0, saveB0, typeOut,
minIter, maxIter, starts, eps,
NULL, verbose ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble fitting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL )
|| ( airStrlen( terrS ) && nrrdSave( terrS, nterr, NULL ) )
|| ( airStrlen( convS ) && nrrdSave( convS, nconv, NULL ) )
|| ( airStrlen( iterS ) && nrrdSave( iterS, niter, NULL ) ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing output:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
147 TEND_CMD( mfit, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Simulate DW images from an image of models"
static const char *_tend_msimInfoL =
( INFO
". The output will be in the same form as the input to \"tend estim\". "
"The B-matrices ( \"-B\" ) can be the output from \"tend bmat\", or the "
"gradients can be given directly ( \"-g\" ); one of these is required. "
"Note that the input tensor image ( \"-i\" ) is the basis of the output "
"per-axis fields and image orientation. NOTE: this includes the "
"measurement frame used in the input tensor image, which implies that "
"the given gradients or B-matrices are already expressed in that "
"measurement frame. " );
int
40 tend_msimMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
tenExperSpec *espec;
const tenModel *model;
int E, seed, keyValueSet, outType, plusB0, insertB0;
Nrrd *nin, *nT2, *_ngrad, *ngrad, *nout;
char *outS, *modS;
double bval, sigma;
/* maybe this can go in tend.c, but for some reason its explicitly
set to AIR_FALSE there */
hparm->elideSingleOtherDefault = AIR_TRUE;
hestOptAdd( &hopt, "sigma", "sigma", airTypeDouble, 1, 1, &sigma, "0.0",
"Gaussian/Rician noise parameter" );
hestOptAdd( &hopt, "seed", "seed", airTypeInt, 1, 1, &seed, "42",
"seed value for RNG which creates noise" );
hestOptAdd( &hopt, "g", "grad list", airTypeOther, 1, 1, &_ngrad, NULL,
"gradient list, one row per diffusion-weighted image",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b0", "b0 image", airTypeOther, 1, 1, &nT2, "",
"reference non-diffusion-weighted ( \"B0\" ) image, which "
"may be needed if it isn't part of give model param image",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "i", "model image", airTypeOther, 1, 1, &nin, "-",
"input model image", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "m", "model", airTypeString, 1, 1, &modS, NULL,
"model with which to simulate DWIs, which must be specified if "
"it is not indicated by the first axis in input model image." );
hestOptAdd( &hopt, "ib0", "bool", airTypeBool, 1, 1, &insertB0, "false",
"insert a non-DW B0 image at the beginning of the experiment "
"specification ( useful if the given gradient list doesn't "
"already have one ) and hence also insert a B0 image at the "
"beginning of the output simulated DWIs" );
hestOptAdd( &hopt, "b", "b", airTypeDouble, 1, 1, &bval, "1000",
"b value for simulated scan" );
hestOptAdd( &hopt, "kvp", "bool", airTypeBool, 1, 1, &keyValueSet, "true",
"generate key/value pairs in the NRRD header corresponding "
"to the input b-value and gradients." );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &outType, "float",
"output type of DWIs", NULL, nrrdType );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output dwis" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_msimInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
espec = tenExperSpecNew( );
airMopAdd( mop, espec, ( airMopper )tenExperSpecNix, airMopAlways );
airSrandMT( seed );
if ( nrrdTypeDouble == _ngrad->type ) {
ngrad = _ngrad;
} else {
ngrad = nrrdNew( );
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ngrad, _ngrad, nrrdTypeDouble ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting grads to %s:\n%s\n", me,
airEnumStr( nrrdType, nrrdTypeDouble ), err );
airMopError( mop ); return 1;
}
}
plusB0 = AIR_FALSE;
if ( airStrlen( modS ) ) {
if ( tenModelParse( &model, &plusB0, AIR_FALSE, modS ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing model \"%s\":\n%s\n",
me, modS, err );
airMopError( mop ); return 1;
}
} else if ( tenModelFromAxisLearnPossible( nin->axis + 0 ) ) {
if ( tenModelFromAxisLearn( &model, &plusB0, nin->axis + 0 ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble parsing model frmo axis 0 of nin:\n%s\n",
me, err );
airMopError( mop ); return 1;
}
} else {
fprintf( stderr, "%s: need model specified either via \"-m\" or input "
"model image axis 0\n", me );
airMopError( mop ); return 1;
}
/* we have learned plusB0, but we don't actually need it;
either: it describes the given model param image
( which is courteous but not necessary since the logic inside
tenModeSimulate will see this ),
or: it is trying to say something about including B0 amongst
model parameters ( which isn't actually meaningful in the
context of simulated DWIs */
E = 0;
if ( !E ) E |= tenGradientCheck( ngrad, nrrdTypeDouble, 1 );
if ( !E ) E |= tenExperSpecGradSingleBValSet( espec, insertB0, bval,
AIR_CAST( const double *,
ngrad->data ),
ngrad->axis[1].size );
if ( !E ) E |= tenModelSimulate( nout, outType, espec,
model, nT2, nin, keyValueSet );
if ( E ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
162 TEND_CMD( msim, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Normalize tensor size"
static const char *_tend_normInfoL =
( INFO
". This operates on the eigenvalues of the tensor, and allows "
"normalizing some user-defined weighting ( \"-w\" ) of the eigenvalues by "
"some user-defined amount ( \"-a\" )." );
int
35 tend_normMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
float amount, target;
double weight[3];
hestOptAdd( &hopt, "w", "w0 w1 w2", airTypeDouble, 3, 3, weight, NULL,
"relative weights to put on major, medium, and minor "
"eigenvalue when performing normalization ( internally "
"rescaled to have a 1.0 L1 norm ). These weightings determine "
"the tensors's \"size\"." );
hestOptAdd( &hopt, "a", "amount", airTypeFloat, 1, 1, &amount, "1.0",
"how much of the normalization to perform" );
hestOptAdd( &hopt, "t", "target", airTypeFloat, 1, 1, &target, "1.0",
"target size, post normalization" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_normInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenSizeNormalize( nout, nin, weight, amount, target ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
83 TEND_CMD( norm, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Describe everything about one sample in a DT volume"
static const char *_tend_pointInfoL =
( INFO
". " );
int
33 tend_pointMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int loc[3], idx, sx, sy, sz, i;
Nrrd *nin;
float *tdata, eval[3], evec[9], angle, axis[3], mat[9];
hestOptAdd( &hopt, "p", "x y z", airTypeInt, 3, 3, loc, NULL,
"coordinates of sample to be described" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_pointInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get a valid DT volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
if ( !( AIR_IN_CL( 0, loc[0], sx-1 ) &&
AIR_IN_CL( 0, loc[1], sy-1 ) &&
AIR_IN_CL( 0, loc[2], sz-1 ) ) ) {
fprintf( stderr, "%s: location ( %d, %d, %d ) not inside volume "
"[0..%d]x[0..%d]x[0..%d]\n",
me, loc[0], loc[1], loc[2],
sx-1, sy-1, sz-1 );
airMopError( mop ); return 1;
}
idx = loc[0] + sx*( loc[1] + sy*loc[2] );
tdata = ( float* )( nin->data ) + 7*idx;
fprintf( stderr, "location = ( %d, %d, %d ) = %d\n", loc[0], loc[1], loc[2], idx );
fprintf( stderr, "confidence = %g\n", tdata[0] );
fprintf( stderr, "tensor =\n" );
fprintf( stderr, "{%.7f, %.7f, %.7f, %.7f, %.7f, %.7f} = \n",
tdata[1], tdata[2], tdata[3], tdata[4], tdata[5], tdata[6] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[1], tdata[2], tdata[3] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[2], tdata[4], tdata[5] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n", tdata[3], tdata[5], tdata[6] );
tenEigensolve_f( eval, evec, tdata );
fprintf( stderr, "eigensystem = ( <eigenvalue> : <eigenvector> ):\n" );
fprintf( stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n",
eval[0], evec[0], evec[1], evec[2] );
fprintf( stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n",
eval[1], evec[3], evec[4], evec[5] );
fprintf( stderr, "% 15.7f : % 15.7f % 15.7f % 15.7f\n",
eval[2], evec[6], evec[7], evec[8] );
angle = ell_3m_to_aa_f( axis, evec );
fprintf( stderr, "eigenvector rotation: %g around {%g, %g, %g}\n",
angle, axis[0], axis[1], axis[2] );
ell_aa_to_3m_f( mat, angle, axis );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
mat[0], mat[1], mat[2] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
mat[3], mat[4], mat[5] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
mat[6], mat[7], mat[8] );
fprintf( stderr, "anisotropies = \n" );
for ( i=1; i<=TEN_ANISO_MAX; i++ ) {
fprintf( stderr, "%s: % 15.7f\n",
airEnumStr( tenAniso, i ), tenAnisoEval_f( eval, i ) );
}
airMopOkay( mop );
return 0;
}
110 TEND_CMD( point, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Generate a pretty synthetic DT volume"
static const char *_tend_satinInfoL =
( INFO
". The surface of a sphere or torus is covered with either linear or "
"planar anisotropic tensors, or somewhere in between." );
void
34 tend_satinSphereEigen( float *eval, float *evec, float x, float y, float z,
float parm, float mina, float maxa,
float thick, float bnd, float evsc ) {
float aniso, bound1, bound2, r, norm, tmp[3];
r = AIR_CAST( float, sqrt( x*x + y*y + z*z ) );
/* 1 on inside, 0 on outside */
bound1 = AIR_CAST( float, 0.5 - 0.5*airErf( ( r-0.9 )/( bnd + 0.0001 ) ) );
/* other way around */
bound2 = AIR_CAST( float, 0.5 - 0.5*airErf( ( 0.9-thick-r )/( bnd + 0.0001 ) ) );
aniso = AIR_CAST( float, AIR_AFFINE( 0.0, AIR_MIN( bound1, bound2 ), 1.0,
mina, maxa ) );
ELL_3V_SET_TT( eval, float,
AIR_LERP( aniso, 1.0/3.0, AIR_AFFINE( 0.0, parm, 2.0, 1.0, 0.0 ) ),
AIR_LERP( aniso, 1.0/3.0, AIR_AFFINE( 0.0, parm, 2.0, 0.0, 1.0 ) ),
AIR_LERP( aniso, 1.0/3.0, 0 ) );
ELL_3V_SCALE( eval, evsc, eval );
/* v0: looking down positive Z, points counter clockwise */
if ( x || y ) {
ELL_3V_SET( evec + 3*0, y, -x, 0 );
ELL_3V_NORM_TT( evec + 3*0, float, evec + 3*0, norm );
/* v1: points towards pole at positive Z */
ELL_3V_SET( tmp, -x, -y, -z );
ELL_3V_NORM_TT( tmp, float, tmp, norm );
ELL_3V_CROSS( evec + 3*1, tmp, evec + 3*0 );
/* v2: v0 x v1 */
ELL_3V_CROSS( evec + 3*2, evec + 3*0, evec + 3*1 );
} else {
/* not optimal, but at least it won't show up in glyph visualizations */
ELL_3M_IDENTITY_SET( evec );
}
return;
}
void
73 tend_satinTorusEigen( float *eval, float *evec, float x, float y, float z,
float parm, float mina, float maxa,
float thick, float bnd, float evsc ) {
float bound, R, r, norm, out[3], up[3], aniso;
thick *= 2;
R = AIR_CAST( float, sqrt( x*x + y*y ) );
r = AIR_CAST( float, sqrt( ( R-1 )*( R-1 ) + z*z ) );
/* 1 on inside, 0 on outside */
bound = AIR_CAST( float, 0.5 - 0.5*airErf( ( r-thick )/( bnd + 0.0001 ) ) );
aniso = AIR_CAST( float, AIR_AFFINE( 0, bound, 1, mina, maxa ) );
ELL_3V_SET_TT( eval, float,
AIR_LERP( aniso, 1.0/3.0, AIR_AFFINE( 0.0, parm, 2.0, 1.0, 0.0 ) ),
AIR_LERP( aniso, 1.0/3.0, AIR_AFFINE( 0.0, parm, 2.0, 0.0, 1.0 ) ),
AIR_LERP( aniso, 1.0/3.0, 0 ) );
ELL_3V_SCALE( eval, evsc, eval );
ELL_3V_SET( up, 0, 0, 1 );
if ( x || y ) {
/* v0: looking down positive Z, points counter clockwise */
ELL_3V_SET( evec + 3*0, y, -x, 0 );
ELL_3V_NORM_TT( evec + 3*0, float, evec + 3*0, norm );
/* v2: points into core of torus */
/* out: points away from ( x, y )=( 0, 0 ) */
ELL_3V_SET( out, x, y, 0 );
ELL_3V_NORM_TT( out, float, out, norm );
ELL_3V_SCALE_ADD2( evec + 3*2, -z, up, ( 1-R ), out );
ELL_3V_NORM_TT( evec + 3*2, float, evec + 3*2, norm );
/* v1: looking at right half of cross-section, points counter clockwise */
ELL_3V_CROSS( evec + 3*1, evec + 3*0, evec + 3*2 );
} else {
/* not optimal, but at least it won't show up in glyph visualizations */
ELL_3M_IDENTITY_SET( evec );
}
return;
}
int
115 tend_satinGen( Nrrd *nout, float parm, float mina, float maxa, int wsize,
float thick, float scaling,
float bnd, float bndRm, float evsc, int torus ) {
static const char me[]="tend_satinGen";
char buff[AIR_STRLEN_SMALL];
Nrrd *nconf, *neval, *nevec;
float *conf, *eval, *evec;
size_t xi, yi, zi, size[3];
float x, y, z, min[3], max[3];
if ( torus ) {
ELL_3V_SET( size, 2*wsize, 2*wsize, wsize );
ELL_3V_SET( min, -2, -2, -1 );
ELL_3V_SET( max, 2, 2, 1 );
} else {
ELL_3V_SET( size, wsize, wsize, wsize );
ELL_3V_SET( min, -1, -1, -1 );
ELL_3V_SET( max, 1, 1, 1 );
}
if ( nrrdMaybeAlloc_va( nconf=nrrdNew( ), nrrdTypeFloat, 3,
size[0], size[1], size[2] ) ||
nrrdMaybeAlloc_va( neval=nrrdNew( ), nrrdTypeFloat, 4,
AIR_CAST( size_t, 3 ), size[0], size[1], size[2] ) ||
nrrdMaybeAlloc_va( nevec=nrrdNew( ), nrrdTypeFloat, 4,
AIR_CAST( size_t, 9 ), size[0], size[1], size[2] ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating temp nrrds", me );
return 1;
}
conf = ( float * )nconf->data;
eval = ( float * )neval->data;
evec = ( float * )nevec->data;
for ( zi=0; zi<size[2]; zi++ ) {
z = AIR_CAST( float, AIR_AFFINE( 0, zi, size[2]-1, min[2], max[2] ) );
z /= scaling;
for ( yi=0; yi<size[1]; yi++ ) {
y = AIR_CAST( float, AIR_AFFINE( 0, yi, size[1]-1, min[1], max[1] ) );
y /= scaling;
for ( xi=0; xi<size[0]; xi++ ) {
x = AIR_CAST( float, AIR_AFFINE( 0, xi, size[0]-1, min[0], max[0] ) );
x /= scaling;
*conf = 1.0;
if ( torus ) {
float aff;
aff = AIR_CAST( float, AIR_AFFINE( 0, yi, size[1]-1, 0, 1 ) );
tend_satinTorusEigen( eval, evec, x, y, z, parm,
mina, maxa, thick, bnd + bndRm*aff, evsc );
} else {
float aff;
aff = AIR_CAST( float, AIR_AFFINE( 0, yi, size[1]-1, 0, 1 ) );
tend_satinSphereEigen( eval, evec, x, y, z, parm,
mina, maxa, thick, bnd + bndRm*aff, evsc );
}
conf += 1;
eval += 3;
evec += 9;
}
}
}
if ( tenMake( nout, nconf, neval, nevec ) ) {
biffAddf( TEN, "%s: trouble generating output", me );
return 1;
}
nrrdNuke( nconf );
nrrdNuke( neval );
nrrdNuke( nevec );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoLabel, "tensor", "x", "y", "z" );
sprintf( buff, "satin( %g, %g, %g )", parm, mina, maxa );
nout->content = airStrdup( buff );
return 0;
}
int
190 tend_satinMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int wsize, torus;
float parm, maxa, mina, thick, scaling, bnd, bndRm, evsc;
Nrrd *nout;
char *outS;
gageShape *shape;
double spd[4][4], orig[4];
hestOptAdd( &hopt, "t", "do torus", airTypeInt, 0, 0, &torus, NULL,
"generate a torus dataset, instead of the default spherical" );
hestOptAdd( &hopt, "p", "aniso parm", airTypeFloat, 1, 1, &parm, NULL,
"anisotropy parameter. 0.0 for one direction of linear ( along "
"the equator for spheres, or along the larger circumference for "
"toruses ), 1.0 for planar, 2.0 for the other direction of linear "
"( from pole to pole for spheres, or along the smaller "
"circumference for toruses )" );
hestOptAdd( &hopt, "max", "max ca1", airTypeFloat, 1, 1, &maxa, "1.0",
"maximum anisotropy in dataset, according to the \"ca1\" "
"anisotropy metric. \"1.0\" means "
"completely linear or completely planar anisotropy" );
hestOptAdd( &hopt, "min", "min ca1", airTypeFloat, 1, 1, &mina, "0.0",
"minimum anisotropy in dataset" );
hestOptAdd( &hopt, "b", "boundary", airTypeFloat, 1, 1, &bnd, "0.05",
"parameter governing how fuzzy the boundary between high and "
"low anisotropy is. Use \"-b 0\" for no fuzziness" );
hestOptAdd( &hopt, "br", "ramp", airTypeFloat, 1, 1, &bndRm, "0.0",
"how much to ramp upeffective \"b\" along Y axis. "
"Use \"-b 0\" for no such ramping." );
hestOptAdd( &hopt, "th", "thickness", airTypeFloat, 1, 1, &thick, "0.3",
"parameter governing how thick region of high anisotropy is" );
hestOptAdd( &hopt, "scl", "scaling", airTypeFloat, 1, 1, &scaling, "1.0",
"scaling on size of sphere or torus within volume; lowering "
"this below default 1.0 produces more background margin" );
hestOptAdd( &hopt, "evsc", "eval scale", airTypeFloat, 1, 1, &evsc, "1.0",
"scaling of eigenvalues" );
hestOptAdd( &hopt, "s", "size", airTypeInt, 1, 1, &wsize, "32",
"dimensions of output volume. For size N, the output is "
"N\tx\tN\tx\tN for spheres, and 2N\tx\t2N\tx\tN for toruses" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_satinInfoL );
JUSTPARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tend_satinGen( nout, parm, mina, maxa, wsize, thick, scaling,
bnd, bndRm, evsc, torus ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
/* use gageShape to determine orientation info */
nrrdAxisInfoSet_va( nout, nrrdAxisInfoCenter,
nrrdCenterUnknown, nrrdCenterCell,
nrrdCenterCell, nrrdCenterCell );
shape = gageShapeNew( );
airMopAdd( mop, shape, ( airMopper )gageShapeNix, airMopAlways );
/* this is a weird mix of new and legacy code. At some point
prior to Wed May 27 19:23:55 CDT 2009, it was okay to pass
in a volume to gageShapeSet that had absolutely no notion
of spacing or orientation. Then gageShapeSet was used to
get a plausible set of space directions and space origin.
Now, we're setting some spacings, so that gageShapeSet can
do its thing, then ( below ) nan-ing out those spacings so
that the nrrd is self-consistent */
nout->axis[1].spacing = 1.0;
nout->axis[2].spacing = 1.0;
nout->axis[3].spacing = 1.0;
if ( gageShapeSet( shape, nout, tenGageKind->baseDim ) ) {
airMopAdd( mop, err=biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble doing shape:\n%s\n", me, err );
airMopError( mop ); return 1;
}
/* the ItoW is a 4x4 matrix, but
we really only care about the first three rows */
ELL_4V_SET( spd[0], AIR_NAN, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_4MV_COL0_GET( spd[1], shape->ItoW ); ELL_4V_SCALE( spd[1], 32, spd[1] );
ELL_4MV_COL1_GET( spd[2], shape->ItoW ); ELL_4V_SCALE( spd[2], 32, spd[2] );
ELL_4MV_COL2_GET( spd[3], shape->ItoW ); ELL_4V_SCALE( spd[3], 32, spd[3] );
ELL_4MV_COL3_GET( orig, shape->ItoW ); ELL_4V_SCALE( orig, 32, orig );
nrrdSpaceSet( nout, nrrdSpaceRightAnteriorSuperior );
nrrdSpaceOriginSet( nout, orig );
nrrdAxisInfoSet_va( nout, nrrdAxisInfoSpaceDirection,
spd[0], spd[1], spd[2], spd[3] );
nout->axis[1].spacing = AIR_NAN;
nout->axis[2].spacing = AIR_NAN;
nout->axis[3].spacing = AIR_NAN;
nrrdAxisInfoSet_va( nout, nrrdAxisInfoKind,
nrrdKind3DMaskedSymMatrix, nrrdKindSpace,
nrrdKindSpace, nrrdKindSpace );
nout->measurementFrame[0][0] = 1;
nout->measurementFrame[1][0] = 0;
nout->measurementFrame[2][0] = 0;
nout->measurementFrame[0][1] = 0;
nout->measurementFrame[1][1] = 1;
nout->measurementFrame[2][1] = 0;
nout->measurementFrame[0][2] = 0;
nout->measurementFrame[1][2] = 0;
nout->measurementFrame[2][2] = 1;
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
/* TEND_CMD( satin, INFO ); */
unrrduCmd tend_satinCmd = { "satin", INFO, tend_satinMain, AIR_FALSE };
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Converts a 9-value DT volume to a 7-value DT volume"
static const char *_tend_shrinkInfoL =
( INFO
". The confidence value is set to 1.0 everwhere. You can \"unu splice\" "
"or nrrdSplice( ) something else in its place later." );
int
34 tend_shrinkMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume, with 9 matrix components "
"per sample",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output tensor volume, with the 7 values per sample" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_shrinkInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenShrink( nout, NULL, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble shrinking tensors:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
74 TEND_CMD( shrink, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Simulate DW images from a tensor field"
static const char *_tend_simInfoL =
( INFO
". The output will be in the same form as the input to \"tend estim\". "
"The B-matrices ( \"-B\" ) can be the output from \"tend bmat\", or the "
"gradients can be given directly ( \"-g\" ); one of these is required. "
"Note that the input tensor field ( \"-i\" ) is the basis of the output "
"per-axis fields and image orientation. NOTE: this includes the "
"measurement frame used in the input tensor field, which implies that "
"the given gradients or B-matrices are already expressed in that "
"measurement frame. " );
int
40 tend_simMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
tenEstimateContext *tec;
airArray *mop;
int E, oldstuff, seed, keyValueSet, outType, preOutType;
Nrrd *nin, *nT2, *nbmat, *ngrad, *nout, *ntmp;
char *outS;
float b, sigma;
/* maybe this can go in tend.c, but for some reason its explicitly
set to AIR_FALSE there */
hparm->elideSingleOtherDefault = AIR_TRUE;
hestOptAdd( &hopt, "old", NULL, airTypeInt, 0, 0, &oldstuff, NULL,
"don't use the new tenEstimateContext functionality" );
hestOptAdd( &hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "0.0",
"Rician noise parameter" );
hestOptAdd( &hopt, "seed", "seed", airTypeInt, 1, 1, &seed, "42",
"seed value for RNG which creates noise" );
hestOptAdd( &hopt, "g", "grad list", airTypeOther, 1, 1, &ngrad, "",
"gradient list, one row per diffusion-weighted image",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "B", "B matrix", airTypeOther, 1, 1, &nbmat, "",
"B matrix, one row per diffusion-weighted image. Using this "
"overrides the gradient list input via \"-g\"",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "r", "reference field", airTypeOther, 1, 1, &nT2, NULL,
"reference anatomical scan, with no diffusion weighting",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "i", "tensor field", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor field", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b", "b", airTypeFloat, 1, 1, &b, "1000",
"b value for simulated scan" );
hestOptAdd( &hopt, "kvp", NULL, airTypeInt, 0, 0, &keyValueSet, NULL,
"generate key/value pairs in the NRRD header corresponding "
"to the input b-value and gradients or B-matrices. " );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &outType, "float",
"output type of DWIs",
NULL, nrrdType );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output image ( floating point )" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_simInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( !( nbmat || ngrad ) ) {
fprintf( stderr, "%s: got neither B-matrix ( \"-B\" ) "
"or gradient list ( \"-g\" )\n", me );
airMopError( mop ); return 1;
}
if ( !oldstuff ) {
airSrandMT( seed );
tec = tenEstimateContextNew( );
airMopAdd( mop, tec, ( airMopper )tenEstimateContextNix, airMopAlways );
preOutType = ( nrrdTypeFloat == outType
? nrrdTypeFloat
: nrrdTypeDouble );
E = 0;
if ( !E ) E |= tenEstimateMethodSet( tec, tenEstimate1MethodLLS );
if ( !E ) E |= tenEstimateValueMinSet( tec, 0.0001 );
if ( nbmat ) {
if ( !E ) E |= tenEstimateBMatricesSet( tec, nbmat, b, AIR_TRUE );
} else {
if ( !E ) E |= tenEstimateGradientsSet( tec, ngrad, b, AIR_TRUE );
}
if ( !E ) E |= tenEstimateThresholdSet( tec, 0, 0 );
if ( !E ) E |= tenEstimateUpdate( tec );
if ( !E ) E |= tenEstimate1TensorSimulateVolume( tec,
nout, sigma, b,
nT2, nin,
preOutType,
keyValueSet );
if ( E ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making DWI volume ( new ):\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( preOutType != outType ) {
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
E = 0;
if ( !E ) E |= nrrdCopy( ntmp, nout );
if ( !E ) E |= nrrdConvert( nout, ntmp, outType );
if ( E ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making output volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
} else {
if ( !nbmat ) {
fprintf( stderr, "%s: need B-matrices for old code\n", me );
airMopError( mop ); return 1;
}
if ( tenSimulate( nout, nT2, nin, nbmat, b ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making DWI volume:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
159 TEND_CMD( sim, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Slice 3D tensors to get slab/image of 3D/2D tensors"
static const char *_tend_sliceInfoL =
( INFO
". " );
int
33 tend_sliceMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
char *outS;
int axis, pos, dim;
Nrrd *nin, *nout;
hestOptAdd( &hopt, "a", "axis", airTypeInt, 1, 1, &axis, NULL,
"axis along which to slice" );
hestOptAdd( &hopt, "p", "pos", airTypeInt, 1, 1, &pos, NULL,
"position to slice at" );
hestOptAdd( &hopt, "d", "dim", airTypeInt, 1, 1, &dim, "3",
"dimension of desired tensor output, can be either 2 or 3" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output tensor slice" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_sliceInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( tenSlice( nout, nin, axis, pos, dim ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
76 TEND_CMD( slice, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Calculate structure tensors from a scalar field"
static const char *_tend_stenInfoL =
( INFO
". Not a diffusion tensor, but it is symmetric and positive-definate." );
int
33 tend_stenMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int iScale, dScale, dsmp;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "ds", "diff. scale", airTypeInt, 1, 1, &dScale, "1",
"differentiation scale, in pixels: the radius of the "
"kernel used for differentation to compute gradient vectors" );
hestOptAdd( &hopt, "is", "int. scale", airTypeInt, 1, 1, &iScale, "2",
"integration scale, in pixels: the radius of the "
"kernel used for blurring outer products of gradients "
"in order compute structure tensors" );
hestOptAdd( &hopt, "df", "downsample factor", airTypeInt, 1, 1, &dsmp, "1",
"the factor by which to downsample when creating volume of "
"structure tensors" );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input scalar volume",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output filename" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_stenInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( gageStructureTensor( nout, nin, dScale, iScale, dsmp ) ) {
airMopAdd( mop, err=biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble calculating structure tensors:\n%s\n",
me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
85 TEND_CMD( sten, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Convert between different shape triples"
static const char *_tend_tconvInfoL =
( INFO
". The triples can be eignvalues, invariants ( J, K, R ), "
"and lots of other things." );
int
34 tend_tconvMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int ttype[2];
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "t", "inType outType", airTypeEnum, 2, 2, ttype, NULL,
"given input and desired output type of triples",
NULL, tenTripleType );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input array of triples", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output array" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_tconvInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenTripleConvert( nout, ttype[1], nin, ttype[0] ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
75 TEND_CMD( tconv, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Compute volume of shape triples"
static const char *_tend_tripleInfoL =
( INFO
". The triple can be eignvalues, invariants ( J, K, R ), "
"and lots of other things." );
int
34 tend_tripleMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
int ttype;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "t", "type", airTypeEnum, 1, 1, &ttype, NULL,
"desired output triple type", NULL, tenTripleType );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, "-",
"input tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output triple volume" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_tripleInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenTripleCalc( nout, ttype, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
74 TEND_CMD( triple, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
#define INFO "Applies and removes the measurement frame"
static const char *_tend_unmfInfoL =
( INFO
". When the given tensor volume has a measurement frame associated "
"with it, this will apply the measurement frame transform to all "
"tensors to convert them into world space, and remove the measurement "
"frame from the nrrd." );
int
36 tend_unmfMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
int pret;
hestOpt *hopt = NULL;
char *perr, *err;
airArray *mop;
Nrrd *nin, *nout;
char *outS;
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input diffusion tensor volume "
"( sorry, can't use usual default of \"-\" for stdin "
"because of hest quirk )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output tensor volume" );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _tend_unmfInfoL );
PARSE( );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( tenMeasurementFrameReduce( nout, nin ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
77 TEND_CMD( unmf, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
int tenVerbose = 0;
void
31 tenRotateSingle_f( float tenOut[7], const float rot[9], const float tenIn[7] ) {
float rotT[9], matIn[9], tmp[9], matOut[9];
ELL_3M_TRANSPOSE( rotT, rot );
TEN_T2M( matIn, tenIn );
ELL_3M_MUL( tmp, matIn, rotT );
ELL_3M_MUL( matOut, rot, tmp );
TEN_M2T_TT( tenOut, float, matOut );
tenOut[0] = tenIn[0];
return;
}
/*
******** tenTensorCheck( )
**
** describes if the given nrrd could be a diffusion tensor dataset,
** either the measured DWI data or the calculated tensor data.
**
** We've been using 7 floats for BOTH kinds of tensor data- both the
** measured DWI and the calculated tensor matrices. The measured data
** comes as one anatomical image and 6 DWIs. For the calculated tensors,
** in addition to the 6 matrix components, we keep a "threshold" value
** which is based on the sum of all the DWIs, which describes if the
** calculated tensor means anything or not.
**
** useBiff controls if biff is used to describe the problem
*/
int
59 tenTensorCheck( const Nrrd *nin, int wantType, int want4D, int useBiff ) {
static const char me[]="tenTensorCheck";
if ( !nin ) {
if ( useBiff ) biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( wantType ) {
if ( nin->type != wantType ) {
if ( useBiff ) biffAddf( TEN, "%s: wanted type %s, got type %s", me,
airEnumStr( nrrdType, wantType ),
airEnumStr( nrrdType, nin->type ) );
return 1;
}
}
else {
if ( !( nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort ) ) {
if ( useBiff ) biffAddf( TEN, "%s: need data of type float or short", me );
return 1;
}
}
if ( want4D && !( 4 == nin->dim ) ) {
if ( useBiff )
biffAddf( TEN, "%s: given dimension is %d, not 4", me, nin->dim );
return 1;
}
if ( !( 7 == nin->axis[0].size ) ) {
if ( useBiff ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: axis 0 has size %s, not 7", me,
airSprintSize_t( stmp, nin->axis[0].size ) );
}
return 1;
}
return 0;
}
int
97 tenMeasurementFrameReduce( Nrrd *nout, const Nrrd *nin ) {
static const char me[]="tenMeasurementFrameReduce";
double MF[9], MFT[9], tenMeasr[9], tenWorld[9];
float *tdata;
size_t ii, nn;
unsigned int si, sj;
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
if ( 3 != nin->spaceDim ) {
biffAddf( TEN, "%s: input nrrd needs 3-D ( not %u-D ) space dimension",
me, nin->spaceDim );
return 1;
}
/*
[0] [1] [2] [0][0] [1][0] [2][0]
[3] [4] [5] = [0][1] [1][1] [2][1]
[6] [7] [8] [0][2] [1][2] [2][2]
*/
MF[0] = nin->measurementFrame[0][0];
MF[1] = nin->measurementFrame[1][0];
MF[2] = nin->measurementFrame[2][0];
MF[3] = nin->measurementFrame[0][1];
MF[4] = nin->measurementFrame[1][1];
MF[5] = nin->measurementFrame[2][1];
MF[6] = nin->measurementFrame[0][2];
MF[7] = nin->measurementFrame[1][2];
MF[8] = nin->measurementFrame[2][2];
if ( !ELL_3M_EXISTS( MF ) ) {
biffAddf( TEN, "%s: 3x3 measurement frame doesn't exist", me );
return 1;
}
ELL_3M_TRANSPOSE( MFT, MF );
if ( nout != nin ) {
if ( nrrdCopy( nout, nin ) ) {
biffAddf( TEN, "%s: trouble with initial copy", me );
return 1;
}
}
nn = nrrdElementNumber( nout )/nout->axis[0].size;
tdata = ( float* )( nout->data );
for ( ii=0; ii<nn; ii++ ) {
TEN_T2M( tenMeasr, tdata );
ell_3m_mul_d( tenWorld, MF, tenMeasr );
ell_3m_mul_d( tenWorld, tenWorld, MFT );
TEN_M2T_TT( tdata, float, tenWorld );
tdata += 7;
}
for ( si=0; si<NRRD_SPACE_DIM_MAX; si++ ) {
for ( sj=0; sj<NRRD_SPACE_DIM_MAX; sj++ ) {
nout->measurementFrame[si][sj] = AIR_NAN;
}
}
for ( si=0; si<3; si++ ) {
for ( sj=0; sj<3; sj++ ) {
nout->measurementFrame[si][sj] = ( si == sj );
}
}
return 0;
}
int
167 tenExpand2D( Nrrd *nout, const Nrrd *nin, double scale, double thresh ) {
static const char me[]="tenExpand2D";
size_t N, I, sx, sy;
float *masked, *redund;
if ( !( nout && nin && AIR_EXISTS( thresh ) ) ) {
biffAddf( TEN, "%s: got NULL pointer or non-existent threshold", me );
return 1;
}
if ( nout == nin ) {
biffAddf( TEN, "%s: sorry, need different nrrds for input and output", me );
return 1;
}
/* HEY copy and paste and tweak of tenTensorCheck */
if ( !nin ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( nin->type != nrrdTypeFloat ) {
biffAddf( TEN, "%s: wanted type %s, got type %s", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nin->type ) );
return 1;
} else {
if ( !( nin->type == nrrdTypeFloat || nin->type == nrrdTypeShort ) ) {
biffAddf( TEN, "%s: need data of type float or short", me );
return 1;
}
}
if ( 3 != nin->dim ) {
biffAddf( TEN, "%s: given dimension is %u, not 3", me, nin->dim );
return 1;
}
if ( !( 4 == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: axis 0 has size %s, not 4", me,
airSprintSize_t( stmp, nin->axis[0].size ) );
return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
N = sx*sy;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 3,
AIR_CAST( size_t, 4 ), sx, sy ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
for ( I=0; I<=N-1; I++ ) {
masked = ( float* )( nin->data ) + I*4;
redund = ( float* )( nout->data ) + I*4;
if ( masked[0] < thresh ) {
ELL_4V_ZERO_SET( redund );
continue;
}
redund[0] = masked[1];
redund[1] = masked[2];
redund[2] = masked[2];
redund[3] = masked[3];
ELL_4V_SCALE( redund, AIR_CAST( float, scale ), redund );
}
if ( nrrdAxisInfoCopy( nout, nin, NULL,
NRRD_AXIS_INFO_SIZE_BIT ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
/* by call above we just copied axis-0 kind, which might be wrong;
we actually know the output kind now, so we might as well set it */
nout->axis[0].kind = nrrdKind2DMatrix;
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
return 0;
}
int
245 tenExpand( Nrrd *nout, const Nrrd *nin, double scale, double thresh ) {
static const char me[]="tenExpand";
size_t N, I, sx, sy, sz;
float *seven, *nine;
if ( !( nout && nin && AIR_EXISTS( thresh ) ) ) {
biffAddf( TEN, "%s: got NULL pointer or non-existent threshold", me );
return 1;
}
if ( nout == nin ) {
biffAddf( TEN, "%s: sorry, need different nrrds for input and output", me );
return 1;
}
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: ", me );
return 1;
}
sx = nin->axis[1].size;
sy = nin->axis[2].size;
sz = nin->axis[3].size;
N = sx*sy*sz;
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 9 ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
for ( I=0; I<=N-1; I++ ) {
seven = ( float* )( nin->data ) + I*7;
nine = ( float* )( nout->data ) + I*9;
if ( seven[0] < thresh ) {
ELL_3M_ZERO_SET( nine );
continue;
}
TEN_T2M( nine, seven );
ELL_3M_SCALE( nine, AIR_CAST( float, scale ), nine );
}
if ( nrrdAxisInfoCopy( nout, nin, NULL,
NRRD_AXIS_INFO_SIZE_BIT ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
/* by call above we just copied axis-0 kind, which might be wrong;
we actually know the output kind now, so we might as well set it */
nout->axis[0].kind = nrrdKind3DMatrix;
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
/* Tue Sep 13 18:36:45 EDT 2005: why did I do this?
nout->axis[0].label = ( char * )airFree( nout->axis[0].label );
nout->axis[0].label = airStrdup( "matrix" );
*/
return 0;
}
int
304 tenShrink( Nrrd *tseven, const Nrrd *nconf, const Nrrd *tnine ) {
static const char me[]="tenShrink";
size_t I, N, sx, sy, sz;
float *seven, *conf, *nine;
if ( !( tseven && tnine ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tseven == tnine ) {
biffAddf( TEN, "%s: sorry, need different nrrds for input and output", me );
return 1;
}
if ( !( nrrdTypeFloat == tnine->type &&
4 == tnine->dim &&
9 == tnine->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: type not %s ( was %s ) or dim not 4 ( was %d ) "
"or first axis size not 9 ( was %s )", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, tnine->type ), tnine->dim,
airSprintSize_t( stmp, tnine->axis[0].size ) );
return 1;
}
sx = tnine->axis[1].size;
sy = tnine->axis[2].size;
sz = tnine->axis[3].size;
if ( nconf ) {
if ( !( nrrdTypeFloat == nconf->type &&
3 == nconf->dim &&
sx == nconf->axis[0].size &&
sy == nconf->axis[1].size &&
sz == nconf->axis[2].size ) ) {
biffAddf( TEN, "%s: confidence type not %s ( was %s ) or dim not 3 ( was %d ) "
"or dimensions didn't match tensor volume", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nconf->type ),
nconf->dim );
return 1;
}
}
if ( nrrdMaybeAlloc_va( tseven, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: trouble allocating output", me );
return 1;
}
seven = ( float * )tseven->data;
conf = nconf ? ( float * )nconf->data : NULL;
nine = ( float * )tnine->data;
N = sx*sy*sz;
for ( I=0; I<N; I++ ) {
TEN_M2T_TT( seven, float, nine );
seven[0] = conf ? conf[I] : 1.0f;
seven += 7;
nine += 9;
}
if ( nrrdAxisInfoCopy( tseven, tnine, NULL,
NRRD_AXIS_INFO_SIZE_BIT ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
/* by call above we just copied axis-0 kind, which might be wrong;
we actually know the output kind now, so we might as well set it */
tseven->axis[0].kind = nrrdKind3DMaskedSymMatrix;
if ( nrrdBasicInfoCopy( tseven, tnine,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
/* Wed Dec 3 11:22:32 EST 2008: no real need to set label string */
return 0;
}
/*
******** tenEigensolve_f
**
** uses ell_3m_eigensolve_d to get the eigensystem of a single tensor
** disregards the confidence value t[0]
**
** return is same as ell_3m_eigensolve_d, which is same as ell_cubic
**
** NOTE: Even in the post-Teem-1.7 switch from column-major to
** row-major- its still the case that the eigenvectors are at
** evec+0, evec+3, evec+6: this means that they USED to be the
** "columns" of the matrix, and NOW they're the rows.
**
** This does NOT use biff
*/
int
394 tenEigensolve_f( float _eval[3], float _evec[9], const float t[7] ) {
double m[9], eval[3], evec[9], trc, iso[9];
int ret;
TEN_T2M( m, t );
trc = ELL_3M_TRACE( m )/3.0;
ELL_3M_IDENTITY_SET( iso );
ELL_3M_SCALE_SET( iso, -trc, -trc, -trc );
ELL_3M_ADD2( m, m, iso );
if ( _evec ) {
ret = ell_3m_eigensolve_d( eval, evec, m, AIR_TRUE );
if ( tenVerbose > 4 ) {
fprintf( stderr, "---- cubic ret = %d\n", ret );
fprintf( stderr, "tensor = {\n" );
fprintf( stderr, " % 15.7f, \n", t[1] );
fprintf( stderr, " % 15.7f, \n", t[2] );
fprintf( stderr, " % 15.7f, \n", t[3] );
fprintf( stderr, " % 15.7f, \n", t[4] );
fprintf( stderr, " % 15.7f, \n", t[5] );
fprintf( stderr, " % 15.7f}\n", t[6] );
fprintf( stderr, "roots = %d:\n", ret );
fprintf( stderr, " % 31.15f\n", trc + eval[0] );
fprintf( stderr, " % 31.15f\n", trc + eval[1] );
fprintf( stderr, " % 31.15f\n", trc + eval[2] );
}
ELL_3V_SET_TT( _eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc );
ELL_3M_COPY_TT( _evec, float, evec );
if ( ell_cubic_root_single_double == ret ) {
/* this was added to fix a stupid problem with very nearly
isotropic glyphs, used for demonstration figures */
if ( eval[0] == eval[1] ) {
ELL_3V_CROSS( _evec+6, _evec+0, _evec+3 );
} else {
ELL_3V_CROSS( _evec+0, _evec+3, _evec+6 );
}
}
if ( ( tenVerbose > 1 ) && _eval[2] < 0 ) {
fprintf( stderr, "tenEigensolve_f -------------\n" );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
t[1], t[2], t[3] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
t[2], t[4], t[5] );
fprintf( stderr, "% 15.7f % 15.7f % 15.7f\n",
t[3], t[5], t[6] );
fprintf( stderr, " --> % 15.7f % 15.7f % 15.7f\n",
_eval[0], _eval[1], _eval[2] );
}
} else {
/* caller only wants eigenvalues */
ret = ell_3m_eigenvalues_d( eval, m, AIR_TRUE );
ELL_3V_SET_TT( _eval, float, eval[0] + trc, eval[1] + trc, eval[2] + trc );
}
return ret;
}
/* HEY: cut and paste !! */
int
451 tenEigensolve_d( double _eval[3], double evec[9], const double t[7] ) {
double m[9], eval[3], trc, iso[9];
int ret;
TEN_T2M( m, t );
trc = ELL_3M_TRACE( m )/3.0;
ELL_3M_SCALE_SET( iso, -trc, -trc, -trc );
ELL_3M_ADD2( m, m, iso );
/*
printf( "!%s: t = %g %g %g; %g %g; %g\n", "tenEigensolve_f",
t[1], t[2], t[3], t[4], t[5], t[6] );
printf( "!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "tenEigensolve_f",
m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8] );
*/
if ( evec ) {
ret = ell_3m_eigensolve_d( eval, evec, m, AIR_TRUE );
ELL_3V_SET( _eval, eval[0] + trc, eval[1] + trc, eval[2] + trc );
if ( ell_cubic_root_single_double == ret ) {
/* this was added to fix a stupid problem with very nearly
isotropic glyphs, used for demonstration figures */
if ( eval[0] == eval[1] ) {
ELL_3V_CROSS( evec+6, evec+0, evec+3 );
} else {
ELL_3V_CROSS( evec+0, evec+3, evec+6 );
}
}
} else {
/* caller only wants eigenvalues */
ret = ell_3m_eigenvalues_d( eval, m, AIR_TRUE );
ELL_3V_SET( _eval, eval[0] + trc, eval[1] + trc, eval[2] + trc );
}
return ret;
}
/* lop A
fprintf( stderr, "################################### I = %d\n", ( int )I );
tenEigensolve( teval, tevec, out );
fprintf( stderr, "evals: ( %g %g %g ) %g %g %g --> %g %g %g\n",
AIR_ABS( eval[0] - teval[0] ) + 1,
AIR_ABS( eval[1] - teval[1] ) + 1,
AIR_ABS( eval[2] - teval[2] ) + 1,
eval[0], eval[1], eval[2],
teval[0], teval[1], teval[2] );
fprintf( stderr, " tevec lens: %g %g %g\n", ELL_3V_LEN( tevec+3*0 ),
ELL_3V_LEN( tevec+3*1 ), ELL_3V_LEN( tevec+3*2 ) );
ELL_3V_CROSS( tmp1, evec+3*0, evec+3*1 ); tmp2[0] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, evec+3*0, evec+3*2 ); tmp2[1] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, evec+3*1, evec+3*2 ); tmp2[2] = ELL_3V_LEN( tmp1 );
fprintf( stderr, " evec[0] = %g %g %g\n",
( evec+3*0 )[0], ( evec+3*0 )[1], ( evec+3*0 )[2] );
fprintf( stderr, " evec[1] = %g %g %g\n",
( evec+3*1 )[0], ( evec+3*1 )[1], ( evec+3*1 )[2] );
fprintf( stderr, " evec[2] = %g %g %g\n",
( evec+3*2 )[0], ( evec+3*2 )[1], ( evec+3*2 )[2] );
fprintf( stderr, " evec crosses: %g %g %g\n",
tmp2[0], tmp2[1], tmp2[2] );
ELL_3V_CROSS( tmp1, tevec+3*0, tevec+3*1 ); tmp2[0] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, tevec+3*0, tevec+3*2 ); tmp2[1] = ELL_3V_LEN( tmp1 );
ELL_3V_CROSS( tmp1, tevec+3*1, tevec+3*2 ); tmp2[2] = ELL_3V_LEN( tmp1 );
fprintf( stderr, " tevec[0] = %g %g %g\n",
( tevec+3*0 )[0], ( tevec+3*0 )[1], ( tevec+3*0 )[2] );
fprintf( stderr, " tevec[1] = %g %g %g\n",
( tevec+3*1 )[0], ( tevec+3*1 )[1], ( tevec+3*1 )[2] );
fprintf( stderr, " tevec[2] = %g %g %g\n",
( tevec+3*2 )[0], ( tevec+3*2 )[1], ( tevec+3*2 )[2] );
fprintf( stderr, " tevec crosses: %g %g %g\n",
tmp2[0], tmp2[1], tmp2[2] );
if ( tmp2[1] < 0.5 ) {
fprintf( stderr, "( panic )\n" );
exit( 0 );
}
*/
void
527 tenMakeSingle_f( float ten[7], float conf, const float eval[3], const float evec[9] ) {
double tmpMat1[9], tmpMat2[9], diag[9], evecT[9];
ELL_3M_ZERO_SET( diag );
ELL_3M_DIAG_SET( diag, eval[0], eval[1], eval[2] );
ELL_3M_TRANSPOSE( evecT, evec );
ELL_3M_MUL( tmpMat1, diag, evec );
ELL_3M_MUL( tmpMat2, evecT, tmpMat1 );
ten[0] = conf;
TEN_M2T_TT( ten, float, tmpMat2 );
return;
}
/* HEY: copy and paste! */
void
542 tenMakeSingle_d( double ten[7], double conf, const double eval[3], const double evec[9] ) {
double tmpMat1[9], tmpMat2[9], diag[9], evecT[9];
ELL_3M_ZERO_SET( diag );
ELL_3M_DIAG_SET( diag, eval[0], eval[1], eval[2] );
ELL_3M_TRANSPOSE( evecT, evec );
ELL_3M_MUL( tmpMat1, diag, evec );
ELL_3M_MUL( tmpMat2, evecT, tmpMat1 );
ten[0] = conf;
TEN_M2T_TT( ten, float, tmpMat2 );
return;
}
/*
******** tenMake
**
** create a tensor nrrd from nrrds of confidence, eigenvalues, and
** eigenvectors
*/
int
562 tenMake( Nrrd *nout, const Nrrd *nconf, const Nrrd *neval, const Nrrd *nevec ) {
static const char me[]="tenTensorMake";
size_t I, N, sx, sy, sz;
float *out, *conf, *eval, *evec;
int map[4];
/* float teval[3], tevec[9], tmp1[3], tmp2[3]; */
char stmp[7][AIR_STRLEN_SMALL];
if ( !( nout && nconf && neval && nevec ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( nrrdCheck( nconf ) || nrrdCheck( neval ) || nrrdCheck( nevec ) ) {
biffMovef( TEN, NRRD, "%s: didn't get three valid nrrds", me );
return 1;
}
if ( !( 3 == nconf->dim && nrrdTypeFloat == nconf->type ) ) {
biffAddf( TEN, "%s: first nrrd not a confidence volume "
"( dim = %d, not 3; type = %s, not %s )", me,
nconf->dim, airEnumStr( nrrdType, nconf->type ),
airEnumStr( nrrdType, nrrdTypeFloat ) );
return 1;
}
sx = nconf->axis[0].size;
sy = nconf->axis[1].size;
sz = nconf->axis[2].size;
if ( !( 4 == neval->dim && 4 == nevec->dim &&
nrrdTypeFloat == neval->type &&
nrrdTypeFloat == nevec->type ) ) {
biffAddf( TEN, "%s: second and third nrrd aren't both 4-D ( %d and %d ) "
"and type %s ( %s and %s )",
me, neval->dim, nevec->dim,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, neval->type ),
airEnumStr( nrrdType, nevec->type ) );
return 1;
}
if ( !( 3 == neval->axis[0].size &&
sx == neval->axis[1].size &&
sy == neval->axis[2].size &&
sz == neval->axis[3].size ) ) {
biffAddf( TEN, "%s: second nrrd sizes wrong: "
"( %s, %s, %s, %s ) not ( 3, %s, %s, %s )", me,
airSprintSize_t( stmp[0], neval->axis[0].size ),
airSprintSize_t( stmp[1], neval->axis[1].size ),
airSprintSize_t( stmp[2], neval->axis[2].size ),
airSprintSize_t( stmp[3], neval->axis[3].size ),
airSprintSize_t( stmp[4], sx ),
airSprintSize_t( stmp[5], sy ),
airSprintSize_t( stmp[6], sz ) );
return 1;
}
if ( !( 9 == nevec->axis[0].size &&
sx == nevec->axis[1].size &&
sy == nevec->axis[2].size &&
sz == nevec->axis[3].size ) ) {
biffAddf( TEN, "%s: third nrrd sizes wrong: "
"( %s, %s, %s, %s ) not ( 9, %s, %s, %s )", me,
airSprintSize_t( stmp[0], nevec->axis[0].size ),
airSprintSize_t( stmp[1], nevec->axis[1].size ),
airSprintSize_t( stmp[2], nevec->axis[2].size ),
airSprintSize_t( stmp[3], nevec->axis[3].size ),
airSprintSize_t( stmp[4], sx ),
airSprintSize_t( stmp[5], sy ),
airSprintSize_t( stmp[6], sz ) );
return 1;
}
/* finally */
if ( nrrdMaybeAlloc_va( nout, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ), sx, sy, sz ) ) {
biffMovef( TEN, NRRD, "%s: couldn't allocate output", me );
return 1;
}
N = sx*sy*sz;
conf = ( float * )( nconf->data );
eval = ( float * )neval->data;
evec = ( float * )nevec->data;
out = ( float * )nout->data;
for ( I=0; I<N; I++ ) {
tenMakeSingle_f( out, conf[I], eval, evec );
/* lop A */
out += 7;
eval += 3;
evec += 9;
}
ELL_4V_SET( map, -1, 0, 1, 2 );
if ( nrrdAxisInfoCopy( nout, nconf, map, NRRD_AXIS_INFO_SIZE_BIT ) ) {
biffMovef( TEN, NRRD, "%s: trouble", me );
return 1;
}
nout->axis[0].label = ( char * )airFree( nout->axis[0].label );
nout->axis[0].label = airStrdup( "tensor" );
nout->axis[0].kind = nrrdKind3DMaskedSymMatrix;
if ( nrrdBasicInfoCopy( nout, nconf,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s:", me );
return 1;
}
return 0;
}
int
674 tenSlice( Nrrd *nout, const Nrrd *nten, unsigned int axis,
size_t pos, unsigned int dim ) {
static const char me[]="tenSlice";
Nrrd *nslice, **ncoeff=NULL;
int ci[4];
airArray *mop;
char stmp[2][AIR_STRLEN_SMALL];
if ( !( nout && nten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( tenTensorCheck( nten, nrrdTypeDefault, AIR_TRUE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a valid tensor field", me );
return 1;
}
if ( !( 2 == dim || 3 == dim ) ) {
biffAddf( TEN, "%s: given dim ( %d ) not 2 or 3", me, dim );
return 1;
}
if ( !( axis <= 2 ) ) {
biffAddf( TEN, "%s: axis %u not in valid range [0, 1, 2]", me, axis );
return 1;
}
if ( !( pos < nten->axis[1+axis].size ) ) {
biffAddf( TEN, "%s: slice position %s not in valid range [0..%s]", me,
airSprintSize_t( stmp[0], pos ),
airSprintSize_t( stmp[1], nten->axis[1+axis].size-1 ) );
return 1;
}
/*
** threshold 0
** Dxx Dxy Dxz 1 2 3
** Dxy Dyy Dyz = ( 2 ) 4 5
** Dxz Dyz Dzz ( 3 ) ( 5 ) 6
*/
mop = airMopNew( );
airMopAdd( mop, nslice=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( 3 == dim ) {
if ( nrrdSlice( nslice, nten, axis+1, pos )
|| nrrdAxesInsert( nout, nslice, axis+1 ) ) {
biffMovef( TEN, NRRD, "%s: trouble making slice", me );
airMopError( mop ); return 1;
}
} else {
/* HEY: this used to be ncoeff[4], but its passing to nrrdJoin caused
"dereferencing type-punned pointer might break strict-aliasing rules"
warning; GLK not sure how else to fix it */
ncoeff = AIR_CALLOC( 4, Nrrd* );
airMopAdd( mop, ncoeff, airFree, airMopAlways );
airMopAdd( mop, ncoeff[0]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ncoeff[1]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ncoeff[2]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, ncoeff[3]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
switch( axis ) {
case 0:
ELL_4V_SET( ci, 0, 4, 5, 6 );
break;
case 1:
ELL_4V_SET( ci, 0, 1, 3, 6 );
break;
case 2:
ELL_4V_SET( ci, 0, 1, 2, 4 );
break;
default:
biffAddf( TEN, "%s: axis %d bogus", me, axis );
airMopError( mop ); return 1;
break;
}
if ( nrrdSlice( nslice, nten, axis+1, pos )
|| nrrdSlice( ncoeff[0], nslice, 0, ci[0] )
|| nrrdSlice( ncoeff[1], nslice, 0, ci[1] )
|| nrrdSlice( ncoeff[2], nslice, 0, ci[2] )
|| nrrdSlice( ncoeff[3], nslice, 0, ci[3] )
|| nrrdJoin( nout, ( const Nrrd *const* )ncoeff, 4, 0, AIR_TRUE ) ) {
biffMovef( TEN, NRRD, "%s: trouble collecting coefficients", me );
airMopError( mop ); return 1;
}
nout->axis[0].kind = nrrdKind2DMaskedSymMatrix;
}
airMopOkay( mop );
return 0;
}
#define Txx ( ten[1] )
#define Txy ( ten[2] )
#define Txz ( ten[3] )
#define Tyy ( ten[4] )
#define Tyz ( ten[5] )
#define Tzz ( ten[6] )
#define SQRT_1_OVER_2 0.70710678118654752440
#define SQRT_1_OVER_3 0.57735026918962576450
#define SQRT_2_OVER_3 0.81649658092772603272
#define SQRT_1_OVER_6 0.40824829046386301635
/*
** very special purpose: compute tensor-valued gradient
** of eigenvalue skewness, but have to be given two
** other invariant gradients, NORMALIZED, to which
** eigenvalue skewness should be perpendicular
*/
void
779 _tenEvalSkewnessGradient_d( double skw[7],
const double perp1[7],
const double perp2[7],
const double ten[7],
const double minnorm ) {
/* static const char me[]="_tenEvalSkewnessGradient_d"; */
double dot, scl, norm;
/* start with gradient of determinant */
TEN_T_SET( skw, ten[0],
Tyy*Tzz - Tyz*Tyz, Txz*Tyz - Txy*Tzz, Txy*Tyz - Txz*Tyy,
Txx*Tzz - Txz*Txz, Txy*Txz - Tyz*Txx,
Txx*Tyy - Txy*Txy );
/* this normalization is so that minnorm comparison below
is meaningful regardless of scale of input */
/* HEY: should have better handling of case where determinant
gradient magnitude is near zero */
scl = 1.0/( DBL_EPSILON + TEN_T_NORM( skw ) );
TEN_T_SCALE( skw, scl, skw );
dot = TEN_T_DOT( skw, perp1 );
TEN_T_SCALE_INCR( skw, -dot, perp1 );
dot = TEN_T_DOT( skw, perp2 );
TEN_T_SCALE_INCR( skw, -dot, perp2 );
norm = TEN_T_NORM( skw );
if ( norm < minnorm ) {
/* skw is at an extremum, should diagonalize */
double eval[3], evec[9], matA[9], matB[9], matC[9], mev, third;
tenEigensolve_d( eval, evec, ten );
mev = ( eval[0] + eval[1] + eval[2] )/3;
eval[0] -= mev;
eval[1] -= mev;
eval[2] -= mev;
third = ( eval[0]*eval[0]*eval[0]
+ eval[1]*eval[1]*eval[1]
+ eval[2]*eval[2]*eval[2] )/3;
if ( third > 0 ) {
/* skw is positive: linear: eval[1] = eval[2] */
ELL_3MV_OUTER( matA, evec + 1*3, evec + 1*3 );
ELL_3MV_OUTER( matB, evec + 2*3, evec + 2*3 );
} else {
/* skw is negative: planar: eval[0] = eval[1] */
ELL_3MV_OUTER( matA, evec + 0*3, evec + 0*3 );
ELL_3MV_OUTER( matB, evec + 1*3, evec + 1*3 );
}
ELL_3M_SCALE_ADD2( matC, SQRT_1_OVER_2, matA, -SQRT_1_OVER_2, matB );
TEN_M2T( skw, matC );
/* have to make sure that this contrived tensor
is indeed orthogonal to perp1 and perp2 */
dot = TEN_T_DOT( skw, perp1 );
TEN_T_SCALE_INCR( skw, -dot, perp1 );
dot = TEN_T_DOT( skw, perp2 );
TEN_T_SCALE_INCR( skw, -dot, perp2 );
norm = TEN_T_NORM( skw );
}
TEN_T_SCALE( skw, 1.0/norm, skw );
return;
}
void
839 tenInvariantGradientsK_d( double mu1[7], double mu2[7], double skw[7],
const double ten[7], const double minnorm ) {
double dot, norm;
TEN_T_SET( mu1, ten[0],
SQRT_1_OVER_3, 0, 0,
SQRT_1_OVER_3, 0,
SQRT_1_OVER_3 );
TEN_T_SET( mu2, ten[0],
2*Txx - Tyy - Tzz, 3*Txy, 3*Txz,
2*Tyy - Txx - Tzz, 3*Tyz,
2*Tzz - Txx - Tyy );
norm = TEN_T_NORM( mu2 );
if ( norm < minnorm ) {
/* they gave us a diagonal matrix */
TEN_T_SET( mu2, ten[0],
SQRT_2_OVER_3, 0, 0,
-SQRT_1_OVER_6, 0,
-SQRT_1_OVER_6 );
}
/* next two lines shouldn't really be necessary */
dot = TEN_T_DOT( mu2, mu1 );
TEN_T_SCALE_INCR( mu2, -dot, mu1 );
norm = TEN_T_NORM( mu2 );
TEN_T_SCALE( mu2, 1.0/norm, mu2 );
_tenEvalSkewnessGradient_d( skw, mu1, mu2, ten, minnorm );
return;
}
void
871 tenInvariantGradientsR_d( double R1[7], double R2[7], double R3[7],
const double ten[7], const double minnorm ) {
double dot, dev[7], norm, tenNorm, devNorm;
TEN_T_COPY( R1, ten );
tenNorm = norm = TEN_T_NORM( R1 );
if ( norm < minnorm ) {
TEN_T_SET( R1, ten[0],
SQRT_1_OVER_3, 0, 0,
SQRT_1_OVER_3, 0,
SQRT_1_OVER_3 );
norm = TEN_T_NORM( R1 );
}
TEN_T_SCALE( R1, 1.0/norm, R1 );
TEN_T_SET( dev, ten[0],
( 2*Txx - Tyy - Tzz )/3, Txy, Txz,
( 2*Tyy - Txx - Tzz )/3, Tyz,
( 2*Tzz - Txx - Tyy )/3 );
devNorm = TEN_T_NORM( dev );
if ( devNorm < minnorm ) {
/* they gave us a diagonal matrix */
TEN_T_SET( R2, ten[0],
SQRT_2_OVER_3, 0, 0,
-SQRT_1_OVER_6, 0,
-SQRT_1_OVER_6 );
} else {
TEN_T_SCALE_ADD2( R2, tenNorm/devNorm, dev, -devNorm/tenNorm, ten );
}
/* next two lines shouldn't really be necessary */
dot = TEN_T_DOT( R2, R1 );
TEN_T_SCALE_INCR( R2, -dot, R1 );
norm = TEN_T_NORM( R2 );
if ( norm < minnorm ) {
/* Traceless tensor */
TEN_T_SET( R2, ten[0],
SQRT_2_OVER_3, 0, 0,
-SQRT_1_OVER_6, 0,
-SQRT_1_OVER_6 );
} else {
TEN_T_SCALE( R2, 1.0/norm, R2 );
}
_tenEvalSkewnessGradient_d( R3, R1, R2, ten, minnorm );
return;
}
/*
** evec must be pre-computed ( unit-length eigenvectors ) and given to us
*/
void
922 tenRotationTangents_d( double phi1[7],
double phi2[7],
double phi3[7],
const double evec[9] ) {
double outA[9], outB[9], mat[9];
if ( phi1 ) {
phi1[0] = 1.0;
ELL_3MV_OUTER( outA, evec + 1*3, evec + 2*3 );
ELL_3MV_OUTER( outB, evec + 2*3, evec + 1*3 );
ELL_3M_SCALE_ADD2( mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB );
TEN_M2T( phi1, mat );
}
if ( phi2 ) {
phi2[0] = 1.0;
ELL_3MV_OUTER( outA, evec + 0*3, evec + 2*3 );
ELL_3MV_OUTER( outB, evec + 2*3, evec + 0*3 );
ELL_3M_SCALE_ADD2( mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB );
TEN_M2T( phi2, mat );
}
if ( phi3 ) {
phi3[0] = 1.0;
ELL_3MV_OUTER( outA, evec + 0*3, evec + 1*3 );
ELL_3MV_OUTER( outB, evec + 1*3, evec + 0*3 );
ELL_3M_SCALE_ADD2( mat, SQRT_1_OVER_2, outA, SQRT_1_OVER_2, outB );
TEN_M2T( phi3, mat );
}
return;
}
void
956 tenInv_f( float inv[7], const float ten[7] ) {
float det;
TEN_T_INV( inv, ten, det );
}
void
963 tenInv_d( double inv[7], const double ten[7] ) {
double det;
TEN_T_INV( inv, ten, det );
}
void
970 tenLogSingle_d( double logten[7], const double ten[7] ) {
double eval[3], evec[9];
unsigned int ii;
tenEigensolve_d( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = log( eval[ii] );
if ( !AIR_EXISTS( eval[ii] ) ) {
eval[ii] = -FLT_MAX; /* making stuff up */
}
}
tenMakeSingle_d( logten, ten[0], eval, evec );
}
void
985 tenLogSingle_f( float logten[7], const float ten[7] ) {
float eval[3], evec[9];
unsigned int ii;
tenEigensolve_f( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = AIR_CAST( float, log( eval[ii] ) );
if ( !AIR_EXISTS( eval[ii] ) ) {
eval[ii] = -FLT_MAX/10; /* still making stuff up */
}
}
tenMakeSingle_f( logten, ten[0], eval, evec );
}
void
1000 tenExpSingle_d( double expten[7], const double ten[7] ) {
double eval[3], evec[9];
unsigned int ii;
tenEigensolve_d( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = exp( eval[ii] );
}
tenMakeSingle_d( expten, ten[0], eval, evec );
}
void
1012 tenExpSingle_f( float expten[7], const float ten[7] ) {
float eval[3], evec[9];
unsigned int ii;
tenEigensolve_f( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = AIR_CAST( float, exp( eval[ii] ) );
}
tenMakeSingle_f( expten, ten[0], eval, evec );
}
void
1024 tenSqrtSingle_d( double sqrtten[7], const double ten[7] ) {
double eval[3], evec[9];
unsigned int ii;
tenEigensolve_d( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = eval[ii] > 0 ? sqrt( eval[ii] ) : 0;
}
tenMakeSingle_d( sqrtten, ten[0], eval, evec );
}
void
1036 tenSqrtSingle_f( float sqrtten[7], const float ten[7] ) {
float eval[3], evec[9];
unsigned int ii;
tenEigensolve_f( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = AIR_CAST( float, eval[ii] > 0 ? sqrt( eval[ii] ) : 0 );
}
tenMakeSingle_f( sqrtten, ten[0], eval, evec );
}
void
1048 tenPowSingle_d( double powten[7], const double ten[7], double power ) {
double eval[3], _eval[3], evec[9];
unsigned int ii;
tenEigensolve_d( _eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = pow( _eval[ii], power );
}
tenMakeSingle_d( powten, ten[0], eval, evec );
}
void
1060 tenPowSingle_f( float powten[7], const float ten[7], float power ) {
float eval[3], evec[9];
unsigned int ii;
tenEigensolve_f( eval, evec, ten );
for ( ii=0; ii<3; ii++ ) {
eval[ii] = AIR_CAST( float, pow( eval[ii], power ) );
}
tenMakeSingle_f( powten, ten[0], eval, evec );
}
double
1072 tenDoubleContract_d( double a[7], double T[21], double b[7] ) {
double ret;
ret = ( + 1*1*T[ 0]*a[1]*b[1] + 1*2*T[ 1]*a[2]*b[1] + 1*2*T[ 2]*a[3]*b[1] + 1*1*T[ 3]*a[4]*b[1] + 1*2*T[ 4]*a[5]*b[1] + 1*1*T[ 5]*a[6]*b[1] +
+ 2*1*T[ 1]*a[1]*b[2] + 2*2*T[ 6]*a[2]*b[2] + 2*2*T[ 7]*a[3]*b[2] + 2*1*T[ 8]*a[4]*b[2] + 2*2*T[ 9]*a[5]*b[2] + 2*1*T[10]*a[6]*b[2] +
+ 2*1*T[ 2]*a[1]*b[3] + 2*2*T[ 7]*a[2]*b[3] + 2*2*T[11]*a[3]*b[3] + 2*1*T[12]*a[4]*b[3] + 2*2*T[13]*a[5]*b[3] + 2*1*T[14]*a[6]*b[3] +
+ 1*1*T[ 3]*a[1]*b[4] + 1*2*T[ 8]*a[2]*b[4] + 1*2*T[12]*a[3]*b[4] + 1*1*T[15]*a[4]*b[4] + 1*2*T[16]*a[5]*b[4] + 1*1*T[17]*a[6]*b[4] +
+ 2*1*T[ 4]*a[1]*b[5] + 2*2*T[ 9]*a[2]*b[5] + 2*2*T[13]*a[3]*b[5] + 2*1*T[16]*a[4]*b[5] + 2*2*T[18]*a[5]*b[5] + 2*1*T[19]*a[6]*b[5] +
+ 1*1*T[ 5]*a[1]*b[6] + 1*2*T[10]*a[2]*b[6] + 1*2*T[14]*a[3]*b[6] + 1*1*T[17]*a[4]*b[6] + 1*2*T[19]*a[5]*b[6] + 1*1*T[20]*a[6]*b[6] );
return ret;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "does contraction between 2 2nd-order "
"and 1 4rth-order tensor." );
int
30 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
char *outS;
Nrrd *_ncov, *ncov, *_nten[2], *nten[2], *nout;
double *cc, *t0, *t1, *out, ww[21];
size_t nn, ii;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i4", "volume", airTypeOther, 1, 1, &_ncov, NULL,
"4th-order tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "i2", "v0 v1", airTypeOther, 2, 2, _nten, NULL,
"two 2nd-order tensor volumes", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( tenTensorCheck( _nten[0], nrrdTypeDefault, AIR_TRUE, AIR_TRUE )
|| tenTensorCheck( _nten[1], nrrdTypeDefault, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't like input:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( !( 4 == _ncov->dim && 21 == _ncov->axis[0].size ) ) {
fprintf( stderr, "%s: didn't get a 4-D 21-by-X volume ( got %u-D %u-by-X )\n",
me, _ncov->dim, AIR_CAST( unsigned int, _ncov->axis[0].size ) );
airMopError( mop );
return 1;
}
if ( !( nrrdElementNumber( _ncov )/21 == nrrdElementNumber( _nten[0] )/7
&& nrrdElementNumber( _nten[0] )/7 == nrrdElementNumber( _nten[1] )/7 ) ) {
fprintf( stderr, "%s: number voxels %u %u %u don't all match\n", me,
AIR_CAST( unsigned int, nrrdElementNumber( _ncov )/21 ),
AIR_CAST( unsigned int, nrrdElementNumber( _nten[0] )/7 ),
AIR_CAST( unsigned int, nrrdElementNumber( _nten[1] )/7 ) );
airMopError( mop );
return 1;
}
ncov = nrrdNew( );
nten[0] = nrrdNew( );
nten[1] = nrrdNew( );
nout = nrrdNew( );
airMopAdd( mop, ncov, ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nten[0], ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nten[1], ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ncov, _ncov, nrrdTypeDouble )
|| nrrdConvert( nten[0], _nten[0], nrrdTypeDouble )
|| nrrdConvert( nten[1], _nten[1], nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting to %s:\n%s\n",
me, airEnumStr( nrrdType, nrrdTypeDouble ), err );
airMopError( mop );
return 1;
}
if ( nrrdSlice( nout, nten[0], 0, 0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble prepping output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
cc = AIR_CAST( double *, ncov->data );
t0 = AIR_CAST( double *, nten[0]->data );
t1 = AIR_CAST( double *, nten[1]->data );
out = AIR_CAST( double *, nout->data );
nn = nrrdElementNumber( nout );
ww[ 0] = 1*1; ww[ 1] = 2*1; ww[ 2] = 2*1; ww[ 3] = 1*1; ww[ 4] = 2*1; ww[ 5] = 1*1;
/* */ ww[ 6] = 2*2; ww[ 7] = 2*2; ww[ 8] = 1*2; ww[ 9] = 2*2; ww[10] = 1*2;
/* */ ww[11] = 2*2; ww[12] = 1*2; ww[13] = 2*2; ww[14] = 1*2;
/* */ ww[15] = 1*1; ww[16] = 2*1; ww[17] = 1*1;
/* */ ww[18] = 2*2; ww[19] = 1*2;
/* */ ww[20] = 1*1;
/*
for ( ii=0; ii<21; ii++ ) {
ww[ii] = sqrt( ww[ii] );
}
*/
for ( ii=0; ii<nn; ii++ ) {
out[ii] = ( + cc[ 0]*ww[ 0]*t0[1]*t1[1] + cc[ 1]*ww[ 1]*t0[2]*t1[1] + cc[ 2]*ww[ 2]*t0[3]*t1[1] + cc[ 3]*ww[ 3]*t0[4]*t1[1] + cc[ 4]*ww[ 4]*t0[5]*t1[1] + cc[ 5]*ww[ 5]*t0[6]*t1[1] +
+ cc[ 1]*ww[ 1]*t0[1]*t1[2] + cc[ 6]*ww[ 6]*t0[2]*t1[2] + cc[ 7]*ww[ 7]*t0[3]*t1[2] + cc[ 8]*ww[ 8]*t0[4]*t1[2] + cc[ 9]*ww[ 9]*t0[5]*t1[2] + cc[10]*ww[10]*t0[6]*t1[2] +
+ cc[ 2]*ww[ 2]*t0[1]*t1[3] + cc[ 7]*ww[ 7]*t0[2]*t1[3] + cc[11]*ww[11]*t0[3]*t1[3] + cc[12]*ww[12]*t0[4]*t1[3] + cc[13]*ww[13]*t0[5]*t1[3] + cc[14]*ww[14]*t0[6]*t1[3] +
+ cc[ 3]*ww[ 3]*t0[1]*t1[4] + cc[ 8]*ww[ 8]*t0[2]*t1[4] + cc[12]*ww[12]*t0[3]*t1[4] + cc[15]*ww[15]*t0[4]*t1[4] + cc[16]*ww[16]*t0[5]*t1[4] + cc[17]*ww[17]*t0[6]*t1[4] +
+ cc[ 4]*ww[ 4]*t0[1]*t1[5] + cc[ 9]*ww[ 9]*t0[2]*t1[5] + cc[13]*ww[13]*t0[3]*t1[5] + cc[16]*ww[16]*t0[4]*t1[5] + cc[18]*ww[18]*t0[5]*t1[5] + cc[19]*ww[19]*t0[6]*t1[5] +
+ cc[ 5]*ww[ 5]*t0[1]*t1[6] + cc[10]*ww[10]*t0[2]*t1[6] + cc[14]*ww[14]*t0[3]*t1[6] + cc[17]*ww[17]*t0[4]*t1[6] + cc[19]*ww[19]*t0[5]*t1[6] + cc[20]*ww[20]*t0[6]*t1[6] );
/* 0:xxxx 1:xxxy 2:xxxz 3:xxyy 4:xxyz 5:xxzz
* 6:xyxy 7:xyxz 8:xyyy 9:xyyz 10:xyzz
* 11:xzxz 12:xzyy 13:xzyz 14:xzzz
* 15:yyyy 16:yyyz 17:yyzz
* 18:yzyz 19:yzzz
* 20:zzzz */
cc += 21;
t0 += 7;
t1 += 7;
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
static int
27 csimDo( double tm[7], double tcov[21], double rm[3], double rv[3],
Nrrd *ntbuff, tenEstimateContext *tec, double *dwibuff, double sigma,
double bvalue, double B0, unsigned int NN, int randrot,
double _tenOrig[7] ) {
char me[]="csimDo";
double *tbuff;
unsigned int II, taa, tbb, cc;
if ( !( ntbuff
&& ntbuff->data
&& 2 == ntbuff->dim
&& 7 == ntbuff->axis[0].size
&& NN == ntbuff->axis[1].size ) ) {
biffAddf( TEN, "%s: ntbuff not allocated for 2-by-%u array of %s", me,
NN, airEnumStr( nrrdType, nrrdTypeDouble ) ); return 1;
}
/* find all tensors from simulated DWIs */
tbuff = AIR_CAST( double *, ntbuff->data );
for ( II=0; II<NN; II++ ) {
double tenOrig[7], rotf[9], rotb[9], matA[9], matB[9], qq[4], tmp;
ELL_3M_IDENTITY_SET( rotf ); /* sssh warnings */
ELL_3M_IDENTITY_SET( rotb ); /* sssh warnings */
if ( randrot ) {
if ( 1 ) {
double eval[3], evec[9], eps, ma[9], mb[9], rf[9], rb[9];
tenEigensolve_d( eval, evec, _tenOrig );
airNormalRand( &eps, NULL );
ell_aa_to_3m_d( rf, 0*eps/20, evec + 0 );
TEN_T_SCALE_INCR( _tenOrig, 0*eps/30, _tenOrig );
TEN_T2M( ma, _tenOrig );
ELL_3M_TRANSPOSE( rb, rf );
ELL_3M_MUL( mb, ma, rf );
ELL_3M_MUL( ma, rb, mb );
TEN_M2T( _tenOrig, ma );
}
TEN_T2M( matA, _tenOrig );
airNormalRand( qq+0, qq+1 );
airNormalRand( qq+2, qq+3 );
ELL_4V_NORM( qq, qq, tmp );
ell_q_to_3m_d( rotf, qq );
ELL_3M_TRANSPOSE( rotb, rotf );
ELL_3M_MUL( matB, matA, rotf );
ELL_3M_MUL( matA, rotb, matB );
TEN_M2T( tenOrig, matA );
} else {
TEN_T_COPY( tenOrig, _tenOrig );
}
if ( tenEstimate1TensorSimulateSingle_d( tec, dwibuff, sigma,
bvalue, B0, tenOrig )
|| tenEstimate1TensorSingle_d( tec, tbuff, dwibuff ) ) {
biffAddf( TEN, "%s: trouble on exp %u/%u", me, II, NN ); return 1;
}
if ( randrot ) {
TEN_T2M( matA, tbuff );
ELL_3M_MUL( matB, matA, rotb );
ELL_3M_MUL( matA, rotf, matB );
TEN_M2T( tbuff, matA );
} /* else we leave tbuff as it is */
/*
if ( _tenOrig[0] > 0.5 ) {
double tdiff[7];
TEN_T_SUB( tdiff, _tenOrig, tbuff );
fprintf( stderr, "!%s: %g\n"
" ( %g ) %g, %g, %g %g, %g %g\n"
" ( %g ) %g, %g, %g %g, %g %g\n",
me, TEN_T_NORM( tdiff ),
_tenOrig[0], _tenOrig[1], _tenOrig[2], _tenOrig[3], _tenOrig[4],
_tenOrig[5], _tenOrig[6],
tbuff[0], tbuff[1], tbuff[2], tbuff[3], tbuff[4],
tbuff[5], tbuff[6] );
}
*/
tbuff += 7;
}
/* find mean tensor, and mean R_i */
tbuff = AIR_CAST( double *, ntbuff->data );
TEN_T_SET( tm, 0, 0, 0, 0, 0, 0, 0 );
ELL_3V_SET( rm, 0, 0, 0 );
for ( II=0; II<NN; II++ ) {
TEN_T_INCR( tm, tbuff );
rm[0] += sqrt( _tenAnisoTen_d[tenAniso_S]( tbuff ) );
rm[1] += _tenAnisoTen_d[tenAniso_FA]( tbuff );
rm[2] += _tenAnisoTen_d[tenAniso_Mode]( tbuff );
tbuff += 7;
}
rm[0] /= NN;
rm[1] /= NN;
rm[2] /= NN;
TEN_T_SCALE( tm, 1.0/NN, tm );
/* accumulate covariance tensor, and R_i variances */
for ( cc=0; cc<21; cc++ ) {
tcov[cc] = 0;
}
ELL_3V_SET( rv, 0, 0, 0 );
tbuff = AIR_CAST( double *, ntbuff->data );
for ( II=0; II<NN; II++ ) {
double r[3];
r[0] = sqrt( _tenAnisoTen_d[tenAniso_S]( tbuff ) );
r[1] = _tenAnisoTen_d[tenAniso_FA]( tbuff );
r[2] = _tenAnisoTen_d[tenAniso_Mode]( tbuff );
cc = 0;
rv[0] += ( r[0] - rm[0] )*( r[0] - rm[0] )/( NN-1 );
rv[1] += ( r[1] - rm[1] )*( r[1] - rm[1] )/( NN-1 );
rv[2] += ( r[2] - rm[2] )*( r[2] - rm[2] )/( NN-1 );
for ( taa=0; taa<6; taa++ ) {
for ( tbb=taa; tbb<6; tbb++ ) {
tcov[cc] += ( 10000*( tbuff[taa+1]-tm[taa+1] )
*10000*( tbuff[tbb+1]-tm[tbb+1] )/( NN-1 ) );
cc++;
}
}
tbuff += 7;
}
return 0;
}
char *info = ( "does something" );
int
151 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
char *outTenS, *outCovarS, *outRmvS;
int seed, E;
unsigned int NN;
Nrrd *_ninTen, *ninTen, *ngrad, *_ninB0, *ninB0, *nmask,
*noutCovar, *noutTen, *noutRmv, *ntbuff;
float sigma, bval;
size_t sizeX, sizeY, sizeZ;
tenEstimateContext *tec;
int axmap[NRRD_DIM_MAX], randrot;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "ten", airTypeOther, 1, 1, &_ninTen, NULL,
"input tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "n", "#sim", airTypeUInt, 1, 1, &NN, "100",
"number of simulations to run" );
hestOptAdd( &hopt, "seed", "seed", airTypeInt, 1, 1, &seed, "42",
"seed value for RNG which creates noise" );
hestOptAdd( &hopt, "r", "reference field", airTypeOther, 1, 1, &_ninB0, NULL,
"reference anatomical scan, with no diffusion weighting",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "rr", NULL, airTypeOther, 0, 0, &randrot, NULL,
"randomize gradient set orientation" );
hestOptAdd( &hopt, "g", "grad list", airTypeOther, 1, 1, &ngrad, "",
"gradient list, one row per diffusion-weighted image",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "b", "b", airTypeFloat, 1, 1, &bval, "1000",
"b value for simulated scan" );
hestOptAdd( &hopt, "sigma", "sigma", airTypeFloat, 1, 1, &sigma, "0.0",
"Rician noise parameter" );
hestOptAdd( &hopt, "ot", "filename", airTypeString, 1, 1, &outTenS,
"tout.nrrd", "file to write output tensor nrrd to" );
hestOptAdd( &hopt, "oc", "filename", airTypeString, 1, 1, &outCovarS,
"cout.nrrd", "file to write output covariance nrrd to" );
hestOptAdd( &hopt, "or", "filename", airTypeString, 1, 1, &outRmvS,
"rout.nrrd", "file to write output R_i means, variances to" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( tenGradientCheck( ngrad, nrrdTypeDefault, 7 ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: problem with gradient list:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( tenTensorCheck( _ninTen, nrrdTypeDefault, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't like input:\n%s\n", me, err );
airMopError( mop );
return 1;
}
sizeX = _ninTen->axis[1].size;
sizeY = _ninTen->axis[2].size;
sizeZ = _ninTen->axis[3].size;
if ( !( 3 == _ninB0->dim &&
sizeX == _ninB0->axis[0].size &&
sizeY == _ninB0->axis[1].size &&
sizeZ == _ninB0->axis[2].size ) ) {
char stmp[3][AIR_STRLEN_SMALL];
fprintf( stderr, "%s: given B0 ( %u-D ) volume not 3-D %sx%sx%s",
me, _ninB0->dim, airSprintSize_t( stmp[0], sizeX ),
airSprintSize_t( stmp[1], sizeY ), airSprintSize_t( stmp[2], sizeZ ) );
airMopError( mop );
return 1;
}
ninTen = nrrdNew( );
airMopAdd( mop, ninTen, ( airMopper )nrrdNuke, airMopOnError );
nmask = nrrdNew( );
airMopAdd( mop, nmask, ( airMopper )nrrdNuke, airMopOnError );
ninB0 = nrrdNew( );
airMopAdd( mop, ninB0, ( airMopper )nrrdNuke, airMopOnError );
noutCovar = nrrdNew( );
airMopAdd( mop, noutCovar, ( airMopper )nrrdNuke, airMopOnError );
noutTen = nrrdNew( );
airMopAdd( mop, noutTen, ( airMopper )nrrdNuke, airMopOnError );
noutRmv = nrrdNew( );
airMopAdd( mop, noutRmv, ( airMopper )nrrdNuke, airMopOnError );
ntbuff = nrrdNew( );
airMopAdd( mop, ntbuff, ( airMopper )nrrdNuke, airMopOnError );
if ( nrrdConvert( ninTen, _ninTen, nrrdTypeDouble )
|| nrrdSlice( nmask, ninTen, 0, 0 )
|| nrrdConvert( ninB0, _ninB0, nrrdTypeDouble )
|| nrrdMaybeAlloc_va( noutTen, nrrdTypeDouble, 4,
AIR_CAST( size_t, 7 ), sizeX, sizeY, sizeZ )
|| nrrdMaybeAlloc_va( noutCovar, nrrdTypeDouble, 4,
AIR_CAST( size_t, 21 ), sizeX, sizeY, sizeZ )
|| nrrdMaybeAlloc_va( noutRmv, nrrdTypeDouble, 4,
AIR_CAST( size_t, 6 ), sizeX, sizeY, sizeZ )
|| nrrdMaybeAlloc_va( ntbuff, nrrdTypeDouble, 2,
AIR_CAST( size_t, 7 ), NN ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up tec:\n%s\n", me, err );
airMopError( mop );
return 1;
}
tec = tenEstimateContextNew( );
airMopAdd( mop, tec, ( airMopper )tenEstimateContextNix, airMopAlways );
E = 0;
if ( !E ) E |= tenEstimateMethodSet( tec, tenEstimate1MethodLLS );
if ( !E ) E |= tenEstimateValueMinSet( tec, 0.000000001 );
if ( !E ) E |= tenEstimateGradientsSet( tec, ngrad, bval, AIR_TRUE );
if ( !E ) E |= tenEstimateThresholdSet( tec, 0, 0 );
if ( !E ) E |= tenEstimateUpdate( tec );
if ( E ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble setting up tec:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airSrandMT( seed );
fprintf( stderr, "!%s: randrot = %d\n", me, randrot );
if ( 1 ) {
unsigned int II;
unsigned int nsamp;
double *inTen, *outTen, *outCovar, *outRmv,
*dwibuff, ( *lup )( const void *, size_t );
char doneStr[AIR_STRLEN_SMALL];
dwibuff = AIR_CAST( double *, calloc( ngrad->axis[1].size, sizeof( double ) ) );
airMopAdd( mop, dwibuff, airFree, airMopAlways );
nsamp = sizeX*sizeY*sizeZ;
inTen = AIR_CAST( double *, ninTen->data );
lup = nrrdDLookup[nrrdTypeDouble];
outTen = AIR_CAST( double *, noutTen->data );
outCovar = AIR_CAST( double *, noutCovar->data );
outRmv = AIR_CAST( double *, noutRmv->data );
fprintf( stderr, "!%s: simulating ... ", me );
fflush( stderr );
for ( II=0; II<nsamp; II++ ) {
if ( !( II % sizeX ) ) {
fprintf( stderr, "%s", airDoneStr( 0, II, nsamp, doneStr ) );
fflush( stderr );
}
if ( csimDo( outTen, outCovar, outRmv + 0, outRmv + 3, ntbuff,
tec, dwibuff, sigma,
bval, lup( ninB0->data, II ), NN, randrot, inTen ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
inTen += 7;
outTen += 7;
outCovar += 21;
outRmv += 6;
}
fprintf( stderr, "%s\n", airDoneStr( 0, II, nsamp, doneStr ) );
}
axmap[0] = -1;
axmap[1] = 1;
axmap[2] = 2;
axmap[3] = 3;
if ( nrrdSplice( noutTen, noutTen, nmask, 0, 0 )
|| nrrdAxisInfoCopy( noutTen, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdAxisInfoCopy( noutCovar, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdAxisInfoCopy( noutRmv, ninTen, axmap, NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( noutTen, ninTen,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE )
|| nrrdBasicInfoCopy( noutCovar, ninTen,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE )
|| nrrdBasicInfoCopy( noutRmv, ninTen,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE )
|| nrrdSave( outTenS, noutTen, NULL )
|| nrrdSave( outCovarS, noutCovar, NULL )
|| nrrdSave( outRmvS, noutRmv, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "does geodesics" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
char *outS;
double _tA[6], tA[7], _tB[6], tB[7], time0, time1, conv, confThresh,
pA[3], pB[3], qA[4], qB[4], rA[9], rB[9], mat1[9], mat2[9], tmp,
stepSize, minNorm, sclA, sclB;
unsigned int NN, maxiter, refIdx[3];
int recurse, ptype, verb;
Nrrd *_nin, *nin, *nout;
tenInterpParm *tip;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "a", "tensor", airTypeDouble, 6, 6, _tA, "1 0 0 1 0 1",
"first tensor" );
hestOptAdd( &hopt, "pa", "qq", airTypeDouble, 3, 3, pA, "0 0 0",
"rotation of first tensor" );
hestOptAdd( &hopt, "sa", "scl", airTypeDouble, 1, 1, &sclA, "1.0",
"scaling of first tensor" );
hestOptAdd( &hopt, "b", "tensor", airTypeDouble, 6, 6, _tB, "1 0 0 1 0 1",
"second tensor" );
hestOptAdd( &hopt, "pb", "qq", airTypeDouble, 3, 3, pB, "0 0 0",
"rotation of second tensor" );
hestOptAdd( &hopt, "sb", "scl", airTypeDouble, 1, 1, &sclB, "1.0",
"scaling of second tensor" );
hestOptAdd( &hopt, "i", "nten", airTypeOther, 1, 1, &_nin, "",
"input tensor volume ( makes previous options moot )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ri", "x y z", airTypeUInt, 3, 3, refIdx, "0 0 0",
"index of reference tensor in input tensor volume" );
hestOptAdd( &hopt, "th", "thresh", airTypeDouble, 1, 1, &confThresh, "0.5",
"conf mask threshold on \"-i\"" );
hestOptAdd( &hopt, "n", "# steps", airTypeUInt, 1, 1, &NN, "100",
"number of steps in between two tensors" );
hestOptAdd( &hopt, "s", "stepsize", airTypeDouble, 1, 1, &stepSize, "1",
"step size in update" );
hestOptAdd( &hopt, "mn", "minnorm", airTypeDouble, 1, 1, &minNorm, "0.000001",
"minnorm of something" );
hestOptAdd( &hopt, "c", "conv", airTypeDouble, 1, 1, &conv, "0.0001",
"convergence threshold of length fraction" );
hestOptAdd( &hopt, "mi", "maxiter", airTypeUInt, 1, 1, &maxiter, "0",
"if non-zero, max # iterations for computation" );
hestOptAdd( &hopt, "r", "recurse", airTypeInt, 0, 0, &recurse, NULL,
"enable recursive solution, when useful" );
hestOptAdd( &hopt, "t", "path type", airTypeEnum, 1, 1, &ptype, "lerp",
"what type of path to compute", NULL, tenInterpType );
hestOptAdd( &hopt, "o", "filename", airTypeString, 1, 1, &outS, "-",
"file to write output nrrd to" );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1, &verb, "0",
"verbosity" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
tip = tenInterpParmNew( );
airMopAdd( mop, tip, ( airMopper )tenInterpParmNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
tip->verbose = verb;
tip->convStep = stepSize;
tip->enableRecurse = recurse;
tip->minNorm = minNorm;
tip->maxIter = maxiter;
tip->convEps = conv;
if ( _nin ) {
double refTen[7], inTen[7], *in, *out;
unsigned int xi, yi, zi, sx, sy, sz, dimOut;
int axmap[NRRD_DIM_MAX], numerical;
size_t size[NRRD_DIM_MAX];
if ( tenTensorCheck( _nin, nrrdTypeDefault, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: input volume not valid:\n%s\n",
me, err );
airMopError( mop );
return 1;
}
sx = AIR_CAST( unsigned int, _nin->axis[1].size );
sy = AIR_CAST( unsigned int, _nin->axis[2].size );
sz = AIR_CAST( unsigned int, _nin->axis[3].size );
if ( !( refIdx[0] < sx
&& refIdx[1] < sy
&& refIdx[2] < sz ) ) {
fprintf( stderr, "%s: index ( %u, %u, %u ) out of bounds ( %u, %u, %u )\n", me,
refIdx[0], refIdx[1], refIdx[2], sx, sy, sz );
airMopError( mop );
return 1;
}
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
numerical = ( ptype == tenInterpTypeGeoLoxK
|| ptype == tenInterpTypeGeoLoxR
|| ptype == tenInterpTypeLoxK
|| ptype == tenInterpTypeLoxR
|| ptype == tenInterpTypeQuatGeoLoxK
|| ptype == tenInterpTypeQuatGeoLoxR );
if ( numerical ) {
tip->lengthFancy = AIR_TRUE;
dimOut = 4;
size[0] = 3;
size[1] = _nin->axis[1].size;
size[2] = _nin->axis[2].size;
size[3] = _nin->axis[3].size;
axmap[0] = -1;
axmap[1] = 1;
axmap[2] = 2;
axmap[3] = 3;
} else {
dimOut = 3;
size[0] = _nin->axis[1].size;
size[1] = _nin->axis[2].size;
size[2] = _nin->axis[3].size;
axmap[0] = 1;
axmap[1] = 2;
axmap[2] = 3;
}
if ( nrrdConvert( nin, _nin, nrrdTypeDouble )
|| nrrdMaybeAlloc_nva( nout, nrrdTypeDouble, dimOut, size )
|| nrrdAxisInfoCopy( nout, nin, axmap,
NRRD_AXIS_INFO_SIZE_BIT )
|| nrrdBasicInfoCopy( nout, nin,
( NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_SAMPLEUNITS_BIT ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
in = AIR_CAST( double *, nin->data );
out = AIR_CAST( double *, nout->data );
TEN_T_COPY( refTen, in + 7*( refIdx[0] + sx*( refIdx[1] + sy*refIdx[2] ) ) );
fprintf( stderr, "!%s: reference tensor = ( %g ) %g %g %g %g %g %g\n",
me, refTen[0], refTen[1], refTen[2], refTen[3],
refTen[4], refTen[5], refTen[6] );
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
TEN_T_COPY( inTen, in + 7*( xi + sx*( yi + sy*zi ) ) );
if ( numerical ) {
fprintf( stderr, "!%s: %u %u %u \n", me, xi, yi, zi );
if ( inTen[0] < confThresh ) {
out[0] = AIR_NAN;
out[1] = AIR_NAN;
out[2] = AIR_NAN;
} else {
tip->verbose = 10*( xi == refIdx[0]
&& yi == refIdx[1]
&& zi == refIdx[2] );
out[0] = tenInterpDistanceTwo_d( inTen, refTen, ptype, tip );
out[1] = tip->lengthShape;
out[2] = tip->lengthOrient;
}
out += 3;
} else {
if ( inTen[0] < confThresh ) {
*out = AIR_NAN;
} else {
*out = tenInterpDistanceTwo_d( inTen, refTen, ptype, tip );
}
out += 1;
}
}
if ( numerical ) {
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
}
}
}
} else {
/* only doing the path between two specified tensors */
ELL_6V_COPY( tA + 1, _tA );
tA[0] = 1.0;
TEN_T_SCALE( tA, sclA, tA );
ELL_6V_COPY( tB + 1, _tB );
tB[0] = 1.0;
TEN_T_SCALE( tB, sclB, tB );
ELL_4V_SET( qA, 1, pA[0], pA[1], pA[2] );
ELL_4V_NORM( qA, qA, tmp );
ELL_4V_SET( qB, 1, pB[0], pB[1], pB[2] );
ELL_4V_NORM( qB, qB, tmp );
ell_q_to_3m_d( rA, qA );
ell_q_to_3m_d( rB, qB );
TEN_T2M( mat1, tA );
ell_3m_mul_d( mat2, rA, mat1 );
ELL_3M_TRANSPOSE_IP( rA, tmp );
ell_3m_mul_d( mat1, mat2, rA );
TEN_M2T( tA, mat1 );
TEN_T2M( mat1, tB );
ell_3m_mul_d( mat2, rB, mat1 );
ELL_3M_TRANSPOSE_IP( rB, tmp );
ell_3m_mul_d( mat1, mat2, rB );
TEN_M2T( tB, mat1 );
/*
fprintf( stderr, "!%s: tA = ( %g ) %g %g %g\n %g %g\n %g\n", me,
tA[0], tA[1], tA[2], tA[3], tA[4], tA[5], tA[6] );
fprintf( stderr, "!%s: tB = ( %g ) %g %g %g\n %g %g\n %g\n", me,
tB[0], tB[1], tB[2], tB[3], tB[4], tB[5], tB[6] );
*/
time0 = airTime( );
if ( tenInterpTwoDiscrete_d( nout, tA, tB, ptype, NN, tip ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble computing path:\n%s\n",
me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "!%s: ------- # iter = %u, conv = %g\n", me,
tip->numIter, tip->convFinal );
time1 = airTime( );
fprintf( stderr, "%s: path length = %g; time = %g\n",
me, tenInterpPathLength( nout, AIR_FALSE, AIR_FALSE, AIR_FALSE ),
time1 - time0 );
if ( 1 ) {
double *geod, eval0[3], eval[3], evec0[9], evec[9], rot[9], diff[7],
nrm, tmp, axis[3], angle;
unsigned int ii, NN;
NN = AIR_CAST( unsigned int, nout->axis[1].size );
geod = AIR_CAST( double *, nout->data );
geod += 7;
for ( ii=1; ii<NN; ii++ ) {
double igrad[3][7];
tenEigensolve_d( eval0, evec0, geod-7 );
ELL_3M_TRANSPOSE_IP( evec0, tmp );
tenEigensolve_d( eval, evec, geod );
ELL_3M_MUL( rot, evec0, evec );
angle = ell_3m_to_aa_d( axis, rot );
TEN_T_SUB( diff, geod, geod-7 );
tenInvariantGradientsK_d( igrad[0], igrad[1], igrad[2], geod, 0 );
nrm = TEN_T_NORM( diff );
TEN_T_SCALE( diff, 1.0/nrm, diff );
fprintf( stderr, "%2u %9.6f ( %9.6f %9.6f %9.6f ) : %9.6f %9.6f %9.6f "
": ( %9.6f, %9.6f, %9.6f ) %g %g %g\n",
ii, angle, axis[0], axis[1], axis[2],
TEN_T_DOT( igrad[0], diff ),
TEN_T_DOT( igrad[1], diff ),
TEN_T_DOT( igrad[2], diff ),
nrm, TEN_T_NORM( diff ), TEN_T_NORM( igrad[2] ),
eval[0], eval[1], eval[2] );
geod += 7;
}
}
if ( 0 ) {
double eval[3], evec[9], rot[9], tt[7], qB[4];
double unitq[8][4] = {{+1, 0, 0, 0},
{-1, 0, 0, 0},
{0, +1, 0, 0},
{0, -1, 0, 0},
{0, 0, +1, 0},
{0, 0, -1, 0},
{0, 0, 0, +1},
{0, 0, 0, -1}};
unsigned qi;
tenEigensolve_d( eval, evec, tB );
ELL_3M_TRANSPOSE( rot, evec );
ell_3m_to_q_d( qB, evec );
fprintf( stderr, "%s: tB: ( %g ) %f %f %f, %f %f, %f; qB = %f %f %f %f\n", me,
tB[0], tB[1], tB[2], tB[3], tB[4], tB[5], tB[6],
qB[0], qB[1], qB[2], qB[3] );
for ( qi=0; qi<8; qi++ ) {
double qm[4];
ell_q_mul_d( qm, qB, unitq[qi] );
ell_q_to_3m_d( rot, qm );
ELL_3M_TRANSPOSE( evec, rot );
tenMakeSingle_d( tt, tB[0], eval, evec );
fprintf( stderr, "%s: tt[%u]: ( %g ) %f %f %f, %f %f, %f; qm = %f %f %f %f\n", me, qi,
tt[0], tt[1], tt[2], tt[3], tt[4], tt[5], tt[6],
qm[0], qm[1], qm[2], qm[3] );
}
}
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "tests invariant grads and rotation tangents." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
double _ten[6], ten[7], minnorm, igrt[6][7], eval[3], evec[9],
pp[3], qq[4], rot[9], matA[9], matB[9], tmp;
int doK, ret, ii, jj;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, NULL, "tensor", airTypeDouble, 6, 6, _ten, NULL,
"tensor value" );
hestOptAdd( &hopt, "mn", "minnorm", airTypeDouble, 1, 1, &minnorm,
"0.00001",
"minimum norm before special handling" );
hestOptAdd( &hopt, "k", NULL, airTypeInt, 0, 0, &doK, NULL,
"Use K invariants, instead of R ( the default )" );
hestOptAdd( &hopt, "p", "x y z", airTypeDouble, 3, 3, pp, "0 0 0",
"location in quaternion quotient space" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
ELL_6V_COPY( ten+1, _ten );
ten[0] = 1.0;
fprintf( stderr, "input tensor = %f %f %f %f %f %f\n",
ten[1], ten[2], ten[3], ten[4], ten[5], ten[6] );
ELL_4V_SET( qq, 1, pp[0], pp[1], pp[2] );
ELL_4V_NORM( qq, qq, tmp );
ell_q_to_3m_d( rot, qq );
TEN_T2M( matA, ten );
ELL_3M_MUL( matB, rot, matA );
ELL_3M_TRANSPOSE_IP( rot, tmp );
ELL_3M_MUL( matA, matB, rot );
TEN_M2T( ten, matA );
fprintf( stderr, "rotated tensor = %f %f %f %f %f %f\n",
ten[1], ten[2], ten[3], ten[4], ten[5], ten[6] );
ret = tenEigensolve_d( eval, evec, ten );
fprintf( stderr, "eigensystem: %s: %g %g %g\n",
airEnumDesc( ell_cubic_root, ret ),
eval[0], eval[1], eval[2] );
if ( doK ) {
tenInvariantGradientsK_d( igrt[0], igrt[1], igrt[2], ten, minnorm );
} else {
tenInvariantGradientsR_d( igrt[0], igrt[1], igrt[2], ten, minnorm );
}
tenRotationTangents_d( igrt[3], igrt[4], igrt[5], evec );
fprintf( stderr, "invariant gradients and rotation tangents:\n" );
for ( ii=0; ii<=2; ii++ ) {
fprintf( stderr, " %s_%d: ( norm=%g ) %f %f %f %f %f %f\n",
doK ? "K" : "R", ii+1,
TEN_T_NORM( igrt[ii] ),
igrt[ii][1], igrt[ii][2], igrt[ii][3],
igrt[ii][4], igrt[ii][5],
igrt[ii][6] );
}
for ( ii=3; ii<=5; ii++ ) {
fprintf( stderr, "phi_%d: ( norm=%g ) %f %f %f %f %f %f\n",
ii-2,
TEN_T_NORM( igrt[ii] ),
igrt[ii][1], igrt[ii][2], igrt[ii][3],
igrt[ii][4], igrt[ii][5],
igrt[ii][6] );
}
fprintf( stderr, "dot products:\n" );
for ( ii=0; ii<=5; ii++ ) {
for ( jj=ii+1; jj<=5; jj++ ) {
fprintf( stderr, "%d, %d==%f ", ii, jj, TEN_T_DOT( igrt[ii], igrt[jj] ) );
}
fprintf( stderr, "\n" );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "The histogram craziness continues." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
char *errS, *outS, *covarS;
Nrrd *_nodf, *nvec, *nhist, *ncovar;
int bins;
size_t size[NRRD_DIM_MAX];
float min;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "i", "odf", airTypeOther, 1, 1, &_nodf, NULL,
"ODF volume to analyze", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "v", "odf", airTypeOther, 1, 1, &nvec, NULL,
"list of vectors by which odf is sampled",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "min", "min", airTypeFloat, 1, 1, &min, "0.0",
"ODF values below this are ignored, and per-voxel ODF is "
"normalized to have sum 1.0. Use \"nan\" to subtract out "
"the per-voxel min." );
hestOptAdd( &hopt, "b", "bins", airTypeInt, 1, 1, &bins, "128",
"number of bins in histograms" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file" );
hestOptAdd( &hopt, "co", "covariance out", airTypeString, 1, 1,
&covarS, "covar.nrrd", "covariance output file" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( nrrdTypeFloat == nvec->type ) ) {
fprintf( stderr, "%s vector type ( %s ) not %s\n", me,
airEnumStr( nrrdType, nvec->type ),
airEnumStr( nrrdType, nrrdTypeFloat ) );
airMopError( mop ); return 1;
}
if ( !( 2 == nvec->dim && 3 == nvec->axis[0].size ) ) {
fprintf( stderr, "%s: nvec not a 2-D 3-by-N array\n", me );
airMopError( mop ); return 1;
}
if ( !( _nodf->axis[0].size == nvec->axis[1].size ) ) {
fprintf( stderr, "%s mismatch of _nodf->axis[0].size ( %d ) vs. "
"nvec->axis[1].size ( %d )\n", me,
( int )_nodf->axis[0].size, ( int )nvec->axis[1].size );
airMopError( mop ); return 1;
}
nrrdAxisInfoGet_nva( _nodf, nrrdAxisInfoSize, size );
size[0] = bins;
nhist = nrrdNew( );
airMopAdd( mop, nhist, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_nva( nhist, nrrdTypeFloat, _nodf->dim, size ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating output:\n%s", me, errS );
airMopError( mop ); return 1;
}
ncovar = nrrdNew( );
airMopAdd( mop, ncovar, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( ncovar, nrrdTypeFloat, 2,
AIR_CAST( size_t, bins ),
AIR_CAST( size_t, bins ) ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating covariance output:\n%s", me, errS );
airMopError( mop ); return 1;
}
{
/* we modify the lengths of the vectors here */
int NN, VV, ii, jj=0, kk, *anglut;
float *odf, *hist, *covar, *vec, *vi, *vj, tmp, pvmin;
double *mean;
Nrrd *nodf, *nanglut;
VV = nvec->axis[1].size;
NN = nrrdElementNumber( _nodf )/VV;
nanglut = nrrdNew( );
airMopAdd( mop, nanglut, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nanglut, nrrdTypeInt, 2,
AIR_CAST( size_t, VV ),
AIR_CAST( size_t, VV ) ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble allocating lookup table:\n%s", me, errS );
airMopError( mop ); return 1;
}
if ( nrrdTypeFloat == _nodf->type ) {
nodf = _nodf;
} else {
nodf = nrrdNew( );
airMopAdd( mop, nodf, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nodf, _nodf, nrrdTypeFloat ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble converting input:\n%s", me, errS );
airMopError( mop ); return 1;
}
}
/* normalize lengths ( MODIFIES INPUT ) */
vec = ( float* )nvec->data;
for ( ii=0; ii<=jj; ii++ ) {
vi = vec + 3*ii;
ELL_3V_NORM( vi, vi, tmp );
}
/* pre-compute pair-wise angles */
anglut = ( int* )nanglut->data;
for ( jj=0; jj<VV; jj++ ) {
vj = vec + 3*jj;
for ( ii=0; ii<=jj; ii++ ) {
vi = vec + 3*ii;
tmp = ELL_3V_DOT( vi, vj );
tmp = AIR_ABS( tmp );
tmp = acos( tmp )/( AIR_PI/2.0 );
anglut[ii + VV*jj] = airIndex( 0.0, tmp, 1.0, bins );
}
}
/* process all samples ( MODIFIES INPUT if input was already float ) */
odf = ( float* )nodf->data;
hist = ( float* )nhist->data;
for ( kk=0; kk<NN; kk++ ) {
if ( !( kk % 100 ) ) {
fprintf( stderr, "%d/%d\n", kk, NN );
}
tmp = 0;
if ( AIR_EXISTS( min ) ) {
for ( ii=0; ii<VV; ii++ ) {
odf[ii] = AIR_MAX( 0.0, odf[ii]-min );
tmp += odf[ii];
}
} else {
/* we do the more sketchy per-voxel min subtraction */
pvmin = airFPGen_f( airFP_POS_INF );
for ( ii=0; ii<VV; ii++ ) {
pvmin = AIR_MIN( pvmin, odf[ii] );
}
for ( ii=0; ii<VV; ii++ ) {
odf[ii] -= pvmin;
tmp += odf[ii];
}
}
if ( tmp ) {
/* something left after subtracting out baseline isotropic */
for ( ii=0; ii<VV; ii++ ) {
odf[ii] /= tmp;
}
/* odf[] is normalized to 1.0 sum */
for ( jj=0; jj<VV; jj++ ) {
for ( ii=0; ii<=jj; ii++ ) {
tmp = odf[ii]*odf[jj];
hist[anglut[ii + VV*jj]] += tmp;
}
}
}
odf += VV;
hist += bins;
}
odf = NULL;
hist = NULL;
/* find mean value of each bin ( needed for covariance ) */
mean = ( double* )calloc( bins, sizeof( double ) );
if ( !mean ) {
fprintf( stderr, "%s: couldn't allocate mean array", me );
airMopError( mop ); return 1;
}
hist = ( float* )nhist->data;
for ( kk=0; kk<NN; kk++ ) {
for ( ii=0; ii<bins; ii++ ) {
mean[ii] += hist[ii];
}
hist += bins;
}
hist = NULL;
for ( ii=0; ii<bins; ii++ ) {
mean[ii] /= NN;
}
/* make covariance matrix of from all histograms */
covar = ( float* )ncovar->data;
hist = ( float* )nhist->data;
for ( kk=0; kk<NN; kk++ ) {
for ( jj=0; jj<bins; jj++ ) {
for ( ii=0; ii<jj; ii++ ) {
tmp = ( hist[ii] - mean[ii] )*( hist[jj] - mean[jj] );
covar[ii + bins*jj] += tmp;
covar[jj + bins*ii] += tmp;
}
covar[jj + bins*jj] += ( hist[jj] - mean[jj] )*( hist[jj] - mean[jj] );
}
hist += bins;
}
hist = NULL;
free( mean );
}
if ( nrrdSave( outS, nhist, NULL ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, errS );
airMopError( mop ); return 1;
}
if ( nrrdSave( covarS, ncovar, NULL ) ) {
airMopAdd( mop, errS = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save covariance output:\n%s\n", me, errS );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
int
27 main( int argc, char **argv ) {
char *me, *err;
hestParm *hparm;
hestOpt *hopt = NULL;
airArray *mop;
char *outS;
int E, ptsNum, ptsIdx;
Nrrd *nin, *nprobe, *nout;
float *idata, *odata, scale[3], power, x, y, z, r, g, b, cl;
gage_t *evec, *aniso, *tensor;
double kparm[NRRD_KERNEL_PARMS_NUM];
gageContext *ctx;
gagePerVolume *pvl;
mop = airMopNew( );
me = argv[0];
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hestOptAdd( &hopt, "i", "nin", airTypeOther, 1, 1, &nin, NULL,
"input diffusion tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "p", "nprobe", airTypeOther, 1, 1, &nprobe, NULL,
"input list of points to probe at, as 3xN float nrrd",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "sc", "scaling", airTypeFloat, 3, 3, scale, "1.0 1.0 1.0",
"scaling that took index space positions ( in nin ) to "
"vertex positions ( in nprobe ); hopefully just the \"spacings\" "
"on the volume that was isosurfaced." );
hestOptAdd( &hopt, "pow", "power", airTypeFloat, 1, 1, &power, "0.4",
"power to raise cl_2 to to determine saturation of color" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, NULL,
"output image ( floating point )" );
hestParseOrDie( hopt, argc-1, argv+1, hparm,
me, "secret testing area", AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( tenTensorCheck( nin, nrrdTypeFloat, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get value tensor volume:\n%s\n", me, err );
airMopError( mop );
return 1;
}
if ( !( nrrdTypeFloat == nprobe->type &&
2 == nprobe->dim &&
3 == nprobe->axis[0].size ) ) {
fprintf( stderr, "%s: didn't get valid probe point list\n", me );
airMopError( mop );
return 1;
}
ptsNum = nprobe->axis[1].size;
ctx = gageContextNew( );
airMopAdd( mop, ctx, ( airMopper )gageContextNix, airMopAlways );
gageSet( ctx, gageParmCheckIntegrals, AIR_TRUE );
kparm[0] = 1.0;
E = 0;
if ( !E ) E |= !( pvl = gagePerVolumeNew( nin, tenGageKind ) );
if ( !E ) E |= gagePerVolumeAttach( ctx, pvl );
if ( !E ) E |= gageKernelSet( ctx, gageKernel00, nrrdKernelTent, kparm );
if ( !E ) E |= gageQuerySet( pvl, ( ( 1 << tenGageEvec ) |
( 1 << tenGageAniso ) |
( 1 << tenGageTensor ) ) );
if ( !E ) E |= gageUpdate( ctx );
if ( E ) {
airMopAdd( mop, err = biffGetDone( GAGE ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop );
return 1;
}
evec = gageAnswerPointer( pvl, tenGageEvec );
aniso = gageAnswerPointer( pvl, tenGageAniso );
tensor = gageAnswerPointer( pvl, tenGageTensor );
if ( nrrdAlloc( nout=nrrdNew( ), nrrdTypeFloat, 2, 3, ptsNum ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
idata = nprobe->data;
odata = nout->data;
for ( ptsIdx=0; ptsIdx<ptsNum; ptsIdx++ ) {
x = idata[3*ptsIdx + 0]/scale[0];
y = idata[3*ptsIdx + 1]/scale[1];
z = idata[3*ptsIdx + 2]/scale[2];
gageProbe( ctx, x, y, z );
r = AIR_ABS( evec[0] );
g = AIR_ABS( evec[1] );
b = AIR_ABS( evec[2] );
cl = aniso[tenAniso_Cl2];
cl = tensor[0]*AIR_CLAMP( 0.0, cl, 1.0 );
cl = pow( cl, power );
odata[3*ptsIdx + 0] = AIR_AFFINE( 0.0, cl, 1.0, 0.5, r );
odata[3*ptsIdx + 1] = AIR_AFFINE( 0.0, cl, 1.0, 0.5, g );
odata[3*ptsIdx + 2] = AIR_AFFINE( 0.0, cl, 1.0, 0.5, b );
}
if ( nrrdSave( outS, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
exit( 0 );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "tensor ROI information." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
unsigned int sx, sy, sz, ss, ii, anisoTypeNum, anisoTypeIdx,
roiVoxNum, roiVoxIdx, statNum, statIdx;
float *ten, *roi, *aniso, eval[3], *stat;
Nrrd *nten, *_nroi, *nroi, *naniso, *nstat;
int *anisoType, *measr;
size_t anisoSize[2];
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "r, roi", "roi", airTypeOther, 1, 1, &_nroi, NULL,
"ROI volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "i, input", "volume", airTypeOther, 1, 1, &nten, "-",
"tensor volume", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "a, aniso", "aniso", airTypeEnum, 1, -1, &anisoType, NULL,
"which anisotropy measures to measure",
&anisoTypeNum, tenAniso );
hestOptAdd( &hopt, "m, measr", "measr", airTypeEnum, 1, -1, &measr, NULL,
"which measures/statistics to calculate",
&statNum, nrrdMeasure );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( tenTensorCheck( nten, nrrdTypeFloat, AIR_TRUE, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: didn't get tensor input:\n%s\n", me, err );
airMopError( mop );
return 1;
}
nroi = nrrdNew( );
airMopAdd( mop, nroi, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
if ( nrrdConvert( nroi, _nroi, nrrdTypeFloat ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't convert ROI to float:\n%s\n", me, err );
airMopError( mop );
return 1;
}
sx = nten->axis[1].size;
sy = nten->axis[2].size;
sz = nten->axis[3].size;
if ( !( 3 == nroi->dim
&& sx == nroi->axis[0].size
&& sy == nroi->axis[1].size
&& sz == nroi->axis[2].size ) ) {
fprintf( stderr, "%s: ROI dimension or axis sizes don't match volume", me );
airMopError( mop );
return 1;
}
ss = sx*sy*sz;
ten = AIR_CAST( float*, nten->data );
roi = AIR_CAST( float*, nroi->data );
/* NOTE: for time being the statistics are not weighted, because
nrrdMeasureLine[]( ) can't take a weight vector... */
/* find number of voxels in ROI */
roiVoxNum = 0;
for ( ii=0; ii<ss; ii++ ) {
roiVoxNum += ( roi[ii] > 0 );
}
/* fprintf( stderr, "%s: # voxels in ROI == %u\n", me, roiVoxNum ); */
/* allocate anisotropy buffers */
naniso = nrrdNew( );
airMopAdd( mop, naniso, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
anisoSize[0] = roiVoxNum;
anisoSize[1] = anisoTypeNum;
if ( nrrdMaybeAlloc_nva( naniso, nrrdTypeFloat, 2, anisoSize ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate aniso:\n%s\n", me, err );
airMopError( mop );
return 1;
}
aniso = AIR_CAST( float *, naniso->data );
/* store all anisotropies in all ROI voxels */
roiVoxIdx = 0;
for ( ii=0; ii<ss; ii++ ) {
if ( roi[ii] > 0 ) {
tenEigensolve_f( eval, NULL, ten + 7*ii );
for ( anisoTypeIdx=0; anisoTypeIdx<anisoTypeNum; anisoTypeIdx++ ) {
aniso[roiVoxIdx + roiVoxNum*anisoTypeIdx]
= tenAnisoEval_f( eval, anisoType[anisoTypeIdx] );
}
roiVoxIdx++;
}
}
printf( "statistic:" );
for ( anisoTypeIdx=0; anisoTypeIdx<anisoTypeNum; anisoTypeIdx++ ) {
printf( " %s", airEnumStr( tenAniso, anisoType[anisoTypeIdx] ) );
}
printf( "\n" );
/* do per-anisotropy statistics */
nstat = nrrdNew( );
airMopAdd( mop, nstat, AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
for ( statIdx=0; statIdx<statNum; statIdx++ ) {
printf( "%s:", airEnumStr( nrrdMeasure, measr[statIdx] ) );
if ( nrrdProject( nstat, naniso, 0, measr[statIdx], nrrdTypeFloat ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't measure:\n%s\n", me, err );
airMopError( mop );
return 1;
}
stat = AIR_CAST( float *, nstat->data );
for ( anisoTypeIdx=0; anisoTypeIdx<anisoTypeNum; anisoTypeIdx++ ) {
printf( " %g", stat[anisoTypeIdx] );
}
printf( "\n" );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info =
( "Oh, just fricken super! "
"Another stupid one-off program to make a thesis-related figure. "
"Reproducibility in visualization, yea, yea fricken great. " );
int
32 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int xi, yi, samp[2], fsd;
float *tdata, mrg, slp;
double x, xx, y,
mRot1[9], mRot2[9], mRot3[9],
mT[9], mR[9], mD[9], mRT[9],
rot1, rot2, rot3, theta, mean, var, varscl, radius;
Nrrd *nten;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "nx ny", "# samples", airTypeInt, 2, 2, samp, "90 90",
"number of samples along each edge of cube" );
hestOptAdd( &hopt, "mrg", "margin", airTypeFloat, 1, 1, &mrg, "0.11",
"margin above and below anisotropic samples" );
hestOptAdd( &hopt, "slp", "slope", airTypeFloat, 1, 1, &slp, "35",
"something about boundary between different shapes" );
hestOptAdd( &hopt, "fsd", NULL, airTypeInt, 0, 0, &fsd, NULL,
"use full \"space\" definition of orientation, instead of "
"the old simple per-axis spacing" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file to save tensors into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nten = nrrdNew( );
airMopAdd( mop, nten, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nten, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, samp[0] ),
AIR_CAST( size_t, samp[1] ),
AIR_CAST( size_t, 1 ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
mean = 0.333333333;
varscl = 0.045;
tdata = ( float* )nten->data;
for ( yi=0; yi<samp[1]; yi++ ) {
y = AIR_AFFINE( 0, yi, samp[1]-1, -mrg, 1+mrg );
theta = AIR_AFFINE( 0, AIR_CLAMP( 0.007, y, 0.993 ), 1, AIR_PI/3, 0 );
var = varscl*( airErf( y*slp )+airErf( ( 1-y )*slp ) )/2;
radius = sqrt( 2*var );
for ( xi=0; xi<samp[0]; xi++ ) {
x = AIR_AFFINE( 0, xi, samp[0]-1, 0, 3 );
ELL_3M_IDENTITY_SET( mD );
ELL_3M_DIAG_SET( mD,
mean + radius*cos( theta ),
mean + radius*cos( theta - 2*AIR_PI/3 ),
mean + radius*cos( theta + 2*AIR_PI/3 ) );
rot1 = rot2 = rot3 = 0;
if ( x < 1 ) {
xx = AIR_CLAMP( 0, x, 1 );
rot1 = AIR_PI*( 1-cos( AIR_PI*xx ) )/2;
} else if ( x < 2 ) {
xx = AIR_CLAMP( 0, x-1, 1 );
rot2 = AIR_PI*( 1-cos( AIR_PI*xx ) )/2;
} else {
xx = AIR_CLAMP( 0, x-2, 1 );
rot3 = AIR_PI*( 1-cos( AIR_PI*xx ) )/2;
}
/* set mRT, mR */
ELL_3M_ROTATE_X_SET( mRot1, rot1 );
ELL_3M_ROTATE_Y_SET( mRot2, rot2 );
ELL_3M_ROTATE_Z_SET( mRot3, rot3 );
ELL_3M_IDENTITY_SET( mR );
ell_3m_post_mul_d( mR, mRot1 );
ell_3m_post_mul_d( mR, mRot2 );
ell_3m_post_mul_d( mR, mRot3 );
ELL_3M_TRANSPOSE( mRT, mR );
ELL_3M_IDENTITY_SET( mT );
ell_3m_post_mul_d( mT, mRT );
ell_3m_post_mul_d( mT, mD );
ell_3m_post_mul_d( mT, mR );
tdata[0] = 1.0;
TEN_M2T( tdata, mT );
tdata += 7;
}
}
if ( fsd ) {
double orig[NRRD_SPACE_DIM_MAX], spcdir[NRRD_SPACE_DIM_MAX][4];
ELL_3V_SET( orig, 0, 0, 0 );
ELL_3V_SET( spcdir[0], AIR_NAN, AIR_NAN, AIR_NAN ); /* axis 0 is tensor */
ELL_3V_SET( spcdir[1], 1, 0, 0 );
ELL_3V_SET( spcdir[2], 0, 1, 0 );
ELL_3V_SET( spcdir[3], 0, 0, 1 );
nrrdSpaceSet( nten, nrrdSpace3DRightHanded );
nrrdAxisInfoSet_va( nten, nrrdAxisInfoSpaceDirection,
spcdir[0], spcdir[1], spcdir[2], spcdir[3] );
/* this should probably be set in any case, oh well */
nrrdAxisInfoSet_va( nten, nrrdAxisInfoCenter,
nrrdCenterUnknown,
nrrdCenterCell, nrrdCenterCell, nrrdCenterCell );
nrrdSpaceOriginSet( nten, orig );
} else {
nten->axis[1].spacing = 1.0;
nten->axis[2].spacing = 1.0;
nten->axis[3].spacing = 1.0;
}
if ( nrrdSave( outS, nten, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "tests anisotropy measures" );
int
29 main( int argc, char *argv[] ) {
char *me;
int aa, isot, equi;
unsigned int ii, nn;
double evalD[3], pp[3], qq[4], rot[9], mat1[9], mat2[9], tenD[7], tmp, mean;
double aniso[4];
float tenF[7], evalF[3];
AIR_UNUSED( argc );
me = argv[0];
nn = 10000000;
for ( ii=0; ii<nn; ii++ ) {
if ( 0 == ( ii % 1000 ) ) {
fprintf( stderr, "." ); fflush( stderr );
}
evalD[0] = airDrandMT( );
evalD[1] = airDrandMT( );
evalD[2] = airDrandMT( );
if ( 0 == airRandInt( 10 ) ) {
evalD[1] = evalD[0];
equi = AIR_TRUE;
if ( 0 == airRandInt( 2 ) ) {
evalD[2] = evalD[1] = evalD[0];
isot = AIR_TRUE;
if ( 0 == airRandInt( 2 ) ) {
evalD[2] = evalD[1] = evalD[0] = 0;
}
} else {
isot = AIR_FALSE;
}
} else {
equi = AIR_FALSE;
isot = AIR_FALSE;
}
pp[0] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0, -1.0, 1.0 );
pp[1] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0, -1.0, 1.0 );
pp[2] = AIR_AFFINE( 0.0, airDrandMT( ), 1.0, -1.0, 1.0 );
ELL_4V_SET( qq, 1, pp[0], pp[1], pp[2] );
ELL_4V_NORM( qq, qq, tmp );
ell_q_to_3m_d( rot, qq );
ELL_3M_ZERO_SET( mat1 );
ELL_3M_DIAG_SET( mat1, evalD[0], evalD[1], evalD[2] );
ell_3m_mul_d( mat2, rot, mat1 );
ELL_3M_TRANSPOSE_IP( rot, tmp );
ell_3m_mul_d( mat1, mat2, rot );
TEN_M2T( tenD, mat1 );
TEN_T_COPY( tenF, tenD );
tenEigensolve_d( evalD, NULL, tenD );
tenEigensolve_f( evalF, NULL, tenF );
for ( aa=tenAnisoUnknown+1; aa<tenAnisoLast; aa++ ) {
int bogus;
aniso[0] = tenAnisoEval_f( evalF, aa );
aniso[1] = tenAnisoEval_d( evalD, aa );
aniso[2] = tenAnisoTen_f( tenF, aa );
aniso[3] = tenAnisoTen_d( tenD, aa );
mean = ( aniso[0] + aniso[1] + aniso[2] + aniso[3] )/4;
tmp = ( ( aniso[0]-mean )*( aniso[0]-mean )
+ ( aniso[1]-mean )*( aniso[1]-mean )
+ ( aniso[2]-mean )*( aniso[2]-mean )
+ ( aniso[3]-mean )*( aniso[3]-mean ) )/4;
bogus = ( tenAniso_Mode == aa
|| tenAniso_Th == aa
|| tenAniso_Skew == aa
|| tenAniso_Ct1 == aa
|| tenAniso_Ct2 == aa );
if ( AIR_EXISTS( tmp ) && bogus && isot ) {
continue;
}
if ( AIR_EXISTS( tmp ) && bogus && equi && tmp < 0.001 ) {
continue;
}
if ( !AIR_EXISTS( tmp ) || tmp > 0.0000003 ) {
fprintf( stderr, "\n%s: %u %d ( %s ) ( isot %s, equi %s ) tmp=%g\n",
me, ii, aa,
airEnumStr( tenAniso, aa ),
isot ? "true" : "false",
equi ? "true" : "false", tmp );
fprintf( stderr, " %g %g %g %g %g %g;\n",
tenD[1], tenD[2], tenD[3], tenD[4], tenD[5], tenD[6] );
fprintf( stderr, " %f %f %f ( %f %f %f )f --->\n",
evalD[0], evalD[1], evalD[2],
evalF[0], evalF[1], evalF[2] );
fprintf( stderr, " %f %f %f %f\n",
aniso[0], aniso[1], aniso[2], aniso[3] );
exit( 1 );
}
}
}
fprintf( stderr, "\n" );
return 0;
}
double mean, norm, rnorm, Q, R, QQQ, D, theta,
M00, M01, M02, M11, M12, M22;
double epsilon = 1.0E-12;
int roots;
/* copy the given matrix elements */
7 M00 = _M00;
8 M01 = _M01;
M02 = _M02;
M11 = _M11;
M12 = _M12;
M22 = _M22;
/*
** subtract out the eigenvalue mean ( will add back to evals later );
** helps with numerical stability
*/
mean = ( M00 + M11 + M22 )/3.0;
M00 -= mean;
M11 -= mean;
M22 -= mean;
/*
** divide out L2 norm of eigenvalues ( will multiply back later );
** this too seems to help with stability
*/
norm = sqrt( M00*M00 + 2*M01*M01 + 2*M02*M02 +
M11*M11 + 2*M12*M12 +
M22*M22 );
rnorm = norm ? 1.0/norm : 1.0;
M00 *= rnorm;
M01 *= rnorm;
M02 *= rnorm;
M11 *= rnorm;
M12 *= rnorm;
M22 *= rnorm;
/* this code is a mix of prior Teem code and ideas from Eberly's
"Eigensystems for 3 x 3 Symmetric Matrices ( Revisited )" */
Q = ( M01*M01 + M02*M02 + M12*M12 - M00*M11 - M00*M22 - M11*M22 )/3.0;
QQQ = Q*Q*Q;
R = ( M00*M11*M22 + M02*( 2*M01*M12 - M02*M11 )
- M00*M12*M12 - M01*M01*M22 )/2.0;
D = QQQ - R*R;
if ( D > epsilon ) {
/* three distinct roots- this is the most common case */
double mm, ss, cc;
theta = atan2( sqrt( D ), R )/3.0;
mm = sqrt( Q );
ss = sin( theta );
cc = cos( theta );
eval[0] = 2*mm*cc;
eval[1] = mm*( -cc + sqrt( 3.0 )*ss );
eval[2] = mm*( -cc - sqrt( 3.0 )*ss );
roots = ROOT_THREE;
/* else D is near enough to zero */
} else if ( R < -epsilon || epsilon < R ) {
double U;
/* one double root and one single root */
U = airCbrt( R ); /* cube root function */
if ( U > 0 ) {
eval[0] = 2*U;
eval[1] = -U;
eval[2] = -U;
} else {
eval[0] = -U;
eval[1] = -U;
eval[2] = 2*U;
}
roots = ROOT_SINGLE_DOUBLE;
} else {
/* a triple root! */
eval[0] = eval[1] = eval[2] = 0.0;
roots = ROOT_TRIPLE;
}
/* multiply back by eigenvalue L2 norm */
3 eval[0] /= rnorm;
4 eval[1] /= rnorm;
eval[2] /= rnorm;
/* add back in the eigenvalue mean */
eval[0] += mean;
eval[1] += mean;
eval[2] += mean;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009, University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "tests tenEigensolve_d and new stand-alone function." );
#define ROOT_TRIPLE 2 /* ell_cubic_root_triple */
#define ROOT_SINGLE_DOUBLE 3 /* ell_cubic_root_single_double */
#define ROOT_THREE 4 /* ell_cubic_root_three */
#define ABS( a ) ( ( ( a ) > 0.0f ? ( a ) : -( a ) ) )
#define VEC_SET( v, a, b, c ) \
( ( v )[0] = ( a ), ( v )[1] = ( b ), ( v )[2] = ( c ) )
#define VEC_DOT( v1, v2 ) \
( ( v1 )[0]*( v2 )[0] + ( v1 )[1]*( v2 )[1] + ( v1 )[2]*( v2 )[2] )
#define VEC_CROSS( v3, v1, v2 ) \
( ( v3 )[0] = ( v1 )[1]*( v2 )[2] - ( v1 )[2]*( v2 )[1], \
( v3 )[1] = ( v1 )[2]*( v2 )[0] - ( v1 )[0]*( v2 )[2], \
( v3 )[2] = ( v1 )[0]*( v2 )[1] - ( v1 )[1]*( v2 )[0] )
#define VEC_ADD( v1, v2 ) \
( ( v1 )[0] += ( v2 )[0], \
( v1 )[1] += ( v2 )[1], \
( v1 )[2] += ( v2 )[2] )
#define VEC_SUB( v1, v2 ) \
( ( v1 )[0] -= ( v2 )[0], \
( v1 )[1] -= ( v2 )[1], \
( v1 )[2] -= ( v2 )[2] )
#define VEC_SCL( v1, s ) \
( ( v1 )[0] *= ( s ), \
52 ( v1 )[1] *= ( s ), \
( v1 )[2] *= ( s ) )
#define VEC_LEN( v ) ( sqrt( VEC_DOT( v, v ) ) )
#define VEC_NORM( v, len ) ( ( len ) = VEC_LEN( v ), VEC_SCL( v, 1.0/len ) )
#define VEC_SCL_SUB( v1, s, v2 ) \
( ( v1 )[0] -= ( s )*( v2 )[0], \
( v1 )[1] -= ( s )*( v2 )[1], \
( v1 )[2] -= ( s )*( v2 )[2] )
#define VEC_COPY( v1, v2 ) \
( ( v1 )[0] = ( v2 )[0], \
( v1 )[1] = ( v2 )[1], \
( v1 )[2] = ( v2 )[2] )
/*
** All the three given vectors span only a 2D space, and this finds
** the normal to that plane. Simply sums up all the pair-wise
** cross-products to get a good estimate. Trick is getting the cross
** products to line up before summing.
*/
void
nullspace1( double ret[3],
const double r0[3], const double r1[3], const double r2[3] ) {
double crs[3];
/* ret = r0 x r1 */
VEC_CROSS( ret, r0, r1 );
/* crs = r1 x r2 */
VEC_CROSS( crs, r1, r2 );
/* ret += crs or ret -= crs; whichever makes ret longer */
if ( VEC_DOT( ret, crs ) > 0 ) {
VEC_ADD( ret, crs );
83 } else {
VEC_SUB( ret, crs );
}
/* crs = r0 x r2 */
VEC_CROSS( crs, r0, r2 );
/* ret += crs or ret -= crs; whichever makes ret longer */
if ( VEC_DOT( ret, crs ) > 0 ) {
VEC_ADD( ret, crs );
} else {
VEC_SUB( ret, crs );
}
return;
}
/*
** All vectors are in the same 1D space, we have to find two
** mutually vectors perpendicular to that span
*/
void
nullspace2( double reta[3], double retb[3],
const double r0[3], const double r1[3], const double r2[3] ) {
double sqr[3], sum[3];
int idx;
VEC_COPY( sum, r0 );
if ( VEC_DOT( sum, r1 ) > 0 ) {
VEC_ADD( sum, r1 );
} else {
VEC_SUB( sum, r1 );
}
if ( VEC_DOT( sum, r2 ) > 0 ) {
VEC_ADD( sum, r2 );
} else {
VEC_SUB( sum, r2 );
}
/* find largest component, to get most stable expression for a
perpendicular vector */
sqr[0] = sum[0]*sum[0];
sqr[1] = sum[1]*sum[1];
sqr[2] = sum[2]*sum[2];
idx = 0;
if ( sqr[0] < sqr[1] )
idx = 1;
if ( sqr[idx] < sqr[2] )
idx = 2;
/* reta will be perpendicular to sum */
if ( 0 == idx ) {
VEC_SET( reta, sum[1] - sum[2], -sum[0], sum[0] );
} else if ( 1 == idx ) {
VEC_SET( reta, -sum[1], sum[0] - sum[2], sum[1] );
} else {
VEC_SET( reta, -sum[2], sum[2], sum[0] - sum[1] );
}
/* and now retb will be perpendicular to both reta and sum */
VEC_CROSS( retb, reta, sum );
return;
}
/*
** Eigensolver for symmetric 3x3 matrix:
**
** M00 M01 M02
** M01 M11 M12
** M02 M12 M22
**
** Must be passed eval[3] vector, and will compute eigenvalues
** only if evec[9] is non-NULL. Computed eigenvectors are at evec+0,
** evec+3, and evec+6.
**
** Return value indicates something about the eigenvalue solution to
** the cubic characteristic equation; see ROOT_ #defines above
**
** Relies on the ABS and VEC_* macros above, as well as math functions
157 ** atan2( ), sin( ), cos( ), sqrt( ), and airCbrt( ), defined as:
double
airCbrt( double v ) {
#if defined( _WIN32 ) || defined( __STRICT_ANSI__ )
return ( v < 0.0 ? -pow( -v, 1.0/3.0 ) : pow( v, 1.0/3.0 ) );
#else
return cbrt( v );
#endif
}
**
** HEY: the numerical precision issues here are very subtle, and
170 ** merit some more scrutiny. With evals ( 1.000001, 1, 1 ), for example,
** whether it comes back as a single/double root, vs three distinct roots,
** is determines by the comparison between "D" and "epsilon", and the
** setting of epsilon seems pretty arbitrary at this point...
**
*/
int
evals( double eval[3],
const double _M00, const double _M01, const double _M02,
const double _M11, const double _M12,
const double _M22 ) {
#include "teigen-evals-A.c"
#include "teigen-evals-B.c"
return roots;
}
int
evals_evecs( double eval[3], double evec[9],
const double _M00, const double _M01, const double _M02,
const double _M11, const double _M12,
const double _M22 ) {
double r0[3], r1[3], r2[3], crs[3], len, dot;
double mean, norm, rnorm, Q, R, QQQ, D, theta,
M00, M01, M02, M11, M12, M22;
double epsilon = 1.0E-12;
int roots;
/* copy the given matrix elements */
M00 = _M00;
M01 = _M01;
M02 = _M02;
M11 = _M11;
M12 = _M12;
M22 = _M22;
/*
** subtract out the eigenvalue mean ( will add back to evals later );
** helps with numerical stability
*/
mean = ( M00 + M11 + M22 )/3.0;
M00 -= mean;
M11 -= mean;
M22 -= mean;
/*
** divide out L2 norm of eigenvalues ( will multiply back later );
** this too seems to help with stability
*/
norm = sqrt( M00*M00 + 2*M01*M01 + 2*M02*M02 +
M11*M11 + 2*M12*M12 +
M22*M22 );
rnorm = norm ? 1.0/norm : 1.0;
M00 *= rnorm;
M01 *= rnorm;
M02 *= rnorm;
M11 *= rnorm;
M12 *= rnorm;
M22 *= rnorm;
/* this code is a mix of prior Teem code and ideas from Eberly's
"Eigensystems for 3 x 3 Symmetric Matrices ( Revisited )" */
Q = ( M01*M01 + M02*M02 + M12*M12 - M00*M11 - M00*M22 - M11*M22 )/3.0;
QQQ = Q*Q*Q;
R = ( M00*M11*M22 + M02*( 2*M01*M12 - M02*M11 )
- M00*M12*M12 - M01*M01*M22 )/2.0;
D = QQQ - R*R;
if ( D > epsilon ) {
/* three distinct roots- this is the most common case */
double mm, ss, cc;
theta = atan2( sqrt( D ), R )/3.0;
mm = sqrt( Q );
ss = sin( theta );
cc = cos( theta );
eval[0] = 2*mm*cc;
eval[1] = mm*( -cc + sqrt( 3.0 )*ss );
eval[2] = mm*( -cc - sqrt( 3.0 )*ss );
roots = ROOT_THREE;
/* else D is near enough to zero */
} else if ( R < -epsilon || epsilon < R ) {
double U;
/* one double root and one single root */
U = airCbrt( R ); /* cube root function */
if ( U > 0 ) {
eval[0] = 2*U;
eval[1] = -U;
eval[2] = -U;
} else {
eval[0] = -U;
eval[1] = -U;
eval[2] = 2*U;
}
roots = ROOT_SINGLE_DOUBLE;
} else {
/* a triple root! */
eval[0] = eval[1] = eval[2] = 0.0;
roots = ROOT_TRIPLE;
}
/* r0, r1, r2 are the vectors we manipulate to
find the nullspaces of M - lambda*I */
VEC_SET( r0, 0.0, M01, M02 );
VEC_SET( r1, M01, 0.0, M12 );
VEC_SET( r2, M02, M12, 0.0 );
if ( ROOT_THREE == roots ) {
r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
nullspace1( evec+0, r0, r1, r2 );
r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1];
nullspace1( evec+3, r0, r1, r2 );
r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2];
nullspace1( evec+6, r0, r1, r2 );
} else if ( ROOT_SINGLE_DOUBLE == roots ) {
if ( eval[1] == eval[2] ) {
/* one big ( eval[0] ) , two small ( eval[1, 2] ) */
r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
nullspace1( evec+0, r0, r1, r2 );
r0[0] = M00 - eval[1]; r1[1] = M11 - eval[1]; r2[2] = M22 - eval[1];
nullspace2( evec+3, evec+6, r0, r1, r2 );
}
else {
/* two big ( eval[0, 1] ), one small ( eval[2] ) */
r0[0] = M00 - eval[0]; r1[1] = M11 - eval[0]; r2[2] = M22 - eval[0];
nullspace2( evec+0, evec+3, r0, r1, r2 );
r0[0] = M00 - eval[2]; r1[1] = M11 - eval[2]; r2[2] = M22 - eval[2];
nullspace1( evec+6, r0, r1, r2 );
}
} else {
/* ROOT_TRIPLE == roots; use any basis for eigenvectors */
VEC_SET( evec+0, 1, 0, 0 );
VEC_SET( evec+3, 0, 1, 0 );
VEC_SET( evec+6, 0, 0, 1 );
}
/* we always make sure its really orthonormal; keeping fixed the
eigenvector associated with the largest-magnitude eigenvalue */
if ( ABS( eval[0] ) > ABS( eval[2] ) ) {
/* normalize evec+0 but don't move it */
VEC_NORM( evec+0, len );
dot = VEC_DOT( evec+0, evec+3 ); VEC_SCL_SUB( evec+3, dot, evec+0 );
VEC_NORM( evec+3, len );
dot = VEC_DOT( evec+0, evec+6 ); VEC_SCL_SUB( evec+6, dot, evec+0 );
dot = VEC_DOT( evec+3, evec+6 ); VEC_SCL_SUB( evec+6, dot, evec+3 );
VEC_NORM( evec+6, len );
} else {
/* normalize evec+6 but don't move it */
VEC_NORM( evec+6, len );
dot = VEC_DOT( evec+6, evec+3 ); VEC_SCL_SUB( evec+3, dot, evec+6 );
VEC_NORM( evec+3, len );
dot = VEC_DOT( evec+3, evec+0 ); VEC_SCL_SUB( evec+0, dot, evec+3 );
dot = VEC_DOT( evec+6, evec+0 ); VEC_SCL_SUB( evec+0, dot, evec+6 );
VEC_NORM( evec+0, len );
}
/* to be nice, make it right-handed */
326 VEC_CROSS( crs, evec+0, evec+3 );
if ( 0 > VEC_DOT( crs, evec+6 ) ) {
VEC_SCL( evec+6, -1 );
}
/* multiply back by eigenvalue L2 norm */
eval[0] /= rnorm;
eval[1] /= rnorm;
eval[2] /= rnorm;
/* add back in the eigenvalue mean */
eval[0] += mean;
eval[1] += mean;
eval[2] += mean;
return roots;
}
void
testeigen( double tt[7], double eval[3], double evec[9] ) {
double mat[9], dot[3], cross[3];
unsigned int ii;
TEN_T2M( mat, tt );
printf( "evals %g %g %g\n", eval[0], eval[1], eval[2] );
printf( "evec0 ( %g ) %g %g %g\n",
ELL_3V_LEN( evec + 0 ), evec[0], evec[1], evec[2] );
printf( "evec1 ( %g ) %g %g %g\n",
ELL_3V_LEN( evec + 3 ), evec[3], evec[4], evec[5] );
printf( "evec2 ( %g ) %g %g %g\n",
357 ELL_3V_LEN( evec + 6 ), evec[6], evec[7], evec[8] );
printf( "Mv - lv: ( len ) X Y Z ( should be ~zeros )\n" );
for ( ii=0; ii<3; ii++ ) {
double uu[3], vv[3], dd[3];
ELL_3MV_MUL( uu, mat, evec + 3*ii );
ELL_3V_SCALE( vv, eval[ii], evec + 3*ii );
ELL_3V_SUB( dd, uu, vv );
printf( "%d: ( %g ) %g %g %g\n", ii, ELL_3V_LEN( dd ), dd[0], dd[1], dd[2] );
}
dot[0] = ELL_3V_DOT( evec + 0, evec + 3 );
dot[1] = ELL_3V_DOT( evec + 0, evec + 6 );
dot[2] = ELL_3V_DOT( evec + 3, evec + 6 );
printf( "pairwise dots: ( %g ) %g %g %g\n",
ELL_3V_LEN( dot ), dot[0], dot[1], dot[2] );
ELL_3V_CROSS( cross, evec+0, evec+3 );
printf( "right-handed: %g\n", ELL_3V_DOT( evec+6, cross ) );
return;
}
int
main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
double _tt[6], tt[7], ss, pp[3], qq[4], rot[9], mat1[9], mat2[9], tmp,
evalA[3], evecA[9], evalB[3], evecB[9];
int roots;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, NULL, "m00 m01 m02 m11 m12 m22",
airTypeDouble, 6, 6, _tt, NULL, "symmtric matrix coeffs" );
hestOptAdd( &hopt, "p", "vec", airTypeDouble, 3, 3, pp, "0 0 0",
"rotation as P vector" );
hestOptAdd( &hopt, "s", "scl", airTypeDouble, 1, 1, &ss, "1.0",
"scaling" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
ELL_6V_COPY( tt + 1, _tt );
tt[0] = 1.0;
TEN_T_SCALE( tt, ss, tt );
ELL_4V_SET( qq, 1, pp[0], pp[1], pp[2] );
ELL_4V_NORM( qq, qq, tmp );
ell_q_to_3m_d( rot, qq );
printf( "%s: rot\n", me );
printf( " %g %g %g\n", rot[0], rot[1], rot[2] );
printf( " %g %g %g\n", rot[3], rot[4], rot[5] );
printf( " %g %g %g\n", rot[6], rot[7], rot[8] );
TEN_T2M( mat1, tt );
ell_3m_mul_d( mat2, rot, mat1 );
ELL_3M_TRANSPOSE_IP( rot, tmp );
ell_3m_mul_d( mat1, mat2, rot );
TEN_M2T( tt, mat1 );
printf( "input matrix = \n %g %g %g\n %g %g\n %g\n",
tt[1], tt[2], tt[3], tt[4], tt[5], tt[6] );
printf( "================== tenEigensolve_d ==================\n" );
roots = tenEigensolve_d( evalA, evecA, tt );
printf( "%s roots\n", airEnumStr( ell_cubic_root, roots ) );
testeigen( tt, evalA, evecA );
printf( "================== new eigensolve ==================\n" );
roots = evals( evalB, tt[1], tt[2], tt[3], tt[4], tt[5], tt[6] );
printf( "%s roots: %g %g %g\n", airEnumStr( ell_cubic_root, roots ),
evalB[0], evalB[1], evalB[2] );
roots = evals_evecs( evalB, evecB,
tt[1], tt[2], tt[3], tt[4], tt[5], tt[6] );
printf( "%s roots\n", airEnumStr( ell_cubic_root, roots ) );
testeigen( tt, evalB, evecB );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Test EM bimodal histogram fitting." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
Nrrd *nhisto;
tenEMBimodalParm *biparm;
double minprob[2];
mop = airMopNew( );
me = argv[0];
biparm = tenEMBimodalParmNew( );
airMopAdd( mop, biparm, ( airMopper )tenEMBimodalParmNix, airMopAlways );
hestOptAdd( &hopt, NULL, "histogram", airTypeOther, 1, 1, &nhisto, NULL,
"The 1-D histogram to analyize", NULL, NULL, nrrdHestNrrd );
hestOptAdd( &hopt, "ts", "two stage", airTypeInt, 0, 0,
&( biparm->twoStage ), NULL,
"use two-stage processing" );
hestOptAdd( &hopt, "v", "verbose", airTypeInt, 1, 1, &( biparm->verbose ), "1",
"verbosity level" );
hestOptAdd( &hopt, "mp", "minprob 1, 2", airTypeDouble, 2, 2, minprob, "0 0",
"minimum significant posterior probabilies, for first and "
"second stages" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
biparm->minProb = minprob[0];
biparm->minProb2 = minprob[1];
if ( tenEMBimodal( biparm, nhisto ) ) {
airMopAdd( mop, err = biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing fitting:\n%s", me, err );
airMopError( mop );
return 1;
}
fprintf( stderr, "%s: bimodal histogram fit\n", me );
fprintf( stderr, "material 1 ( %g%% ): mean = %g, stdv = %g\n",
100*( biparm->fraction1 ), biparm->mean1, biparm->stdv1 );
fprintf( stderr, "material 2 ( %g%% ): mean = %g, stdv = %g\n",
100*( 1 - biparm->fraction1 ), biparm->mean2, biparm->stdv2 );
fprintf( stderr, " ---> optimal threshold = %g ( confidence = %g )\n",
biparm->threshold, biparm->confidence );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Compute the makings of a new tensor.dat file." );
int
29 main( int argc, const char *argv[] ) {
const char *me;
char *err;
hestOpt *hopt=NULL;
airArray *mop;
int E, optimizeEdge, workToDo;
unsigned int ii, numRange[2], seedRange[2], seed, seedDone;
double *log, minAngle, minEdge, pot, potNorm, time0, time1;
char *outStr, logFilename[AIR_STRLEN_MED], gradFilename[AIR_STRLEN_MED],
keyStr[AIR_STRLEN_MED], valStr[AIR_STRLEN_MED];
tenGradientParm *tgparm;
Nrrd *nlog, *ngrad;
size_t size[2];
mop = airMopNew( );
tgparm = tenGradientParmNew( );
airMopAdd( mop, tgparm, ( airMopper )tenGradientParmNix, airMopAlways );
tgparm->single = AIR_FALSE;
tgparm->snap = 0;
tgparm->minMeanImprovement = 0.0;
nlog = nrrdNew( );
airMopAdd( mop, nlog, ( airMopper )nrrdNuke, airMopAlways );
ngrad = nrrdNew( );
airMopAdd( mop, ngrad, ( airMopper )nrrdNuke, airMopAlways );
me = argv[0];
hestOptAdd( &hopt, "num", "min max", airTypeUInt, 2, 2, numRange, "6 129",
"range of number of gradients to be computed" );
hestOptAdd( &hopt, "seed", "min max", airTypeUInt, 2, 2, seedRange, "1 0",
"range of seed values to use with the RNG. Using max lower "
"than min means that the seed values should be increased "
"( and used for computation ) without bound" );
hestOptAdd( &hopt, "p", "exponent", airTypeUInt, 1, 1, &( tgparm->expo ), "1",
"the exponent p that defines the 1/r^p potential energy "
"( Coulomb is 1 )" );
hestOptAdd( &hopt, "step", "step", airTypeDouble, 1, 1, &( tgparm->initStep ),
"1", "time increment in solver" );
hestOptAdd( &hopt, "miniter", "# iters", airTypeInt, 1, 1,
&( tgparm->minIteration ), "0",
"required minimum number of simulation iterations" );
hestOptAdd( &hopt, "maxiter", "# iters", airTypeInt, 1, 1,
&( tgparm->maxIteration ), "1000000",
"max number of simulations iterations" );
hestOptAdd( &hopt, "minvelo", "vel", airTypeDouble, 1, 1,
&( tgparm->minVelocity ), "0.00000000001",
"low threshold on mean velocity of repelling points, "
"at which point repulsion phase of algorithm terminates." );
hestOptAdd( &hopt, "dp", "potential change", airTypeDouble, 1, 1,
&( tgparm->minPotentialChange ), "0.00000000001",
"low threshold on fractional change of potential at "
"which point repulsion phase of algorithm terminates." );
hestOptAdd( &hopt, "minimprov", "delta", airTypeDouble, 1, 1,
&( tgparm->minMeanImprovement ), "0.00005",
"in the second phase of the algorithm, "
"when stochastically balancing the sign of the gradients, "
"the ( small ) improvement in length of mean gradient "
"which triggers termination ( as further improvements "
"are unlikely. " );
hestOptAdd( &hopt, "minmean", "len", airTypeDouble, 1, 1,
&( tgparm->minMean ), "0.0005",
"if length of mean gradient falls below this, finish "
"the balancing phase" );
hestOptAdd( &hopt, "oe", NULL, airTypeInt, 0, 0, &optimizeEdge, NULL,
"optimize for the maximal minimal edge length, "
"instead of potential." );
hestOptAdd( &hopt, "odir", "out", airTypeString, 1, 1, &outStr, ".",
"output directory for all grad files and logs, you should "
"leave off the trailing /" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( 0 == seedRange[0] ) {
fprintf( stderr, "%s: sorry, initial seed must be non-zero\n", me );
airMopError( mop ); return 1;
}
if ( !( numRange[0] <= numRange[1]
&& numRange[0] >= 6 ) ) {
fprintf( stderr, "%s: number range [%u, %u] invalid\n", me,
numRange[0], numRange[1] );
airMopError( mop ); return 1;
}
/* in master log ( per gradient set ):
0: # grads
1: last seed tried
2: seed of best so far
3: best phi, not normalized
4: best phi, normalized
5: best edge min
6: ideal edge
7: iters used
8: time used ( in seconds )
*/
/* see if we can open the log */
sprintf( logFilename, "%s/000-%04u-log.nrrd", outStr, tgparm->expo );
if ( nrrdLoad( nlog, logFilename, NULL ) ) {
/* no, we couldn't load it, and we don't care why */
free( biffGetDone( NRRD ) );
/* create a log nrrd of the correct size */
size[0] = 9;
size[1] = numRange[1]+1;
if ( nrrdMaybeAlloc_nva( nlog, nrrdTypeDouble, 2, size ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making log:\n%s\n", me, err );
airMopError( mop ); return 1;
}
} else {
/* we could open the log, see if its the right size */
if ( !( nrrdTypeDouble == nlog->type
&& 2 == nlog->dim
&& 9 == nlog->axis[0].size
&& numRange[1]+1 == nlog->axis[1].size ) ) {
fprintf( stderr, "%s: given log ( %s %u-D %ux%ux? ) doesn't match "
"desired ( %s 2-D 9x%u )\n", me,
airEnumStr( nrrdType, nlog->type ),
nlog->dim,
AIR_CAST( unsigned int, nlog->axis[0].size ),
AIR_CAST( unsigned int, nlog->axis[1].size ),
airEnumStr( nrrdType, nrrdTypeDouble ),
numRange[1]+1 );
airMopError( mop ); return 1;
}
}
/* nlog is the right size */
/* initialize log's first column and key/value pairs, and ( re )save */
log = AIR_CAST( double *, nlog->data );
for ( ii=numRange[0]; ii<=numRange[1]; ii++ ) {
log[0 + 9*ii] = ii;
}
E = 0;
if ( !E ) strcpy( keyStr, "maxiter" );
if ( !E ) sprintf( valStr, "%d", tgparm->maxIteration );
if ( !E ) E |= nrrdKeyValueAdd( nlog, keyStr, valStr );
if ( !E ) strcpy( keyStr, "step" );
if ( !E ) sprintf( valStr, "%g", tgparm->initStep );
if ( !E ) E |= nrrdKeyValueAdd( nlog, keyStr, valStr );
if ( !E ) strcpy( keyStr, "dp" );
if ( !E ) sprintf( valStr, "%g", tgparm->minPotentialChange );
if ( !E ) E |= nrrdKeyValueAdd( nlog, keyStr, valStr );
if ( !E ) strcpy( keyStr, "minvelo" );
if ( !E ) sprintf( valStr, "%g", tgparm->minVelocity );
if ( !E ) E |= nrrdKeyValueAdd( nlog, keyStr, valStr );
if ( !E ) strcpy( keyStr, "minmean" );
if ( !E ) sprintf( valStr, "%g", tgparm->minMean );
if ( !E ) E |= nrrdKeyValueAdd( nlog, keyStr, valStr );
if ( !E ) E |= nrrdSave( logFilename, nlog, NULL );
if ( E ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing log:\n%s\n", me, err );
airMopError( mop ); return 1;
}
/* in master log ( per gradient set ):
0: # grads
1: last seed for which solution was computed
2: seed of best solution so far
3: best phi, not edge normalized
4: best phi, edge normalized
5: best minimum edge length
6: ideal edge length
7: iters used
8: time used ( in seconds )
*/
workToDo = AIR_FALSE;
for ( seed=seedRange[0];
seedRange[1] < seedRange[0] || seed <= seedRange[1];
seed++ ) {
for ( ii=numRange[0]; ii<=numRange[1]; ii++ ) {
seedDone = AIR_CAST( unsigned int, log[1 + 9*ii] );
/* if no seeds have been tried, seedDone will be zero */
if ( seedDone >= seed ) {
/* have already tried this seed, move on */
continue;
}
workToDo = AIR_TRUE;
tgparm->seed = seed;
fprintf( stderr, "%s ================ %u %u\n", me, ii, tgparm->seed );
time0 = airTime( );
if ( tenGradientGenerate( ngrad, ii, tgparm ) ) {
airMopAdd( mop, err=biffGetDone( TEN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making distribution:\n%s\n", me, err );
airMopError( mop ); return 1;
}
time1 = airTime( );
tenGradientMeasure( &potNorm, &minAngle, &minEdge,
ngrad, tgparm, AIR_TRUE );
if ( !seedDone
|| ( ( optimizeEdge && minEdge > log[5 + 9*ii] )
|| potNorm < log[4 + 9*ii] ) ) {
/* this gradient set is best so far */
tenGradientMeasure( &pot, NULL, NULL, ngrad, tgparm, AIR_FALSE );
log[2 + 9*ii] = tgparm->seed;
log[3 + 9*ii] = pot;
log[4 + 9*ii] = potNorm;
log[5 + 9*ii] = minEdge;
log[6 + 9*ii] = tenGradientIdealEdge( ii, AIR_FALSE );
log[7 + 9*ii] = tgparm->itersUsed;
log[8 + 9*ii] = time1 - time0;
sprintf( gradFilename, "%s/%03u-%04u.nrrd", outStr, ii, tgparm->expo );
if ( nrrdSave( gradFilename, ngrad, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
log[1 + 9*ii] = tgparm->seed;
if ( nrrdSave( logFilename, nlog, NULL ) ) {
airMopAdd( mop, err=biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing log:\n%s\n", me, err );
airMopError( mop ); return 1;
}
}
}
if ( !workToDo ) {
fprintf( stderr, "%s: apparently finished requested computations.\n", me );
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Sample space of tensor shape." );
void
29 _clp2xyz( double xyz[3], double clp[2] ) {
double cl, cp, cs;
cl = clp[0];
cp = clp[1];
cs = 1 - cl - cp;
xyz[0] = cs*0.7 + cl*1.0 + cp*0.75;
xyz[1] = cs*0.7 + cl*0.0 + cp*0.75;
xyz[2] = cs*0.7 + cl*0.0 + cp*0.00;
}
void
41 washQtoM3( double m[9], double q[4] ) {
double p[4], w, x, y, z, len;
ELL_4V_COPY( p, q );
len = ELL_4V_LEN( p );
ELL_4V_SCALE( p, 1.0/len, p );
w = p[0];
x = p[1];
y = p[2];
z = p[3];
/* mathematica work implies that we should be
setting ROW vectors here */
ELL_3V_SET( m+0,
1 - 2*( y*y + z*z ),
2*( x*y - w*z ),
2*( x*z + w*y ) );
ELL_3V_SET( m+3,
2*( x*y + w*z ),
1 - 2*( x*x + z*z ),
2*( y*z - w*x ) );
ELL_3V_SET( m+6,
2*( x*z - w*y ),
2*( y*z + w*x ),
1 - 2*( x*x + y*y ) );
}
int
68 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int xi, yi, zi, samp;
float *tdata;
double clp[2], xyz[3], q[4], len;
double mD[9], mRF[9], mRI[9], mT[9];
Nrrd *nten;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "n", "# samples", airTypeInt, 1, 1, &samp, "4",
"number of samples along each edge of cube" );
hestOptAdd( &hopt, "c", "cl cp", airTypeDouble, 2, 2, clp, NULL,
"shape of tensor to use; \"cl\" and \"cp\" are cl1 "
"and cp1 values, both in [0.0, 1.0]" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file to save tensors into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nten = nrrdNew( );
airMopAdd( mop, nten, ( airMopper )nrrdNuke, airMopAlways );
_clp2xyz( xyz, clp );
fprintf( stderr, "%s: want evals = %g %g %g\n", me, xyz[0], xyz[1], xyz[2] );
if ( nrrdMaybeAlloc_va( nten, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, samp ),
AIR_CAST( size_t, samp ),
AIR_CAST( size_t, samp ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
ELL_3M_IDENTITY_SET( mD );
ELL_3M_DIAG_SET( mD, xyz[0], xyz[1], xyz[2] );
tdata = ( float* )nten->data;
for ( zi=0; zi<samp; zi++ ) {
for ( yi=0; yi<samp; yi++ ) {
for ( xi=0; xi<samp; xi++ ) {
q[0] = 1.0;
q[1] = AIR_AFFINE( -0.5, ( float )xi, samp-0.5, -1, 1 );
q[2] = AIR_AFFINE( -0.5, ( float )yi, samp-0.5, -1, 1 );
q[3] = AIR_AFFINE( -0.5, ( float )zi, samp-0.5, -1, 1 );
len = ELL_4V_LEN( q );
ELL_4V_SCALE( q, 1.0/len, q );
washQtoM3( mRF, q );
ELL_3M_TRANSPOSE( mRI, mRF );
ELL_3M_IDENTITY_SET( mT );
ell_3m_post_mul_d( mT, mRI );
ell_3m_post_mul_d( mT, mD );
ell_3m_post_mul_d( mT, mRF );
tdata[0] = 1.0;
TEN_M2T( tdata, mT );
tdata += 7;
}
}
}
if ( nrrdSave( outS, nten, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Sample space of tensor orientation." );
void
29 washQtoM3( double m[9], double q[4] ) {
double p[4], w, x, y, z, len;
ELL_4V_COPY( p, q );
len = ELL_4V_LEN( p );
ELL_4V_SCALE( p, 1.0/len, p );
w = p[0];
x = p[1];
y = p[2];
z = p[3];
/* mathematica work implies that we should be
setting ROW vectors here */
ELL_3V_SET( m+0,
1 - 2*( y*y + z*z ),
2*( x*y - w*z ),
2*( x*z + w*y ) );
ELL_3V_SET( m+3,
2*( x*y + w*z ),
1 - 2*( x*x + z*z ),
2*( y*z - w*x ) );
ELL_3V_SET( m+6,
2*( x*z - w*y ),
2*( y*z + w*x ),
1 - 2*( x*x + y*y ) );
}
int
56 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int xi, yi, zi, sz;
float *tdata;
double q[4];
double mD[9], mRF[9], mRI[9], mT[9], eval[3], len;
Nrrd *nten;
size_t size[4];
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "s", "size", airTypeInt, 1, 1, &sz, "4",
"number of samples along each edge of cube" );
hestOptAdd( &hopt, "eval", "l1 l2 l3", airTypeDouble, 3, 3, eval, "0.8 0.1 0.1",
"eigenvalues" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file to save tensors into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nten = nrrdNew( );
airMopAdd( mop, nten, ( airMopper )nrrdNuke, airMopAlways );
size[0] = 7;
size[1] = sz;
size[2] = sz;
size[3] = sz;
if ( nrrdMaybeAlloc_nva( nten, nrrdTypeFloat, 4, size ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
tdata = AIR_CAST( float*, nten->data );
for ( zi=0; zi<sz; zi++ ) {
for ( yi=0; yi<sz; yi++ ) {
for ( xi=0; xi<sz; xi++ ) {
q[0] = 1.0;
q[1] = AIR_AFFINE( -0.5, xi, sz-0.5, -1, 1 );
q[2] = AIR_AFFINE( -0.5, yi, sz-0.5, -1, 1 );
q[3] = AIR_AFFINE( -0.5, zi, sz-0.5, -1, 1 );
len = ELL_4V_LEN( q );
ELL_4V_SCALE( q, 1.0/len, q );
washQtoM3( mRF, q );
ELL_3M_TRANSPOSE( mRI, mRF );
ELL_3M_IDENTITY_SET( mD );
ELL_3M_DIAG_SET( mD, eval[0], eval[1], eval[2] );
ELL_3M_IDENTITY_SET( mT );
ell_3m_post_mul_d( mT, mRI );
ell_3m_post_mul_d( mT, mD );
ell_3m_post_mul_d( mT, mRF );
tdata[0] = 1.0;
TEN_M2T( tdata, mT );
tdata += 7;
}
}
}
if ( nrrdSave( outS, nten, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
#include "../privateTen.h"
/* BAD gordon */
28 extern double _tenQGL_Kdist( const double RThZA[3], const double RThZB[3] );
29 extern void _tenQGL_Klog( double klog[3],
const double RThZA[3], const double RThZB[3] );
31 extern void _tenQGL_Kexp( double RThZB[3],
const double RThZA[3], const double klog[3] );
34 extern double _tenQGL_Rdist( const double RThPhA[3], const double RThPhB[3] );
35 extern void _tenQGL_Rlog( double rlog[3],
const double RThPhA[3], const double RThPhB[3] );
37 extern void _tenQGL_Rexp( double RThPhB[3],
const double RThPhA[3], const double rlog[3] );
/* normalized gradients of k or r invariants, in XYZ space,
to help determine if path is really a loxodrome */
void
43 kgrads( double grad[3][3], const double eval[3] ) {
double rtz[3];
tenTripleConvertSingle_d( rtz, tenTripleTypeRThetaZ,
eval, tenTripleTypeEigenvalue );
ELL_3V_SET( grad[0], cos( rtz[1] ), sin( rtz[1] ), 0 );
ELL_3V_SET( grad[1], -sin( rtz[1] ), cos( rtz[1] ), 0 );
ELL_3V_SET( grad[2], 0, 0, 1 );
}
void
55 rgrads( double grad[3][3], const double eval[3] ) {
double rtp[3];
tenTripleConvertSingle_d( rtp, tenTripleTypeRThetaPhi,
eval, tenTripleTypeEigenvalue );
ELL_3V_SET( grad[0],
cos( rtp[1] )*sin( rtp[2] ),
sin( rtp[1] )*sin( rtp[2] ),
cos( rtp[2] ) );
ELL_3V_SET( grad[1], -sin( rtp[1] ), cos( rtp[1] ), 0 );
ELL_3V_SET( grad[2],
cos( rtp[1] )*cos( rtp[2] ),
sin( rtp[1] )*cos( rtp[2] ),
-sin( rtp[2] ) );
}
char *info = ( "quaternion geo-lox hacking. Actually all this does is "
"help debug the analytic loxodrome part, not the quaternion "
"geodesic part." );
int
77 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
double tripA[3], tripB[3], evalA[3], evalB[3],
rt_A[3], rt_B[3], trip[3], eval[3], lasteval[3], lastxyz[3],
logAB[3], ndist;
int ittype, ottype, ptype, rttype;
unsigned int NN, ii;
tenInterpParm *tip;
void ( *interp )( double oeval[3], const double evalA[3],
const double evalB[3], const double tt );
double ( *qdist )( const double RTh_A[3], const double RTh_B[3] );
void ( *qlog )( double klog[3],
const double RThZA[3], const double RThZB[3] );
void ( *qexp )( double RThZB[3],
const double RThZA[3], const double klog[3] );
void ( *grads )( double grad[3][3], const double eval[3] );
me = argv[0];
mop = airMopNew( );
tip = tenInterpParmNew( );
airMopAdd( mop, tip, ( airMopper )tenInterpParmNix, airMopAlways );
hestOptAdd( &hopt, "a", "start", airTypeDouble, 3, 3, tripA, NULL,
"start triple of values" );
hestOptAdd( &hopt, "b", "end", airTypeDouble, 3, 3, tripB, NULL,
"end triple of values" );
hestOptAdd( &hopt, "it", "type", airTypeEnum, 1, 1, &ittype, NULL,
"type of given start and end triples", NULL, tenTripleType );
hestOptAdd( &hopt, "ot", "type", airTypeEnum, 1, 1, &ottype, NULL,
"type of triples for output", NULL, tenTripleType );
hestOptAdd( &hopt, "p", "type", airTypeEnum, 1, 1, &ptype, NULL,
"type of path interpolation", NULL, tenInterpType );
hestOptAdd( &hopt, "n", "# steps", airTypeUInt, 1, 1, &NN, "100",
"number of steps along path" );
hestOptAdd( &hopt, "v", "verbosity", airTypeInt, 1, 1,
&( tip->verbose ), "0", "verbosity" );
hestOptAdd( &hopt, "s", "stepsize", airTypeDouble, 1, 1,
&( tip->convStep ), "1", "step size in update" );
hestOptAdd( &hopt, "r", "recurse", airTypeInt, 0, 0,
&( tip->enableRecurse ), NULL,
"enable recursive solution, when useful" );
hestOptAdd( &hopt, "mn", "minnorm", airTypeDouble, 1, 1,
&( tip->minNorm ), "0.000001",
"minnorm of something" );
hestOptAdd( &hopt, "mi", "maxiter", airTypeUInt, 1, 1,
&( tip->maxIter ), "0",
"if non-zero, max # iterations for computation" );
hestOptAdd( &hopt, "c", "conv", airTypeDouble, 1, 1,
&( tip->convEps ), "0.0001",
"convergence threshold of length fraction" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
if ( !( tenInterpTypeQuatGeoLoxK == ptype
|| tenInterpTypeQuatGeoLoxR == ptype ) ) {
fprintf( stderr, "%s: need type %s or %s, not %s\n", me,
airEnumStr( tenInterpType, tenInterpTypeQuatGeoLoxK ),
airEnumStr( tenInterpType, tenInterpTypeQuatGeoLoxR ),
airEnumStr( tenInterpType, ptype ) );
airMopError( mop );
return 1;
}
if ( tenInterpTypeQuatGeoLoxK == ptype ) {
interp = tenQGLInterpTwoEvalK;
qdist = _tenQGL_Kdist;
qlog = _tenQGL_Klog;
qexp = _tenQGL_Kexp;
grads = kgrads;
rttype = tenTripleTypeRThetaZ;
} else {
interp = tenQGLInterpTwoEvalR;
qdist = _tenQGL_Rdist;
qlog = _tenQGL_Rlog;
qexp = _tenQGL_Rexp;
grads = rgrads;
rttype = tenTripleTypeRThetaPhi;
}
fprintf( stderr, "%s: ( %s ) %f %f %f \n--%s--> %f %f %f\n", me,
airEnumStr( tenTripleType, ittype ),
tripA[0], tripA[1], tripA[2],
airEnumStr( tenInterpType, ptype ),
tripB[0], tripB[1], tripB[2] );
tenTripleConvertSingle_d( evalA, tenTripleTypeEigenvalue, tripA, ittype );
tenTripleConvertSingle_d( evalB, tenTripleTypeEigenvalue, tripB, ittype );
tenTripleConvertSingle_d( rt_A, rttype, tripA, ittype );
tenTripleConvertSingle_d( rt_B, rttype, tripB, ittype );
ndist = 0;
ELL_3V_SET( lasteval, AIR_NAN, AIR_NAN, AIR_NAN );
ELL_3V_SET( lastxyz, AIR_NAN, AIR_NAN, AIR_NAN );
qlog( logAB, rt_A, rt_B );
fprintf( stderr, "%s: log = %g %g %g ( %g )\n", me,
logAB[0], logAB[1], logAB[2], ELL_3V_LEN( logAB ) );
for ( ii=0; ii<NN; ii++ ) {
double tt, xyz[3], dot[3], ll[3], prayRT[3], prayO[3];
tt = AIR_AFFINE( 0, ii, NN-1, 0.0, 1.0 );
interp( eval, evalA, evalB, tt );
tenTripleConvertSingle_d( trip, ottype,
eval, tenTripleTypeEigenvalue );
tenTripleConvertSingle_d( xyz, tenTripleTypeXYZ,
eval, tenTripleTypeEigenvalue );
ELL_3V_SCALE( ll, tt, logAB );
qexp( prayRT, rt_A, ll );
tenTripleConvertSingle_d( prayO, ottype, prayRT, rttype );
if ( ii ) {
double diff[3], gr[3][3];
ELL_3V_SUB( diff, lasteval, eval );
ndist += ELL_3V_LEN( diff );
ELL_3V_SUB( diff, lastxyz, xyz );
grads( gr, eval );
dot[0] = ELL_3V_DOT( diff, gr[0] );
dot[1] = ELL_3V_DOT( diff, gr[1] );
dot[2] = ELL_3V_DOT( diff, gr[2] );
} else {
ELL_3V_SET( dot, 0, 0, 0 );
}
printf( "%03u %g %g %g %g %g %g 00 %g %g %g\n", ii,
trip[0], prayO[0],
trip[1], prayO[1],
trip[2], prayO[2],
dot[0], dot[1], dot[2] );
ELL_3V_COPY( lasteval, eval );
ELL_3V_COPY( lastxyz, xyz );
}
fprintf( stderr, "%s: dist %g =?= %g\n", me,
qdist( rt_A, rt_B ), ndist );
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Save a single ellipsoid or superquadric into an OFF file." );
int
29 soidDoit( limnObject *obj, int look,
int gtype, float gamma, int res,
float AB[2], float ten[7] ) {
int partIdx, axis;
float cl, cp, qA, qB, eval[3], evec[9], matA[16], matB[16];
if ( AB ) {
qA = AB[0];
qB = AB[1];
axis = 2;
} else {
tenEigensolve_f( eval, evec, ten );
ELL_SORT3( eval[0], eval[1], eval[2], cl );
cl = ( eval[0] - eval[1] )/( eval[0] + eval[1] + eval[2] );
cp = 2*( eval[1] - eval[2] )/( eval[0] + eval[1] + eval[2] );
if ( cl > cp ) {
axis = 0;
qA = pow( 1-cp, gamma );
qB = pow( 1-cl, gamma );
} else {
axis = 2;
qA = pow( 1-cl, gamma );
qB = pow( 1-cp, gamma );
}
/*
fprintf( stderr, "eval = %g %g %g -> cl=%g %s cp=%g -> axis = %d\n",
eval[0], eval[1], eval[2], cl, cl > cp ? ">" : "<", cp, axis );
*/
}
if ( tenGlyphTypeBox == gtype ) {
partIdx = limnObjectCubeAdd( obj, look );
} else if ( tenGlyphTypeSphere == gtype ) {
partIdx = limnObjectPolarSphereAdd( obj, look,
0, 2*res, res );
} else {
partIdx = limnObjectPolarSuperquadAdd( obj, look,
axis, qA, qB, 2*res, res );
}
ELL_4M_IDENTITY_SET( matA );
ELL_4V_SET( matB + 0*4, eval[0], 0, 0, 0 );
ELL_4V_SET( matB + 1*4, 0, eval[1], 0, 0 );
ELL_4V_SET( matB + 2*4, 0, 0, eval[2], 0 );
ELL_4V_SET( matB + 3*4, 0, 0, 0, 1 );
ELL_4M_SCALE_SET( matB, eval[0], eval[1], eval[2] );
ell_4m_post_mul_f( matA, matB );
ELL_4V_SET( matB + 0*4, evec[0 + 0*3], evec[0 + 1*3], evec[0 + 2*3], 0 );
ELL_4V_SET( matB + 1*4, evec[1 + 0*3], evec[1 + 1*3], evec[1 + 2*3], 0 );
ELL_4V_SET( matB + 2*4, evec[2 + 0*3], evec[2 + 1*3], evec[2 + 2*3], 0 );
ELL_4V_SET( matB + 3*4, 0, 0, 0, 1 );
ell_4m_post_mul_f( matA, matB );
limnObjectPartTransform( obj, partIdx, matA );
return partIdx;
}
static void
86 scalingMatrix( double mat[9], double vec[3], double scl ) {
double dir[3], tmp[9], len;
ELL_3V_NORM( dir, vec, len );
ELL_3MV_OUTER( tmp, dir, dir );
ELL_3M_SCALE( tmp, scl-1, tmp );
ELL_3M_IDENTITY_SET( mat );
ELL_3M_ADD2( mat, mat, tmp );
return;
}
int
98 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
double eval[3], matA[9], matB[9], sval[3], uu[9], vv[9], escl[5],
view[3];
float matAf[9], matBf[16];
float pp[3], qq[4], mR[9], len, gamma;
float os, vs, rad, AB[2], ten[7];
hestOpt *hopt=NULL;
airArray *mop;
limnObject *obj;
limnLook *look; int lookRod, lookSoid;
float kadsRod[3], kadsSoid[3];
int gtype, partIdx=-1; /* sssh */
int res;
FILE *file;
me = argv[0];
hestOptAdd( &hopt, "sc", "evals", airTypeDouble, 3, 3, eval, "1 1 1",
"original eigenvalues of tensor to be visualized" );
hestOptAdd( &hopt, "AB", "A, B exponents", airTypeFloat, 2, 2, AB, "nan nan",
"Directly set the A, B parameters to the superquadric surface, "
"over-riding the default behavior of determining them from the "
"scalings \"-sc\" as superquadric tensor glyphs" );
hestOptAdd( &hopt, "os", "over-all scaling", airTypeFloat, 1, 1, &os, "1",
"over-all scaling ( multiplied by scalings )" );
hestOptAdd( &hopt, "vs", "view-dir scaling", airTypeFloat, 1, 1, &vs, "1",
"scaling along view-direction ( to show off bas-relief "
"ambibuity of ellipsoids versus superquads )" );
hestOptAdd( &hopt, "es", "extra scaling", airTypeDouble, 5, 5, escl,
"2 1 0 0 1", "extra scaling specified with five values "
"0:tensor|1:geometry|2:none vx vy vz scaling" );
hestOptAdd( &hopt, "fr", "from ( eye ) point", airTypeDouble, 3, 3, &view,
"4 4 4", "eye point, needed for non-unity \"-vs\"" );
hestOptAdd( &hopt, "gamma", "superquad sharpness", airTypeFloat, 1, 1,
&gamma, "0",
"how much to sharpen edges as a "
"function of differences between eigenvalues" );
hestOptAdd( &hopt, "g", "glyph shape", airTypeEnum, 1, 1, >ype, "sqd",
"glyph to use; not all are implemented here",
NULL, tenGlyphType );
hestOptAdd( &hopt, "pp", "x y z", airTypeFloat, 3, 3, pp, "0 0 0",
"transform: rotation identified by"
"location in quaternion quotient space" );
hestOptAdd( &hopt, "r", "radius", airTypeFloat, 1, 1, &rad, "0.015",
"black axis cylinder radius ( or 0.0 to not drawn these )" );
hestOptAdd( &hopt, "res", "resolution", airTypeInt, 1, 1, &res, "25",
"tesselation resolution for both glyph and axis cylinders" );
hestOptAdd( &hopt, "pg", "ka kd ks", airTypeFloat, 3, 3, kadsSoid,
"0.2 0.8 0.0",
"phong coefficients for glyph" );
hestOptAdd( &hopt, "pr", "ka kd ks", airTypeFloat, 3, 3, kadsRod, "1 0 0",
"phong coefficients for black rods ( if being drawn )" );
hestOptAdd( &hopt, "o", "output OFF", airTypeString, 1, 1, &outS, "out.off",
"output file to save OFF into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
obj = limnObjectNew( 1000, AIR_TRUE );
airMopAdd( mop, obj, ( airMopper )limnObjectNix, airMopAlways );
if ( !( 0 == escl[0] || 1 == escl[0] || 2 == escl[0] ) ) {
fprintf( stderr, "%s: escl[0] %g not 0, 1 or 2\n", me, escl[0] );
airMopError( mop ); return 1;
}
if ( !( tenGlyphTypeBox == gtype ||
tenGlyphTypeSphere == gtype ||
tenGlyphTypeSuperquad == gtype ) ) {
fprintf( stderr, "%s: got %s %s, but here only do %s, %s, or %s\n", me,
tenGlyphType->name,
airEnumStr( tenGlyphType, gtype ),
airEnumStr( tenGlyphType, tenGlyphTypeBox ),
airEnumStr( tenGlyphType, tenGlyphTypeSphere ),
airEnumStr( tenGlyphType, tenGlyphTypeSuperquad ) );
airMopError( mop ); return 1;
}
/* create limnLooks for glyph and for rods */
lookSoid = limnObjectLookAdd( obj );
look = obj->look + lookSoid;
ELL_4V_SET( look->rgba, 1, 1, 1, 1 );
ELL_3V_COPY( look->kads, kadsSoid );
look->spow = 0;
lookRod = limnObjectLookAdd( obj );
look = obj->look + lookRod;
ELL_4V_SET( look->rgba, 0, 0, 0, 1 );
ELL_3V_COPY( look->kads, kadsRod );
look->spow = 0;
ELL_3M_IDENTITY_SET( matA ); /* A = I */
ELL_3V_SCALE( eval, os, eval );
ELL_3M_SCALE_SET( matB, eval[0], eval[1], eval[2] ); /* B = diag( eval ) */
ell_3m_post_mul_d( matA, matB ); /* A = B*A = diag( eval ) */
if ( 0 == escl[0] ) {
scalingMatrix( matB, escl + 1, escl[4] );
ell_3m_post_mul_d( matA, matB );
}
if ( 1 != vs ) {
if ( !ELL_3V_LEN( view ) ) {
fprintf( stderr, "%s: need non-zero view for vs %g != 1\n", me, vs );
airMopError( mop ); return 1;
}
scalingMatrix( matB, view, vs );
/* the scaling along the view direction is a symmetric matrix,
but applying that scaling to the symmetric input tensor
is not necessarily symmetric */
ell_3m_post_mul_d( matA, matB ); /* A = B*A */
}
/* so we do an SVD to get rotation U and the scalings sval[] */
/* U * diag( sval ) * V */
ell_3m_svd_d( uu, sval, vv, matA, AIR_TRUE );
/*
fprintf( stderr, "%s: ____________________________________\n", me );
fprintf( stderr, "%s: mat = \n", me );
ell_3m_print_d( stderr, matA );
fprintf( stderr, "%s: uu = \n", me );
ell_3m_print_d( stderr, uu );
ELL_3M_TRANSPOSE( matC, uu );
ELL_3M_MUL( matB, uu, matC );
fprintf( stderr, "%s: uu * uu^T = \n", me );
ell_3m_print_d( stderr, matB );
fprintf( stderr, "%s: sval = %g %g %g\n", me, sval[0], sval[1], sval[2] );
fprintf( stderr, "%s: vv = \n", me );
ell_3m_print_d( stderr, vv );
ELL_3M_MUL( matB, vv, vv );
fprintf( stderr, "%s: vv * vv^T = \n", me );
ELL_3M_TRANSPOSE( matC, vv );
ELL_3M_MUL( matB, vv, matC );
ell_3m_print_d( stderr, matB );
ELL_3M_IDENTITY_SET( matA );
ell_3m_pre_mul_d( matA, uu );
ELL_3M_SCALE_SET( matB, sval[0], sval[1], sval[2] );
ell_3m_pre_mul_d( matA, matB );
ell_3m_pre_mul_d( matA, vv );
fprintf( stderr, "%s: uu * diag( sval ) * vv = \n", me );
ell_3m_print_d( stderr, matA );
fprintf( stderr, "%s: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", me );
*/
/* now create symmetric matrix out of U and sval */
/* A = I */
ELL_3M_IDENTITY_SET( matA );
ell_3m_pre_mul_d( matA, uu ); /* A = A*U = I*U = U */
ELL_3M_SCALE_SET( matB, sval[0], sval[1], sval[2] ); /* B = diag( sval ) */
ell_3m_pre_mul_d( matA, matB ); /* A = U*diag( sval ) */
ELL_3M_TRANSPOSE( matB, uu );
ell_3m_pre_mul_d( matA, matB ); /* A = U*diag( sval )*U^T */
TEN_M2T( ten, matA );
partIdx = soidDoit( obj, lookSoid,
gtype, gamma, res,
( AIR_EXISTS( AB[0] ) && AIR_EXISTS( AB[1] ) ) ? AB : NULL,
ten );
if ( 1 == escl[0] ) {
scalingMatrix( matB, escl + 1, escl[4] );
ELL_43M_INSET( matBf, matB );
limnObjectPartTransform( obj, partIdx, matBf );
}
/* this is a rotate on the geomtry; nothing to do with the tensor */
ELL_4V_SET( qq, 1, pp[0], pp[1], pp[2] );
ELL_4V_NORM( qq, qq, len );
ell_q_to_3m_f( mR, qq );
ELL_43M_INSET( matBf, mR );
limnObjectPartTransform( obj, partIdx, matBf );
if ( rad ) {
partIdx = limnObjectCylinderAdd( obj, lookRod, 0, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, ( 1-eval[0] )/2, rad, rad );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, ( 1+eval[0] )/2, 0.0, 0.0 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
partIdx = limnObjectCylinderAdd( obj, lookRod, 0, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, ( 1-eval[0] )/2, rad, rad );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, -( 1+eval[0] )/2, 0.0, 0.0 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
partIdx = limnObjectCylinderAdd( obj, lookRod, 1, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, rad, ( 1-eval[1] )/2, rad );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, 0.0, ( 1+eval[1] )/2, 0.0 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
partIdx = limnObjectCylinderAdd( obj, lookRod, 1, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, rad, ( 1-eval[1] )/2, rad );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, 0.0, -( 1+eval[1] )/2, 0.0 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
partIdx = limnObjectCylinderAdd( obj, lookRod, 2, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, rad, rad, ( 1-eval[2] )/2 );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, 0.0, 0.0, ( 1+eval[2] )/2 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
partIdx = limnObjectCylinderAdd( obj, lookRod, 2, res );
ELL_4M_IDENTITY_SET( matAf );
ELL_4M_SCALE_SET( matBf, rad, rad, ( 1-eval[2] )/2 );
ell_4m_post_mul_f( matAf, matBf );
ELL_4M_TRANSLATE_SET( matBf, 0.0, 0.0, -( 1+eval[2] )/2 );
ell_4m_post_mul_f( matAf, matBf );
limnObjectPartTransform( obj, partIdx, matAf );
}
file = airFopen( outS, stdout, "w" );
airMopAdd( mop, file, ( airMopper )airFclose, airMopAlways );
if ( limnObjectWriteOFF( file, obj ) ) {
airMopAdd( mop, err = biffGetDone( LIMN ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s\n", me, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "Sample space of tensor shape." );
void
29 _ra2t( Nrrd *nten, double rad, double angle,
double mRI[9], double mRF[9], double hack ) {
double x, y, xyz[3], XX[3], YY[3], CC[3], EE[3], VV[3], tmp, mD[9], mT[9];
float *tdata;
int xi, yi, sx, sy;
sx = nten->axis[1].size;
sy = nten->axis[2].size;
x = rad*sin( AIR_PI*angle/180 );
y = rad*cos( AIR_PI*angle/180 );
xi = airIndexClamp( 0.0, x, sqrt( 3.0 )/2.0, sx );
yi = airIndexClamp( 0.0, y, 0.5, sy );
ELL_3V_SET( VV, 0, 3, 0 );
ELL_3V_SET( EE, 1.5, 1.5, 0 );
ELL_3V_SET( CC, 1, 1, 1 );
ELL_3V_SUB( YY, EE, CC );
ELL_3V_SUB( XX, VV, EE );
ELL_3V_NORM( XX, XX, tmp );
ELL_3V_NORM( YY, YY, tmp );
ELL_3V_SCALE_ADD3( xyz, 1.0, CC, hack*x, XX, hack*y, YY );
ELL_3M_IDENTITY_SET( mD );
ELL_3M_DIAG_SET( mD, xyz[0], xyz[1], xyz[2] );
ELL_3M_IDENTITY_SET( mT );
ell_3m_post_mul_d( mT, mRI );
ell_3m_post_mul_d( mT, mD );
ell_3m_post_mul_d( mT, mRF );
tdata = ( float* )( nten->data ) + 7*( xi + sx*( yi + 1*sy ) );
tdata[0] = 1.0;
TEN_M2T( tdata, mT );
}
void
62 _cap2xyz( double xyz[3], double ca, double cp, int version, int whole ) {
double cl, cs, mean;
cs = 1 - ca;
cl = 1 - cs - cp;
mean = ( cs + cp + cl )/3;
/*
xyz[0] = cs*0.333 + cl*1.0 + cp*0.5;
xyz[1] = cs*0.333 + cl*0.0 + cp*0.5;
xyz[2] = cs*0.333 + cl*0.0 + cp*0.0;
xyz[0] = AIR_AFFINE( 0, ca, 1, 1.1*xyz[0], 0.86*xyz[0] );
xyz[1] = AIR_AFFINE( 0, ca, 1, 1.1*xyz[1], 0.86*xyz[1] );
xyz[2] = AIR_AFFINE( 0, ca, 1, 1.1*xyz[2], 0.86*xyz[2] );
*/
if ( whole ) {
ELL_3V_SET( xyz,
AIR_AFFINE( 0.0, 0.9, 1.0, mean, cl ),
AIR_AFFINE( 0.0, 0.9, 1.0, mean, cp ),
AIR_AFFINE( 0.0, 0.9, 1.0, mean, cs ) );
ELL_3V_SET( xyz, cl, cp, cs );
} else {
if ( 1 == version ) {
ELL_3V_SET( xyz,
( 3 + 3*cl - cs )/6,
( 2 - 2*cl + cp )/6,
2*cs/6 );
} else {
ELL_3V_SET( xyz, 1, 1 - cl, cs );
}
}
}
void
95 washQtoM3( double m[9], double q[4] ) {
double p[4], w, x, y, z, len;
ELL_4V_COPY( p, q );
len = ELL_4V_LEN( p );
ELL_4V_SCALE( p, 1.0/len, p );
w = p[0];
x = p[1];
y = p[2];
z = p[3];
/* mathematica work implies that we should be
setting ROW vectors here */
ELL_3V_SET( m+0,
1 - 2*( y*y + z*z ),
2*( x*y - w*z ),
2*( x*z + w*y ) );
ELL_3V_SET( m+3,
2*( x*y + w*z ),
1 - 2*( x*x + z*z ),
2*( y*z - w*x ) );
ELL_3V_SET( m+6,
2*( x*z - w*y ),
2*( y*z + w*x ),
1 - 2*( x*x + y*y ) );
}
int
122 main( int argc, const char *argv[] ) {
const char *me;
char *err, *outS;
hestOpt *hopt=NULL;
airArray *mop;
int sx, sy, xi, yi, samp, version, whole, right;
float *tdata;
double p[3], xyz[3], q[4], len, hackcp=0, maxca;
double ca, cp, mD[9], mRF[9], mRI[9], mT[9], hack;
Nrrd *nten;
mop = airMopNew( );
me = argv[0];
hestOptAdd( &hopt, "n", "# samples", airTypeInt, 1, 1, &samp, "4",
"number of glyphs along each edge of triangle" );
hestOptAdd( &hopt, "p", "x y z", airTypeDouble, 3, 3, p, NULL,
"location in quaternion quotient space" );
hestOptAdd( &hopt, "ca", "max ca", airTypeDouble, 1, 1, &maxca, "0.8",
"maximum ca to use at bottom edge of triangle" );
hestOptAdd( &hopt, "r", NULL, airTypeInt, 0, 0, &right, NULL,
"sample a right-triangle-shaped region, instead of "
"a roughly equilateral triangle. " );
hestOptAdd( &hopt, "w", NULL, airTypeInt, 0, 0, &whole, NULL,
"sample the whole triangle of constant trace, "
"instead of just the "
"sixth of it in which the eigenvalues have the "
"traditional sorted order. " );
hestOptAdd( &hopt, "hack", "hack", airTypeDouble, 1, 1, &hack, "0.04",
"this is a hack" );
hestOptAdd( &hopt, "v", "version", airTypeInt, 1, 1, &version, "1",
"which version of the Westin metrics to use to parameterize "
"triangle; \"1\" for ISMRM 97, \"2\" for MICCAI 99" );
hestOptAdd( &hopt, "o", "nout", airTypeString, 1, 1, &outS, "-",
"output file to save tensors into" );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
nten = nrrdNew( );
airMopAdd( mop, nten, ( airMopper )nrrdNuke, airMopAlways );
if ( !( 1 == version || 2 == version ) ) {
fprintf( stderr, "%s: version must be 1 or 2 ( not %d )\n", me, version );
airMopError( mop );
return 1;
}
if ( right ) {
sx = samp;
sy = ( int )( 1.0*samp/sqrt( 3.0 ) );
} else {
sx = 2*samp-1;
sy = samp;
}
if ( nrrdMaybeAlloc_va( nten, nrrdTypeFloat, 4,
AIR_CAST( size_t, 7 ),
AIR_CAST( size_t, sx ),
AIR_CAST( size_t, sy ),
AIR_CAST( size_t, 3 ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't allocate output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
q[0] = 1.0;
q[1] = p[0];
q[2] = p[1];
q[3] = p[2];
len = ELL_4V_LEN( q );
ELL_4V_SCALE( q, 1.0/len, q );
washQtoM3( mRF, q );
ELL_3M_TRANSPOSE( mRI, mRF );
if ( right ) {
_ra2t( nten, 0.00, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.10, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.10, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.20, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.20, 30.0, mRI, mRF, hack );
_ra2t( nten, 0.20, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.30, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.30, 20.0, mRI, mRF, hack );
_ra2t( nten, 0.30, 40.0, mRI, mRF, hack );
_ra2t( nten, 0.30, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.40, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.40, 15.0, mRI, mRF, hack );
_ra2t( nten, 0.40, 30.0, mRI, mRF, hack );
_ra2t( nten, 0.40, 45.0, mRI, mRF, hack );
_ra2t( nten, 0.40, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 12.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 24.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 36.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 48.0, mRI, mRF, hack );
_ra2t( nten, 0.50, 60.0, mRI, mRF, hack );
/* _ra2t( nten, 0.60, 30.0, mRI, mRF, hack ); */
_ra2t( nten, 0.60, 40.0, mRI, mRF, hack );
_ra2t( nten, 0.60, 50.0, mRI, mRF, hack );
_ra2t( nten, 0.60, 60.0, mRI, mRF, hack );
/* _ra2t( nten, 0.70, 34.3, mRI, mRF, hack ); */
/* _ra2t( nten, 0.70, 42.8, mRI, mRF, hack ); */
_ra2t( nten, 0.70, 51.4, mRI, mRF, hack );
_ra2t( nten, 0.70, 60.0, mRI, mRF, hack );
/* _ra2t( nten, 0.80, 45.0, mRI, mRF, hack ); */
_ra2t( nten, 0.80, 52.5, mRI, mRF, hack );
_ra2t( nten, 0.80, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.90, 60.0, mRI, mRF, hack );
_ra2t( nten, 1.00, 60.0, mRI, mRF, hack );
/*
_ra2t( nten, 0.000, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.125, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.125, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.250, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.250, 30.0, mRI, mRF, hack );
_ra2t( nten, 0.250, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.375, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.375, 20.0, mRI, mRF, hack );
_ra2t( nten, 0.375, 40.0, mRI, mRF, hack );
_ra2t( nten, 0.375, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.500, 0.0, mRI, mRF, hack );
_ra2t( nten, 0.500, 15.0, mRI, mRF, hack );
_ra2t( nten, 0.500, 30.0, mRI, mRF, hack );
_ra2t( nten, 0.500, 45.0, mRI, mRF, hack );
_ra2t( nten, 0.500, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.625, 37.0, mRI, mRF, hack );
_ra2t( nten, 0.625, 47.5, mRI, mRF, hack );
_ra2t( nten, 0.625, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.750, 49.2, mRI, mRF, hack );
_ra2t( nten, 0.750, 60.0, mRI, mRF, hack );
_ra2t( nten, 0.875, 60.0, mRI, mRF, hack );
_ra2t( nten, 1.000, 60.0, mRI, mRF, hack );
*/
nten->axis[1].spacing = 1;
nten->axis[2].spacing = ( sx-1 )/( sqrt( 3.0 )*( sy-1 ) );
nten->axis[3].spacing = 1;
} else {
for ( yi=0; yi<samp; yi++ ) {
if ( whole ) {
ca = AIR_AFFINE( 0, yi, samp-1, 0.0, 1.0 );
} else {
ca = AIR_AFFINE( 0, yi, samp-1, hack, maxca );
hackcp = AIR_AFFINE( 0, yi, samp-1, hack, 0 );
}
for ( xi=0; xi<=yi; xi++ ) {
if ( whole ) {
cp = AIR_AFFINE( 0, xi, samp-1, 0.0, 1.0 );
} else {
cp = AIR_AFFINE( 0, xi, samp-1, hackcp, maxca-hack/2.0 );
}
_cap2xyz( xyz, ca, cp, version, whole );
/*
fprintf( stderr, "%s: ( %d, %d ) -> ( %g, %g ) -> %g %g %g\n", me,
yi, xi, ca, cp, xyz[0], xyz[1], xyz[2] );
*/
ELL_3M_IDENTITY_SET( mD );
ELL_3M_DIAG_SET( mD, xyz[0], xyz[1], xyz[2] );
ELL_3M_IDENTITY_SET( mT );
ell_3m_post_mul_d( mT, mRI );
ell_3m_post_mul_d( mT, mD );
ell_3m_post_mul_d( mT, mRF );
tdata = ( float* )nten->data +
7*( 2*( samp-1-xi ) - ( samp-1-yi ) + ( 2*samp-1 )*( ( samp-1-yi ) + samp ) );
tdata[0] = 1.0;
TEN_M2T( tdata, mT );
}
}
nten->axis[1].spacing = 1;
nten->axis[2].spacing = 1.5;
nten->axis[3].spacing = 1;
}
if ( nrrdSave( outS, nten, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't save output:\n%s\n", me, err );
airMopError( mop );
return 1;
}
airMopOkay( mop );
return 0;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "../ten.h"
char *info = ( "tests conversions between info triples" );
int
29 main( int argc, const char *argv[] ) {
const char *me;
hestOpt *hopt=NULL;
airArray *mop;
int *itype, itypeNum, ii;
double src[3], last[3], dst[3];
char space[] = " ";
me = argv[0];
hestOptAdd( &hopt, NULL, "v1 v2 v3", airTypeDouble, 3, 3, src, NULL,
"source triple" );
hestOptAdd( &hopt, "t", "type", airTypeEnum, 2, -1, &itype, NULL,
"sequence of triple types to convert through",
&itypeNum, tenTripleType );
hestParseOrDie( hopt, argc-1, argv+1, NULL,
me, info, AIR_TRUE, AIR_TRUE, AIR_TRUE );
mop = airMopNew( );
airMopAdd( mop, hopt, ( airMopper )hestOptFree, airMopAlways );
airMopAdd( mop, hopt, ( airMopper )hestParseFree, airMopAlways );
printf( "%s", space + strlen( airEnumStr( tenTripleType, itype[0] ) ) );
printf( "%s", airEnumStr( tenTripleType, itype[0] ) );
ell_3v_print_d( stdout, src );
ELL_3V_COPY( last, src );
for ( ii=1; ii<itypeNum; ii++ ) {
tenTripleConvertSingle_d( dst, itype[ii], src, itype[ii-1] );
printf( "%s", space + strlen( airEnumStr( tenTripleType, itype[ii] ) ) );
printf( "%s", airEnumStr( tenTripleType, itype[ii] ) );
ell_3v_print_d( stdout, dst );
ELL_3V_COPY( src, dst );
}
/*
tenTripleConvert_d( dst, dstType, src, srcType );
tenTripleConvert_d( tst, srcType, dst, dstType );
*/
/*
printf( "%s: %s %s --> %s --> %s\n", me,
tenTriple->name,
airEnumStr( tenTriple, srcType ),
airEnumStr( tenTriple, dstType ),
airEnumStr( tenTriple, srcType ) );
ell_3v_print_d( stdout, src );
ell_3v_print_d( stdout, dst );
ell_3v_print_d( stdout, tst );
*/
airMopOkay( mop );
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ten.h"
#include "privateTen.h"
typedef void ( *tenTripleConverter )( double dst[3], const double src[3] );
#define SQRT6 2.449489742783178098197284
#define SQRT2 1.414213562373095048801689
#define SQRT3 1.732050807568877293527446
#define J1 ( j[0] )
#define J2 ( j[1] )
#define J3 ( j[2] )
#define K1 ( k[0] )
#define K2 ( k[1] )
#define K3 ( k[2] )
#define R1 ( r[0] )
#define R2 ( r[1] )
#define R3 ( r[2] )
#define MU1 ( mu[0] )
#define MU2 ( mu[1] )
#define MU3 ( mu[2] )
static void
51 _iden( double dst[3], const double src[3] ) {
ELL_3V_COPY( dst, src );
return;
}
/* in the function names below, the format is _<dst>_<src>( ) */
static void
58 _mu_ev( double mu[3], const double ev[3] ) {
double mm;
mm = mu[0] = ( ev[0] + ev[1] + ev[2] )/3;
mu[1] = ( ( ev[0] - mm )*( ev[0] - mm ) +
( ev[1] - mm )*( ev[1] - mm ) +
( ev[2] - mm )*( ev[2] - mm ) )/3;
mu[2] = ( ( ev[0] - mm )*( ev[0] - mm )*( ev[0] - mm ) +
( ev[1] - mm )*( ev[1] - mm )*( ev[1] - mm ) +
( ev[2] - mm )*( ev[2] - mm )*( ev[2] - mm ) )/3;
}
static double
_xyzmat[] = {2/SQRT6, -1/SQRT6, -1/SQRT6,
0, 1/SQRT2, -1/SQRT2,
1/SQRT3, 1/SQRT3, 1/SQRT3};
static void
76 _xyz_ev( double xyz[3], const double _ev[3] ) {
double ev[3], tmp;
ELL_3V_COPY( ev, _ev );
ELL_SORT3( ev[0], ev[1], ev[2], tmp );
ELL_3MV_MUL( xyz, _xyzmat, ev );
}
static void
85 _ev_xyz( double ev[3], const double xyz[3] ) {
ELL_3MV_TMUL( ev, _xyzmat, xyz );
}
static void
91 _j_ev( double j[3], const double ev[3] ) {
J1 = ev[0] + ev[1] + ev[2];
J2 = ev[0]*ev[1] + ev[0]*ev[2] + ev[1]*ev[2];
J3 = ev[0]*ev[1]*ev[2];
}
static void
99 _k_mu( double k[3], const double mu[3] ) {
double stdv;
K1 = 3*MU1;
stdv = sqrt( MU2 );
K2 = SQRT3*stdv;
K3 = stdv ? SQRT2*MU3/( stdv*stdv*stdv ) : 0;
}
static void
109 _r_ev( double r[3], const double ev[3] ) {
double mu[3], stdv;
_mu_ev( mu, ev );
R1 = sqrt( ev[0]*ev[0] + ev[1]*ev[1] + ev[2]*ev[2] );
stdv = sqrt( MU2 );
R2 = R1 ? ( 3/SQRT2 )*stdv/R1 : 0;
R3 = stdv ? SQRT2*MU3/( stdv*stdv*stdv ) : 0;
}
static void
120 _r_mu( double r[3], const double mu[3] ) {
double stdv;
R1 = sqrt( 3*( MU1*MU1 + MU2 ) );
stdv = sqrt( MU2 );
R2 = R1 ? ( 3/SQRT2 )*stdv/R1 : 0;
R3 = stdv ? SQRT2*MU3/( stdv*stdv*stdv ) : 0;
}
static void
130 _ev_wp( double ev[3], const double wp[3] ) {
ev[0] = wp[0] + wp[1]*cos( wp[2] );
ev[1] = wp[0] + wp[1]*cos( wp[2] - 2*AIR_PI/3 );
ev[2] = wp[0] + wp[1]*cos( wp[2] + 2*AIR_PI/3 );
}
static void
138 _wp_mu( double wp[3], const double mu[3] ) {
double stdv, mode;
wp[0] = MU1;
stdv = sqrt( MU2 );
wp[1] = SQRT2*stdv;
mode = stdv ? SQRT2*MU3/( stdv*stdv*stdv ) : 0;
mode = AIR_CLAMP( -1, mode, 1 );
wp[2] = acos( AIR_CLAMP( -1, mode, 1 ) )/3;
}
static void
150 _mu_j( double mu[3], const double j[3] ) {
MU1 = J1/3;
MU2 = 2*( J1*J1 - 3*J2 )/9;
MU3 = 2*J1*J1*J1/27 - J1*J2/3 + J3;
}
static void
158 _r_j( double r[3], const double j[3] ) {
double mu[3], stdv;
R1 = sqrt( J1*J1 - 2*J2 );
R2 = sqrt( J1*J1 - 3*J2 )/R1;
_mu_j( mu, j );
stdv = sqrt( MU2 );
R3 = stdv ? SQRT2*MU3/( stdv*stdv*stdv ) : 0;
}
static void
169 _k_r( double k[3], const double r[3] ) {
K1 = R1*sqrt( 3 - 2*R2*R2 );
K2 = ( SQRT2/SQRT3 )*R1*R2;
K3 = R3;
}
/*
_j_r( double j[3], const double r[3] ) {
double ss, nmu3;
J1 = R1*sqrt( 3 - 2*R2*R2 );
J2 = R1*R1*( 1 - R2*R2 );
ss = R1*R2;
nmu3 = 2*R3*ss*ss*ss;
J3 =
}
*/
static void
189 _wp_k( double wp[3], const double k[3] ) {
wp[0] = K1/3;
wp[1] = ( SQRT2/SQRT3 )*K2;
wp[2] = acos( AIR_CLAMP( -1, K3, 1 ) )/3;
}
static void
197 _k_wp( double k[3], const double wp[3] ) {
K1 = 3*wp[0];
K2 = ( SQRT3/SQRT2 )*wp[1];
K3 = cos( 3*wp[2] );
}
static void
205 _rtz_xyz( double rThZ[3], const double XYZ[3] ) {
rThZ[0] = sqrt( XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1] );
rThZ[1] = atan2( XYZ[1], XYZ[0] );
rThZ[2] = XYZ[2];
}
static void
213 _rtp_xyz( double RThPh[3], const double XYZ[3] ) {
RThPh[0] = sqrt( XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1] + XYZ[2]*XYZ[2] );
RThPh[1] = atan2( XYZ[1], XYZ[0] );
RThPh[2] = atan2( sqrt( XYZ[0]*XYZ[0] + XYZ[1]*XYZ[1] ), XYZ[2] );
}
static void
221 _xyz_rtz( double XYZ[3], const double rThZ[3] ) {
XYZ[0] = rThZ[0]*cos( rThZ[1] );
XYZ[1] = rThZ[0]*sin( rThZ[1] );
XYZ[2] = rThZ[2];
}
static void
229 _xyz_rtp( double XYZ[3], const double RThPh[3] ) {
XYZ[0] = RThPh[0]*cos( RThPh[1] )*sin( RThPh[2] );
XYZ[1] = RThPh[0]*sin( RThPh[1] )*sin( RThPh[2] );
XYZ[2] = RThPh[0]*cos( RThPh[2] );
}
static void
237 _rtz_k( double rThZ[3], const double k[3] ) {
rThZ[0] = K2;
rThZ[1] = acos( AIR_CLAMP( -1, K3, 1 ) )/3;
rThZ[2] = K1/SQRT3;
}
static void
245 _k_rtz( double k[3], const double rThZ[3] ) {
K1 = SQRT3*rThZ[2];
K2 = rThZ[0];
K3 = cos( 3*rThZ[1] );
}
static void
253 _rtp_r( double RThPh[3], const double r[3] ) {
RThPh[0] = R1;
RThPh[1] = acos( AIR_CLAMP( -1, R3, 1 ) )/3;
RThPh[2] = asin( AIR_CLAMP( -1, ( SQRT2/SQRT3 )*R2, 1 ) );
}
static void
261 _r_rtp( double r[3], const double RThPh[3] ) {
R1 = RThPh[0];
R2 = sin( RThPh[2] )*SQRT3/SQRT2;
R3 = cos( 3*RThPh[1] );
}
static void
269 _wp_rtz( double wp[3], const double rThZ[3] ) {
wp[0] = rThZ[2]/SQRT3;
wp[1] = ( SQRT2/SQRT3 )*rThZ[0];
wp[2] = rThZ[1];
}
static void
277 _rtz_wp( double rThZ[3], const double wp[3] ) {
rThZ[0] = ( SQRT3/SQRT2 )*wp[1];
rThZ[1] = wp[2];
rThZ[2] = SQRT3*wp[0];
}
#define CONVERT1( dst, mid, src ) \
static void \
_##dst##_##src( double dst[3], const double src[3] ) { \
double mid[3]; \
288 _##mid##_##src( mid, src ); \
_##dst##_##mid( dst, mid ); \
}
#define CONVERT2( dst, mdB, mdA, src ) \
static void \
_##dst##_##src( double dst[3], const double src[3] ) { \
double mdA[3], mdB[3]; \
_##mdA##_##src( mdA, src ); \
_##mdB##_##mdA( mdB, mdA ); \
_##dst##_##mdB( dst, mdB ); \
}
CONVERT1( ev, xyz, rtz ) /* _ev_rtz */
CONVERT1( rtz, xyz, ev ) /* _rtz_ev */
CONVERT1( ev, xyz, rtp ) /* _ev_rtp */
CONVERT1( rtp, xyz, ev ) /* _rtp_ev */
305
CONVERT1( k, mu, ev ) /* _k_ev */
CONVERT1( wp, mu, ev ) /* _wp_ev */
CONVERT1( ev, wp, mu ) /* _ev_mu */
CONVERT2( ev, wp, mu, j ) /* _ev_j */
CONVERT1( ev, wp, k ) /* _ev_k */
CONVERT2( ev, xyz, rtp, r ) /* _ev_r */
static tenTripleConverter
_convert[TEN_TRIPLE_TYPE_MAX+1][TEN_TRIPLE_TYPE_MAX+1] = {
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/* DEST: SRC: ev mu xyz rtz rtp J K R WP */
/* ev */ {NULL, _iden, _ev_mu, _ev_xyz, _ev_rtz, _ev_rtp, _ev_j, _ev_k, _ev_r, _ev_wp},
/* mu */ {NULL, _mu_ev, _iden, NULL, NULL, NULL, _mu_j, NULL, NULL, NULL},
/* xyz */ {NULL, _xyz_ev, NULL, _iden, _xyz_rtz, _xyz_rtp, NULL, NULL, NULL, NULL},
/* rtz */ {NULL, _rtz_ev, NULL, _rtz_xyz, _iden, NULL, NULL, _rtz_k, NULL, _rtz_wp},
/* rtp */ {NULL, _rtp_ev, NULL, _rtp_xyz, NULL, _iden, NULL, NULL, _rtp_r, NULL},
/* J */ {NULL, _j_ev, NULL, NULL, NULL, NULL, _iden, NULL, NULL, NULL},
/* K */ {NULL, _k_ev, _k_mu, NULL, _k_rtz, NULL, NULL, _iden, _k_r, _k_wp},
/* R */ {NULL, _r_ev, _r_mu, NULL, NULL, _r_rtp, _r_j, NULL, _iden, NULL},
/* WP */ {NULL, _wp_ev, _wp_mu, NULL, _wp_rtz, NULL, NULL, _wp_k, NULL, _iden}};
void
tenTripleConvertSingle_d( double dst[3], int dstType,
const double src[3], const int srcType ) {
static const char me[]="tenTripleConvertSingle_d";
int direct;
if ( airEnumValCheck( tenTripleType, dstType )
|| airEnumValCheck( tenTripleType, srcType ) ) {
/* got invalid source or destination type */
ELL_3V_SET( dst, AIR_NAN, AIR_NAN, AIR_NAN );
return;
}
if ( _convert[dstType][srcType] ) {
/* we have a direct converter */
_convert[dstType][srcType]( dst, src );
direct = AIR_TRUE;
} else {
double eval[3];
/* else, for lack of anything clever, we convert via evals */
_convert[tenTripleTypeEigenvalue][srcType]( eval, src );
_convert[dstType][tenTripleTypeEigenvalue]( dst, eval );
direct = AIR_FALSE;
}
/* warn if conversion created non-existent values from
existent input */
if ( ELL_3V_EXISTS( src ) && !ELL_3V_EXISTS( dst ) ) {
fprintf( stderr, "%s: problem? ( %s ) %g %g %g <-%s- ( %s ) %g %g %g\n", me,
airEnumStr( tenTripleType, dstType ),
dst[0], dst[1], dst[2],
direct ? "-" : "...",
airEnumStr( tenTripleType, srcType ),
src[0], src[1], src[2] );
}
return;
}
void
tenTripleConvertSingle_f( float _dst[3], int dstType,
const float _src[3], const int srcType ) {
double dst[3], src[3];
ELL_3V_COPY( src, _src );
tenTripleConvertSingle_d( dst, dstType, src, srcType );
ELL_3V_COPY_TT( _dst, float, dst );
}
void
tenTripleCalcSingle_d( double dst[3], int ttype, double ten[7] ) {
double eval[3];
/* in time this can become more efficient ... */
switch ( ttype ) {
case tenTripleTypeEigenvalue:
tenEigensolve_d( dst, NULL, ten );
break;
case tenTripleTypeMoment:
case tenTripleTypeXYZ:
case tenTripleTypeRThetaZ:
case tenTripleTypeRThetaPhi:
case tenTripleTypeK:
case tenTripleTypeJ:
case tenTripleTypeWheelParm:
tenEigensolve_d( eval, NULL, ten );
tenTripleConvertSingle_d( dst, ttype, eval, tenTripleTypeEigenvalue );
break;
case tenTripleTypeR:
dst[0] = sqrt( _tenAnisoTen_d[tenAniso_S]( ten ) );
dst[1] = _tenAnisoTen_d[tenAniso_FA]( ten );
dst[2] = _tenAnisoTen_d[tenAniso_Mode]( ten );
break;
default:
/* what on earth? */
ELL_3V_SET( dst, AIR_NAN, AIR_NAN, AIR_NAN );
}
return;
}
void
tenTripleCalcSingle_f( float dst[3], int ttype, float ten[7] ) {
double dst_d[3], ten_d[7];
TEN_T_COPY( ten_d, ten );
tenTripleCalcSingle_d( dst_d, ttype, ten_d );
ELL_3V_COPY_TT( dst, float, dst_d );
return;
}
int
tenTripleCalc( Nrrd *nout, int ttype, const Nrrd *nten ) {
static const char me[]="tenTripleCalc";
size_t II, NN, size[NRRD_DIM_MAX];
double ( *ins )( void *, size_t, double ), ( *lup )( const void *, size_t );
if ( !( nout && nten ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( tenTripleType, ttype ) ) {
biffAddf( TEN, "%s: got invalid %s ( %d )", me,
tenTripleType->name, ttype );
return 1;
}
if ( tenTensorCheck( nten, nrrdTypeDefault, AIR_FALSE, AIR_TRUE ) ) {
biffAddf( TEN, "%s: didn't get a valid DT array", me );
return 1;
}
if ( !( nrrdTypeFloat == nten->type ||
nrrdTypeDouble == nten->type ) ) {
biffAddf( TEN, "%s: need input type %s or %s, not %s\n", me,
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nrrdTypeFloat ),
airEnumStr( nrrdType, nten->type ) );
}
nrrdAxisInfoGet_nva( nten, nrrdAxisInfoSize, size );
size[0] = 3;
if ( nrrdMaybeAlloc_nva( nout, nten->type, nten->dim, size ) ) {
biffMovef( TEN, NRRD, "%s: couldn't alloc output", me );
return 1;
}
NN = nrrdElementNumber( nten )/7;
lup = nrrdDLookup[nten->type];
ins = nrrdDInsert[nten->type];
for ( II=0; II<NN; II++ ) {
double ten[7], trip[3];
unsigned int vv;
for ( vv=0; vv<7; vv++ ) {
ten[vv] = lup( nten->data, vv + 7*II );
}
tenTripleCalcSingle_d( trip, ttype, ten );
for ( vv=0; vv<3; vv++ ) {
ins( nout->data, vv + 3*II, trip[vv] );
}
}
if ( nrrdAxisInfoCopy( nout, nten, NULL, ( NRRD_AXIS_INFO_SIZE_BIT ) ) ) {
biffMovef( TEN, NRRD, "%s: couldn't copy axis info", me );
return 1;
}
nout->axis[0].kind = nrrdKindUnknown;
if ( nrrdBasicInfoCopy( nout, nten,
NRRD_BASIC_INFO_ALL ^ NRRD_BASIC_INFO_SPACE ) ) {
biffAddf( TEN, "%s:", me );
return 1;
}
return 0;
}
int
tenTripleConvert( Nrrd *nout, int dstType,
const Nrrd *nin, int srcType ) {
static const char me[]="tenTripleConvert";
size_t II, NN;
double ( *ins )( void *, size_t, double ), ( *lup )( const void *, size_t );
if ( !( nout && nin ) ) {
biffAddf( TEN, "%s: got NULL pointer", me );
return 1;
}
if ( airEnumValCheck( tenTripleType, dstType ) ||
airEnumValCheck( tenTripleType, srcType ) ) {
biffAddf( TEN, "%s: got invalid %s dst ( %d ) or src ( %d )", me,
tenTripleType->name, dstType, srcType );
return 1;
}
if ( 3 != nin->axis[0].size ) {
char stmp[AIR_STRLEN_SMALL];
biffAddf( TEN, "%s: need axis[0].size 3, not %s", me,
airSprintSize_t( stmp, nin->axis[0].size ) );
return 1;
}
if ( nrrdTypeBlock == nin->type ) {
biffAddf( TEN, "%s: input has non-scalar %s type",
me, airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( nrrdCopy( nout, nin ) ) {
biffMovef( TEN, NRRD, "%s: couldn't initialize output", me );
return 1;
}
lup = nrrdDLookup[nin->type];
ins = nrrdDInsert[nout->type];
NN = nrrdElementNumber( nin )/3;
for ( II=0; II<NN; II++ ) {
double src[3], dst[3];
src[0] = lup( nin->data, 0 + 3*II );
src[1] = lup( nin->data, 1 + 3*II );
src[2] = lup( nin->data, 2 + 3*II );
tenTripleConvertSingle_d( dst, dstType, src, srcType );
ins( nout->data, 0 + 3*II, dst[0] );
ins( nout->data, 1 + 3*II, dst[1] );
ins( nout->data, 2 + 3*II, dst[2] );
}
return 0;
}
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2010, 2009, 2008 Thomas Schultz
Copyright ( C ) 2010, 2009, 2008 Gordon Kindlmann
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Implementation of two-dimensional tensors */
#include "tijk.h"
#include "privateTijk.h"
#include "convertQuietPush.h"
/* 2nd order 2D unsymmetric */
double
33 _tijk_2o2d_unsym_tsp_d ( const double *A, const double *B ) {
return ELL_4V_DOT( A, B );
}
float
38 _tijk_2o2d_unsym_tsp_f ( const float *A, const float *B ) {
return ELL_4V_DOT( A, B );
}
double
43 _tijk_2o2d_unsym_norm_d ( const double *A ) {
return sqrt( ELL_4V_DOT( A, A ) );
}
float
48 _tijk_2o2d_unsym_norm_f ( const float *A ) {
return sqrt( ELL_4V_DOT( A, A ) );
}
void
53 _tijk_2o2d_unsym_trans_d ( double *res, const double *A, const double *M ) {
double _ma[4], _mt[4];
ELL_2M_MUL( _ma, M, A );
ELL_2M_TRANSPOSE( _mt, M );
ELL_2M_MUL( res, _ma, _mt );
}
void
61 _tijk_2o2d_unsym_trans_f ( float *res, const float *A, const float *M ) {
float _ma[4], _mt[4];
ELL_2M_MUL( _ma, M, A );
ELL_2M_TRANSPOSE( _mt, M );
ELL_2M_MUL( res, _ma, _mt );
}
/* macro-based pseudo-template for type-generic code */
#define _TIJK_2O2D_UNSYM_CONVERT( TYPE, SUF ) \
70 int \
_tijk_2o2d_unsym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o2d_unsym ) { /* copy over */ \
ELL_4V_COPY( res, A ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o2d_unsym ); \
else \
return 1; \
}
_TIJK_2O2D_UNSYM_CONVERT( double, d )
_TIJK_2O2D_UNSYM_CONVERT( float, f )
#define _TIJK_2O2D_UNSYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o2d_unsym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o2d_sym ) { \
res[0]=A[0]; res[1]=0.5*( A[1]+A[2] ); res[2]=A[3]; \
return 0; \
} else if ( res_type==tijk_2o2d_asym ) { \
res[0]=0.5*( A[1]-A[2] ); \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o2d_unsym ); \
else \
return 1; \
}
_TIJK_2O2D_UNSYM_APPROX( double, d )
_TIJK_2O2D_UNSYM_APPROX( float, f )
TIJK_TYPE_UNSYM( 2o2d_unsym, 2, 2, 4 )
/* 2nd order 2D symmetric */
unsigned int _tijk_2o2d_sym_mult[3] = {1, 2, 1};
int _tijk_2o2d_sym_unsym2uniq[4] = {1, 2, 2, 3};
int _tijk_2o2d_sym_uniq2unsym[4] = {1, 2, 3, 4};
unsigned int _tijk_2o2d_sym_uniq_idx[3] = {0, 1, 3};
#define _TIJK_2O2D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+2*( A )[1]*( B )[1]+( A )[2]*( B )[2] )
double
_tijk_2o2d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_2O2D_SYM_TSP( A, B );
}
float
_tijk_2o2d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_2O2D_SYM_TSP( A, B );
}
double
_tijk_2o2d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_2O2D_SYM_TSP( A, A ) );
}
float
_tijk_2o2d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_2O2D_SYM_TSP( A, A ) );
}
void
_tijk_2o2d_sym_trans_d ( double *res, const double *A, const double *M ) {
/* sym( M*unsym( A )*M^T ) written out: */
res[0]=M[0]*M[0]*A[0]+2*M[0]*M[1]*A[1]+M[1]*M[1]*A[2];
res[1]=M[0]*M[2]*A[0]+( M[0]*M[3]+M[1]*M[2] )*A[1]+M[1]*M[3]*A[2];
res[2]=M[2]*M[2]*A[0]+2*M[2]*M[3]*A[1]+M[3]*M[3]*A[2];
}
void
_tijk_2o2d_sym_trans_f ( float *res, const float *A, const float *M ) {
/* sym( M*unsym( A )*M^T ) written out: */
res[0]=M[0]*M[0]*A[0]+2*M[0]*M[1]*A[1]+M[1]*M[1]*A[2];
res[1]=M[0]*M[2]*A[0]+( M[0]*M[3]+M[1]*M[2] )*A[1]+M[1]*M[3]*A[2];
res[2]=M[2]*M[2]*A[0]+2*M[2]*M[3]*A[1]+M[3]*M[3]*A[2];
}
#define _TIJK_2O2D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_2o2d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o2d_sym ) { /* copy over */ \
ELL_3V_COPY( res, A ); \
return 0; \
} else if ( res_type==tijk_2o2d_unsym ) { \
res[0]=A[0]; res[1]=res[2]=A[1]; res[3]=A[2]; \
return 0; \
} else if ( res_type==tijk_4o2d_sym ) { \
res[0]=A[0]; res[1]=res[3]=0.5*A[1]; \
res[2]=( A[0]+A[2] )/6.0; res[4]=A[2]; \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o2d_sym ); \
else \
return 1; \
}
_TIJK_2O2D_SYM_CONVERT( double, d )
_TIJK_2O2D_SYM_CONVERT( float, f )
#define _TIJK_2O2D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o2d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o2d_sym ); \
else \
return 1; \
}
_TIJK_2O2D_SYM_APPROX( double, d )
_TIJK_2O2D_SYM_APPROX( float, f )
double
_tijk_2o2d_sym_s_form_d ( const double *A, const double *v ) {
return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+A[2]*v[1]*v[1];
}
float
_tijk_2o2d_sym_s_form_f ( const float *A, const float *v ) {
return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+A[2]*v[1]*v[1];
}
double
_tijk_2o2d_sym_mean_d ( const double *A ) {
return 0.5*( A[0]+A[2] );
}
float
_tijk_2o2d_sym_mean_f ( const float *A ) {
return 0.5*( A[0]+A[2] );
}
double
_tijk_2o2d_sym_var_d ( const double *A ) {
return 0.125*( A[0]*A[0]+A[2]*A[2] )-0.25*A[0]*A[2]+0.5*A[1]*A[1];
}
float
_tijk_2o2d_sym_var_f ( const float *A ) {
return 0.125*( A[0]*A[0]+A[2]*A[2] )-0.25*A[0]*A[2]+0.5*A[1]*A[1];
}
void
_tijk_2o2d_sym_v_form_d ( double *res, const double *A, const double *v ) {
res[0]=A[0]*v[0]+A[1]*v[1];
res[1]=A[1]*v[0]+A[2]*v[1];
}
void
_tijk_2o2d_sym_v_form_f ( float *res, const float *A, const float *v ) {
res[0]=A[0]*v[0]+A[1]*v[1];
res[1]=A[1]*v[0]+A[2]*v[1];
}
void
_tijk_2o2d_sym_m_form_d ( double *res, const double *A, const double *v ) {
( void ) v; /* v is only used in higher-order cases */
res[0]=A[0]; res[1]=A[1]; res[2]=A[2];
}
void
_tijk_2o2d_sym_m_form_f ( float *res, const float *A, const float *v ) {
( void ) v; /* v is only used in higher-order cases */
res[0]=A[0]; res[1]=A[1]; res[2]=A[2];
}
void
_tijk_2o2d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[1]*v[1];
}
void
_tijk_2o2d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[1]*v[1];
}
void
_tijk_2o2d_sym_make_iso_d ( double *res, const double s ) {
res[0]=s; res[1]=0; res[2]=s;
}
void
_tijk_2o2d_sym_make_iso_f ( float *res, const float s ) {
res[0]=s; res[1]=0; res[2]=s;
}
void
_tijk_2o2d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[2];
res[0]=2*( A[0]*v[0]+A[1]*v[1] );
res[1]=2*( A[1]*v[0]+A[2]*v[1] );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_2o2d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[2];
res[0]=2*( A[0]*v[0]+A[1]*v[1] );
res[1]=2*( A[1]*v[0]+A[2]*v[1] );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_2o2d_sym_hess_d ( double *res, const double *A, const double *v ) {
double tang[2], s;
ELL_2V_SET( tang, v[1], -v[0] );
s=2*_tijk_2o2d_sym_s_form_d( A, tang )-2*_tijk_2o2d_sym_s_form_d( A, v );
_tijk_2o2d_sym_make_rank1_d( res, s, tang );
}
void
_tijk_2o2d_sym_hess_f ( float *res, const float *A, const float *v ) {
float tang[2], s;
ELL_2V_SET( tang, v[1], -v[0] );
s=2*_tijk_2o2d_sym_s_form_f( A, tang )-2*_tijk_2o2d_sym_s_form_f( A, v );
_tijk_2o2d_sym_make_rank1_f( res, s, tang );
}
TIJK_TYPE_SYM( 2o2d_sym, 2, 2, 3 )
/* 2nd order 2D antisymmetric */
unsigned int _tijk_2o2d_asym_mult[1] = {2};
int _tijk_2o2d_asym_unsym2uniq[4] = {0, 1, -1, 0};
int _tijk_2o2d_asym_uniq2unsym[2] = {2, -3};
unsigned int _tijk_2o2d_asym_uniq_idx[1] = {0};
double
_tijk_2o2d_asym_tsp_d ( const double *A, const double *B ) {
return 2*A[0]*B[0];
}
float
_tijk_2o2d_asym_tsp_f ( const float *A, const float *B ) {
return 2*A[0]*B[0];
}
double
_tijk_2o2d_asym_norm_d ( const double *A ) {
return sqrt( 2*A[0]*A[0] );
}
float
_tijk_2o2d_asym_norm_f ( const float *A ) {
return sqrt( 2*A[0]*A[0] );
}
void
_tijk_2o2d_asym_trans_d ( double *res, const double *A, const double *M ) {
/* if M is a rotation, this amounts to the identity */
res[0]=A[0]*( M[0]*M[3]-M[1]*M[2] );
}
void
_tijk_2o2d_asym_trans_f ( float *res, const float *A, const float *M ) {
res[0]=A[0]*( M[0]*M[3]-M[1]*M[2] );
}
#define _TIJK_2O2D_ASYM_CONVERT( TYPE, SUF ) \
int \
_tijk_2o2d_asym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o2d_asym ) { /* copy over */ \
res[0]=A[0]; \
return 0; \
} else if ( res_type==tijk_2o2d_unsym ) { \
res[0]=0; res[1]=A[0]; res[2]=-A[0]; res[3]=0; \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o2d_asym ); \
else \
return 1; \
}
_TIJK_2O2D_ASYM_CONVERT( double, d )
_TIJK_2O2D_ASYM_CONVERT( float, f )
#define _TIJK_2O2D_ASYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o2d_asym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o2d_asym ); \
else \
return 1; \
}
_TIJK_2O2D_ASYM_APPROX( double, d )
_TIJK_2O2D_ASYM_APPROX( float, f )
TIJK_TYPE( 2o2d_asym, 2, 2, 1 )
/* 3rd order 2D symmetric */
/* unsymmetric counterpart currently not implemented */
unsigned int _tijk_3o2d_sym_mult[4] = {1, 3, 3, 1};
#define _tijk_3o2d_sym_unsym2uniq NULL
#define _tijk_3o2d_sym_uniq2unsym NULL
#define _tijk_3o2d_sym_uniq_idx NULL
#define _TIJK_3O2D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+3*( A )[1]*( B )[1]+3*( A )[2]*( B )[2]+( A )[3]*( B )[3] )
double
_tijk_3o2d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_3O2D_SYM_TSP( A, B );
}
float
_tijk_3o2d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_3O2D_SYM_TSP( A, B );
}
double
_tijk_3o2d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_3O2D_SYM_TSP( A, A ) );
}
float
_tijk_3o2d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_3O2D_SYM_TSP( A, A ) );
}
#define _TIJK_3O2D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_3o2d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_3o2d_sym ) { /* copy over */ \
ELL_4V_COPY( res, A ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_3o2d_sym ); \
else \
return 1; \
}
_TIJK_3O2D_SYM_CONVERT( double, d )
_TIJK_3O2D_SYM_CONVERT( float, f )
#define _TIJK_3O2D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_3o2d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_3o2d_sym ); \
else \
return 1; \
}
_TIJK_3O2D_SYM_APPROX( double, d )
_TIJK_3O2D_SYM_APPROX( float, f )
void
_tijk_3o2d_sym_trans_d ( double *res, const double *A, const double *M ) {
res[0]=M[0]*M[0]*M[0]*A[0]+3*M[0]*M[0]*M[1]*A[1]+
3*M[0]*M[1]*M[1]*A[2]+M[1]*M[1]*M[1]*A[3];
res[1]=M[0]*M[0]*M[2]*A[0]+( M[0]*M[0]*M[3]+2*M[0]*M[1]*M[2] )*A[1]+
( 2*M[0]*M[1]*M[3]+M[1]*M[1]*M[2] )*A[2]+M[1]*M[1]*M[3]*A[3];
res[2]=M[0]*M[2]*M[2]*A[0]+( M[1]*M[2]*M[2]+2*M[0]*M[2]*M[3] )*A[1]+
( 2*M[1]*M[2]*M[3]+M[0]*M[3]*M[3] )*A[2]+M[1]*M[3]*M[3]*A[3];
res[3]=M[2]*M[2]*M[2]*A[0]+3*M[2]*M[2]*M[3]*A[1]+
3*M[2]*M[3]*M[3]*A[2]+M[3]*M[3]*M[3]*A[3];
}
void
_tijk_3o2d_sym_trans_f ( float *res, const float *A, const float *M ) {
res[0]=M[0]*M[0]*M[0]*A[0]+3*M[0]*M[0]*M[1]*A[1]+
3*M[0]*M[1]*M[1]*A[2]+M[1]*M[1]*M[1]*A[3];
res[1]=M[0]*M[0]*M[2]*A[0]+( M[0]*M[0]*M[3]+2*M[0]*M[1]*M[2] )*A[1]+
( 2*M[0]*M[1]*M[3]+M[1]*M[1]*M[2] )*A[2]+M[1]*M[1]*M[3]*A[3];
res[2]=M[0]*M[2]*M[2]*A[0]+( M[1]*M[2]*M[2]+2*M[0]*M[2]*M[3] )*A[1]+
( 2*M[1]*M[2]*M[3]+M[0]*M[3]*M[3] )*A[2]+M[1]*M[3]*M[3]*A[3];
res[3]=M[2]*M[2]*M[2]*A[0]+3*M[2]*M[2]*M[3]*A[1]+
3*M[2]*M[3]*M[3]*A[2]+M[3]*M[3]*M[3]*A[3];
}
double
_tijk_3o2d_sym_s_form_d ( const double *A, const double *v ) {
return A[0]*v[0]*v[0]*v[0]+3*A[1]*v[0]*v[0]*v[1]+
3*A[2]*v[0]*v[1]*v[1]+A[3]*v[1]*v[1]*v[1];
}
float
_tijk_3o2d_sym_s_form_f ( const float *A, const float *v ) {
return A[0]*v[0]*v[0]*v[0]+3*A[1]*v[0]*v[0]*v[1]+
3*A[2]*v[0]*v[1]*v[1]+A[3]*v[1]*v[1]*v[1];
}
double
_tijk_3o2d_sym_mean_d ( const double *A ) {
( void ) A; /* odd order; mean is zero irrespective of coefficients */
return 0;
}
float
_tijk_3o2d_sym_mean_f ( const float *A ) {
( void ) A; /* odd order; mean is zero irrespective of coefficients */
return 0;
}
double
_tijk_3o2d_sym_var_d ( const double *A ) {
return ( 5*( A[0]*A[0]+A[3]*A[3] )+9*( A[1]*A[1]+A[2]*A[2] )+
6*( A[0]*A[2]+A[1]*A[3] ) )/16.0;
}
float
_tijk_3o2d_sym_var_f ( const float *A ) {
return ( 5*( A[0]*A[0]+A[3]*A[3] )+9*( A[1]*A[1]+A[2]*A[2] )+
6*( A[0]*A[2]+A[1]*A[3] ) )/16.0;
}
void
_tijk_3o2d_sym_v_form_d ( double *res, const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11;
res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11;
}
void
_tijk_3o2d_sym_v_form_f ( float *res, const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11;
res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11;
}
void
_tijk_3o2d_sym_m_form_d ( double *res, const double *A, const double *v ) {
res[0]=A[0]*v[0]+A[1]*v[1];
res[1]=A[1]*v[0]+A[2]*v[1];
res[2]=A[2]*v[0]+A[3]*v[1];
}
void
_tijk_3o2d_sym_m_form_f ( float *res, const float *A, const float *v ) {
res[0]=A[0]*v[0]+A[1]*v[1];
res[1]=A[1]*v[0]+A[2]*v[1];
res[2]=A[2]*v[0]+A[3]*v[1];
}
void
_tijk_3o2d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
res[0]=s*v[0]*v[0]*v[0];
res[1]=s*v[0]*v[0]*v[1];
res[2]=s*v[0]*v[1]*v[1];
res[3]=s*v[1]*v[1]*v[1];
}
void
_tijk_3o2d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
res[0]=s*v[0]*v[0]*v[0];
res[1]=s*v[0]*v[0]*v[1];
res[2]=s*v[0]*v[1]*v[1];
res[3]=s*v[1]*v[1]*v[1];
}
#define _tijk_3o2d_sym_make_iso_d NULL
#define _tijk_3o2d_sym_make_iso_f NULL
void
_tijk_3o2d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[2];
_tijk_3o2d_sym_v_form_d ( res, A, v );
ELL_2V_SCALE( res, 3.0, res );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_3o2d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[2];
_tijk_3o2d_sym_v_form_f ( res, A, v );
ELL_2V_SCALE( res, 3.0, res );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_3o2d_sym_hess_d ( double *res, const double *A, const double *v ) {
double tang[2];
double hess[3], s;
ELL_2V_SET( tang, v[1], -v[0] );
_tijk_3o2d_sym_m_form_d( hess, A, v );
s=6*_tijk_2o2d_sym_s_form_d( hess, tang )-3*_tijk_2o2d_sym_s_form_d( hess, v );
_tijk_2o2d_sym_make_rank1_d( res, s, tang );
}
void
_tijk_3o2d_sym_hess_f ( float *res, const float *A, const float *v ) {
float tang[2];
float hess[3], s;
ELL_2V_SET( tang, v[1], -v[0] );
_tijk_3o2d_sym_m_form_f( hess, A, v );
s=6*_tijk_2o2d_sym_s_form_f( hess, tang )-3*_tijk_2o2d_sym_s_form_f( hess, v );
_tijk_2o2d_sym_make_rank1_f( res, s, tang );
}
TIJK_TYPE_SYM( 3o2d_sym, 3, 2, 4 )
/* 4th order 2D unsymmetric */
double
_tijk_4o2d_unsym_tsp_d ( const double *A, const double *B ) {
return ELL_4V_DOT( A, B )+ELL_4V_DOT( A+4, B+4 )+
ELL_4V_DOT( A+8, B+8 )+ELL_4V_DOT( A+12, B+12 );
}
float
_tijk_4o2d_unsym_tsp_f ( const float *A, const float *B ) {
return ELL_4V_DOT( A, B )+ELL_4V_DOT( A+4, B+4 )+
ELL_4V_DOT( A+8, B+8 )+ELL_4V_DOT( A+12, B+12 );
}
double
_tijk_4o2d_unsym_norm_d ( const double *A ) {
return sqrt( ELL_4V_DOT( A, A )+ELL_4V_DOT( A+4, A+4 )+
ELL_4V_DOT( A+8, A+8 )+ELL_4V_DOT( A+12, A+12 ) );
}
float
_tijk_4o2d_unsym_norm_f ( const float *A ) {
return sqrt( ELL_4V_DOT( A, A )+ELL_4V_DOT( A+4, A+4 )+
ELL_4V_DOT( A+8, A+8 )+ELL_4V_DOT( A+12, A+12 ) );
}
#define _TIJK_4O2D_UNSYM_TRANS( TYPE, SUF ) \
void \
_tijk_4o2d_unsym_trans_##SUF ( TYPE *res, const TYPE *A, const TYPE *M ) \
{ /* Tijkl = Mim Mjn Mko Mlp Tmnop \
* For efficiency, we transform mode by mode, right to left */ \
TYPE tmp[16]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; \
int i, init; \
for ( i=0; i<16; i+=2 ) { /* 4th mode */ \
tmp[i] = M[0]*A[i] + M[1]*A[i+1]; \
tmp[i+1]= M[2]*A[i] + M[3]*A[i+1]; \
} \
/* using res as additional tmp space */ \
for ( init=0; init<2; init++ ) { /* 3rd mode */ \
for ( i=init; i<16; i+=4 ) { \
res[i] = M[0]*tmp[i] + M[1]*tmp[i+2]; \
res[i+2]= M[2]*tmp[i] + M[3]*tmp[i+2]; \
} \
} \
for ( init=0; init<4; init++ ) { /* 2nd mode */ \
for ( i=init; i<16; i+=8 ) { \
tmp[i] = M[0]*res[i] + M[1]*res[i+4]; \
tmp[i+4]= M[2]*res[i] + M[3]*res[i+4]; \
} \
} \
for ( i=0; i<8; i++ ) { /* 1st mode */ \
res[i] = M[0]*tmp[i] + M[1]*tmp[i+8]; \
res[i+8]= M[2]*tmp[i] + M[3]*tmp[i+8]; \
} \
}
_TIJK_4O2D_UNSYM_TRANS( double, d )
_TIJK_4O2D_UNSYM_TRANS( float, f )
#define _TIJK_4O2D_UNSYM_CONVERT( TYPE, SUF ) \
int \
_tijk_4o2d_unsym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_4o2d_unsym ) { /* copy over */ \
ELL_4V_COPY( res, A ); ELL_4V_COPY( res+4, A+4 ); \
ELL_4V_COPY( res+8, A+8 ); ELL_4V_COPY( res+12, A+12 ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_4o2d_unsym ); \
else \
return 1; \
}
_TIJK_4O2D_UNSYM_CONVERT( double, d )
_TIJK_4O2D_UNSYM_CONVERT( float, f )
#define _TIJK_4O2D_UNSYM_APPROX( TYPE, SUF ) \
int \
_tijk_4o2d_unsym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_4o2d_sym ) { \
res[0]=A[0]; res[1]=0.25*( A[1]+A[2]+A[4]+A[8] ); \
res[2]=( A[3]+A[5]+A[6]+A[9]+A[10]+A[12] )/6.0; \
res[3]=0.25*( A[7]+A[11]+A[13]+A[14] ); res[4]=A[15]; \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_4o2d_unsym ); \
else \
return 1; \
}
_TIJK_4O2D_UNSYM_APPROX( double, d )
_TIJK_4O2D_UNSYM_APPROX( float, f )
TIJK_TYPE_UNSYM( 4o2d_unsym, 4, 2, 16 )
/* 4th order 2D symmetric */
unsigned int _tijk_4o2d_sym_mult[5] = {1, 4, 6, 4, 1};
int _tijk_4o2d_sym_unsym2uniq[16] = {1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,
3, 4, 4, 5};
int _tijk_4o2d_sym_uniq2unsym[16] = {1, 2, 3, 5, 9, 4, 6, 7, 10, 11, 13,
8, 12, 14, 15, 16};
unsigned int _tijk_4o2d_sym_uniq_idx[5] = {0, 1, 5, 11, 15};
#define _TIJK_4O2D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+4*( A )[1]*( B )[1]+6*( A )[2]*( B )[2]+4*( A )[3]*( B )[3]+( A )[4]*( B )[4] )
double
_tijk_4o2d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_4O2D_SYM_TSP( A, B );
}
float
_tijk_4o2d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_4O2D_SYM_TSP( A, B );
}
double
_tijk_4o2d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_4O2D_SYM_TSP( A, A ) );
}
float
_tijk_4o2d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_4O2D_SYM_TSP( A, A ) );
}
#define _TIJK_4O2D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_4o2d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_4o2d_sym ) { /* copy over */ \
ELL_4V_COPY( res, A ); res[4]=A[4]; \
return 0; \
} else if ( res_type==tijk_4o2d_unsym ) { \
res[0]=A[0]; res[1]=res[2]=res[4]=res[8]=A[1]; \
res[3]=res[5]=res[6]=res[9]=res[10]=res[12]=A[2]; \
res[7]=res[11]=res[13]=res[14]=A[3]; res[15]=A[4]; \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_4o2d_sym ); \
else \
return 1; \
}
_TIJK_4O2D_SYM_CONVERT( double, d )
_TIJK_4O2D_SYM_CONVERT( float, f )
#define _TIJK_4O2D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_4o2d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o2d_sym ) { \
res[0]=0.875*A[0]+0.75*A[2]-0.125*A[4]; \
res[1]=A[1]+A[3]; \
res[2]=-0.125*A[0]+0.75*A[2]+0.875*A[4]; \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_4o2d_sym ); \
else \
return 1; \
}
_TIJK_4O2D_SYM_APPROX( double, d )
_TIJK_4O2D_SYM_APPROX( float, f )
void
_tijk_4o2d_sym_trans_d ( double *res, const double *A, const double *M ) {
/* this code should be optimized at some point */
double tmp[16], tmpout[16];
_tijk_4o2d_sym_convert_d( tmp, tijk_4o2d_unsym, A );
_tijk_4o2d_unsym_trans_d( tmpout, tmp, M );
_tijk_4o2d_unsym_approx_d( res, tijk_4o2d_sym, tmpout );
}
void
_tijk_4o2d_sym_trans_f ( float *res, const float *A, const float *M ) {
float tmp[16], tmpout[16];
_tijk_4o2d_sym_convert_f( tmp, tijk_4o2d_unsym, A );
_tijk_4o2d_unsym_trans_f( tmpout, tmp, M );
_tijk_4o2d_unsym_approx_f( res, tijk_4o2d_sym, tmpout );
}
double
_tijk_4o2d_sym_s_form_d ( const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
return A[0]*v00*v00+4*A[1]*v00*v01+6*A[2]*v00*v11+
4*A[3]*v01*v11+A[4]*v11*v11;
}
float
_tijk_4o2d_sym_s_form_f ( const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
return A[0]*v00*v00+4*A[1]*v00*v01+6*A[2]*v00*v11+
4*A[3]*v01*v11+A[4]*v11*v11;
}
double
_tijk_4o2d_sym_mean_d ( const double *A ) {
return 0.375*( A[0]+A[4] )+0.75*A[2];
}
float
_tijk_4o2d_sym_mean_f ( const float *A ) {
return 0.375*( A[0]+A[4] )+0.75*A[2];
}
double
_tijk_4o2d_sym_var_d ( const double *A ) {
return A[0]*( 0.1328125*A[0]-0.09375*A[2]-0.234375*A[4] ) +
A[1]*( 0.625*A[1]+0.75*A[3] ) + 0.28125*A[2]*A[2] +
0.625*A[3]*A[3] + A[4]*( 0.1328125*A[4]-0.09375*A[2] );
}
float
_tijk_4o2d_sym_var_f ( const float *A ) {
return A[0]*( 0.1328125*A[0]-0.09375*A[2]-0.234375*A[4] ) +
A[1]*( 0.625*A[1]+0.75*A[3] ) + 0.28125*A[2]*A[2] +
0.625*A[3]*A[3] + A[4]*( 0.1328125*A[4]-0.09375*A[2] );
}
void
_tijk_4o2d_sym_v_form_d ( double *res, const double *A, const double *v ) {
double v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1],
v011=v[0]*v[1]*v[1], v111=v[1]*v[1]*v[1];
res[0]=A[0]*v000+3*A[1]*v001+3*A[2]*v011+A[3]*v111;
res[1]=A[1]*v000+3*A[2]*v001+3*A[3]*v011+A[4]*v111;
}
void
_tijk_4o2d_sym_v_form_f ( float *res, const float *A, const float *v ) {
float v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1],
v011=v[0]*v[1]*v[1], v111=v[1]*v[1]*v[1];
res[0]=A[0]*v000+3*A[1]*v001+3*A[2]*v011+A[3]*v111;
res[1]=A[1]*v000+3*A[2]*v001+3*A[3]*v011+A[4]*v111;
}
void
_tijk_4o2d_sym_m_form_d ( double *res, const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11;
res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11;
res[2]=A[2]*v00+2*A[3]*v01+A[4]*v11;
}
void
_tijk_4o2d_sym_m_form_f ( float *res, const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=A[0]*v00+2*A[1]*v01+A[2]*v11;
res[1]=A[1]*v00+2*A[2]*v01+A[3]*v11;
res[2]=A[2]*v00+2*A[3]*v01+A[4]*v11;
}
void
_tijk_4o2d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v11;
res[3]=s*v01*v11; res[4]=s*v11*v11;
}
void
_tijk_4o2d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v11=v[1]*v[1];
res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v11;
res[3]=s*v01*v11; res[4]=s*v11*v11;
}
void
_tijk_4o2d_sym_make_iso_d ( double *res, const double s ) {
res[0]=res[4]=s; res[2]=s/3.0; res[1]=res[3]=0;
}
void
_tijk_4o2d_sym_make_iso_f ( float *res, const float s ) {
res[0]=res[4]=s; res[2]=s/3.0; res[1]=res[3]=0;
}
void
_tijk_4o2d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[2];
_tijk_4o2d_sym_v_form_d ( res, A, v );
ELL_2V_SCALE( res, 4.0, res );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_4o2d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[2];
_tijk_4o2d_sym_v_form_f ( res, A, v );
ELL_2V_SCALE( res, 4.0, res );
proj=ELL_2V_DOT( res, v );
ELL_2V_SCALE( projv, -proj, v );
ELL_2V_INCR( res, projv );
}
void
_tijk_4o2d_sym_hess_d ( double *res, const double *A, const double *v ) {
double tang[2];
double hess[3], s;
ELL_2V_SET( tang, v[1], -v[0] );
_tijk_4o2d_sym_m_form_d( hess, A, v );
s=12*_tijk_2o2d_sym_s_form_d( hess, tang )-4*_tijk_2o2d_sym_s_form_d( hess, v );
_tijk_2o2d_sym_make_rank1_d( res, s, tang );
}
void
_tijk_4o2d_sym_hess_f ( float *res, const float *A, const float *v ) {
float tang[2];
float hess[3], s;
ELL_2V_SET( tang, v[1], -v[0] );
_tijk_4o2d_sym_m_form_f( hess, A, v );
s=12*_tijk_2o2d_sym_s_form_f( hess, tang )-4*_tijk_2o2d_sym_s_form_f( hess, v );
_tijk_2o2d_sym_make_rank1_f( res, s, tang );
}
TIJK_TYPE_SYM( 4o2d_sym, 4, 2, 5 )
#include "convertQuietPop.h"
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009, 2008 Thomas Schultz
Copyright ( C ) 2010, 2009, 2008 Gordon Kindlmann
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Implementation of three-dimensional tensors */
#include "tijk.h"
#include "privateTijk.h"
#include "convertQuietPush.h"
/* 1st order 3D - a simple vector */
/* ( un )symmetric doesn't really mean anything in this case */
unsigned int _tijk_1o3d_mult[3]={1, 1, 1};
int _tijk_1o3d_unsym2uniq[3] = {1, 2, 3};
int _tijk_1o3d_uniq2unsym[3] = {1, 2, 3};
unsigned int _tijk_1o3d_uniq_idx[3] = {0, 1, 2};
double
39 _tijk_1o3d_tsp_d ( const double *A, const double *B ) {
return ELL_3V_DOT( A, B );
}
float
44 _tijk_1o3d_tsp_f ( const float *A, const float *B ) {
return ELL_3V_DOT( A, B );
}
double
49 _tijk_1o3d_norm_d ( const double *A ) {
return sqrt( ELL_3V_DOT( A, A ) );
}
float
54 _tijk_1o3d_norm_f ( const float *A ) {
return sqrt( ELL_3V_DOT( A, A ) );
}
void
59 _tijk_1o3d_trans_d ( double *res, const double *A, const double *M ) {
ell_3mv_mul_d( res, M, A );
}
void
64 _tijk_1o3d_trans_f ( float *res, const float *A, const float *M ) {
ell_3mv_mul_f( res, M, A );
}
/* macro-based pseudo-template for type-generic code */
#define _TIJK_1O3D_CONVERT( TYPE, SUF ) \
70 int \
_tijk_1o3d_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_1o3d ) { /* copy over */ \
ELL_3V_COPY( res, A ); \
return 0; \
} else if ( res_type==tijk_3o3d_sym ) { \
res[0]=A[0]; res[1]=A[1]/3.0; res[2]=A[2]/3.0; \
res[3]=A[0]/3.0; res[4]=0; res[5]=A[0]/3.0; res[6]=A[1]; \
res[7]=A[2]/3.0; res[8]=A[1]/3.0; res[9]=A[2]; \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_1o3d ); \
else \
return 1; \
}
_TIJK_1O3D_CONVERT( double, d )
_TIJK_1O3D_CONVERT( float, f )
#define _TIJK_1O3D_APPROX( TYPE, SUF ) \
int \
_tijk_1o3d_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_1o3d ); \
else \
return 1; \
}
_TIJK_1O3D_APPROX( double, d )
_TIJK_1O3D_APPROX( float, f )
double
_tijk_1o3d_s_form_d ( const double *A, const double *v ) {
return ELL_3V_DOT( A, v );
}
float
_tijk_1o3d_s_form_f ( const float *A, const float *v ) {
return ELL_3V_DOT( A, v );
}
double
_tijk_1o3d_mean_d ( const double *A ) {
( void ) A; /* odd order; mean does not depend on coeffs */
return 0.0;
}
float
_tijk_1o3d_mean_f ( const float *A ) {
( void ) A; /* odd order; mean does not depend on coeffs */
return 0.0f;
}
double
_tijk_1o3d_var_d ( const double *A ) {
/* result from MATHEMATICA */
return ELL_3V_DOT( A, A )/3.0;
}
float
_tijk_1o3d_var_f ( const float *A ) {
/* result from MATHEMATICA */
return ELL_3V_DOT( A, A )/3.0;
}
void
_tijk_1o3d_v_form_d ( double *res, const double *A, const double *v ) {
( void ) v; /* not used in this case */
ELL_3V_COPY( res, A );
}
void
_tijk_1o3d_v_form_f ( float *res, const float *A, const float *v ) {
( void ) v; /* not used in this case */
ELL_3V_COPY( res, A );
}
void
_tijk_1o3d_m_form_d ( double *res, const double *A, const double *v ) {
( void ) A; ( void ) v; /* not used in this case */
ELL_3V_SET( res, 0, 0, 0 );
ELL_3V_SET( res+3, 0, 0, 0 );
}
void
_tijk_1o3d_m_form_f ( float *res, const float *A, const float *v ) {
( void ) A; ( void ) v; /* not used in this case */
ELL_3V_SET( res, 0, 0, 0 );
ELL_3V_SET( res+3, 0, 0, 0 );
}
void
_tijk_1o3d_make_rank1_d ( double *res, const double s, const double *v ) {
ELL_3V_SCALE( res, s, v );
}
void
_tijk_1o3d_make_rank1_f ( float *res, const float s, const float *v ) {
ELL_3V_SCALE( res, s, v );
}
#define _tijk_1o3d_make_iso_d NULL
#define _tijk_1o3d_make_iso_f NULL
void
_tijk_1o3d_grad_d ( double *res, const double *A, const double *v ) {
( void ) v; /* not used in this case */
ELL_3V_COPY( res, A );
}
void
_tijk_1o3d_grad_f ( float *res, const float *A, const float *v ) {
( void ) v; /* not used in this case */
ELL_3V_COPY( res, A );
}
void
_tijk_1o3d_hess_d ( double *res, const double *A, const double *v ) {
( void ) A; ( void ) v; /* not used in this case */
ELL_3V_SET( res, 0, 0, 0 );
ELL_3V_SET( res+3, 0, 0, 0 );
}
void
_tijk_1o3d_hess_f ( float *res, const float *A, const float *v ) {
( void ) A; ( void ) v; /* not used in this case */
ELL_3V_SET( res, 0, 0, 0 );
ELL_3V_SET( res+3, 0, 0, 0 );
}
TIJK_TYPE_SYM( 1o3d, 1, 3, 3 )
/* 2nd order 3D unsymmetric */
double
_tijk_2o3d_unsym_tsp_d ( const double *A, const double *B ) {
return ELL_3V_DOT( A, B )+ELL_3V_DOT( A+3, B+3 )+ELL_3V_DOT( A+6, B+6 );
}
float
_tijk_2o3d_unsym_tsp_f ( const float *A, const float *B ) {
return ELL_3V_DOT( A, B )+ELL_3V_DOT( A+3, B+3 )+ELL_3V_DOT( A+6, B+6 );
}
double
_tijk_2o3d_unsym_norm_d ( const double *A ) {
return sqrt( ELL_3V_DOT( A, A )+ELL_3V_DOT( A+3, A+3 )+ELL_3V_DOT( A+6, A+6 ) );
}
float
_tijk_2o3d_unsym_norm_f ( const float *A ) {
return sqrt( ELL_3V_DOT( A, A )+ELL_3V_DOT( A+3, A+3 )+ELL_3V_DOT( A+6, A+6 ) );
}
void
_tijk_2o3d_unsym_trans_d ( double *res, const double *A, const double *M ) {
double _ma[9], _mt[9];
ELL_3M_MUL( _ma, M, A );
ELL_3M_TRANSPOSE( _mt, M );
ELL_3M_MUL( res, _ma, _mt );
}
void
_tijk_2o3d_unsym_trans_f ( float *res, const float *A, const float *M ) {
float _ma[9], _mt[9];
ELL_3M_MUL( _ma, M, A );
ELL_3M_TRANSPOSE( _mt, M );
ELL_3M_MUL( res, _ma, _mt );
}
/* macro-based pseudo-template for type-generic code */
#define _TIJK_2O3D_UNSYM_CONVERT( TYPE, SUF ) \
int \
_tijk_2o3d_unsym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o3d_unsym ) { /* copy over */ \
ELL_3M_COPY( res, A ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o3d_unsym ); \
else \
return 1; \
}
_TIJK_2O3D_UNSYM_CONVERT( double, d )
_TIJK_2O3D_UNSYM_CONVERT( float, f )
#define _TIJK_2O3D_UNSYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o3d_unsym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o3d_sym ) { \
res[0]=A[0]; res[1]=0.5*( A[1]+A[3] ); res[2]=0.5*( A[2]+A[6] ); \
res[3]=A[4]; res[4]=0.5*( A[5]+A[7] ); res[5]=A[8]; \
return 0; \
} else if ( res_type==tijk_2o3d_asym ) { \
res[0]=0.5*( A[1]-A[3] ); res[1]=0.5*( A[2]-A[6] ); \
res[2]=0.5*( A[5]-A[7] ); \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o3d_unsym ); \
else \
return 1; \
}
_TIJK_2O3D_UNSYM_APPROX( double, d )
_TIJK_2O3D_UNSYM_APPROX( float, f )
TIJK_TYPE_UNSYM( 2o3d_unsym, 2, 3, 9 )
/* 2nd order 3D symmetric */
unsigned int _tijk_2o3d_sym_mult[6] = {1, 2, 2, 1, 2, 1};
int _tijk_2o3d_sym_unsym2uniq[9] = {1, 2, 3, 2, 4, 5, 3, 5, 6};
int _tijk_2o3d_sym_uniq2unsym[9] = {1, 2, 4, 3, 7, 5, 6, 7, 9};
unsigned int _tijk_2o3d_sym_uniq_idx[6] = {0, 1, 3, 5, 6, 8};
#define _TIJK_2O3D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+2*( A )[1]*( B )[1]+2*( A )[2]*( B )[2]+ \
( A )[3]*( B )[3]+2*( A )[4]*( B )[4]+( A )[5]*( B )[5] )
double
_tijk_2o3d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_2O3D_SYM_TSP( A, B );
}
float
_tijk_2o3d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_2O3D_SYM_TSP( A, B );
}
double
_tijk_2o3d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_2O3D_SYM_TSP( A, A ) );
}
float
_tijk_2o3d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_2O3D_SYM_TSP( A, A ) );
}
#define _TIJK_2O3D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_2o3d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o3d_sym ) { /* copy over */ \
ELL_3V_COPY( res, A ); ELL_3V_COPY( res+3, A+3 ); \
return 0; \
} else if ( res_type==tijk_2o3d_unsym ) { \
res[0]=A[0]; res[1]=res[3]=A[1]; res[2]=res[6]=A[2]; \
res[4]=A[3]; res[5]=res[7]=A[4]; res[8]=A[5]; \
return 0; \
} else if ( res_type==tijk_4o3d_sym || \
res_type==tijk_6o3d_sym ) { \
/* do this by going to SH and zero-padding */ \
TYPE tmp[28]; \
memset( tmp, 0, sizeof( tmp ) ); \
tijk_3d_sym_to_esh_##SUF ( tmp, A, tijk_2o3d_sym ); \
tijk_esh_to_3d_sym_##SUF ( res, tmp, res_type->order ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o3d_sym ); \
return 1; \
}
_TIJK_2O3D_SYM_CONVERT( double, d )
_TIJK_2O3D_SYM_CONVERT( float, f )
#define _TIJK_2O3D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o3d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o3d_sym ); \
else \
return 1; \
}
_TIJK_2O3D_SYM_APPROX( double, d )
_TIJK_2O3D_SYM_APPROX( float, f )
void
_tijk_2o3d_sym_trans_d ( double *res, const double *A, const double *M ) {
/* this code could be optimized at some point */
double tmp[9], tmpout[9];
_tijk_2o3d_sym_convert_d( tmp, tijk_2o3d_unsym, A );
_tijk_2o3d_unsym_trans_d( tmpout, tmp, M );
_tijk_2o3d_unsym_approx_d( res, tijk_2o3d_sym, tmpout );
}
void
_tijk_2o3d_sym_trans_f ( float *res, const float *A, const float *M ) {
/* this code could be optimized at some point */
float tmp[9], tmpout[9];
_tijk_2o3d_sym_convert_f( tmp, tijk_2o3d_unsym, A );
_tijk_2o3d_unsym_trans_f( tmpout, tmp, M );
_tijk_2o3d_unsym_approx_f( res, tijk_2o3d_sym, tmpout );
}
double
_tijk_2o3d_sym_s_form_d ( const double *A, const double *v ) {
return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+2*A[2]*v[0]*v[2]+
A[3]*v[1]*v[1]+2*A[4]*v[1]*v[2]+A[5]*v[2]*v[2];
}
float
_tijk_2o3d_sym_s_form_f ( const float *A, const float *v ) {
return A[0]*v[0]*v[0]+2*A[1]*v[0]*v[1]+2*A[2]*v[0]*v[2]+
A[3]*v[1]*v[1]+2*A[4]*v[1]*v[2]+A[5]*v[2]*v[2];
}
double
_tijk_2o3d_sym_mean_d ( const double *A ) {
return ( A[0]+A[3]+A[5] )/3.0;
}
float
_tijk_2o3d_sym_mean_f ( const float *A ) {
return ( A[0]+A[3]+A[5] )/3.0f;
}
double
_tijk_2o3d_sym_var_d ( const double *A ) {
return 4.0/45.0*( A[0]*A[0]+A[3]*A[3]+A[5]*A[5]+
3*( A[1]*A[1]+A[2]*A[2]+A[4]*A[4] )-
A[3]*A[5]-A[0]*( A[3]+A[5] ) );
}
float
_tijk_2o3d_sym_var_f ( const float *A ) {
return 4.0f/45.0f*( A[0]*A[0]+A[3]*A[3]+A[5]*A[5]+
3.0f*( A[1]*A[1]+A[2]*A[2]+A[4]*A[4] )-
A[3]*A[5]-A[0]*( A[3]+A[5] ) );
}
void
_tijk_2o3d_sym_v_form_d ( double *res, const double *A, const double *v ) {
res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2];
res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2];
res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2];
}
void
_tijk_2o3d_sym_v_form_f ( float *res, const float *A, const float *v ) {
res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2];
res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2];
res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2];
}
void
_tijk_2o3d_sym_m_form_d ( double *res, const double *A, const double *v ) {
( void ) v; /* v is only used in higher-order cases */
ELL_3V_COPY( res, A ); ELL_3V_COPY( res+3, A+3 );
}
void
_tijk_2o3d_sym_m_form_f ( float *res, const float *A, const float *v ) {
( void ) v; /* v is only used in higher-order cases */
ELL_3V_COPY( res, A ); ELL_3V_COPY( res+3, A+3 );
}
void
_tijk_2o3d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[0]*v[2];
res[3]=s*v[1]*v[1]; res[4]=s*v[1]*v[2]; res[5]=s*v[2]*v[2];
}
void
_tijk_2o3d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
res[0]=s*v[0]*v[0]; res[1]=s*v[0]*v[1]; res[2]=s*v[0]*v[2];
res[3]=s*v[1]*v[1]; res[4]=s*v[1]*v[2]; res[5]=s*v[2]*v[2];
}
void
_tijk_2o3d_sym_make_iso_d ( double *res, const double s ) {
res[0]=res[3]=res[5]=s;
res[1]=res[2]=res[4]=0;
}
void
_tijk_2o3d_sym_make_iso_f ( float *res, const float s ) {
res[0]=res[3]=res[5]=s;
res[1]=res[2]=res[4]=0;
}
void
_tijk_2o3d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[3];
res[0]=2*( A[0]*v[0]+A[1]*v[1]+A[2]*v[2] );
res[1]=2*( A[1]*v[0]+A[3]*v[1]+A[4]*v[2] );
res[2]=2*( A[2]*v[0]+A[4]*v[1]+A[5]*v[2] );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
void
_tijk_2o3d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[3];
res[0]=2.0f*( A[0]*v[0]+A[1]*v[1]+A[2]*v[2] );
res[1]=2.0f*( A[1]*v[0]+A[3]*v[1]+A[4]*v[2] );
res[2]=2.0f*( A[2]*v[0]+A[4]*v[1]+A[5]*v[2] );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
#define _TIJK_2O3D_SYM_HESS( TYPE, SUF ) \
void \
_tijk_2o3d_sym_hess_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
/* get two orthonormal tangents */ \
TYPE t[2][3], h[4], der, norm, tmp[6]; \
int r, c; \
ell_3v_perp_##SUF( t[0], v ); \
ELL_3V_NORM( t[0], t[0], norm ); \
ELL_3V_CROSS( t[1], v, t[0] ); \
ELL_3V_NORM( t[1], t[1], norm ); \
/* compute Hessian w.r.t. t1/t2 */ \
der=2*_tijk_2o3d_sym_s_form_##SUF( A, v ); /* first der in direction v*/ \
h[0]=2*_tijk_2o3d_sym_s_form_##SUF( A, t[0] )-der; \
h[1]=2*( A[0]*t[0][0]*t[1][0]+A[1]*t[0][0]*t[1][1]+A[2]*t[0][0]*t[1][2]+ \
A[1]*t[0][1]*t[1][0]+A[3]*t[0][1]*t[1][1]+A[4]*t[0][1]*t[1][2]+ \
A[2]*t[0][2]*t[1][0]+A[4]*t[0][2]*t[1][1]+A[5]*t[0][2]*t[1][2] ); \
h[2]=h[1]; \
h[3]=2*_tijk_2o3d_sym_s_form_##SUF( A, t[1] )-der; \
/* now turn this into a symmetric order-2 rank-2 3D tensor */ \
for ( r=0; r<2; r++ ) { \
for ( c=0; c<3; c++ ) { \
tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \
} \
} \
res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \
res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \
res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \
res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \
res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \
res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \
}
_TIJK_2O3D_SYM_HESS( double, d )
_TIJK_2O3D_SYM_HESS( float, f )
TIJK_TYPE_SYM( 2o3d_sym, 2, 3, 6 )
/* 2nd order 3D antisymmetric */
unsigned int _tijk_2o3d_asym_mult[3] = {2, 2, 2};
int _tijk_2o3d_asym_unsym2uniq[9] = {0, 1, 2, -1, 0, 3, -2, -3, 0};
int _tijk_2o3d_asym_uniq2unsym[6] = {2, -4, 3, -7, 6, -8};
unsigned int _tijk_2o3d_asym_uniq_idx[3] = {0, 2, 4};
double
_tijk_2o3d_asym_tsp_d ( const double *A, const double *B ) {
return 2*ELL_3V_DOT( A, B );
}
float
_tijk_2o3d_asym_tsp_f ( const float *A, const float *B ) {
return 2*ELL_3V_DOT( A, B );
}
double
_tijk_2o3d_asym_norm_d ( const double *A ) {
return sqrt( 2*ELL_3V_DOT( A, A ) );
}
float
_tijk_2o3d_asym_norm_f ( const float *A ) {
return sqrt( 2*ELL_3V_DOT( A, A ) );
}
#define _TIJK_2O3D_ASYM_CONVERT( TYPE, SUF ) \
int \
_tijk_2o3d_asym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_2o3d_asym ) { /* copy over */ \
ELL_3V_COPY( res, A ); \
return 0; \
} else if ( res_type==tijk_2o3d_unsym ) { \
res[0]=0; res[1]=A[0]; res[2]=A[1]; \
res[3]=-A[0]; res[4]=0; res[5]=A[2]; \
res[6]=-A[1]; res[7]=-A[2]; res[8]=0; \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_2o3d_asym ); \
else \
return 1; \
}
_TIJK_2O3D_ASYM_CONVERT( double, d )
_TIJK_2O3D_ASYM_CONVERT( float, f )
#define _TIJK_2O3D_ASYM_APPROX( TYPE, SUF ) \
int \
_tijk_2o3d_asym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_2o3d_asym ); \
else \
return 1; \
}
_TIJK_2O3D_ASYM_APPROX( double, d )
_TIJK_2O3D_ASYM_APPROX( float, f )
void
_tijk_2o3d_asym_trans_d ( double *res, const double *A, const double *M ) {
/* this code could be optimized at some point */
double tmp[9], tmpout[9];
_tijk_2o3d_asym_convert_d( tmp, tijk_2o3d_unsym, A );
_tijk_2o3d_unsym_trans_d( tmpout, tmp, M );
_tijk_2o3d_unsym_approx_d( res, tijk_2o3d_asym, tmpout );
}
void
_tijk_2o3d_asym_trans_f ( float *res, const float *A, const float *M ) {
/* this code could be optimized at some point */
float tmp[9], tmpout[9];
_tijk_2o3d_asym_convert_f( tmp, tijk_2o3d_unsym, A );
_tijk_2o3d_unsym_trans_f( tmpout, tmp, M );
_tijk_2o3d_unsym_approx_f( res, tijk_2o3d_asym, tmpout );
}
TIJK_TYPE( 2o3d_asym, 2, 3, 3 )
/* 3rd order 3D unsymmetric */
double
_tijk_3o3d_unsym_tsp_d ( const double *A, const double *B ) {
double retval=0;
unsigned int i;
for ( i=0; i<27; i++ )
retval+=( *A++ )*( *B++ );
return retval;
}
float
_tijk_3o3d_unsym_tsp_f ( const float *A, const float *B ) {
float retval=0;
unsigned int i;
for ( i=0; i<27; i++ )
retval+=( *A++ )*( *B++ );
return retval;
}
double
_tijk_3o3d_unsym_norm_d ( const double *A ) {
return sqrt( _tijk_3o3d_unsym_tsp_d( A, A ) );
}
float
_tijk_3o3d_unsym_norm_f ( const float *A ) {
return sqrt( _tijk_3o3d_unsym_tsp_f( A, A ) );
}
void
_tijk_3o3d_unsym_trans_d ( double *res, const double *A, const double *M ) {
double _tmp1[27], _tmp2[27];
unsigned int i, j, k;
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
_tmp1[3*( 3*i+j )+k]=M[3*k]*A[3*( 3*i+j )]+
M[3*k+1]*A[3*( 3*i+j )+1]+
M[3*k+2]*A[3*( 3*i+j )+2];
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
_tmp2[3*( 3*i+j )+k]=M[3*j]*_tmp1[3*( 3*i )+k]+
M[3*j+1]*_tmp1[3*( 3*i+1 )+k]+
M[3*j+2]*_tmp1[3*( 3*i+2 )+k];
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
res[3*( 3*i+j )+k]=M[3*i]*_tmp2[3*j+k]+
M[3*i+1]*_tmp2[3*( 3+j )+k]+
M[3*i+2]*_tmp2[3*( 6+j )+k];
}
void
_tijk_3o3d_unsym_trans_f ( float *res, const float *A, const float *M ) {
float _tmp1[27], _tmp2[27];
unsigned int i, j, k;
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
_tmp1[3*( 3*i+j )+k]=M[3*k]*A[3*( 3*i+j )]+
M[3*k+1]*A[3*( 3*i+j )+1]+
M[3*k+2]*A[3*( 3*i+j )+2];
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
_tmp2[3*( 3*i+j )+k]=M[3*j]*_tmp1[3*( 3*i )+k]+
M[3*j+1]*_tmp1[3*( 3*i+1 )+k]+
M[3*j+2]*_tmp1[3*( 3*i+2 )+k];
for ( i=0; i<3; i++ )
for ( j=0; j<3; j++ )
for ( k=0; k<3; k++ )
res[3*( 3*i+j )+k]=M[3*i]*_tmp2[3*j+k]+
M[3*i+1]*_tmp2[3*( 3+j )+k]+
M[3*i+2]*_tmp2[3*( 6+j )+k];
}
#define _TIJK_3O3D_UNSYM_CONVERT( TYPE, SUF ) \
int \
_tijk_3o3d_unsym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_3o3d_unsym ) { /* copy over */ \
unsigned int i; \
for ( i=0; i<27; i++ ) \
( *res++ )=( *A++ ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_3o3d_unsym ); \
else \
return 1; \
}
_TIJK_3O3D_UNSYM_CONVERT( double, d )
_TIJK_3O3D_UNSYM_CONVERT( float, f )
#define _TIJK_3O3D_UNSYM_APPROX( TYPE, SUF ) \
int \
_tijk_3o3d_unsym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_3o3d_sym ) { \
res[0]=A[0]; res[1]=( A[1]+A[3]+A[9] )/3.0; \
res[2]=( A[2]+A[6]+A[18] )/3.0; res[3]=( A[4]+A[10]+A[12] )/3.0; \
res[4]=( A[5]+A[7]+A[11]+A[15]+A[19]+A[21] )/6.0; \
res[5]=( A[8]+A[20]+A[24] )/3.0; res[6]=A[13]; \
res[7]=( A[14]+A[16]+A[25] )/3.0; res[8]=( A[17]+A[23]+A[25] )/3.0; \
res[9]=A[26]; \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_3o3d_unsym ); \
else \
return 1; \
}
_TIJK_3O3D_UNSYM_APPROX( double, d )
_TIJK_3O3D_UNSYM_APPROX( float, f )
TIJK_TYPE_UNSYM( 3o3d_unsym, 3, 3, 27 )
/* 3rd order 3D symmetric */
unsigned int _tijk_3o3d_sym_mult[10]={1, 3, 3, 3, 6, 3, 1, 3, 3, 1};
int _tijk_3o3d_sym_unsym2uniq[27] = {1, 2, 3, 2, 4, 5, 3, 5, 6,
2, 4, 5, 4, 7, 8, 5, 8, 9,
3, 5, 6, 5, 8, 9, 6, 9, 10};
int _tijk_3o3d_sym_uniq2unsym[27] = {1, 2, 4, 10, 3, 7, 19, 5, 11, 13,
6, 8, 12, 16, 20, 22, 9, 21, 25, 14,
15, 17, 23, 18, 24, 26, 27};
unsigned int _tijk_3o3d_sym_uniq_idx[10] = {0, 1, 4, 7, 10, 16, 19, 20, 23, 26};
#define _TIJK_3O3D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+( A )[6]*( B )[6]+( A )[9]*( B )[9]+ \
3*( ( A )[1]*( B )[1]+( A )[2]*( B )[2]+( A )[3]*( B )[3]+( A )[5]*( B )[5]+ \
( A )[7]*( B )[7]+( A )[8]*( B )[8] )+ \
6*( A )[4]*( B )[4] )
double
_tijk_3o3d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_3O3D_SYM_TSP( A, B );
}
float
_tijk_3o3d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_3O3D_SYM_TSP( A, B );
}
double
_tijk_3o3d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_3O3D_SYM_TSP( A, A ) );
}
float
_tijk_3o3d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_3O3D_SYM_TSP( A, A ) );
}
#define _TIJK_3O3D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_3o3d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_3o3d_sym ) { /* copy over */ \
tijk_copy_##SUF( res, A, tijk_3o3d_sym ); \
return 0; \
} else if ( res_type==tijk_3o3d_unsym ) { \
unsigned int i; \
for ( i=0; i<27; i++ ) { \
res[i]=A[_tijk_3o3d_sym_unsym2uniq[i]-1]; \
} \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_3o3d_sym ); \
return 1; \
}
_TIJK_3O3D_SYM_CONVERT( double, d )
_TIJK_3O3D_SYM_CONVERT( float, f )
#define _TIJK_3O3D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_3o3d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ){ \
if ( res_type==tijk_1o3d ) { \
res[0]=0.6*( A[0]+A[3]+A[5] ); \
res[1]=0.6*( A[1]+A[6]+A[8] ); \
res[2]=0.6*( A[2]+A[7]+A[9] ); \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_3o3d_sym ); \
else \
return 1; \
}
_TIJK_3O3D_SYM_APPROX( double, d )
_TIJK_3O3D_SYM_APPROX( float, f )
void
_tijk_3o3d_sym_trans_d ( double *res, const double *A, const double *M ) {
/* this code could be optimized at some point */
double tmp[27], tmpout[27];
_tijk_3o3d_sym_convert_d( tmp, tijk_3o3d_unsym, A );
_tijk_3o3d_unsym_trans_d( tmpout, tmp, M );
_tijk_3o3d_unsym_approx_d( res, tijk_3o3d_sym, tmpout );
}
void
_tijk_3o3d_sym_trans_f ( float *res, const float *A, const float *M ) {
/* this code could be optimized at some point */
float tmp[27], tmpout[27];
_tijk_3o3d_sym_convert_f( tmp, tijk_3o3d_unsym, A );
_tijk_3o3d_unsym_trans_f( tmpout, tmp, M );
_tijk_3o3d_unsym_approx_f( res, tijk_3o3d_sym, tmpout );
}
double
_tijk_3o3d_sym_s_form_d ( const double *A, const double *v ) {
double v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2];
return A[0]*v00*v[0]+3*A[1]*v00*v[1]+3*A[2]*v00*v[2]+3*A[3]*v11*v[0]+
6*A[4]*v[0]*v[1]*v[2]+3*A[5]*v22*v[0]+A[6]*v11*v[1]+3*A[7]*v11*v[2]+
3*A[8]*v[1]*v22+A[9]*v22*v[2];
}
float
_tijk_3o3d_sym_s_form_f ( const float *A, const float *v ) {
float v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2];
return A[0]*v00*v[0]+3*A[1]*v00*v[1]+3*A[2]*v00*v[2]+3*A[3]*v11*v[0]+
6*A[4]*v[0]*v[1]*v[2]+3*A[5]*v22*v[0]+A[6]*v11*v[1]+3*A[7]*v11*v[2]+
3*A[8]*v[1]*v22+A[9]*v22*v[2];
}
double
_tijk_3o3d_sym_mean_d ( const double *A ) {
( void ) A; /* odd order; mean does not depend on coeffs */
return 0.0;
}
float
_tijk_3o3d_sym_mean_f ( const float *A ) {
( void ) A; /* odd order; mean does not depend on coeffs */
return 0.0f;
}
double
_tijk_3o3d_sym_var_d ( const double *A ) {
/* numerical result taken from MATHEMATICA */
return 1.0/35.0*( 5*( A[0]*A[0]+A[6]*A[6]+A[9]*A[9] )+
6*( A[3]*A[5]+A[2]*( A[7]+A[9] )+A[6]*A[8]+A[0]*( A[3]+A[5] )+
A[1]*( A[6]+A[8] )+A[7]*A[9] )+
9*( A[1]*A[1]+A[2]*A[2]+A[3]*A[3]+A[5]*A[5]+A[7]*A[7]+
A[8]*A[8] )+
12*A[4]*A[4] );
}
float
_tijk_3o3d_sym_var_f ( const float *A ) {
/* numerical result taken from MATHEMATICA */
return 1.0/35.0*( 5*( A[0]*A[0]+A[6]*A[6]+A[9]*A[9] )+
6*( A[3]*A[5]+A[2]*( A[7]+A[9] )+A[6]*A[8]+A[0]*( A[3]+A[5] )+
A[1]*( A[6]+A[8] )+A[7]*A[9] )+
9*( A[1]*A[1]+A[2]*A[2]+A[3]*A[3]+A[5]*A[5]+A[7]*A[7]+
A[8]*A[8] )+
12*A[4]*A[4] );
}
void
_tijk_3o3d_sym_v_form_d ( double *res, const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0] = A[0]*v00+A[3]*v11+A[5]*v22+ 2*( A[1]*v01+A[2]*v02+A[4]*v12 );
res[1] = A[1]*v00+A[6]*v11+A[8]*v22+ 2*( A[3]*v01+A[4]*v02+A[7]*v12 );
res[2] = A[2]*v00+A[7]*v11+A[9]*v22+ 2*( A[4]*v01+A[5]*v02+A[8]*v12 );
}
void
_tijk_3o3d_sym_v_form_f ( float *res, const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0] = A[0]*v00+A[3]*v11+A[5]*v22+ 2*( A[1]*v01+A[2]*v02+A[4]*v12 );
res[1] = A[1]*v00+A[6]*v11+A[8]*v22+ 2*( A[3]*v01+A[4]*v02+A[7]*v12 );
res[2] = A[2]*v00+A[7]*v11+A[9]*v22+ 2*( A[4]*v01+A[5]*v02+A[8]*v12 );
}
void
_tijk_3o3d_sym_m_form_d ( double *res, const double *A, const double *v ) {
res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2];
res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2];
res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2];
res[3]=A[3]*v[0]+A[6]*v[1]+A[7]*v[2];
res[4]=A[4]*v[0]+A[7]*v[1]+A[8]*v[2];
res[5]=A[5]*v[0]+A[8]*v[1]+A[9]*v[2];
}
void
_tijk_3o3d_sym_m_form_f ( float *res, const float *A, const float *v ) {
res[0]=A[0]*v[0]+A[1]*v[1]+A[2]*v[2];
res[1]=A[1]*v[0]+A[3]*v[1]+A[4]*v[2];
res[2]=A[2]*v[0]+A[4]*v[1]+A[5]*v[2];
res[3]=A[3]*v[0]+A[6]*v[1]+A[7]*v[2];
res[4]=A[4]*v[0]+A[7]*v[1]+A[8]*v[2];
res[5]=A[5]*v[0]+A[8]*v[1]+A[9]*v[2];
}
void
_tijk_3o3d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
double v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2];
res[0]=s*v00*v[0]; res[1]=s*v00*v[1]; res[2]=s*v00*v[2];
res[3]=s*v[0]*v11; res[4]=s*v[0]*v[1]*v[2]; res[5]=s*v[0]*v22;
res[6]=s*v[1]*v11; res[7]=s*v11*v[2]; res[8]=s*v[1]*v22;
res[9]=s*v[2]*v22;
}
void
_tijk_3o3d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
float v00=v[0]*v[0], v11=v[1]*v[1], v22=v[2]*v[2];
res[0]=s*v00*v[0]; res[1]=s*v00*v[1]; res[2]=s*v00*v[2];
res[3]=s*v[0]*v11; res[4]=s*v[0]*v[1]*v[2]; res[5]=s*v[0]*v22;
res[6]=s*v[1]*v11; res[7]=s*v11*v[2]; res[8]=s*v[1]*v22;
res[9]=s*v[2]*v22;
}
#define _tijk_3o3d_sym_make_iso_d NULL
#define _tijk_3o3d_sym_make_iso_f NULL
void
_tijk_3o3d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[3];
_tijk_3o3d_sym_v_form_d ( res, A, v );
ELL_3V_SCALE( res, 3.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
void
_tijk_3o3d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[3];
_tijk_3o3d_sym_v_form_f ( res, A, v );
ELL_3V_SCALE( res, 3.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
#define _TIJK_3O3D_SYM_HESS( TYPE, SUF ) \
void \
_tijk_3o3d_sym_hess_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
/* get two orthonormal tangents */ \
TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \
int r, c; \
ell_3v_perp_##SUF( t[0], v ); \
ELL_3V_NORM( t[0], t[0], norm ); \
ELL_3V_CROSS( t[1], v, t[0] ); \
ELL_3V_NORM( t[1], t[1], norm ); \
/* compute Hessian w.r.t. t1/t2 */ \
_tijk_3o3d_sym_m_form_##SUF( h, A, v ); \
der=3*_tijk_3o3d_sym_s_form_##SUF( A, v ); /* first der in direction v*/ \
_tijk_2o3d_sym_v_form_##SUF( cv[0], h, t[0] ); \
_tijk_2o3d_sym_v_form_##SUF( cv[1], h, t[1] ); \
h[0]=6*ELL_3V_DOT( cv[0], t[0] )-der; \
h[1]=6*ELL_3V_DOT( cv[0], t[1] ); \
h[2]=h[1]; \
h[3]=6*ELL_3V_DOT( cv[1], t[1] )-der; \
/* now turn this into a symmetric order-2 rank-2 3D tensor */ \
for ( r=0; r<2; r++ ) { \
for ( c=0; c<3; c++ ) { \
tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \
} \
} \
res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \
res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \
res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \
res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \
res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \
res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \
}
_TIJK_3O3D_SYM_HESS( double, d )
_TIJK_3O3D_SYM_HESS( float, f )
TIJK_TYPE_SYM( 3o3d_sym, 3, 3, 10 )
/* 4th order 3D symmetric */
/* ( unsymmetric counterpart currently not implemented ) */
unsigned int _tijk_4o3d_sym_mult[15]={1, 4, 4, 6, 12, 6, 4, 12, 12, 4, 1, 4, 6, 4, 1};
#define _tijk_4o3d_sym_unsym2uniq NULL
#define _tijk_4o3d_sym_uniq2unsym NULL
#define _tijk_4o3d_sym_uniq_idx NULL
#define _TIJK_4O3D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+( A )[10]*( B )[10]+( A )[14]*( B )[14]+ \
4*( ( A )[1]*( B )[1]+( A )[2]*( B )[2]+( A )[6]*( B )[6]+( A )[9]*( B )[9]+ \
( A )[11]*( B )[11]+( A )[13]*( B )[13] )+ \
6*( ( A )[3]*( B )[3]+( A )[5]*( B )[5]+( A )[12]*( B )[12] )+ \
12*( ( A )[4]*( B )[4]+( A )[7]*( B )[7]+( A )[8]*( B )[8] ) )
double
_tijk_4o3d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_4O3D_SYM_TSP( A, B );
}
float
_tijk_4o3d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_4O3D_SYM_TSP( A, B );
}
double
_tijk_4o3d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_4O3D_SYM_TSP( A, A ) );
}
float
_tijk_4o3d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_4O3D_SYM_TSP( A, A ) );
}
#define _TIJK_4O3D_SYM_TRANS( TYPE, SUF ) \
void \
_tijk_4o3d_sym_trans_##SUF ( TYPE *res, const TYPE *A, const TYPE *M ) { \
/* Tijkl = Mim Mjn Mko Mlp Tmnop \
* For efficiency, we transform mode by mode; the intermediate results \
* have incomplete symmetries! */ \
TYPE tmps[30], tmpl[36]; \
int i; \
{ /* mode 4 */ \
int m[30]={0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6, 0, 3, 6}; \
int idx[90]={0, 1, 2, 0, 1, 2, 0, 1, 2, 1, 3, 4, 1, 3, 4, 1, 3, 4, 2, 4, 5, 2, 4, 5, 2, 4, 5, 3, 6, 7, 3, 6, 7, 3, 6, 7, 4, 7, 8, 4, 7, 8, 4, 7, 8, 5, 8, 9, 5, 8, 9, 5, 8, 9, 6, 10, 11, 6, 10, 11, 6, 10, 11, 7, 11, 12, 7, 11, 12, 7, 11, 12, 8, 12, 13, 8, 12, 13, 8, 12, 13, 9, 13, 14, 9, 13, 14, 9, 13, 14}; \
for ( i=0; i<30; i++ ) \
tmps[i]=M[m[i]]*A[idx[3*i]]+ \
M[m[i]+1]*A[idx[3*i+1]]+ \
M[m[i]+2]*A[idx[3*i+2]]; \
} \
{ /* mode 3 */ \
int m[36]={0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6}; \
int idx[108]={0, 3, 6, 1, 4, 7, 2, 5, 8, 1, 4, 7, 2, 5, 8, 2, 5, 8, 3, 9, 12, 4, 10, 13, 5, 11, 14, 4, 10, 13, 5, 11, 14, 5, 11, 14, 6, 12, 15, 7, 13, 16, 8, 14, 17, 7, 13, 16, 8, 14, 17, 8, 14, 17, 9, 18, 21, 10, 19, 22, 11, 20, 23, 10, 19, 22, 11, 20, 23, 11, 20, 23, 12, 21, 24, 13, 22, 25, 14, 23, 26, 13, 22, 25, 14, 23, 26, 14, 23, 26, 15, 24, 27, 16, 25, 28, 17, 26, 29, 16, 25, 28, 17, 26, 29, 17, 26, 29}; \
for ( i=0; i<36; i++ ) \
tmpl[i]=M[m[i]]*tmps[idx[3*i]]+ \
M[m[i]+1]*tmps[idx[3*i+1]]+ \
M[m[i]+2]*tmps[idx[3*i+2]]; \
} \
{ /* mode 2 */ \
int m[30]={0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6}; \
int idx[90]={0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 3, 9, 15, 4, 10, 16, 5, 11, 17, 5, 11, 17, 6, 18, 24, 7, 19, 25, 8, 20, 26, 9, 21, 27, 10, 22, 28, 11, 23, 29, 9, 21, 27, 10, 22, 28, 11, 23, 29, 11, 23, 29, 12, 24, 30, 13, 25, 31, 14, 26, 32, 15, 27, 33, 16, 28, 34, 17, 29, 35, 15, 27, 33, 16, 28, 34, 17, 29, 35, 17, 29, 35}; \
for ( i=0; i<30; i++ ) \
tmps[i]=M[m[i]]*tmpl[idx[3*i]]+ \
M[m[i]+1]*tmpl[idx[3*i+1]]+ \
M[m[i]+2]*tmpl[idx[3*i+2]]; \
} \
{ /* mode 1 */ \
int m[15]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6}; \
int idx[45]={0, 10, 20, 1, 11, 21, 2, 12, 22, 3, 13, 23, 4, 14, 24, 5, 15, 25, 6, 16, 26, 7, 17, 27, 8, 18, 28, 9, 19, 29, 6, 16, 26, 7, 17, 27, 8, 18, 28, 9, 19, 29, 9, 19, 29}; \
for ( i=0; i<15; i++ ) \
res[i]=M[m[i]]*tmps[idx[3*i]]+ \
M[m[i]+1]*tmps[idx[3*i+1]]+ \
M[m[i]+2]*tmps[idx[3*i+2]]; \
} \
}
_TIJK_4O3D_SYM_TRANS( double, d )
_TIJK_4O3D_SYM_TRANS( float, f )
#define _TIJK_4O3D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_4o3d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_4o3d_sym ) { /* copy over */ \
tijk_copy_##SUF( res, A, tijk_4o3d_sym ); \
return 0; \
} else if ( res_type==tijk_6o3d_sym ) { \
/* do this by going to SH and zero-padding */ \
TYPE tmp[28]; \
memset( tmp, 0, sizeof( tmp ) ); \
tijk_3d_sym_to_esh_##SUF ( tmp, A, tijk_4o3d_sym ); \
tijk_esh_to_3d_sym_##SUF ( res, tmp, res_type->order ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_4o3d_sym ); \
return 1; \
}
_TIJK_4O3D_SYM_CONVERT( double, d )
_TIJK_4O3D_SYM_CONVERT( float, f )
#define _TIJK_4O3D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_4o3d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ){ \
if ( res_type==tijk_2o3d_sym ) { \
res[0]=3.0/35.0*( 9*A[0]+8*A[3]+8*A[5]-A[10]-A[14]-2*A[12] ); \
res[1]=6.0/7.0*( A[1]+A[6]+A[8] ); \
res[2]=6.0/7.0*( A[2]+A[9]+A[7] ); \
res[3]=3.0/35.0*( 9*A[10]+8*A[3]+8*A[12]-A[0]-A[14]-2*A[5] ); \
res[4]=6.0/7.0*( A[11]+A[13]+A[4] ); \
res[5]=3.0/35.0*( 9*A[14]+8*A[5]+8*A[12]-A[0]-A[10]-2*A[3] ); \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_4o3d_sym ); \
else \
return 1; \
}
_TIJK_4O3D_SYM_APPROX( double, d )
_TIJK_4O3D_SYM_APPROX( float, f )
double
_tijk_4o3d_sym_s_form_d ( const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
return A[0]*v00*v00+4*A[1]*v00*v01+4*A[2]*v00*v02+6*A[3]*v00*v11+
12*A[4]*v00*v12+6*A[5]*v00*v22+4*A[6]*v01*v11+12*A[7]*v01*v12+
12*A[8]*v01*v22+4*A[9]*v02*v22+A[10]*v11*v11+4*A[11]*v11*v12+
6*A[12]*v11*v22+4*A[13]*v12*v22+A[14]*v22*v22;
}
float
_tijk_4o3d_sym_s_form_f ( const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
return A[0]*v00*v00+4*A[1]*v00*v01+4*A[2]*v00*v02+6*A[3]*v00*v11+
12*A[4]*v00*v12+6*A[5]*v00*v22+4*A[6]*v01*v11+12*A[7]*v01*v12+
12*A[8]*v01*v22+4*A[9]*v02*v22+A[10]*v11*v11+4*A[11]*v11*v12+
6*A[12]*v11*v22+4*A[13]*v12*v22+A[14]*v22*v22;
}
double
_tijk_4o3d_sym_mean_d ( const double *A ) {
return 0.2*( A[0]+A[10]+A[14]+2*( A[3]+A[5]+A[12] ) );
}
float
_tijk_4o3d_sym_mean_f ( const float *A ) {
return 0.2f*( A[0]+A[10]+A[14]+2.0f*( A[3]+A[5]+A[12] ) );
}
double
_tijk_4o3d_sym_var_d ( const double *A ) {
/* numerical result taken from MATHEMATICA */
return 0.0795775*( -1.5319*( A[14]*A[3]+A[10]*A[5]+A[0]*A[12] )
-1.14893*( A[12]*A[3]+A[12]*A[5]+A[3]*A[5] )
-0.76595*( A[10]*A[14]+A[10]*A[0]+A[0]*A[14] )
+0.382975*( A[10]*A[12]+A[12]*A[14]+A[10]*A[3]+A[14]*A[5]+
A[0]*A[3]+A[0]*A[5] )
+0.893609*( A[0]*A[0]+A[10]*A[10]+A[14]*A[14] )
+2.29785*( A[12]*A[12] + A[3]*A[3] + A[5]*A[5] )
+3.19146*( A[1]*A[1]+A[2]*A[2]+A[11]*A[11]+A[13]*A[13]+
A[6]*A[6]+A[9]*A[9] )
+3.82975*( A[11]*A[13]+A[11]*A[4]+A[13]*A[4]+A[1]*A[6]+
A[2]*A[7]+A[1]*A[8]+A[6]*A[8]+A[2]*A[9]+
A[7]*A[9] )
+5.74463*( A[4]*A[4]+A[7]*A[7]+A[8]*A[8] ) );
}
float
_tijk_4o3d_sym_var_f ( const float *A ) {
/* numerical result taken from MATHEMATICA */
return 0.0795775*( -1.5319*( A[14]*A[3]+A[10]*A[5]+A[0]*A[12] )
-1.14893*( A[12]*A[3]+A[12]*A[5]+A[3]*A[5] )
-0.76595*( A[10]*A[14]+A[10]*A[0]+A[0]*A[14] )
+0.382975*( A[10]*A[12]+A[12]*A[14]+A[10]*A[3]+A[14]*A[5]+
A[0]*A[3]+A[0]*A[5] )
+0.893609*( A[0]*A[0]+A[10]*A[10]+A[14]*A[14] )
+2.29785*( A[12]*A[12] + A[3]*A[3] + A[5]*A[5] )
+3.19146*( A[1]*A[1]+A[2]*A[2]+A[11]*A[11]+A[13]*A[13]+
A[6]*A[6]+A[9]*A[9] )
+3.82975*( A[11]*A[13]+A[11]*A[4]+A[13]*A[4]+A[1]*A[6]+
A[2]*A[7]+A[1]*A[8]+A[6]*A[8]+A[2]*A[9]+
A[7]*A[9] )
+5.74463*( A[4]*A[4]+A[7]*A[7]+A[8]*A[8] ) );
}
void
_tijk_4o3d_sym_v_form_d ( double *res, const double *A, const double *v ) {
double v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v002=v[0]*v[0]*v[2],
v011=v[0]*v[1]*v[1], v012=v[0]*v[1]*v[2], v022=v[0]*v[2]*v[2],
v111=v[1]*v[1]*v[1], v112=v[1]*v[1]*v[2], v122=v[1]*v[2]*v[2],
v222=v[2]*v[2]*v[2];
res[0] = A[0]*v000+A[6]*v111+A[9]*v222+6*A[4]*v012+
3*( A[1]*v001+A[2]*v002+A[3]*v011+A[5]*v022+A[7]*v112+A[8]*v122 );
res[1] = A[1]*v000+A[10]*v111+A[13]*v222+6*A[7]*v012+
3*( A[3]*v001+A[4]*v002+A[6]*v011+A[8]*v022+A[11]*v112+A[12]*v122 );
res[2] = A[2]*v000+A[11]*v111+A[14]*v222+6*A[8]*v012+
3*( A[4]*v001+A[5]*v002+A[7]*v011+A[9]*v022+A[12]*v112+A[13]*v122 );
}
void
_tijk_4o3d_sym_v_form_f ( float *res, const float *A, const float *v ) {
float v000=v[0]*v[0]*v[0], v001=v[0]*v[0]*v[1], v002=v[0]*v[0]*v[2],
v011=v[0]*v[1]*v[1], v012=v[0]*v[1]*v[2], v022=v[0]*v[2]*v[2],
v111=v[1]*v[1]*v[1], v112=v[1]*v[1]*v[2], v122=v[1]*v[2]*v[2],
v222=v[2]*v[2]*v[2];
res[0] = A[0]*v000+A[6]*v111+A[9]*v222+6*A[4]*v012+
3*( A[1]*v001+A[2]*v002+A[3]*v011+A[5]*v022+A[7]*v112+A[8]*v122 );
res[1] = A[1]*v000+A[10]*v111+A[13]*v222+6*A[7]*v012+
3*( A[3]*v001+A[4]*v002+A[6]*v011+A[8]*v022+A[11]*v112+A[12]*v122 );
res[2] = A[2]*v000+A[11]*v111+A[14]*v222+6*A[8]*v012+
3*( A[4]*v001+A[5]*v002+A[7]*v011+A[9]*v022+A[12]*v112+A[13]*v122 );
}
void
_tijk_4o3d_sym_m_form_d ( double *res, const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0]=A[0]*v00+A[3]*v11+A[5]*v22+2*( A[1]*v01+A[2]*v02+A[4]*v12 );
res[1]=A[1]*v00+A[6]*v11+A[8]*v22+2*( A[3]*v01+A[4]*v02+A[7]*v12 );
res[2]=A[2]*v00+A[7]*v11+A[9]*v22+2*( A[4]*v01+A[5]*v02+A[8]*v12 );
res[3]=A[3]*v00+A[10]*v11+A[12]*v22+2*( A[6]*v01+A[7]*v02+A[11]*v12 );
res[4]=A[4]*v00+A[11]*v11+A[13]*v22+2*( A[7]*v01+A[8]*v02+A[12]*v12 );
res[5]=A[5]*v00+A[12]*v11+A[14]*v22+2*( A[8]*v01+A[9]*v02+A[13]*v12 );
}
void
_tijk_4o3d_sym_m_form_f ( float *res, const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0]=A[0]*v00+A[3]*v11+A[5]*v22+2*( A[1]*v01+A[2]*v02+A[4]*v12 );
res[1]=A[1]*v00+A[6]*v11+A[8]*v22+2*( A[3]*v01+A[4]*v02+A[7]*v12 );
res[2]=A[2]*v00+A[7]*v11+A[9]*v22+2*( A[4]*v01+A[5]*v02+A[8]*v12 );
res[3]=A[3]*v00+A[10]*v11+A[12]*v22+2*( A[6]*v01+A[7]*v02+A[11]*v12 );
res[4]=A[4]*v00+A[11]*v11+A[13]*v22+2*( A[7]*v01+A[8]*v02+A[12]*v12 );
res[5]=A[5]*v00+A[12]*v11+A[14]*v22+2*( A[8]*v01+A[9]*v02+A[13]*v12 );
}
void
_tijk_4o3d_sym_make_rank1_d ( double *res, const double s, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v02; res[3]=s*v00*v11;
res[4]=s*v00*v12; res[5]=s*v00*v22; res[6]=s*v01*v11; res[7]=s*v01*v12;
res[8]=s*v01*v22; res[9]=s*v02*v22; res[10]=s*v11*v11; res[11]=s*v11*v12;
res[12]=s*v11*v22; res[13]=s*v12*v22; res[14]=s*v22*v22;
}
void
_tijk_4o3d_sym_make_rank1_f ( float *res, const float s, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
res[0]=s*v00*v00; res[1]=s*v00*v01; res[2]=s*v00*v02; res[3]=s*v00*v11;
res[4]=s*v00*v12; res[5]=s*v00*v22; res[6]=s*v01*v11; res[7]=s*v01*v12;
res[8]=s*v01*v22; res[9]=s*v02*v22; res[10]=s*v11*v11; res[11]=s*v11*v12;
res[12]=s*v11*v22; res[13]=s*v12*v22; res[14]=s*v22*v22;
}
void
_tijk_4o3d_sym_make_iso_d ( double *res, const double s ) {
res[0]=res[10]=res[14]=s;
res[3]=res[5]=res[12]=s/3.0;
res[1]=res[2]=res[4]=res[6]=res[7]=res[8]=res[9]=res[11]=res[13]=0.0;
}
void
_tijk_4o3d_sym_make_iso_f ( float *res, const float s ) {
res[0]=res[10]=res[14]=s;
res[3]=res[5]=res[12]=s/3.0f;
res[1]=res[2]=res[4]=res[6]=res[7]=res[8]=res[9]=res[11]=res[13]=0.0f;
}
void
_tijk_4o3d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[3];
_tijk_4o3d_sym_v_form_d ( res, A, v );
ELL_3V_SCALE( res, 4.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
void
_tijk_4o3d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[3];
_tijk_4o3d_sym_v_form_f ( res, A, v );
ELL_3V_SCALE( res, 4.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
#define _TIJK_4O3D_SYM_HESS( TYPE, SUF ) \
void \
_tijk_4o3d_sym_hess_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
/* get two orthonormal tangents */ \
TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \
int r, c; \
ell_3v_perp_##SUF( t[0], v ); \
ELL_3V_NORM( t[0], t[0], norm ); \
ELL_3V_CROSS( t[1], v, t[0] ); \
ELL_3V_NORM( t[1], t[1], norm ); \
/* compute Hessian w.r.t. t1/t2 */ \
_tijk_4o3d_sym_m_form_##SUF( h, A, v ); \
der=4*_tijk_4o3d_sym_s_form_##SUF( A, v ); /* first der in direction v*/ \
_tijk_2o3d_sym_v_form_##SUF( cv[0], h, t[0] ); \
_tijk_2o3d_sym_v_form_##SUF( cv[1], h, t[1] ); \
h[0]=12*ELL_3V_DOT( cv[0], t[0] )-der; \
h[1]=12*ELL_3V_DOT( cv[0], t[1] ); \
h[2]=h[1]; \
h[3]=12*ELL_3V_DOT( cv[1], t[1] )-der; \
/* now turn this into a symmetric order-2 rank-2 3D tensor */ \
for ( r=0; r<2; r++ ) { \
for ( c=0; c<3; c++ ) { \
tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \
} \
} \
res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \
res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \
res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \
res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \
res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \
res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \
}
_TIJK_4O3D_SYM_HESS( double, d )
_TIJK_4O3D_SYM_HESS( float, f )
TIJK_TYPE_SYM( 4o3d_sym, 4, 3, 15 )
/* 6th order 3D symmetric */
/* ( unsymmetric counterpart currently not implemented ) */
unsigned int _tijk_6o3d_sym_mult[28]={1, 6, 6, 15, 30, 15, 20, 60, 60, 20, 15, 60, 90,
60, 15, 6, 30, 60, 60, 30, 6, 1, 6, 15, 20, 15, 6, 1};
#define _tijk_6o3d_sym_unsym2uniq NULL
#define _tijk_6o3d_sym_uniq2unsym NULL
#define _tijk_6o3d_sym_uniq_idx NULL
#define _TIJK_6O3D_SYM_TSP( A, B ) \
( ( A )[0]*( B )[0]+( A )[21]*( B )[21]+( A )[27]*( B )[27]+ \
6*( ( A )[1]*( B )[1]+( A )[2]*( B )[2]+( A )[15]*( B )[15]+( A )[20]*( B )[20]+ \
( A )[22]*( B )[22]+( A )[26]*( B )[26] )+ \
15*( ( A )[3]*( B )[3]+( A )[5]*( B )[5]+( A )[10]*( B )[10]+( A )[14]*( B )[14]+ \
( A )[23]*( B )[23]+( A )[25]*( B )[25] )+ \
30*( ( A )[4]*( B )[4]+( A )[16]*( B )[16]+( A )[19]*( B )[19] )+ \
20*( ( A )[6]*( B )[6]+( A )[9]*( B )[9]+( A )[24]*( B )[24] )+ \
60*( ( A )[7]*( B )[7]+( A )[8]*( B )[8]+( A )[11]*( B )[11]+ \
( A )[13]*( B )[13]+( A )[17]*( B )[17]+( A )[18]*( B )[18] )+ \
90*( A )[12]*( B )[12] ) \
double
_tijk_6o3d_sym_tsp_d ( const double *A, const double *B ) {
return _TIJK_6O3D_SYM_TSP( A, B );
}
float
_tijk_6o3d_sym_tsp_f ( const float *A, const float *B ) {
return _TIJK_6O3D_SYM_TSP( A, B );
}
double
_tijk_6o3d_sym_norm_d ( const double *A ) {
return sqrt( _TIJK_6O3D_SYM_TSP( A, A ) );
}
float
_tijk_6o3d_sym_norm_f ( const float *A ) {
return sqrt( _TIJK_6O3D_SYM_TSP( A, A ) );
}
#define _TIJK_6O3D_SYM_TRANS( TYPE, SUF ) \
void \
_tijk_6o3d_sym_trans_##SUF ( TYPE *res, const TYPE *A, const TYPE *M ) { \
/* Tijklmn = Mio Mjp Mkq Mlr Mms Mnt Topqrst \
* For efficiency, we transform mode by mode; the intermediate results \
* have incomplete symmetries! */ \
TYPE tmpl[100], tmpr[100]; \
int i; \
{ /* mode 6 */ \
int m[3]={0, 3, 6}; \
int idx[189]={0, 1, 2, 0, 1, 2, 0, 1, 2, 1, 3, 4, 1, 3, 4, 1, 3, 4, 2, 4, 5, 2, 4, 5, 2, 4, 5, 3, 6, 7, 3, 6, 7, 3, 6, 7, 4, 7, 8, 4, 7, 8, 4, 7, 8, 5, 8, 9, 5, 8, 9, 5, 8, 9, 6, 10, 11, 6, 10, 11, 6, 10, 11, 7, 11, 12, 7, 11, 12, 7, 11, 12, 8, 12, 13, 8, 12, 13, 8, 12, 13, 9, 13, 14, 9, 13, 14, 9, 13, 14, 10, 15, 16, 10, 15, 16, 10, 15, 16, 11, 16, 17, 11, 16, 17, 11, 16, 17, 12, 17, 18, 12, 17, 18, 12, 17, 18, 13, 18, 19, 13, 18, 19, 13, 18, 19, 14, 19, 20, 14, 19, 20, 14, 19, 20, 15, 21, 22, 15, 21, 22, 15, 21, 22, 16, 22, 23, 16, 22, 23, 16, 22, 23, 17, 23, 24, 17, 23, 24, 17, 23, 24, 18, 24, 25, 18, 24, 25, 18, 24, 25, 19, 25, 26, 19, 25, 26, 19, 25, 26, 20, 26, 27, 20, 26, 27, 20, 26, 27}; \
for ( i=0; i<63; i++ ) \
tmpl[i]=M[m[i%3]]*A[idx[3*i]]+ \
M[m[i%3]+1]*A[idx[3*i+1]]+ \
M[m[i%3]+2]*A[idx[3*i+2]]; \
} \
{ /* mode 5 */ \
int m[90]={0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6, 0, 0, 0, 3, 3, 6}; \
int idx[270]={0, 3, 6, 1, 4, 7, 2, 5, 8, 1, 4, 7, 2, 5, 8, 2, 5, 8, 3, 9, 12, 4, 10, 13, 5, 11, 14, 4, 10, 13, 5, 11, 14, 5, 11, 14, 6, 12, 15, 7, 13, 16, 8, 14, 17, 7, 13, 16, 8, 14, 17, 8, 14, 17, 9, 18, 21, 10, 19, 22, 11, 20, 23, 10, 19, 22, 11, 20, 23, 11, 20, 23, 12, 21, 24, 13, 22, 25, 14, 23, 26, 13, 22, 25, 14, 23, 26, 14, 23, 26, 15, 24, 27, 16, 25, 28, 17, 26, 29, 16, 25, 28, 17, 26, 29, 17, 26, 29, 18, 30, 33, 19, 31, 34, 20, 32, 35, 19, 31, 34, 20, 32, 35, 20, 32, 35, 21, 33, 36, 22, 34, 37, 23, 35, 38, 22, 34, 37, 23, 35, 38, 23, 35, 38, 24, 36, 39, 25, 37, 40, 26, 38, 41, 25, 37, 40, 26, 38, 41, 26, 38, 41, 27, 39, 42, 28, 40, 43, 29, 41, 44, 28, 40, 43, 29, 41, 44, 29, 41, 44, 30, 45, 48, 31, 46, 49, 32, 47, 50, 31, 46, 49, 32, 47, 50, 32, 47, 50, 33, 48, 51, 34, 49, 52, 35, 50, 53, 34, 49, 52, 35, 50, 53, 35, 50, 53, 36, 51, 54, 37, 52, 55, 38, 53, 56, 37, 52, 55, 38, 53, 56, 38, 53, 56, 39, 54, 57, 40, 55, 58, 41, 56, 59, 40, 55, 58, 41, 56, 59, 41, 56, 59, 42, 57, 60, 43, 58, 61, 44, 59, 62, 43, 58, 61, 44, 59, 62, 44, 59, 62}; \
for ( i=0; i<90; i++ ) \
tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \
M[m[i]+1]*tmpl[idx[3*i+1]]+ \
M[m[i]+2]*tmpl[idx[3*i+2]]; \
} \
{ /* mode 4 */ \
int m[100]={0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 3, 3, 3, 6}; \
int idx[300]={0, 6, 12, 1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 3, 9, 15, 4, 10, 16, 5, 11, 17, 5, 11, 17, 6, 18, 24, 7, 19, 25, 8, 20, 26, 9, 21, 27, 10, 22, 28, 11, 23, 29, 9, 21, 27, 10, 22, 28, 11, 23, 29, 11, 23, 29, 12, 24, 30, 13, 25, 31, 14, 26, 32, 15, 27, 33, 16, 28, 34, 17, 29, 35, 15, 27, 33, 16, 28, 34, 17, 29, 35, 17, 29, 35, 18, 36, 42, 19, 37, 43, 20, 38, 44, 21, 39, 45, 22, 40, 46, 23, 41, 47, 21, 39, 45, 22, 40, 46, 23, 41, 47, 23, 41, 47, 24, 42, 48, 25, 43, 49, 26, 44, 50, 27, 45, 51, 28, 46, 52, 29, 47, 53, 27, 45, 51, 28, 46, 52, 29, 47, 53, 29, 47, 53, 30, 48, 54, 31, 49, 55, 32, 50, 56, 33, 51, 57, 34, 52, 58, 35, 53, 59, 33, 51, 57, 34, 52, 58, 35, 53, 59, 35, 53, 59, 36, 60, 66, 37, 61, 67, 38, 62, 68, 39, 63, 69, 40, 64, 70, 41, 65, 71, 39, 63, 69, 40, 64, 70, 41, 65, 71, 41, 65, 71, 42, 66, 72, 43, 67, 73, 44, 68, 74, 45, 69, 75, 46, 70, 76, 47, 71, 77, 45, 69, 75, 46, 70, 76, 47, 71, 77, 47, 71, 77, 48, 72, 78, 49, 73, 79, 50, 74, 80, 51, 75, 81, 52, 76, 82, 53, 77, 83, 51, 75, 81, 52, 76, 82, 53, 77, 83, 53, 77, 83, 54, 78, 84, 55, 79, 85, 56, 80, 86, 57, 81, 87, 58, 82, 88, 59, 83, 89, 57, 81, 87, 58, 82, 88, 59, 83, 89, 59, 83, 89}; \
for ( i=0; i<100; i++ ) \
tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \
M[m[i]+1]*tmpr[idx[3*i+1]]+ \
M[m[i]+2]*tmpr[idx[3*i+2]]; \
} \
{ /* mode 3 */ \
int m[90]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 6}; \
int idx[270]={0, 10, 20, 1, 11, 21, 2, 12, 22, 3, 13, 23, 4, 14, 24, 5, 15, 25, 6, 16, 26, 7, 17, 27, 8, 18, 28, 9, 19, 29, 6, 16, 26, 7, 17, 27, 8, 18, 28, 9, 19, 29, 9, 19, 29, 10, 30, 40, 11, 31, 41, 12, 32, 42, 13, 33, 43, 14, 34, 44, 15, 35, 45, 16, 36, 46, 17, 37, 47, 18, 38, 48, 19, 39, 49, 16, 36, 46, 17, 37, 47, 18, 38, 48, 19, 39, 49, 19, 39, 49, 20, 40, 50, 21, 41, 51, 22, 42, 52, 23, 43, 53, 24, 44, 54, 25, 45, 55, 26, 46, 56, 27, 47, 57, 28, 48, 58, 29, 49, 59, 26, 46, 56, 27, 47, 57, 28, 48, 58, 29, 49, 59, 29, 49, 59, 30, 60, 70, 31, 61, 71, 32, 62, 72, 33, 63, 73, 34, 64, 74, 35, 65, 75, 36, 66, 76, 37, 67, 77, 38, 68, 78, 39, 69, 79, 36, 66, 76, 37, 67, 77, 38, 68, 78, 39, 69, 79, 39, 69, 79, 40, 70, 80, 41, 71, 81, 42, 72, 82, 43, 73, 83, 44, 74, 84, 45, 75, 85, 46, 76, 86, 47, 77, 87, 48, 78, 88, 49, 79, 89, 46, 76, 86, 47, 77, 87, 48, 78, 88, 49, 79, 89, 49, 79, 89, 50, 80, 90, 51, 81, 91, 52, 82, 92, 53, 83, 93, 54, 84, 94, 55, 85, 95, 56, 86, 96, 57, 87, 97, 58, 88, 98, 59, 89, 99, 56, 86, 96, 57, 87, 97, 58, 88, 98, 59, 89, 99, 59, 89, 99}; \
for ( i=0; i<90; i++ ) \
tmpr[i]=M[m[i]]*tmpl[idx[3*i]]+ \
M[m[i]+1]*tmpl[idx[3*i+1]]+ \
M[m[i]+2]*tmpl[idx[3*i+2]]; \
} \
{ /* mode 2 */ \
int m[63]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 6}; \
int idx[189]={0, 15, 30, 1, 16, 31, 2, 17, 32, 3, 18, 33, 4, 19, 34, 5, 20, 35, 6, 21, 36, 7, 22, 37, 8, 23, 38, 9, 24, 39, 10, 25, 40, 11, 26, 41, 12, 27, 42, 13, 28, 43, 14, 29, 44, 10, 25, 40, 11, 26, 41, 12, 27, 42, 13, 28, 43, 14, 29, 44, 14, 29, 44, 15, 45, 60, 16, 46, 61, 17, 47, 62, 18, 48, 63, 19, 49, 64, 20, 50, 65, 21, 51, 66, 22, 52, 67, 23, 53, 68, 24, 54, 69, 25, 55, 70, 26, 56, 71, 27, 57, 72, 28, 58, 73, 29, 59, 74, 25, 55, 70, 26, 56, 71, 27, 57, 72, 28, 58, 73, 29, 59, 74, 29, 59, 74, 30, 60, 75, 31, 61, 76, 32, 62, 77, 33, 63, 78, 34, 64, 79, 35, 65, 80, 36, 66, 81, 37, 67, 82, 38, 68, 83, 39, 69, 84, 40, 70, 85, 41, 71, 86, 42, 72, 87, 43, 73, 88, 44, 74, 89, 40, 70, 85, 41, 71, 86, 42, 72, 87, 43, 73, 88, 44, 74, 89, 44, 74, 89}; \
for ( i=0; i<63; i++ ) \
tmpl[i]=M[m[i]]*tmpr[idx[3*i]]+ \
M[m[i]+1]*tmpr[idx[3*i+1]]+ \
M[m[i]+2]*tmpr[idx[3*i+2]]; \
} \
{ /* mode 1 */ \
int m[28]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 6}; \
int idx[84]={0, 21, 42, 1, 22, 43, 2, 23, 44, 3, 24, 45, 4, 25, 46, 5, 26, 47, 6, 27, 48, 7, 28, 49, 8, 29, 50, 9, 30, 51, 10, 31, 52, 11, 32, 53, 12, 33, 54, 13, 34, 55, 14, 35, 56, 15, 36, 57, 16, 37, 58, 17, 38, 59, 18, 39, 60, 19, 40, 61, 20, 41, 62, 15, 36, 57, 16, 37, 58, 17, 38, 59, 18, 39, 60, 19, 40, 61, 20, 41, 62, 20, 41, 62}; \
for ( i=0; i<28; i++ ) \
res[i]=M[m[i]]*tmpl[idx[3*i]]+ \
M[m[i]+1]*tmpl[idx[3*i+1]]+ \
M[m[i]+2]*tmpl[idx[3*i+2]]; \
} \
}
_TIJK_6O3D_SYM_TRANS( double, d )
_TIJK_6O3D_SYM_TRANS( float, f )
#define _TIJK_6O3D_SYM_CONVERT( TYPE, SUF ) \
int \
_tijk_6o3d_sym_convert_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ) { \
if ( res_type==tijk_6o3d_sym ) { /* copy over */ \
tijk_copy_##SUF( res, A, tijk_6o3d_sym ); \
return 0; \
} else if ( NULL!=res_type->_convert_from_##SUF ) \
return ( *res_type->_convert_from_##SUF )( res, A, tijk_6o3d_sym ); \
return 1; \
}
_TIJK_6O3D_SYM_CONVERT( double, d )
_TIJK_6O3D_SYM_CONVERT( float, f )
#define _TIJK_6O3D_SYM_APPROX( TYPE, SUF ) \
int \
_tijk_6o3d_sym_approx_##SUF ( TYPE *res, const tijk_type *res_type, \
const TYPE *A ){ \
if ( res_type==tijk_2o3d_sym ) { \
/* do this in two steps */ \
TYPE tmp[15]; \
_tijk_6o3d_sym_approx_##SUF( tmp, tijk_4o3d_sym, A ); \
_tijk_4o3d_sym_approx_##SUF( res, tijk_2o3d_sym, tmp ); \
return 0; \
} else if ( res_type==tijk_4o3d_sym ) { \
res[0]=5.0/231.0*( 43*A[0]+A[21]+A[27]+24*( A[3]+A[5] )+ \
3*( A[23]+A[25] )-18*( A[10]+A[14] )-36*A[12] ); \
res[1]=5.0/22.0*( 5*A[1]+4*( A[6]+A[8] )-A[15]-A[19]-2*A[17] ); \
res[2]=5.0/22.0*( 5*A[2]+4*( A[9]+A[7] )-A[20]-A[16]-2*A[18] ); \
res[3]=5.0/1386.0*( 321*( A[3]+A[10] )+306*A[12]-36*( A[5]+A[23] )- \
19*( A[0]+A[21] )-15*( A[14]+A[25] )+2*A[27] ); \
res[4]=5.0/66.0*( 17*A[4]+16*( A[11]+A[13] )-A[22]-A[26]-2*A[24] ); \
res[5]=5.0/1386.0*( 321*( A[5]+A[14] )+306*A[12]-36*( A[3]+A[25] )- \
19*( A[0]+A[27] )-15*( A[10]+A[23] )+2*A[21] ); \
res[6]=5.0/22.0*( 5*A[15]+4*( A[6]+A[17] )-A[1]-A[19]-2*A[8] ); \
res[7]=5.0/66.0*( 17*A[16]+16*( A[7]+A[18] )-A[2]-A[20]-2*A[9] ); \
res[8]=5.0/66.0*( 17*A[19]+16*( A[8]+A[17] )-A[1]-A[15]-2*A[6] ); \
res[9]=5.0/22.0*( 5*A[20]+4*( A[9]+A[18] )-A[2]-A[16]-2*A[7] ); \
res[10]=5.0/231.0*( 43*A[21]+A[0]+A[27]+24*( A[10]+A[23] )+ \
3*( A[5]+A[14] )-18*( A[3]+A[25] )-36*A[12] ); \
res[11]=5.0/22.0*( 5*A[22]+4*( A[23]+A[11] )-A[26]-A[4]-2*A[13] ); \
res[12]=5.0/1386.0*( 312*( A[23]+A[25] )+306*A[12]-36*( A[10]+A[14] )- \
19*( A[21]+A[27] )-15*( A[5]-A[3] )+2*A[0] ); \
res[13]=5.0/22.0*( 5*A[26]+4*( A[24]+A[13] )-A[22]-A[4]-2*A[11] ); \
res[14]=5.0/231.0*( 43*A[27]+A[0]+A[21]+24*( A[14]+A[25] )+ \
3*( A[3]+A[10] )-18*( A[5]+A[23] )-36*A[12] ); \
return 0; \
} else if ( NULL!=res_type->_approx_from_##SUF ) \
return ( *res_type->_approx_from_##SUF )( res, A, tijk_6o3d_sym ); \
return 1; \
}
_TIJK_6O3D_SYM_APPROX( double, d )
_TIJK_6O3D_SYM_APPROX( float, f )
double
_tijk_6o3d_sym_s_form_d ( const double *A, const double *v ) {
double v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
return A[0]*v00*v00*v00+
A[21]*v11*v11*v11+
A[27]*v22*v22*v22+
6*( A[1]*v00*v00*v01+
A[2]*v00*v00*v02+
A[15]*v01*v11*v11+
A[20]*v02*v22*v22+
A[22]*v11*v11*v12+
A[26]*v12*v22*v22 )+
15*( A[3]*v00*v00*v11+
A[5]*v00*v00*v22+
A[10]*v00*v11*v11+
A[14]*v00*v22*v22+
A[23]*v11*v11*v22+
A[25]*v11*v22*v22 )+
30*( A[4]*v00*v00*v12+
A[16]*v01*v11*v12+
A[19]*v01*v22*v22 )+
20*( A[6]*v00*v01*v11+
A[9]*v00*v02*v22+
A[24]*v11*v12*v22 )+
60*( A[7]*v00*v01*v12+
A[8]*v00*v01*v22+
A[11]*v00*v11*v12+
A[13]*v00*v12*v22+
A[17]*v01*v11*v22+
A[18]*v01*v12*v22 )+
90*A[12]*v00*v11*v22;
}
float
_tijk_6o3d_sym_s_form_f ( const float *A, const float *v ) {
float v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2],
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2];
return A[0]*v00*v00*v00+
A[21]*v11*v11*v11+
A[27]*v22*v22*v22+
6*( A[1]*v00*v00*v01+
A[2]*v00*v00*v02+
A[15]*v01*v11*v11+
A[20]*v02*v22*v22+
A[22]*v11*v11*v12+
A[26]*v12*v22*v22 )+
15*( A[3]*v00*v00*v11+
A[5]*v00*v00*v22+
A[10]*v00*v11*v11+
A[14]*v00*v22*v22+
A[23]*v11*v11*v22+
A[25]*v11*v22*v22 )+
30*( A[4]*v00*v00*v12+
A[16]*v01*v11*v12+
A[19]*v01*v22*v22 )+
20*( A[6]*v00*v01*v11+
A[9]*v00*v02*v22+
A[24]*v11*v12*v22 )+
60*( A[7]*v00*v01*v12+
A[8]*v00*v01*v22+
A[11]*v00*v11*v12+
A[13]*v00*v12*v22+
A[17]*v01*v11*v22+
A[18]*v01*v12*v22 )+
90*A[12]*v00*v11*v22;
}
double
_tijk_6o3d_sym_mean_d ( const double *A ) {
return ( A[0]+A[21]+A[27]+
3*( A[3]+A[5]+A[10]+A[14]+A[23]+A[25] )+
6*A[12] )/7.0;
}
float
_tijk_6o3d_sym_mean_f ( const float *A ) {
return ( A[0]+A[21]+A[27]+
3*( A[3]+A[5]+A[10]+A[14]+A[23]+A[25] )+
6*A[12] )/7.0;
}
double
_tijk_6o3d_sym_var_d ( const double *A ) {
double mean=( A[0]+A[21]+A[27]+
3*( A[3]+A[5]+A[10]+A[14]+A[23]+A[25] )+
6*A[12] )/7.0;
return -mean*mean + ( 1.0/3003.0 )*
( + 10*A[21]*A[27]
+ 30*( A[14]*A[21]+A[27]*A[3] )
+ 210*( A[21]*( A[3]+A[25] )+A[23]*A[27] )
+ 231*( A[0]*A[0]+A[21]*A[21]+A[27]*A[27] )
+ 270*A[25]*A[3]
+ 420*A[12]*( A[21]+A[27] )
+ 450*( A[14]*( A[3]+A[23] )+A[23]*A[3] )
+ 630*( A[21]*A[23]+A[14]*A[27]+A[25]*A[27] )
+ 1050*A[14]*A[25]
+ 1575*( A[3]*A[3]+A[5]*A[5]+A[10]*A[10]+A[14]*A[14]+
A[23]*A[23]+A[25]*A[25] )
+ 2250*A[23]*A[25]
+ 2700*A[12]*( A[3]+A[14]+A[23]+A[25] )
+ 4860*A[12]*A[12]
+ 30*A[5]*( 90*A[12]+ 75*A[14]+ A[21]+ 9*A[23]+ 15*A[25]+ 7*A[27]+ 35*A[3] )
+ 30*A[10]*( 90*A[12]+9*A[14]+21*A[21]+35*A[23]+15*A[25]+A[27]+75*A[3]+
15*A[5] )
+ 10*A[0]*( 21*A[10]+42*A[12]+21*A[14]+A[21]+3*A[23]+3*A[25]+A[27]+
63*( A[3]+A[5] ) )
+ 4*( +30*A[1]*( 3*A[15] + 6*A[17] + 3*A[19] + 14*( A[6] + A[8] ) )
+60*( A[8]*( 3*( A[15] + 6*A[17] + 5*A[19] ) + 10*A[6] )+
A[9]*( 3*A[16] + 10*A[18] + 7*( A[2] + A[20] ) + 10*A[7] )+
A[11]*( 18*A[13] + 7*A[22] + 10*A[24] + 3*A[26] + 15*A[4] )+
A[13]*( 3*A[22] + 10*A[24] + 7*A[26] + 15*A[4] )+
A[18]*( 3*A[2]+7*A[20]+18*A[7] ) )
+90*( A[2]*A[20]+A[15]*A[19]+A[22]*A[26]+
A[4]*( A[22]+2*A[24]+A[26] )+
A[16]*( 10*A[18]+A[2]+A[20]+10*A[7] ) )
+180*( A[20]*A[7]+A[19]*A[6] )
+189*( A[1]*A[1]+A[2]*A[2]+A[15]*A[15]+A[20]*A[20]+
A[22]*A[22]+A[26]*A[26] )
+420*( A[2]*A[7]+A[15]*A[17]+A[22]*A[24]+A[24]*A[26]+A[15]*A[6] )
+500*( A[9]*A[9]+A[24]*A[24]+A[6]*A[6] )
+525*( A[4]*A[4]+A[16]*A[16]+A[19]*A[19] )
+600*A[17]*A[6]
+900*( A[7]*A[7]+A[8]*A[8]+A[11]*A[11]+A[13]*A[13]+A[17]*A[17]+
A[18]*A[18]+A[17]*A[19] ) ) );
}
float
_tijk_6o3d_sym_var_f ( const float *A ) {
float mean=( A[0]+A[21]+A[27]+
3*( A[3]+A[5]+A[10]+A[14]+A[23]+A[25] )+
6*A[12] )/7.0;
return -mean*mean + ( 1.0/3003.0 )*
( + 10*A[21]*A[27]
+ 30*( A[14]*A[21]+A[27]*A[3] )
+ 210*( A[21]*( A[3]+A[25] )+A[23]*A[27] )
+ 231*( A[0]*A[0]+A[21]*A[21]+A[27]*A[27] )
+ 270*A[25]*A[3]
+ 420*A[12]*( A[21]+A[27] )
+ 450*( A[14]*( A[3]+A[23] )+A[23]*A[3] )
+ 630*( A[21]*A[23]+A[14]*A[27]+A[25]*A[27] )
+ 1050*A[14]*A[25]
+ 1575*( A[3]*A[3]+A[5]*A[5]+A[10]*A[10]+A[14]*A[14]+
A[23]*A[23]+A[25]*A[25] )
+ 2250*A[23]*A[25]
+ 2700*A[12]*( A[3]+A[14]+A[23]+A[25] )
+ 4860*A[12]*A[12]
+ 30*A[5]*( 90*A[12]+ 75*A[14]+ A[21]+ 9*A[23]+ 15*A[25]+ 7*A[27]+ 35*A[3] )
+ 30*A[10]*( 90*A[12]+9*A[14]+21*A[21]+35*A[23]+15*A[25]+A[27]+75*A[3]+
15*A[5] )
+ 10*A[0]*( 21*A[10]+42*A[12]+21*A[14]+A[21]+3*A[23]+3*A[25]+A[27]+
63*( A[3]+A[5] ) )
+ 4*( +30*A[1]*( 3*A[15] + 6*A[17] + 3*A[19] + 14*( A[6] + A[8] ) )
+60*( A[8]*( 3*( A[15] + 6*A[17] + 5*A[19] ) + 10*A[6] )+
A[9]*( 3*A[16] + 10*A[18] + 7*( A[2] + A[20] ) + 10*A[7] )+
A[11]*( 18*A[13] + 7*A[22] + 10*A[24] + 3*A[26] + 15*A[4] )+
A[13]*( 3*A[22] + 10*A[24] + 7*A[26] + 15*A[4] )+
A[18]*( 3*A[2]+7*A[20]+18*A[7] ) )
+90*( A[2]*A[20]+A[15]*A[19]+A[22]*A[26]+
A[4]*( A[22]+2*A[24]+A[26] )+
A[16]*( 10*A[18]+A[2]+A[20]+10*A[7] ) )
+180*( A[20]*A[7]+A[19]*A[6] )
+189*( A[1]*A[1]+A[2]*A[2]+A[15]*A[15]+A[20]*A[20]+
A[22]*A[22]+A[26]*A[26] )
+420*( A[2]*A[7]+A[15]*A[17]+A[22]*A[24]+A[24]*A[26]+A[15]*A[6] )
+500*( A[9]*A[9]+A[24]*A[24]+A[6]*A[6] )
+525*( A[4]*A[4]+A[16]*A[16]+A[19]*A[19] )
+600*A[17]*A[6]
+900*( A[7]*A[7]+A[8]*A[8]+A[11]*A[11]+A[13]*A[13]+A[17]*A[17]+
A[18]*A[18]+A[17]*A[19] ) ) );
}
#define _TIJK_6O3D_SYM_V_FORM( TYPE, SUF ) \
void \
_tijk_6o3d_sym_v_form_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \
TYPE v00000=v00*v00*v[0], v00001=v00*v00*v[1], v00002=v00*v00*v[2], \
v00011=v00*v01*v[1], v00012=v00*v01*v[2], v00022=v00*v02*v[2], \
v00111=v00*v11*v[1], v00112=v00*v11*v[2], v00122=v00*v12*v[2], \
v00222=v00*v22*v[2], v01111=v01*v11*v[1], v01112=v01*v11*v[2], \
v01122=v01*v12*v[2], v01222=v01*v22*v[2], v02222=v02*v22*v[2], \
v11111=v11*v11*v[1], v11112=v11*v11*v[2], v11122=v11*v12*v[2], \
v11222=v11*v22*v[2], v12222=v12*v22*v[2], v22222=v22*v22*v[2]; \
res[0] = A[0]*v00000+ \
5*A[1]*v00001+ \
5*A[2]*v00002+ \
10*A[3]*v00011+ \
20*A[4]*v00012+ \
10*A[5]*v00022+ \
10*A[6]*v00111+ \
30*A[7]*v00112+ \
30*A[8]*v00122+ \
10*A[9]*v00222+ \
5*A[10]*v01111+ \
20*A[11]*v01112+ \
30*A[12]*v01122+ \
20*A[13]*v01222+ \
5*A[14]*v02222+ \
A[15]*v11111+ \
5*A[16]*v11112+ \
10*A[17]*v11122+ \
10*A[18]*v11222+ \
5*A[19]*v12222+ \
A[20]*v22222; \
res[1] = A[1]*v00000+ \
5*A[3]*v00001+ \
5*A[4]*v00002+ \
10*A[6]*v00011+ \
20*A[7]*v00012+ \
10*A[8]*v00022+ \
10*A[10]*v00111+ \
30*A[11]*v00112+ \
30*A[12]*v00122+ \
10*A[13]*v00222+ \
5*A[15]*v01111+ \
20*A[16]*v01112+ \
30*A[17]*v01122+ \
20*A[18]*v01222+ \
5*A[19]*v02222+ \
A[21]*v11111+ \
5*A[22]*v11112+ \
10*A[23]*v11122+ \
10*A[24]*v11222+ \
5*A[25]*v12222+ \
A[26]*v22222; \
res[2] = A[2]*v00000+ \
5*A[4]*v00001+ \
5*A[5]*v00002+ \
10*A[7]*v00011+ \
20*A[8]*v00012+ \
10*A[9]*v00022+ \
10*A[11]*v00111+ \
30*A[12]*v00112+ \
30*A[13]*v00122+ \
10*A[14]*v00222+ \
5*A[16]*v01111+ \
20*A[17]*v01112+ \
30*A[18]*v01122+ \
20*A[19]*v01222+ \
5*A[20]*v02222+ \
A[22]*v11111+ \
5*A[23]*v11112+ \
10*A[24]*v11122+ \
10*A[25]*v11222+ \
5*A[26]*v12222+ \
A[27]*v22222; \
}
_TIJK_6O3D_SYM_V_FORM( double, d )
_TIJK_6O3D_SYM_V_FORM( float, f )
#define _TIJK_6O3D_SYM_M_FORM( TYPE, SUF ) \
void \
_tijk_6o3d_sym_m_form_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \
TYPE v0000=v00*v00, v0001=v00*v01, v0002=v00*v02, v0011=v00*v11, \
v0012=v00*v12, v0022=v00*v22, v0111=v01*v11, v0112=v01*v12, \
v0122=v01*v22, v0222=v02*v22, v1111=v11*v11, v1112=v11*v12, \
v1122=v11*v22, v1222=v12*v22, v2222=v22*v22; \
res[0] = A[0]*v0000+ \
4*A[1]*v0001+ \
4*A[2]*v0002+ \
6*A[3]*v0011+ \
12*A[4]*v0012+ \
6*A[5]*v0022+ \
4*A[6]*v0111+ \
12*A[7]*v0112+ \
12*A[8]*v0122+ \
4*A[9]*v0222+ \
A[10]*v1111+ \
4*A[11]*v1112+ \
6*A[12]*v1122+ \
4*A[13]*v1222+ \
A[14]*v2222; \
res[1] = A[1]*v0000+ \
4*A[3]*v0001+ \
4*A[4]*v0002+ \
6*A[6]*v0011+ \
12*A[7]*v0012+ \
6*A[8]*v0022+ \
4*A[10]*v0111+ \
12*A[11]*v0112+ \
12*A[12]*v0122+ \
4*A[13]*v0222+ \
A[15]*v1111+ \
4*A[16]*v1112+ \
6*A[17]*v1122+ \
4*A[18]*v1222+ \
A[19]*v2222; \
res[2] = A[2]*v0000+ \
4*A[4]*v0001+ \
4*A[5]*v0002+ \
6*A[7]*v0011+ \
12*A[8]*v0012+ \
6*A[9]*v0022+ \
4*A[11]*v0111+ \
12*A[12]*v0112+ \
12*A[13]*v0122+ \
4*A[14]*v0222+ \
A[16]*v1111+ \
4*A[17]*v1112+ \
6*A[18]*v1122+ \
4*A[19]*v1222+ \
A[20]*v2222; \
res[3] = A[3]*v0000+ \
4*A[6]*v0001+ \
4*A[7]*v0002+ \
6*A[10]*v0011+ \
12*A[11]*v0012+ \
6*A[12]*v0022+ \
4*A[15]*v0111+ \
12*A[16]*v0112+ \
12*A[17]*v0122+ \
4*A[18]*v0222+ \
A[21]*v1111+ \
4*A[22]*v1112+ \
6*A[23]*v1122+ \
4*A[24]*v1222+ \
A[25]*v2222; \
res[4] = A[4]*v0000+ \
4*A[7]*v0001+ \
4*A[8]*v0002+ \
6*A[11]*v0011+ \
12*A[12]*v0012+ \
6*A[13]*v0022+ \
4*A[16]*v0111+ \
12*A[17]*v0112+ \
12*A[18]*v0122+ \
4*A[19]*v0222+ \
A[22]*v1111+ \
4*A[23]*v1112+ \
6*A[24]*v1122+ \
4*A[25]*v1222+ \
A[26]*v2222; \
res[5] = A[5]*v0000+ \
4*A[8]*v0001+ \
4*A[9]*v0002+ \
6*A[12]*v0011+ \
12*A[13]*v0012+ \
6*A[14]*v0022+ \
4*A[17]*v0111+ \
12*A[18]*v0112+ \
12*A[19]*v0122+ \
4*A[20]*v0222+ \
A[23]*v1111+ \
4*A[24]*v1112+ \
6*A[25]*v1122+ \
4*A[26]*v1222+ \
A[27]*v2222; \
}
_TIJK_6O3D_SYM_M_FORM( double, d )
_TIJK_6O3D_SYM_M_FORM( float, f )
#define _TIJK_6O3D_SYM_MAKE_RANK1( TYPE, SUF ) \
void \
_tijk_6o3d_sym_make_rank1_##SUF ( TYPE *res, const TYPE s, const TYPE *v ) { \
TYPE v00=v[0]*v[0], v01=v[0]*v[1], v02=v[0]*v[2], \
v11=v[1]*v[1], v12=v[1]*v[2], v22=v[2]*v[2]; \
res[0]=s*v00*v00*v00; res[1]=s*v00*v00*v01; res[2]=s*v00*v00*v02; \
res[3]=s*v00*v00*v11; res[4]=s*v00*v00*v12; res[5]=s*v00*v00*v22; \
res[6]=s*v00*v01*v11; res[7]=s*v00*v01*v12; res[8]=s*v00*v01*v22; \
res[9]=s*v00*v02*v22; res[10]=s*v00*v11*v11; res[11]=s*v00*v11*v12; \
res[12]=s*v00*v11*v22; res[13]=s*v00*v12*v22; res[14]=s*v00*v22*v22; \
res[15]=s*v01*v11*v11; res[16]=s*v01*v11*v12; res[17]=s*v01*v11*v22; \
res[18]=s*v01*v12*v22; res[19]=s*v01*v22*v22; res[20]=s*v02*v22*v22; \
res[21]=s*v11*v11*v11; res[22]=s*v11*v11*v12; res[23]=s*v11*v11*v22; \
res[24]=s*v11*v12*v22; res[25]=s*v11*v22*v22; res[26]=s*v12*v22*v22; \
res[27]=s*v22*v22*v22; \
}
_TIJK_6O3D_SYM_MAKE_RANK1( double, d )
_TIJK_6O3D_SYM_MAKE_RANK1( float, f )
void
_tijk_6o3d_sym_make_iso_d ( double *res, const double s ) {
/* initialize to zero, then set non-zero elements */
unsigned int i;
for ( i=0; i<28; i++ )
res[i]=0;
res[0]=res[21]=res[27]=s;
res[3]=res[5]=res[10]=res[14]=res[23]=res[25]=0.2*s;
res[12]=s/15.0;
}
void
_tijk_6o3d_sym_make_iso_f ( float *res, const float s ) {
/* initialize to zero, then set non-zero elements */
unsigned int i;
for ( i=0; i<28; i++ )
res[i]=0;
res[0]=res[21]=res[27]=s;
res[3]=res[5]=res[10]=res[14]=res[23]=res[25]=0.2*s;
res[12]=s/15.0;
}
void
_tijk_6o3d_sym_grad_d ( double *res, const double *A, const double *v ) {
double proj, projv[3];
_tijk_6o3d_sym_v_form_d ( res, A, v );
ELL_3V_SCALE( res, 6.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
void
_tijk_6o3d_sym_grad_f ( float *res, const float *A, const float *v ) {
float proj, projv[3];
_tijk_6o3d_sym_v_form_f ( res, A, v );
ELL_3V_SCALE( res, 6.0, res );
proj=ELL_3V_DOT( res, v );
ELL_3V_SCALE( projv, -proj, v );
ELL_3V_INCR( res, projv );
}
#define _TIJK_6O3D_SYM_HESS( TYPE, SUF ) \
void \
_tijk_6o3d_sym_hess_##SUF ( TYPE *res, const TYPE *A, const TYPE *v ) { \
/* get two orthonormal tangents */ \
TYPE t[2][3], cv[2][3], h[6], der, norm, tmp[6]; \
int r, c; \
ell_3v_perp_##SUF( t[0], v ); \
ELL_3V_NORM( t[0], t[0], norm ); \
ELL_3V_CROSS( t[1], v, t[0] ); \
ELL_3V_NORM( t[1], t[1], norm ); \
/* compute Hessian w.r.t. t1/t2 */ \
_tijk_6o3d_sym_m_form_##SUF( h, A, v ); \
der=6*_tijk_6o3d_sym_s_form_##SUF( A, v ); /* first der in direction v*/ \
_tijk_2o3d_sym_v_form_##SUF( cv[0], h, t[0] ); \
_tijk_2o3d_sym_v_form_##SUF( cv[1], h, t[1] ); \
h[0]=30*ELL_3V_DOT( cv[0], t[0] )-der; \
h[1]=30*ELL_3V_DOT( cv[0], t[1] ); \
h[2]=h[1]; \
h[3]=30*ELL_3V_DOT( cv[1], t[1] )-der; \
/* now turn this into a symmetric order-2 rank-2 3D tensor */ \
for ( r=0; r<2; r++ ) { \
for ( c=0; c<3; c++ ) { \
tmp[3*r+c]=h[2*r]*t[0][c]+h[2*r+1]*t[1][c]; \
} \
} \
res[0]=t[0][0]*tmp[0]+t[1][0]*tmp[3]; \
res[1]=t[0][0]*tmp[1]+t[1][0]*tmp[4]; \
res[2]=t[0][0]*tmp[2]+t[1][0]*tmp[5]; \
res[3]=t[0][1]*tmp[1]+t[1][1]*tmp[4]; \
res[4]=t[0][1]*tmp[2]+t[1][1]*tmp[5]; \
res[5]=t[0][2]*tmp[2]+t[1][2]*tmp[5]; \
}
_TIJK_6O3D_SYM_HESS( double, d )
_TIJK_6O3D_SYM_HESS( float, f )
TIJK_TYPE_SYM( 6o3d_sym, 6, 3, 28 )
/* 8th order 3D symmetric */
/* ( unsymmetric counterpart currently not implemented ) */
unsigned int _tijk_8o3d_sym_mult[45]={1, 8, 8, 28, 56, 28, 56, 168, 168, 56, 70, 280, 420, 280, 70, 56, 280, 560, 560, 280, 56, 28, 168, 420, 560, 420, 168, 28, 8, 56, 168, 280, 280, 168, 56, 8, 1, 8, 28, 56, 70, 56, 28, 8, 1};
#define _tijk_8o3d_sym_unsym2uniq NULL
#define _tijk_8o3d_sym_uniq2unsym NULL
#define _tijk_8o3d_sym_uniq_idx NULL
double
_tijk_8o3d_sym_tsp_d ( const double *A, const double *B ) {
double retval=0.0;
int i;
for ( i=0; i<45; i++ ) {
retval+=_tijk_8o3d_sym_mult[i]*A[i]*B[i];
}
return retval;
}
float
_tijk_8o3d_sym_tsp_f ( const float *A, const float *B ) {
float retval=0.0;
int i;
for ( i=0; i<45; i++ ) {
retval+=_tijk_8o3d_sym_mult[i]*A[i]*B[i];
}
return retval;
}
double
_tijk_8o3d_sym_norm_d ( const double *A ) {
return sqrt( _tijk_8o3d_sym_tsp_d( A, A ) );
}
float
_tijk_8o3d_sym_norm_f ( const float *A ) {
return sqrt( _tijk_8o3d_sym_tsp_f( A, A ) );
}
#define _TIJK_8O3D_SYM_TRANS( TYPE, SUF ) \
void \
_tijk_8o3d_sym_trans_##SUF ( TYPE *res, const TYPE *A, const TYPE *M ) { \
/* Tijklmnop = Miq Mjr Mks Mlt Mmu Mnv Mow Mpx Tqrstuvwx \
* For efficiency, we transform mode by mode; the intermediate results \
* have incomplete symmetries! */ \
TYPE tmpl[225], tmpr[225]; \
int i; \
{ /* mode 8: */ \
int m[108]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}; \
int idx[324]={0, 1, 2, 1, 3, 4, 2, 4, 5, 3, 6, 7, 4, 7, 8, 5, 8, 9, 6, 10, 11, 7, 11, 12, 8, 12, 13, 9, 13, 14, 10, 15, 16, 11, 16, 17, 12, 17, 18, 13, 18, 19, 14, 19, 20, 15, 21, 22, 16, 22, 23, 17, 23, 24, 18, 24, 25, 19, 25, 26, 20, 26, 27, 21, 28, 29, 22, 29, 30, 23, 30, 31, 24, 31, 32, 25, 32, 33, 26, 33, 34, 27, 34, 35, 28, 36, 37, 29, 37, 38, 30, 38, 39, 31, 39, 40, 32, 40, 41, 33, 41, 42, 34, 42, 43, 35, 43, 44, 0, 1, 2, 1, 3, 4, 2, 4, 5, 3, 6, 7, 4, 7, 8, 5, 8, 9, 6, 10, 11, 7, 11, 12, 8, 12, 13, 9, 13, 14, 10, 15, 16, 11, 16, 17, 12, 17, 18, 13, 18, 19, 14, 19, 20, 15, 21, 22, 16, 22, 23, 17, 23, 24, 18, 24, 25, 19, 25, 26, 20, 26, 27, 21, 28, 29, 22, 29, 30, 23, 30, 31, 24, 31, 32, 25, 32, 33, 26, 33, 34, 27, 34, 35, 28, 36, 37, 29, 37, 38, 30, 38, 39, 31, 39, 40, 32, 40, 41, 33, 41, 42, 34, 42, 43, 35, 43, 44, 0, 1, 2, 1, 3, 4, 2, 4, 5, 3, 6, 7, 4, 7, 8, 5, 8, 9, 6, 10, 11, 7, 11, 12, 8, 12, 13, 9, 13, 14, 10, 15, 16, 11, 16, 17, 12, 17, 18, 13, 18, 19, 14, 19, 20, 15, 21, 22, 16, 22, 23, 17, 23, 24, 18, 24, 25, 19, 25, 26, 20, 26, 27, 21, 28, 29, 22, 29, 30, 23, 30, 31, 24, 31, 32, 25, 32, 33, 26, 33, 34, 27, 34, 35, 28, 36, 37, 29, 37, 38, 30, 38, 39, 31, 39, 40, 32, 40, 41, 33, 41, 42, 34, 42, 43, 35, 43, 44}; \
for ( i=0; i<108; i++ ) \
tmpl[i]=M[m[i]]*A[idx[3*i]]+ \
M[m[i]+1]*A[idx[3*i+1]]+ \
M[m[i]+2]*A[idx[3*i+2]]; \
} \
{ /* mode 7: */ \
int m[168]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6}; \
/*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011, 2010, 2009, 2008 Thomas Schultz
Copyright ( C ) 2010, 2009, 2008 Gordon Kindlmann
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tijk.h"
#include "privateTijk.h"
#include "convertQuietPush.h"
/* Functions for symmetric tensor approximation */
/* a coarse sampling of the unit semicircle */
const unsigned int _tijk_max_candidates_2d=8;
#define _CANDIDATES_2D( TYPE, SUF ) \
35 static TYPE _candidates_2d_##SUF[16] = { \
1.0, 0.0, \
0.92387953251128674, 0.38268343236508978, \
0.70710678118654757, 0.70710678118654746, \
0.38268343236508984, 0.92387953251128674, \
0.0, 1.0, \
-0.38268343236508973, 0.92387953251128674, \
-0.70710678118654746, 0.70710678118654757, \
43 -0.92387953251128674, 0.38268343236508989 \
};
_CANDIDATES_2D( double, d )
_CANDIDATES_2D( float, f )
/* a coarse sampling of the unit sphere */
const unsigned int _tijk_max_candidates_3d=30;
#define _CANDIDATES_3D( TYPE, SUF ) \
static TYPE _candidates_3d_##SUF[90] = { \
-0.546405, 0.619202, 0.563943, \
-0.398931, -0.600006, 0.693432, \
0.587973, 0.521686, 0.618168, \
0.055894, -0.991971, -0.113444, \
-0.666933, -0.677984, 0.309094, \
0.163684, 0.533013, 0.830123, \
0.542826, 0.133898, 0.829102, \
-0.074751, -0.350412, 0.933608, \
0.845751, -0.478624, -0.235847, \
0.767148, -0.610673, 0.196372, \
-0.283810, 0.381633, 0.879663, \
0.537228, -0.616249, 0.575868, \
-0.711387, 0.197778, 0.674398, \
0.886511, 0.219025, 0.407586, \
0.296061, 0.842985, 0.449136, \
-0.937540, -0.340990, 0.068877, \
0.398833, 0.917023, 0.000835, \
0.097278, -0.711949, 0.695460, \
-0.311534, 0.908623, -0.278121, \
72 -0.432043, -0.089758, 0.897375, \
-0.949980, 0.030810, 0.310788, \
0.146722, -0.811981, -0.564942, \
-0.172201, -0.908573, 0.380580, \
0.507209, -0.848578, -0.150513, \
-0.730808, -0.654136, -0.194999, \
0.077744, 0.094961, 0.992441, \
0.383976, -0.293959, 0.875300, \
0.788208, -0.213656, 0.577130, \
-0.752333, -0.301447, 0.585769, \
-0.975732, 0.165497, -0.143382 \
};
_CANDIDATES_3D( double, d )
_CANDIDATES_3D( float, f )
/* Produces a rough estimate of the position and value of the ABSOLUTE
* maximum of the homogeneous form.
* s and v will be set to the corresponding value and ( unit-length ) direction.
* ten is the input tensor, type is its type.
* returns 0 upon success, 1 upon erroneous parameters. */
#define _TIJK_INIT_RANK1( TYPE, SUF, DIM ) \
int \
tijk_init_rank1_##DIM##d_##SUF( TYPE *s, TYPE *v, const TYPE *ten, \
const tijk_type *type ) { \
TYPE absmax=-1; \
unsigned int i; \
TYPE *candidate=_candidates_##DIM##d_##SUF; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 1; \
for ( i=0; i<_tijk_max_candidates_##DIM##d; i++ ) { \
TYPE val=( *type->sym->s_form_##SUF )( ten, candidate ); \
TYPE absval=fabs( val ); \
if ( absval>absmax ) { \
absmax=absval; \
*s=val; \
ELL_##DIM##V_COPY( v, candidate ); \
} \
candidate+=DIM; \
} \
return 0; \
}
_TIJK_INIT_RANK1( double, d, 2 )
_TIJK_INIT_RANK1( float, f, 2 )
_TIJK_INIT_RANK1( double, d, 3 )
_TIJK_INIT_RANK1( float, f, 3 )
/* Produces a rough estimate of the position and value of the SIGNED
* maximum of the homogeneous form.
* s and v will be set to the corresponding value and ( unit-length ) direction.
* ten is the input tensor, type is its type.
* returns 0 upon success, 1 upon erroneous parameters.
*/
#define _TIJK_INIT_MAX( TYPE, SUF, DIM ) \
int \
tijk_init_max_##DIM##d_##SUF( TYPE *s, TYPE *v, const TYPE *ten, \
const tijk_type *type ) { \
TYPE max=0; \
unsigned int i; \
TYPE *candidate=_candidates_##DIM##d_##SUF; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 1; \
*s=max=( *type->sym->s_form_##SUF )( ten, candidate ); \
ELL_##DIM##V_COPY( v, candidate ); \
for ( i=1; i<_tijk_max_candidates_##DIM##d; i++ ) { \
TYPE val; \
candidate+=DIM; \
val=( *type->sym->s_form_##SUF )( ten, candidate ); \
if ( val>max ) { \
max=val; \
*s=val; \
ELL_##DIM##V_COPY( v, candidate ); \
} \
} \
return 0; \
}
_TIJK_INIT_MAX( double, d, 2 )
_TIJK_INIT_MAX( float, f, 2 )
_TIJK_INIT_MAX( double, d, 3 )
_TIJK_INIT_MAX( float, f, 3 )
static const tijk_refine_rank1_parm refine_rank1_parm_default = {
1e-10, 1e-6, 0.3, 0.9, 0.5, 50};
tijk_refine_rank1_parm *tijk_refine_rank1_parm_new( ) {
tijk_refine_rank1_parm *parm;
parm = ( tijk_refine_rank1_parm * )malloc( sizeof( tijk_refine_rank1_parm ) );
if ( parm ) {
memcpy( parm, &refine_rank1_parm_default, sizeof( tijk_refine_rank1_parm ) );
}
return parm;
}
tijk_refine_rank1_parm
*tijk_refine_rank1_parm_nix( tijk_refine_rank1_parm *parm ) {
airFree( parm );
return NULL;
}
/* Refines a given rank-1 tensor to a locally optimal rank-1 approximation,
* using a gradient descent scheme with Armijo stepsize.
* s and v are scalar and ( unit-len ) vector part of the rank-1 tensor and
* will be updated by this function.
* ten is the input tensor, type is its type.
* parm: if non-NULL, used to change the default optimization parameters
* We improve the rank-1 approximation by moving s away from zero
* ( unless refinemax is nonzero, in which case we try to make the
* signed value of s as large as possible )
* returns 0 upon success
* 1 when given wrong parameters
* 2 when the Armijo scheme failed to produce a valid stepsize
*/
#define _TIJK_REFINE_RANK1ORMAX( TYPE, SUF, DIM ) \
int \
_tijk_refine_rank1ormax_##DIM##d_##SUF( TYPE *s, TYPE *v, const TYPE *ten, \
const tijk_type *type, \
const tijk_refine_rank1_parm *parm, \
const int refinemax ) { \
TYPE isoten[TIJK_TYPE_MAX_NUM], anisoten[TIJK_TYPE_MAX_NUM]; \
TYPE der[DIM], iso, anisonorm, anisonorminv, oldval; \
char sign=( refinemax || *s>0 )?1:-1; \
TYPE alpha, beta; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 1; \
if ( parm==NULL ) parm=&refine_rank1_parm_default; \
/* It's easier to do the optimization on the deviatoric */ \
iso=( *type->sym->mean_##SUF )( ten ); \
( *type->sym->make_iso_##SUF )( isoten, iso ); \
tijk_sub_##SUF( anisoten, ten, isoten, type ); \
anisonorm=( *type->norm_##SUF )( anisoten ); \
if ( anisonorm<parm->eps_start ) { \
return 0; /* nothing to do */ \
} else { \
anisonorminv=1.0/anisonorm; \
} \
alpha=beta=parm->beta*anisonorminv; \
oldval=*s-iso; \
/* set initial derivative */ \
( *type->sym->grad_##SUF )( der, anisoten, v ); \
while ( 1 ) { /* refine until convergence */ \
/* stepsize needs to depend on norm to make the descent */ \
/* scale invariant */ \
unsigned int armijoct=0; \
TYPE testv[DIM], val; \
TYPE dist, derlen=ELL_##DIM##V_LEN( der ); \
/* determine stepsize based on Armijo's rule */ \
while ( 1 ) { \
++armijoct; \
if ( armijoct>parm->maxtry ) { \
/* failed to find a valid stepsize */ \
return 2; \
} \
ELL_##DIM##V_SCALE_ADD2( testv, 1.0, v, sign*alpha, der ); \
ELL_##DIM##V_NORM( testv, testv, dist ); \
dist=1-ELL_##DIM##V_DOT( v, testv ); \
val=( *type->sym->s_form_##SUF )( anisoten, testv ); \
if ( sign*val>=sign*oldval+parm->sigma*derlen*dist ) { \
/* accept step */ \
ELL_##DIM##V_COPY( v, testv ); \
*s=val+iso; \
( *type->sym->grad_##SUF )( der, anisoten, v ); \
if ( alpha<beta ) alpha /= parm->gamma; \
break; \
} \
alpha *= parm->gamma; /* try again with decreased stepsize */ \
} \
if ( sign*( val-oldval )<=parm->eps_impr*anisonorm ) { \
break; /* declare convergence */ \
} \
oldval=val; \
} \
return 0; \
}
_TIJK_REFINE_RANK1ORMAX( double, d, 2 )
_TIJK_REFINE_RANK1ORMAX( float, f, 2 )
_TIJK_REFINE_RANK1ORMAX( double, d, 3 )
_TIJK_REFINE_RANK1ORMAX( float, f, 3 )
#define _TIJK_REFINE_RANK1( TYPE, SUF, DIM ) \
int \
tijk_refine_rank1_##DIM##d_##SUF( TYPE *s, TYPE *v, const TYPE *ten, \
const tijk_type *type, \
const tijk_refine_rank1_parm *parm ) { \
return _tijk_refine_rank1ormax_##DIM##d_##SUF( s, v, ten, type, parm, 0 ); \
}
_TIJK_REFINE_RANK1( double, d, 2 )
_TIJK_REFINE_RANK1( float, f, 2 )
_TIJK_REFINE_RANK1( double, d, 3 )
_TIJK_REFINE_RANK1( float, f, 3 )
#define _TIJK_REFINE_MAX( TYPE, SUF, DIM ) \
int \
tijk_refine_max_##DIM##d_##SUF( TYPE *s, TYPE *v, const TYPE *ten, \
const tijk_type *type, \
const tijk_refine_rank1_parm *parm ) { \
return _tijk_refine_rank1ormax_##DIM##d_##SUF( s, v, ten, type, parm, 1 ); \
}
_TIJK_REFINE_MAX( double, d, 2 )
_TIJK_REFINE_MAX( float, f, 2 )
_TIJK_REFINE_MAX( double, d, 3 )
_TIJK_REFINE_MAX( float, f, 3 )
static const tijk_refine_rankk_parm refine_rankk_parm_default = {
1e-10, 1e-4, 0, NULL};
tijk_refine_rankk_parm *tijk_refine_rankk_parm_new( ) {
tijk_refine_rankk_parm *parm;
parm = ( tijk_refine_rankk_parm * )malloc( sizeof( tijk_refine_rankk_parm ) );
if ( parm ) {
memcpy( parm, &refine_rankk_parm_default, sizeof( tijk_refine_rankk_parm ) );
}
return parm;
}
tijk_refine_rankk_parm
*tijk_refine_rankk_parm_nix( tijk_refine_rankk_parm *parm ) {
if ( parm!=NULL ) {
parm->rank1_parm=tijk_refine_rank1_parm_nix( parm->rank1_parm );
free( parm );
}
return NULL;
}
/* Refines an existing rank-k approximation, using the iterative algorithm
* described in Schultz and Seidel, TVCG/Vis 2008.
* This is mostly used as a helper routine for tijk_approx_heur and
* tijk_approx_rankk. It should only appear in user code if you know
* what you are doing.
*
* ls and vs are the scalar and ( unit-len ) vector parts of the rank-1 tensors.
* tens has the rank-1 tensors themselves ( same information as ls and vs, but
* "multiplied out" ).
* res is the approximation residual.
* resnorm is the norm of the approximation residual.
* orignorm is the original norm of the tensor
* type is its type of tens and res
* k is the rank ( determines the length of ls, vs, and tens.
* returns 0 upon success, 1 upon erroneous parameters.
*/
#define _TIJK_REFINE_RANKK( TYPE, SUF, DIM ) \
int \
tijk_refine_rankk_##DIM##d_##SUF( TYPE *ls, TYPE *vs, TYPE *tens, \
TYPE *res, TYPE *resnorm, \
const TYPE orignorm, \
const tijk_type *type, \
const unsigned int k, \
const tijk_refine_rankk_parm *parm ) \
{ \
TYPE newnorm=( *resnorm ); \
unsigned int i; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 1; \
if ( parm==NULL ) parm=&refine_rankk_parm_default; \
if ( *resnorm<parm->eps_res || k==0 ) { \
return 0; /* nothing to do */ \
} \
do { \
*resnorm=newnorm; \
for ( i=0; i<k; i++ ) { \
if ( ls[i]!=0.0 ) { /* refine an existing term */ \
tijk_incr_##SUF( res, tens+i*type->num, type ); \
ls[i]=( *type->sym->s_form_##SUF )( res, vs+DIM*i ); \
if ( parm->pos && ls[i]<0.0 ) { /* try a new one */ \
tijk_init_max_##DIM##d_##SUF( ls+i, vs+DIM*i, res, type ); \
} \
} else { /* add a new term */ \
if ( parm->pos ) \
tijk_init_max_##DIM##d_##SUF( ls+i, vs+DIM*i, res, type ); \
else \
tijk_init_rank1_##DIM##d_##SUF( ls+i, vs+DIM*i, res, type ); \
} \
tijk_refine_rank1_##DIM##d_##SUF( ls+i, vs+DIM*i, res, type, \
parm->rank1_parm ); \
if ( !parm->pos || ls[i]>0.0 ) { \
( *type->sym->make_rank1_##SUF )( tens+i*type->num, ls[i], vs+DIM*i ); \
tijk_sub_##SUF( res, res, tens+i*type->num, type ); \
} else { \
ls[i]=0.0; \
} \
} \
newnorm=( *type->norm_##SUF )( res ); \
} while ( newnorm>parm->eps_res && \
( *resnorm )-newnorm>parm->eps_impr*orignorm ); \
*resnorm=newnorm; \
return 0; \
}
_TIJK_REFINE_RANKK( double, d, 2 )
_TIJK_REFINE_RANKK( float, f, 2 )
_TIJK_REFINE_RANKK( double, d, 3 )
_TIJK_REFINE_RANKK( float, f, 3 )
static const tijk_approx_heur_parm approx_heur_parm_default = {
1e-10, 0.1, NULL, NULL};
tijk_approx_heur_parm *tijk_approx_heur_parm_new( ) {
tijk_approx_heur_parm *parm;
parm = ( tijk_approx_heur_parm * )malloc( sizeof( tijk_approx_heur_parm ) );
if ( parm ) {
memcpy( parm, &approx_heur_parm_default, sizeof( tijk_approx_heur_parm ) );
}
return parm;
}
tijk_approx_heur_parm
*tijk_approx_heur_parm_nix( tijk_approx_heur_parm *parm ) {
if ( parm!=NULL ) {
parm->refine_parm=tijk_refine_rankk_parm_nix( parm->refine_parm );
free( parm );
}
return NULL;
}
/* Approximates a given tensor with a rank-k approximation, guessing the best
* rank by using a heuristic similar to Schultz and Seidel, TVCG/Vis 2008.
* ls and vs are the scalar and ( unit-len ) vector parts of the rank-1 tensors.
* res is the approximation residual.
* ten is the input tensor, type is its type.
* k is the maximum rank that should be used ( ls and vs need to have enough
* space for k and DIM*k values, respectively ).
* parms: if non-NULL, allows to modify the parameters of the heuristic
* returns the rank determined by the heuristic.
*/
#define _TIJK_APPROX_HEUR( TYPE, SUF, DIM ) \
int \
tijk_approx_heur_##DIM##d_##SUF( TYPE *ls, TYPE *vs, TYPE *res, \
const TYPE *ten, const tijk_type *type, \
const unsigned int k, \
const tijk_approx_heur_parm *parm ) \
{ \
TYPE *lstmp=NULL, *vstmp=NULL, *tens=NULL, *restmp=NULL; \
TYPE oldnorm, newnorm, orignorm; \
unsigned int currank=1; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 0; \
if ( parm==NULL ) parm=&approx_heur_parm_default; \
/* initializations */ \
orignorm=newnorm=oldnorm=( *type->norm_##SUF )( ten ); \
if ( k==0 ) { \
if ( res!=NULL ) memcpy( res, ten, sizeof( TYPE )*type->num ); \
return 0; /* nothing to do */ \
} \
lstmp=AIR_CALLOC( k, TYPE ); \
if ( lstmp==NULL ) goto cleanup_and_exit; \
vstmp=AIR_CALLOC( DIM*k, TYPE ); \
if ( vstmp==NULL ) goto cleanup_and_exit; \
tens=AIR_CALLOC( k*type->num, TYPE ); \
if ( tens==NULL ) goto cleanup_and_exit; \
restmp=AIR_CALLOC( type->num, TYPE ); \
if ( restmp==NULL ) goto cleanup_and_exit; \
memcpy( restmp, ten, sizeof( TYPE )*type->num ); \
for ( currank=1; currank<=k; currank++ ) { \
int accept=1; \
tijk_refine_rankk_##DIM##d_##SUF( lstmp, vstmp, tens, restmp, \
&newnorm, orignorm, type, \
currank, parm->refine_parm ); \
if ( currank>1 && parm->ratios!=NULL ) { \
/* make sure the desired ratio is fulfilled */ \
TYPE largest=fabs( lstmp[0] ), smallest=largest; \
unsigned int i; \
for ( i=1; i<currank; i++ ) { \
if ( fabs( lstmp[i] )>largest ) largest=fabs( lstmp[i] ); \
if ( fabs( lstmp[i] )<smallest ) smallest=fabs( lstmp[i] ); \
} \
if ( largest/smallest>parm->ratios[currank-2] ) \
accept=0; \
} \
if ( currank>1 && oldnorm-newnorm<parm->eps_impr*orignorm ) \
accept=0; \
if ( accept ) { \
/* copy over */ \
memcpy( vs, vstmp, sizeof( TYPE )*DIM*currank ); \
memcpy( ls, lstmp, sizeof( TYPE )*currank ); \
if ( res!=NULL ) \
memcpy( res, restmp, sizeof( TYPE )*type->num ); \
oldnorm=newnorm; \
} else { \
break; \
} \
if ( newnorm<parm->eps_res*orignorm ) { \
currank++; \
break; /* mission accomplished */ \
} \
} \
cleanup_and_exit: \
if ( lstmp!=NULL ) free( lstmp ); \
if ( vstmp!=NULL ) free( vstmp ); \
if ( tens!=NULL ) free( tens ); \
if ( restmp!=NULL ) free( restmp ); \
return currank-1; \
}
_TIJK_APPROX_HEUR( double, d, 2 )
_TIJK_APPROX_HEUR( float, f, 2 )
_TIJK_APPROX_HEUR( double, d, 3 )
_TIJK_APPROX_HEUR( float, f, 3 )
/* Approximates a given tensor with a rank-k approximation, using the
* iterative algorithm described in Schultz and Seidel, TVCG/Vis 2008.
* ls and vs are the scalar and ( unit-len ) vector parts of the rank-1 tensors.
* They can be NULL if you're just interested in the residual.
* res is the approximation residual. Can be NULL if you're not interested.
* ten is the input tensor, type is its type.
* k is the rank that should be used ( if ls and vs are non-NULL, they need
* to have enough space for k and DIM*k values, respectively ).
* returns 0 upon success, 1 upon error
*/
#define _TIJK_APPROX_RANKK( TYPE, SUF, DIM ) \
int \
tijk_approx_rankk_##DIM##d_##SUF( TYPE *ls, TYPE *vs, TYPE *res, \
const TYPE *ten, const tijk_type *type, \
const unsigned int k, \
const tijk_refine_rankk_parm *parm ) \
{ \
char hadls=1, hadvs=1, hadres=1; \
TYPE *tens=NULL; \
TYPE newnorm, orignorm; \
int retval=0; \
if ( type->dim!=DIM || type->sym==NULL ) \
return 1; \
if ( parm==NULL ) parm=&refine_rankk_parm_default; \
/* initializations */ \
orignorm=newnorm=( *type->norm_##SUF )( ten ); \
if ( orignorm<parm->eps_res || k==0 ) { \
if ( ls!=NULL ) { \
unsigned int i; \
for ( i=0; i<k; i++ ) \
ls[i]=0.0; \
} \
if ( res!=NULL ) memcpy( res, ten, sizeof( TYPE )*type->num ); \
return 0; /* nothing to do */ \
} \
if ( ls==NULL ) { \
ls=AIR_CALLOC( k, TYPE ); \
if ( ls==NULL ) {retval=1; goto cleanup_and_exit;} \
hadls=0; \
} else { \
unsigned int i; \
for ( i=0; i<k; i++ ) \
ls[i]=0.0; \
} \
if ( vs==NULL ) { \
vs=AIR_CALLOC( DIM*k, TYPE ); \
if ( vs==NULL ) {retval=1; goto cleanup_and_exit;} \
hadvs=0; \
} \
if ( res==NULL ) { \
res=AIR_CALLOC( type->num, TYPE ); \
if ( res==NULL ) {retval=1; goto cleanup_and_exit;} \
hadres=0; \
} \
tens=AIR_CALLOC( k*type->num, TYPE ); \
if ( tens==NULL ) {retval=1; goto cleanup_and_exit;} \
memcpy( res, ten, sizeof( TYPE )*type->num ); \
tijk_refine_rankk_##DIM##d_##SUF( ls, vs, tens, res, &newnorm, \
orignorm, type, k, parm ); \
cleanup_and_exit: \
if ( !hadls ) free( ls ); \
if ( !hadvs ) free( vs ); \
if ( !hadres ) free( res ); \
if ( tens!=NULL ) free( tens ); \
return retval; \
}
_TIJK_APPROX_RANKK( double, d, 2 )
_TIJK_APPROX_RANKK( float, f, 2 )
_TIJK_APPROX_RANKK( double, d, 3 )
_TIJK_APPROX_RANKK( float, f, 3 )
#include "convertQuietPop.h"
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tijk.h"
const char *
_tijk_class_str[TIJK_CLASS_MAX+1] = {
"( unknown_class )",
"tensor",
"esh",
"efs",
};
const char *
_tijk_class_desc[TIJK_CLASS_MAX+1] = {
"unknown class",
"tensor, specified by a tijk_type",
"even-order spherical harmonics, specified by order",
"even-order fourier series, specified by order",
};
const char *
_tijk_class_str_eqv[] = {
"tensor",
"esh",
"efs",
""
};
int
_tijk_class_val_eqv[] = {
tijk_class_tensor,
tijk_class_esh,
tijk_class_efs,
};
airEnum
_tijk_class_enum = {
"class",
TIJK_CLASS_MAX,
_tijk_class_str, NULL,
_tijk_class_desc,
_tijk_class_str_eqv, _tijk_class_val_eqv,
AIR_FALSE
};
64 const airEnum *const
tijk_class = &_tijk_class_enum;
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2010, 2009, 2008 Thomas Schultz
Copyright ( C ) 2010, 2009, 2008 Gordon Kindlmann
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tijk.h"
const int
tijkPresent = 42;
/* Some basic component-wise operations that don't depend on tensor type */
void
32 tijk_add_d ( double *res, const double *A, const double *B,
const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ )+*( B++ );
}
void
40 tijk_add_f ( float *res, const float *A, const float *B,
const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ )+*( B++ );
}
void
48 tijk_sub_d ( double *res, const double *A, const double *B,
const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ )-*( B++ );
}
void
56 tijk_sub_f ( float *res, const float *A, const float *B,
const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ )-*( B++ );
}
void
64 tijk_incr_d ( double *res, const double *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )+=*( A++ );
}
void
71 tijk_incr_f ( float *res, const float *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )+=*( A++ );
}
void
78 tijk_negate_d ( double *res, const double *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=-*( A++ );
}
void
85 tijk_negate_f ( float *res, const float *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=-*( A++ );
}
void
92 tijk_scale_d ( double *res, const double s,
const double *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=s*( *A++ );
}
void
100 tijk_scale_f ( float *res, const float s,
const float *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=s*( *A++ );
}
void
108 tijk_zero_d ( double *res, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=0.0;
}
void
115 tijk_zero_f ( float *res, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=0.0;
}
void
122 tijk_copy_d ( double *res, const double *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ );
}
void
129 tijk_copy_f ( float *res, const float *A, const tijk_type *type ) {
unsigned int i;
for ( i=0; i<type->num; i++ )
*( res++ )=*( A++ );
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2011 Thomas Schultz
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/* Reading and setting axis labels to encode Tijk types */
#include "tijk.h"
/* tijk_set_axis_tensor
*
* Marks a given nrrd axis as containing tensors of a given type
* Based on axis size, determines if values are masked.
* Returns 0 on success
* 1 if given NULL pointer
* 2 if nrrd has wrong type ( neither float nor double )
* 3 if axis doesn't exist
* 4 if axis has wrong size
*/
36 int tijk_set_axis_tensor( Nrrd *nrrd, unsigned int axis,
const tijk_type *type ) {
NrrdAxisInfo *axinfo = NULL;
unsigned int masked = 0, lablen;
if ( nrrd==NULL || type==NULL ) return 1;
if ( nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble ) return 2;
if ( axis>=nrrd->dim ) return 3;
axinfo = nrrd->axis+axis;
if ( axinfo->size==type->num+1 )
masked = 1;
else if ( axinfo->size!=type->num )
return 4;
axinfo->label = ( char* ) airFree( axinfo->label );
lablen = strlen( "tijk__" ) + strlen( type->name ) +
( masked?strlen( "mask_" ):0 ) + 1;
axinfo->label = AIR_CALLOC( lablen, char );
sprintf( axinfo->label, "tijk_%s%s", masked?"mask_":"", type->name );
return 0;
}
/* tijk_set_axis_esh
*
* Marks a given nrrd axis as containing even-order spherical harmonics of
* a given order. Based on axis size, determines if values are masked.
* Returns 0 on success
* 1 if given NULL pointer
* 2 if nrrd has wrong type ( neither float nor double )
* 3 if axis doesn't exist
* 4 if order is unsupported
* 5 if axis has wrong size
*/
67 int tijk_set_axis_esh( Nrrd *nrrd, unsigned int axis, unsigned int order ) {
NrrdAxisInfo *axinfo = NULL;
unsigned int masked = 0, lablen;
if ( nrrd==NULL ) return 1;
if ( nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble ) return 2;
if ( axis>=nrrd->dim ) return 3;
axinfo = nrrd->axis+axis;
if ( order>tijk_max_esh_order ) return 4;
if ( axinfo->size==tijk_esh_len[order/2]+1 )
masked = 1;
else if ( axinfo->size!=tijk_esh_len[order/2] )
return 5;
axinfo->label = ( char* ) airFree( axinfo->label );
lablen = strlen( "tijk_esh_" ) + ( masked?strlen( "mask_" ):0 ) + 3;
axinfo->label = AIR_CALLOC( lablen, char );
sprintf( axinfo->label, "tijk_%sesh_%02u", masked?"mask_":"", order );
return 0;
}
/* tijk_set_axis_efs
*
* Marks a given nrrd axis as containing even-order fourier series of
* a given order. Based on axis size, determines if values are masked.
* Returns 0 on success
* 1 if given NULL pointer
* 2 if nrrd has wrong type ( neither float nor double )
* 3 if axis doesn't exist
* 4 if order is unsupported
* 5 if axis has wrong size
*/
97 int tijk_set_axis_efs( Nrrd *nrrd, unsigned int axis, unsigned int order ) {
NrrdAxisInfo *axinfo = NULL;
unsigned int masked = 0, lablen;
if ( nrrd==NULL ) return 1;
if ( nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble ) return 2;
if ( axis>=nrrd->dim ) return 3;
axinfo = nrrd->axis+axis;
if ( order>tijk_max_efs_order ) return 4;
if ( axinfo->size==order+2 )
masked = 1;
else if ( axinfo->size!=order+1 )
return 5;
axinfo->label = ( char* ) airFree( axinfo->label );
lablen = strlen( "tijk_efs_" ) + ( masked?strlen( "mask_" ):0 ) + 3;
axinfo->label = AIR_CALLOC( lablen, char );
sprintf( axinfo->label, "tijk_%sefs_%02u", masked?"mask_":"", order );
return 0;
}
/* tijk_get_axis_type
*
* Extracts Tijk information from a given nrrd axis and writes it to info.
* Returns 0 on success
* no "tijk_*" label counts as success -> tijk_class_unknown
* 1 if given NULL pointer
* 2 if nrrd has wrong type ( neither float nor double )
* 3 if axis doesn't exist
* 4 if "tijk_*" label couldn't be parsed
* 5 if label didn't fit axis size
*/
127 int tijk_get_axis_type( tijk_axis_info *info,
const Nrrd *nrrd, unsigned int axis ) {
const tijk_type *
tijk_types[] = {
NULL, /* 0: tijk_2o2d_unsym */
NULL, /* 1: tijk_2o2d_sym */
NULL, /* 2: tijk_2o2d_asym */
NULL, /* 3: tijk_3o2d_sym */
NULL, /* 4: tijk_4o2d_unsym */
NULL, /* 5: tijk_4o2d_sym */
NULL, /* 6: tijk_1o3d */
NULL, /* 7: tijk_2o3d_unsym */
NULL, /* 8: tijk_2o3d_sym */
NULL, /* 9: tijk_2o3d_asym */
NULL, /* 10: tijk_3o3d_unsym */
NULL, /* 11: tijk_3o3d_sym */
NULL, /* 12: tijk_4o3d_sym */
NULL, /* 13: tijk_6o3d_sym */
NULL, /* 14: tijk_8o3d_sym */
NULL
};
const NrrdAxisInfo *axinfo = NULL;
const char *labelp = NULL;
unsigned int i=0;
if ( info==NULL || nrrd==NULL ) return 1;
if ( nrrd->type!=nrrdTypeFloat && nrrd->type!=nrrdTypeDouble ) return 2;
if ( axis>=nrrd->dim ) return 3;
axinfo = nrrd->axis+axis;
if ( axinfo->label==NULL || strncmp( axinfo->label, "tijk_", 5 ) ) {
info->tclass = tijk_class_unknown;
return 0;
}
labelp = axinfo->label+5;
if ( !strncmp( labelp, "mask_", 5 ) ) {
info->masked = 1;
labelp += 5;
} else
info->masked = 0;
if ( !strncmp( labelp, "esh_", 4 ) ) {
info->tclass = tijk_class_esh;
if ( 1!=sscanf( labelp+4, "%02u", &( info->order ) ) ||
info->order>tijk_max_esh_order || info->order%2!=0 )
return 4;
if ( axinfo->size!=tijk_esh_len[info->order/2]+info->masked )
return 5;
return 0;
}
if ( !strncmp( labelp, "efs_", 4 ) ) {
info->tclass = tijk_class_efs;
if ( 1!=sscanf( labelp+4, "%02u", &( info->order ) ) ||
info->order>tijk_max_efs_order || info->order%2!=0 )
return 4;
if ( axinfo->size!=info->order+1+info->masked )
return 5;
return 0;
}
tijk_types[ 0] = tijk_2o2d_unsym;
tijk_types[ 1] = tijk_2o2d_sym;
tijk_types[ 2] = tijk_2o2d_asym;
tijk_types[ 3] = tijk_3o2d_sym;
tijk_types[ 4] = tijk_4o2d_unsym;
tijk_types[ 5] = tijk_4o2d_sym;
tijk_types[ 6] = tijk_1o3d;
tijk_types[ 7] = tijk_2o3d_unsym;
tijk_types[ 8] = tijk_2o3d_sym;
tijk_types[ 9] = tijk_2o3d_asym;
tijk_types[10] = tijk_3o3d_unsym;
tijk_types[11] = tijk_3o3d_sym;
tijk_types[12] = tijk_4o3d_sym;
tijk_types[13] = tijk_6o3d_sym;
tijk_types[14] = tijk_8o3d_sym;
while ( tijk_types[i]!=NULL ) {
if ( !strcmp( labelp, tijk_types[i]->name ) ) {
info->tclass = tijk_class_tensor;
info->type = tijk_types[i];
if ( axinfo->size!=tijk_types[i]->num+info->masked )
return 5;
return 0;
}
i++;
}
return 4;
}
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Unary operation on a nrrd"
static const char *_unrrdu_1opInfoL =
( INFO
".\n "
"* Uses nrrdArithUnaryOp" );
int
34 unrrdu_1opMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *seedS;
Nrrd *nin, *nout, *ntmp=NULL;
int op, pret, type;
airArray *mop;
unsigned int seed;
hestOptAdd( &opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL,
"Unary operator. Possibilities include:\n "
"\b\bo \"-\": negative ( multiply by -1.0 )\n "
"\b\bo \"r\": reciprocal ( 1.0/value )\n "
"\b\bo \"sin\", \"cos\", \"tan\", \"asin\", \"acos\", \"atan\": "
"same as in C\n "
"\b\bo \"exp\", \"log\", \"log10\": same as in C\n "
"\b\bo \"log1p\", \"expm1\": accurate log( x+1 ) and exp( x )-1\n "
"\b\bo \"log2\": log base 2\n "
"\b\bo \"sqrt\", \"cbrt\", \"ceil\", \"floor\": same as in C\n "
"\b\bo \"erf\": error function ( integral of Gaussian )\n "
"\b\bo \"rup\", \"rdn\": round up or down to integral value\n "
"\b\bo \"abs\": absolute value\n "
"\b\bo \"sgn\": -1, 0, 1 if value is <0, ==0, or >0\n "
"\b\bo \"exists\": 1 iff not NaN or +/-Inf, 0 otherwise\n "
"\b\bo \"rand\": random value in [0.0, 1.0 ), "
"no relation to input\n "
"\b\bo \"nrand\": random sample from normal distribution with "
"mean 0.0 and stdv 1.0, no relation to input\n "
"\b\bo \"if\": if input is non-zero, 1, else 0\n "
"\b\bo \"0\": output always 0\n "
"\b\bo \"1\": output always 1",
NULL, nrrdUnaryOp );
hestOptAdd( &opt, "s, seed", "seed", airTypeString, 1, 1, &seedS, "",
"seed value for RNG for rand and nrand, so that you "
"can get repeatable results between runs, or, "
"by not using this option, the RNG seeding will be "
"based on the current time" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"convert input nrrd to this type prior to "
"doing operation. Useful when desired output is float "
"( e.g., with log1p ), but input is integral. By default "
"( not using this option ), the types of "
"the input nrrds are left unchanged.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_1opInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdTypeDefault != type ) {
/* they requested conversion to another type prior to the 1op */
airMopAdd( mop, ntmp=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( ntmp, nin, type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting input nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
} else {
ntmp = nin;
}
/* see note in 2op.c about the hazards of trying to be clever
** about minimizing the seeding of the RNG
** if ( nrrdUnaryOpRand == op
** || nrrdUnaryOpNormalRand == op ) {
*/
if ( airStrlen( seedS ) ) {
if ( 1 != sscanf( seedS, "%u", &seed ) ) {
fprintf( stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS );
airMopError( mop );
return 1;
} else {
airSrandMT( seed );
}
} else {
/* got no request for specific seed */
airSrandMT( AIR_CAST( unsigned int, airTime( ) ) );
}
if ( nrrdArithUnaryOp( nout, op, ntmp ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing unary operation:\n%s", me, err );
airMopError( mop );
return 1;
}
/* if we had to create ntmp with nrrdConvert, it will be mopped,
otherwise ntmp is an alias for nin, which will also be mopped */
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
135 UNRRDU_CMD( 1op, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Binary operation on two nrrds, or on a nrrd and a constant"
static const char *_unrrdu_2opInfoL =
( INFO
". Either the first or second operand can be a float constant, "
"but not both. Use \"-\" for an operand to signify "
"a nrrd to be read from stdin ( a pipe ). Note, however, "
"that \"-\" can probably only be used once ( reliably ).\n "
"* Uses nrrdArithIterBinaryOp or ( with -w ) nrrdArithIterBinaryOpSelect" );
int
37 unrrdu_2opMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *seedS;
NrrdIter *in1, *in2;
Nrrd *nout, *ntmp=NULL;
int op, type, E, pret, which;
airArray *mop;
unsigned int seed;
hestOptAdd( &opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL,
"Binary operator. Possibilities include:\n "
"\b\bo \"+\", \"-\", \"x\", \"/\": "
"add, subtract, multiply, divide\n "
"\b\bo \"+c\", \"-c\", \"xc\": add, subtract, multiply, with "
"clamping to range of output, in case its integral\n "
"\b\bo \"^\": exponentiation ( pow )\n "
"\b\bo \"spow\": signed exponentiation: sgn( x )pow( abs( x ), p )\n "
"\b\bo \"fpow\": like spow but with curves flipped\n "
"\b\bo \"%\": integer modulo\n "
"\b\bo \"fmod\": same as fmod( ) in C\n "
"\b\bo \"atan2\": same as atan2( ) in C\n "
"\b\bo \"min\", \"max\": minimum, maximum\n "
"\b\bo \"lt\", \"lte\", \"gt\", \"gte\": same as C's <, <=, >, <=\n "
"\b\bo \"eq\", \"neq\": same as C's == and !=\n "
"\b\bo \"comp\": -1, 0, or 1 if 1st value is less than, "
"equal to, or greater than 2nd value\n "
"\b\bo \"if\": if 1st value is non-zero, use it, "
"else use 2nd value\n "
"\b\bo \"exists\": if 1st value exists, use it, "
"else use 2nd value\n "
"\b\bo \"nrand\": scale unit-stdv Gaussian noise by 2nd value "
"and add to first value\n "
"\b\bo \"rrand\": sample Rician distribution with 1st value "
"for \"true\" mean, and 2nd value for sigma",
NULL, nrrdBinaryOp );
hestOptAdd( &opt, NULL, "in1", airTypeOther, 1, 1, &in1, NULL,
"First input. Can be a single value or a nrrd.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "in2", airTypeOther, 1, 1, &in2, NULL,
"Second input. Can be a single value or a nrrd.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, "s, seed", "seed", airTypeString, 1, 1, &seedS, "",
"seed value for RNG for nrand, so that you "
"can get repeatable results between runs, or, "
"by not using this option, the RNG seeding will be "
"based on the current time" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to convert all INPUT nrrds to, prior to "
"doing operation, useful for doing, for instance, the difference "
"between two unsigned char nrrds. This will also determine "
"output type. By default ( not using this option ), the types of "
"the input nrrds are left unchanged.",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &opt, "w, which", "arg", airTypeInt, 1, 1, &which, "-1",
"Which argument ( 0 or 1 ) should be used to determine the "
"shape of the output nrrd. By default ( not using this option ), "
"the first non-constant argument is used. " );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_2opInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/*
fprintf( stderr, "%s: op = %d\n", me, op );
fprintf( stderr, "%s: in1->left = %d, in2->left = %d\n", me,
( int )( in1->left ), ( int )( in2->left ) );
*/
if ( nrrdTypeDefault != type ) {
/* they wanted to convert nrrds to some other type first */
E = 0;
if ( in1->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), in1->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( in1, ntmp );
}
if ( in2->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), in2->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( in2, ntmp );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting input nrrd( s ):\n%s", me, err );
airMopError( mop );
return 1;
}
/* this will still leave a nrrd in the NrrdIter for nrrdIterNix( )
( called by hestParseFree( ) called be airMopOkay( ) ) to clear up */
}
/*
** Used to only deal with RNG seed for particular op:
** if ( nrrdBinaryOpNormalRandScaleAdd == op ) {
** but then when nrrdBinaryOpRicianRand was added, then the seed wasn't being
** used ==> BUG. To be more future proof, we try to parse and use seed
** whenever a non-empty string is given, and end up *ALWAYS* calling
** airSrandMT, even for operations that have nothing to do with random
** numbers. Could also have a new array that indicates if an op involves
** the RNG, but this would add rarely-needed complexity
*/
if ( airStrlen( seedS ) ) {
if ( 1 != sscanf( seedS, "%u", &seed ) ) {
fprintf( stderr, "%s: couldn't parse seed \"%s\" as uint\n", me, seedS );
airMopError( mop );
return 1;
} else {
airSrandMT( seed );
}
} else {
/* got no request for specific seed */
airSrandMT( AIR_CAST( unsigned int, airTime( ) ) );
}
if ( -1 == which
? nrrdArithIterBinaryOp( nout, op, in1, in2 )
: nrrdArithIterBinaryOpSelect( nout, op, in1, in2,
AIR_CAST( unsigned int, which ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing binary operation:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
170 UNRRDU_CMD( 2op, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Ternary operation on three nrrds or constants"
static const char *_unrrdu_3opInfoL =
( INFO
". Can have one, two, or three nrrds, but not zero. "
"Use \"-\" for an operand to signify "
"a nrrd to be read from stdin ( a pipe ). Note, however, "
"that \"-\" can probably only be used once ( reliably ).\n "
"* Uses nrrdArithIterTernaryOp or ( with -w ) nrrdArithIterTernaryOpSelect" );
int
37 unrrdu_3opMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
NrrdIter *in1, *in2, *in3;
Nrrd *nout, *ntmp=NULL;
int op, type, E, pret, which;
airArray *mop;
hestOptAdd( &opt, NULL, "operator", airTypeEnum, 1, 1, &op, NULL,
"Ternary operator. Possibilities include:\n "
"\b\bo \"+\", \"x\": sum or product of three values\n "
"\b\bo \"min\", \"max\": minimum, maximum\n "
"\b\bo \"min_sm\": smoothed minimum function; "
"min_sm( x, w, M ) is like min( x, M ) but for x > M-w ( with w > 0 ) "
"there is a smooth transition from x to asymptotic to M\n "
"\b\bo \"max_sm\": smoothed maximum function; "
"max_sm( M, w, x ) is like max( M, x ) but for x < m+w ( with w > m ) "
"there is a smooth transition from x to asymptotic to m\n "
"\b\bo \"lt_sm\": 1st less than 3rd, smoothed by 2nd\n "
"\b\bo \"gt_sm\": 1st greater than 3rd, smoothed by 2nd\n "
"\b\bo \"clamp\": 2nd value is clamped to range between "
"the 1st and the 3rd\n "
"\b\bo \"ifelse\": if 1st value non-zero, then 2nd value, else "
"3rd value\n "
"\b\bo \"lerp\": linear interpolation between the 2nd and "
"3rd values, as the 1st value varies between 0.0 and 1.0, "
"respectively\n "
"\b\bo \"exists\": if the 1st value exists, use the 2nd "
"value, otherwise use the 3rd\n "
"\b\bo \"in_op\": 1 iff 2nd value is > 1st and < 3rd, "
"0 otherwise\n "
"\b\bo \"in_cl\": 1 iff 2nd value is >= 1st and <= 3rd, "
"0 otherwise\n "
"\b\bo \"gauss\": evaluate ( at 1st value ) Gaussian with mean=2nd "
"and stdv=3rd value\n "
"\b\bo \"rician\": evaluate ( at 1st value ) Rician with mean=2nd "
"and stdv=3rd value",
NULL, nrrdTernaryOp );
hestOptAdd( &opt, NULL, "in1", airTypeOther, 1, 1, &in1, NULL,
"First input. Can be a single value or a nrrd.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "in2", airTypeOther, 1, 1, &in2, NULL,
"Second input. Can be a single value or a nrrd.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "in3", airTypeOther, 1, 1, &in3, NULL,
"Third input. Can be a single value or a nrrd.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to convert all nrrd inputs to, prior to "
"doing operation. This also determines output type. "
"By default ( not using this option ), the types of the input "
"nrrds are left unchanged.",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &opt, "w, which", "arg", airTypeInt, 1, 1, &which, "-1",
"Which argument ( 0, 1, or 2 ) should be used to determine the "
"shape of the output nrrd. By default ( not using this option ), "
"the first non-constant argument is used. " );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_3opInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/*
fprintf( stderr, "%s: op = %d\n", me, op );
fprintf( stderr, "%s: in1->left = %d, in2->left = %d\n", me,
( int )( in1->left ), ( int )( in2->left ) );
*/
if ( nrrdTypeDefault != type ) {
/* they wanted to convert nrrds to some other type first */
E = 0;
if ( in1->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), in1->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( in1, ntmp );
}
if ( in2->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), in2->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( in2, ntmp );
}
if ( in3->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), in3->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( in3, ntmp );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting input nrrd( s ):\n%s", me, err );
airMopError( mop );
return 1;
}
/* this will still leave a nrrd in the NrrdIter for nrrdIterNix( )
( called by hestParseFree( ) called be airMopOkay( ) ) to clear up */
}
/* HEY: will need to add handling of RNG seed ( as in 1op and 2op )
if there are any 3ops involving random numbers */
if ( -1 == which
? nrrdArithIterTernaryOp( nout, op, in1, in2, in3 )
: nrrdArithIterTernaryOpSelect( nout, op, in1, in2, in3,
AIR_CAST( unsigned int, which ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing ternary operation:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
156 UNRRDU_CMD( 3op, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Draws ASCII-art box plots"
static const char *_unrrdu_aabplotInfoL =
( INFO
". Because why not.\n "
"* ( uses nrrd, but no Nrrd implements this functionality )" );
int
34 unrrdu_aabplotMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
/* these are stock for unrrdu */
hestOpt *opt = NULL;
airArray *mop;
int pret;
char *err;
/* these are specific to this command */
int medshow, rshow;
Nrrd *_nin, *nin, *_nsingle, *nsingle;
unsigned int plen;
double vrange[2], *single;
hestOptAdd( &opt, "l", "len", airTypeUInt, 1, 1, &plen, "78",
"number of characters in box plot" );
hestOptAdd( &opt, "r", "min max", airTypeDouble, 2, 2, vrange, "0 100",
"values to use as absolute min and max ( unfortunately "
"has to be same for all scanlines ( rows )." );
hestOptAdd( &opt, "rs", "show", airTypeBool, 1, 1, &rshow, "false",
"show range above plots" );
hestOptAdd( &opt, "ms", "show", airTypeBool, 1, 1, &medshow, "false",
"print the median value" );
OPT_ADD_NIN( _nin, "input nrrd" );
hestOptAdd( &opt, "s", "single", airTypeOther, 1, 1, &_nsingle, "",
"if given a 1D nrrd here that matches the number of "
"rows in the \"-i\" input, interpret it as a list of values "
"that should be indicated with \"X\"s in the plots.",
NULL, NULL, nrrdHestNrrd );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_aabplotInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !( 2 == _nin->dim || 1 == _nin->dim ) ) {
fprintf( stderr, "%s: need 1-D or 2-D array\n", me );
airMopError( mop );
return 1;
}
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nin, _nin, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting \"-s\" input:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( 1 == nin->dim ) {
if ( nrrdAxesInsert( nin, nin, 1 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error making 2-D from 1-D:\n%s", me, err );
airMopError( mop );
return 1;
}
}
if ( _nsingle ) {
if ( nrrdElementNumber( _nsingle ) != nin->axis[1].size ) {
fprintf( stderr, "%s: \"-s\" input doesn't match size of \"-i\" input",
me );
airMopError( mop );
return 1;
}
nsingle = nrrdNew( );
airMopAdd( mop, nsingle, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nsingle, _nsingle, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting \"-s\" input:\n%s", me, err );
airMopError( mop );
return 1;
}
single = ( double* )nsingle->data;
} else {
nsingle = NULL;
single = NULL;
}
{
#define PTNUM 5
double *in, *buff, ptile[PTNUM]={5, 25, 50, 75, 95};
unsigned int xi, yi, pi, ti, sx, sy, pti[PTNUM];
char *line, rbuff[128];
Nrrd *nbuff;
sx = AIR_CAST( unsigned int, nin->axis[0].size );
sy = AIR_CAST( unsigned int, nin->axis[1].size );
nbuff = nrrdNew( );
airMopAdd( mop, nbuff, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( nbuff, nin, 1, 0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error making buffer:\n%s", me, err );
airMopError( mop );
return 1;
}
line = calloc( plen+1, sizeof( char ) );
in = ( double* )nin->data;
buff = ( double* )nbuff->data;
if ( rshow ) {
for ( pi=0; pi<plen; pi++ ) {
line[pi] = ' ';
}
sprintf( rbuff, "|<-- %g", vrange[0] );
memcpy( line, rbuff, strlen( rbuff ) );
sprintf( rbuff, "%g -->|", vrange[1] );
memcpy( line + plen - strlen( rbuff ), rbuff, strlen( rbuff ) );
printf( "%s", line );
if ( medshow ) {
printf( " median:" );
}
printf( "\n" );
}
for ( yi=0; yi<sy; yi++ ) {
for ( xi=0; xi<sx; xi++ ) {
buff[xi] = in[xi + sx*yi];
}
qsort( buff, sx, sizeof( double ), nrrdValCompare[nrrdTypeDouble] );
for ( ti=0; ti<PTNUM; ti++ ) {
pti[ti] = airIndexClamp( vrange[0],
buff[airIndexClamp( 0, ptile[ti], 100, sx )],
vrange[1], plen );
/*
fprintf( stderr, "ti %u ( %g ) -> buff[%u] = %g -> %u\n", ti,
ptile[ti], airIndexClamp( 0, ptile[ti], 100, sx ),
buff[airIndexClamp( 0, ptile[ti], 100, sx )], pti[ti] );
*/
}
for ( pi=0; pi<plen; pi++ ) {
line[pi] = pi % 2 ? ' ' : '.';
}
for ( pi=pti[0]; pi<=pti[4]; pi++ ) {
line[pi] = '-';
}
for ( pi=pti[1]; pi<=pti[3]; pi++ ) {
line[pi] = '=';
}
line[pti[2]]='m';
if ( pti[2] > 0 ) {
line[pti[2]-1]='<';
}
if ( pti[2] < plen-1 ) {
line[pti[2]+1]='>';
}
if ( single ) {
line[airIndexClamp( vrange[0], single[yi], vrange[1], plen )]='X';
}
printf( "%s", line );
if ( medshow ) {
printf( " %g", buff[airIndexClamp( 0, 50, 100, sx )] );
}
printf( "\n" );
#if 0
unsigned int ltt = ( unsigned int )( -1 );
/* printf( "[" ); */
for ( pi=0; pi<plen; pi++ ) {
for ( tt=0; tt<PTNUM && pti[tt] < pi; tt++ ) {
/*
fprintf( stderr, "( pi %u < pti[%u]==%u )", pi, tt, pti[tt] );
*/
}
/* fprintf( stderr, " --> tt=%u\n", tt ); */
if ( 2 == ltt && 3 == tt ) {
printf( "M" );
} else {
printf( "%c", cc[tt] );
}
ltt = tt;
}
/* printf( "]\n" ); */
printf( "\n" );
#endif
}
}
airMopOkay( mop );
return 0;
}
211 UNRRDU_CMD_HIDE( aabplot, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Information about this program and its use"
int
30 unrrdu_aboutMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
char buff[AIR_STRLEN_MED], fmt[AIR_STRLEN_MED];
char par1[] = "\t\t\t\t"
"\"unu\" is a command-line interface to much of the functionality "
"in \"nrrd\", a C library for raster data processing. Nrrd is one "
"library in the \"Teem\" collection of libraries. More information "
"about Teem is at <http://teem.sf.net>. A checkout of Teem source "
"is available via:\n "
"svn co http://svn.code.sf.net/p/teem/code/teem/trunk teem\n ";
/* "svn co http://teem.svn.sf.net/svnroot/teem/teem/trunk teem\n "; */
char par2[] = "\t\t\t\t"
"Long-term maintenance of this software depends on funding, and "
"funding depends on being able to document who is using it for what. "
"If unu or Nrrd has helped in your research, including for simple one-off "
"experiments or mundane data hacking, the developers of Teem would love "
"to know. There are multiple ways of communicating this. "
"In your publications, consider adding a line such as this "
"in the Acknowledgments: "
"\"Data processing performed with the unu tool, "
"part of the Teem toolkit available at "
"http://teem.sf.net\". "
"Alternatively, please email glk@uchicago.edu and briefly describe "
"how Teem software has helped in your work. "
"Please also consider joining the teem-users mailing list: "
"<http://lists.sourceforge.net/lists/listinfo/teem-users>. This is "
"the primary forum for feedback, questions, and feature requests.\n ";
char par3[] = "\t\t\t\t"
"A summary list of unu commands is generated by running simply \"unu\". "
"Running a unu command without additional arguments "
"( e.g. \"unu slice\" ) generates its description and usage information. "
"This includes information ( marked with \"* Uses ...\" ) "
"about the underling Nrrd library calls "
"( e.g. nrrdSlice( ) ) that implement the basic functionality in "
"that unu command.\n ";
char par4[] = "\t\t\t\t"
"The utility of unu is mainly as a pre-processing tool for getting "
"data into a type, encoding, format, or dimensions best suited for some "
"visualization or rendering task. Also, slices and projections are "
"effective ways to visually inspect the contents of a dataset. "
"Especially useful commands include make, resample, crop, slice, "
"project, histo, dhisto, quantize, and save. Unu can process "
"CT and MRI volume datasets, grayscale and color images, "
"time-varying volumes of vector fields ( 5-D arrays ), and more. "
"Currently supported formats are plain text files ( 2-D float arrays ), "
"NRRD, VTK structured points, and PNG and PNM images. "
"\"unu make -bs -1\" can read from most DICOM files. "
"\"unu save\" can generate EPS files. "
"Supported encodings are raw, ascii, hex, gzip, and bzip2.\n";
char par5[] = "\t\t\t\t"
"Much of the functionality of unu derives from chaining multiple "
"invocations together with pipes ( \"|\" ), minimizing the "
"need to save out intermediate files. For example, if "
"\"data.raw.gz\" is a gzip'ed 256\tx\t256\tx\t80 volume of raw floats "
"written from a PC, "
"then the following will save to \"zsum.png\" a histogram "
"equalized summation projection along the slowest axis:\n";
char par6[] = "\tunu make -i data.raw.gz -t float -s 256 256 80 "
"-e gzip -en little \\\n "
" | unu project -a 2 -m sum \\\n "
" | unu heq -b 2000 -s 1 \\\n "
" | unu quantize -b 8 -o zsum.png"
"\n";
int enc, form, miss;
AIR_UNUSED( argc );
AIR_UNUSED( argv );
AIR_UNUSED( me );
fprintf( stdout, "\n" );
sprintf( buff, "--- unu: Utah Nrrd Utilities command-line interface ---" );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stdout, fmt, buff );
airTeemVersionSprint( buff );
sprintf( fmt, "%%%ds\n",
( int )( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( stdout, fmt, buff );
fprintf( stdout, "\n" );
_hestPrintStr( stdout, 1, 0, 78, par1, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par2, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par3, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par4, AIR_FALSE );
_hestPrintStr( stdout, 1, 0, 78, par5, AIR_FALSE );
_hestPrintStr( stdout, 2, 0, 78, par6, AIR_FALSE );
printf( " Formats available:" );
miss = AIR_FALSE;
for ( form=nrrdFormatTypeUnknown+1; form<nrrdFormatTypeLast; form++ ) {
if ( nrrdFormatArray[form]->available( ) ) {
printf( " %s", airEnumStr( nrrdFormatType, form ) );
} else {
miss = AIR_TRUE;
}
}
printf( "\n" );
if ( miss ) {
printf( " ( not available:" );
for ( enc=nrrdFormatTypeUnknown+1; enc<nrrdFormatTypeLast; enc++ ) {
if ( !nrrdFormatArray[enc]->available( ) ) {
printf( " %s", airEnumStr( nrrdFormatType, enc ) );
}
}
printf( " )\n" );
}
printf( " Nrrd data encodings available:" );
miss = AIR_FALSE;
for ( enc=nrrdEncodingTypeUnknown+1; enc<nrrdEncodingTypeLast; enc++ ) {
if ( nrrdEncodingArray[enc]->available( ) ) {
printf( " %s", airEnumStr( nrrdEncodingType, enc ) );
} else {
miss = AIR_TRUE;
}
}
printf( "\n" );
if ( miss ) {
printf( " ( not available:" );
for ( enc=nrrdEncodingTypeUnknown+1; enc<nrrdEncodingTypeLast; enc++ ) {
if ( !nrrdEncodingArray[enc]->available( ) ) {
printf( " %s", airEnumStr( nrrdEncodingType, enc ) );
}
}
printf( " )\n" );
}
printf( "\n" );
/* NOTE: this is an exceptional unu command that doesn't rely on
privateUnrrdu.h USAGE( ) macro; so we determine our own return value */
return 0;
}
163 UNRRDU_CMD( about, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO " Automatically crop axes based on given measure"
static const char *_unrrdu_acropInfoL =
( INFO ". For the axes that are to be cropped, the slices perpendicular "
"to that axis are projected down to a scalar with the specified measure. "
"The resulting 1D array is analyzed by determining what portions at the "
"beginning and end constitute less than some portion of the cumulative "
"array sum; these ends are cropped off. The cropping bounds determined "
"here can be saved and applied to other arrays via the \"-b\" option.\n "
"* Uses nrrdCropAuto" );
int
38 unrrdu_acropMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
airArray *mop;
size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
unsigned int *axes, axesLen;
double frac;
int measr, offset;
Nrrd *nbounds;
char *boundsSave;
hestOptAdd( &opt, "a, axes", "ax0", airTypeUInt, 0, -1, &axes, "",
"the axes ( if any ) that should NOT be cropped", &axesLen );
hestOptAdd( &opt, "m, measure", "measr", airTypeEnum, 1, 1, &measr, NULL,
"How to measure slices ( along axes to crop ) as scalars, "
"to form 1-D array analyzed to determine cropping extent. "
"All the measures from \"unu project\" can be used, but "
"those that make more sense here include:\n "
"\b\bo \"max\", \"mean\", \"median\", "
"\"variance\": ( self-explanatory )\n "
"\b\bo \"stdv\": standard deviation\n "
"\b\bo \"cov\": coefficient of variation\n "
"\b\bo \"product\", \"sum\": product or sum of all values\n "
"\b\bo \"L1\", \"L2\", \"NL2\", \"RMS\", \"Linf\": "
"different norms.", NULL, nrrdMeasure );
hestOptAdd( &opt, "f, frac", "frac", airTypeDouble, 1, 1, &frac, "0.1",
"threshold of cumulative sum of 1-D array at which to crop. "
"Needs to be in interval [0.0, 0.5 )." );
hestOptAdd( &opt, "off, offset", "offset", airTypeInt, 1, 1, &offset, "1",
"how much to offset the numerically determined cropping; "
"positive offsets means expanding the interval of kept "
"indices ( less cropping )" );
hestOptAdd( &opt, "b, bounds", "filename", airTypeString, 1, 1,
&boundsSave, "",
"if a filename is given here, the automatically determined "
"min and max bounds for cropping are saved to this file "
"as a 2-D array; first scanline is for -min, second is for -max. "
"Unfortunately nothing using the \"m\" and \"M\" semantics "
"( above ) can currently be saved in the bounds file." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_acropInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCropAuto( nout, nin, min, max,
axes, axesLen,
measr, frac, offset ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error cropping nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( airStrlen( boundsSave ) ) {
unsigned int axi;
airULLong *bounds;
nbounds = nrrdNew( );
airMopAdd( mop, nbounds, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdMaybeAlloc_va( nbounds, nrrdTypeULLong, 2,
AIR_CAST( airULLong, nin->dim ),
AIR_CAST( airULLong, 2 ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error allocating cropping bounds array:\n%s",
me, err );
airMopError( mop );
return 1;
}
bounds = AIR_CAST( airULLong*, nbounds->data );
for ( axi=0; axi<nin->dim; axi++ ) {
bounds[axi + 0*( nin->dim )] = AIR_CAST( airULLong, min[axi] );
bounds[axi + 1*( nin->dim )] = AIR_CAST( airULLong, max[axi] );
}
if ( nrrdSave( boundsSave, nbounds, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error saving cropping bounds array:\n%s",
me, err );
airMopError( mop );
return 1;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
136 UNRRDU_CMD( acrop, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Affine ( lerp ) mapping on 5 nrrds or constants"
static const char *_unrrdu_affineInfoL =
( INFO
". All the 5 arguments can be either nrrds or single "
"floating-point values. When all args are single values, this "
"is subsuming the functionality of the previous stand-alone "
"\"affine\" program. "
"Use \"-\" for an operand to signify "
"a nrrd to be read from stdin ( a pipe ). Note, however, "
"that \"-\" can probably only be used once ( reliably ).\n "
"* Uses nrrdArithAffine or nrrdArithIterAffine" );
int
40 unrrdu_affineMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
NrrdIter *minIn, *in, *maxIn, *minOut, *maxOut, *args[5];
Nrrd *nout, *ntmp=NULL;
int type, E, pret, clamp;
airArray *mop;
unsigned int ai, nn;
hestOptAdd( &opt, NULL, "minIn", airTypeOther, 1, 1, &minIn, NULL,
"Lower end of input value range.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "in", airTypeOther, 1, 1, &in, NULL,
"Input value.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "maxIn", airTypeOther, 1, 1, &maxIn, NULL,
"Upper end of input value range.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "minOut", airTypeOther, 1, 1, &minOut, NULL,
"Lower end of output value range.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, NULL, "maxOut", airTypeOther, 1, 1, &maxOut, NULL,
"Upper end of output value range.",
NULL, NULL, nrrdHestIter );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to convert all nrrd inputs to, prior to "
"doing operation. This also determines output type. "
"By default ( not using this option ), the types of the input "
"nrrds are left unchanged.",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &opt, "clamp", "bool", airTypeBool, 1, 1, &clamp, "false",
"clamp output values to specified output range" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_affineInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
args[0] = minIn;
args[1] = in;
args[2] = maxIn;
args[3] = minOut;
args[4] = maxOut;
nn = 0;
for ( ai=0; ai<5; ai++ ) {
nn += !!args[ai]->ownNrrd;
}
if ( nrrdTypeDefault != type ) {
/* they wanted to convert nrrds to some other type first */
E = 0;
for ( ai=0; ai<5; ai++ ) {
if ( args[ai]->ownNrrd ) {
if ( !E ) E |= nrrdConvert( ntmp=nrrdNew( ), args[ai]->ownNrrd, type );
if ( !E ) nrrdIterSetOwnNrrd( args[ai], ntmp );
}
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting input nrrd( s ):\n%s", me, err );
airMopError( mop );
return 1;
}
}
if ( 0 == nn ) {
/* actually, there are no nrrds; we represent the functionality
of the previous stand-alone binary "affine" */
double valOut;
valOut = AIR_AFFINE( minIn->val, in->val, maxIn->val,
minOut->val, maxOut->val );
if ( clamp ) {
double mmin = AIR_MIN( minOut->val, maxOut->val );
double mmax = AIR_MAX( minOut->val, maxOut->val );
valOut = AIR_CLAMP( mmin, valOut, mmax );
}
printf( "%g\n", valOut );
} else {
/* we have a nrrd output */
if ( 1 == nn && in->ownNrrd ) {
E = nrrdArithAffine( nout, minIn->val, in->ownNrrd, maxIn->val,
minOut->val, maxOut->val, clamp );
} else {
E = nrrdArithIterAffine( nout, minIn, in, maxIn, minOut, maxOut, clamp );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing ternary operation:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
}
airMopOkay( mop );
return 0;
}
144 UNRRDU_CMD( affine, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Remove one or more singleton axes from a nrrd"
static const char *_unrrdu_axdeleteInfoL =
( INFO
". Singleton axes have only a single sample along them. "
"The underlying linear ordering of the samples is "
"unchanged, and the information about the other axes is "
"shifted downwards as needed. As a total hack, if you give "
"-1 as the axis, this will do a matlab-style \"squeeze\", in which "
"any and all singleton axes are removed.\n "
"* Uses nrrdAxesDelete" );
int
39 unrrdu_axdeleteMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *ntmp;
int pret, _axis;
unsigned axis;
airArray *mop;
hestOptAdd( &opt, "a, axis", "axis", airTypeInt, 1, 1, &_axis, NULL,
"dimension ( axis index ) of the axis to remove" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_axdeleteInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( -1 == _axis ) {
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error copying axis:\n%s", me, err );
airMopError( mop ); return 1;
}
for ( axis=0;
axis<nout->dim && nout->axis[axis].size > 1;
axis++ )
;
while ( axis<nout->dim ) {
if ( nrrdAxesDelete( ntmp, nout, axis )
|| nrrdCopy( nout, ntmp ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error deleting axis:\n%s", me, err );
airMopError( mop ); return 1;
}
for ( axis=0;
axis<nout->dim && nout->axis[axis].size > 1;
axis++ )
;
}
} else {
if ( nrrdAxesDelete( nout, nin, _axis ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error deleting axis:\n%s", me, err );
airMopError( mop ); return 1;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
101 UNRRDU_CMD( axdelete, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Modify attributes of one or more axes"
static const char *_unrrdu_axinfoInfoL =
( INFO
". The only attributes which are set are those for which command-line "
"options are given.\n "
"* Uses no particular function; just sets fields in the NrrdAxisInfo" );
int
35 unrrdu_axinfoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *label, *units, *centerStr, *kindStr,
*_dirStr, *dirStr, *mmStr[2];
Nrrd *nin, *nout;
int pret, center, kind;
unsigned int *axes, axesLen, axi, mmIdx;
double mm[2], spc, sdir[NRRD_SPACE_DIM_MAX];
airArray *mop;
hestOptAdd( &opt, "a, axes", "ax0", airTypeUInt, 1, -1, &axes, NULL,
"the one or more axes that should be modified", &axesLen );
hestOptAdd( &opt, "l, label", "label", airTypeString, 1, 1, &label, "",
"label to associate with axis" );
hestOptAdd( &opt, "u, units", "units", airTypeString, 1, 1, &units, "",
"units of measurement" );
mmIdx =
hestOptAdd( &opt, "mm, minmax", "min max", airTypeString, 2, 2, mmStr, "nan nan",
"min and max values along axis" );
hestOptAdd( &opt, "sp, spacing", "spacing", airTypeDouble, 1, 1, &spc, "nan",
"spacing between samples along axis" );
/* There used to be a complaint here about how hest doesn't allow
you to learn whether the option was parsed from the supplied
default versus from the command-line itself. That issue has been
solved: opt[oi].source now takes on values from the hestSource*
enum; axinsert.c now provides an example of this. However,
parsing from a string here is still needed here, because here we
need to allow the string that represents "no centering"; this
is a current weakness of airEnumStr.
hestOptAdd( &opt, "c, center", "center", airTypeEnum, 1, 1, ¢, "unknown",
"centering of axis: \"cell\" or \"node\"",
NULL, nrrdCenter );
*/
hestOptAdd( &opt, "c, center", "center", airTypeString, 1, 1, ¢erStr, "",
"axis centering: \"cell\" or \"node\". Not using this option "
"leaves the centering as it is on input" );
hestOptAdd( &opt, "dir, direction", "svec", airTypeString, 1, 1, &_dirStr, "",
"( NOTE: must quote vector ) The \"space direction\": the vector "
"in space spanned by incrementing ( by one ) the axis index." );
hestOptAdd( &opt, "k, kind", "kind", airTypeString, 1, 1, &kindStr, "",
"axis kind. Not using this option "
"leaves the kind as it is on input" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_axinfoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
for ( axi=0; axi<axesLen; axi++ ) {
if ( !( axes[axi] < nin->dim ) ) {
fprintf( stderr, "%s: axis %u not in valid range [0, %u]\n",
me, axes[axi], nin->dim-1 );
airMopError( mop );
return 1;
}
}
/* parse the strings given via -mm */
if ( 2 != airSingleSscanf( mmStr[0], "%lf", mm+0 )
+ airSingleSscanf( mmStr[1], "%lf", mm+1 ) ) {
fprintf( stderr, "%s: couldn't parse both \"%s\" and \"%s\" "
"( from \"-mm\" ) as doubles\n", me, mmStr[0], mmStr[1] );
airMopError( mop );
return 1;
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error copying input:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( airStrlen( _dirStr ) ) {
if ( !nin->spaceDim ) {
fprintf( stderr, "%s: wanted to add space direction, but input "
"doesn't have space dimension set", me );
airMopError( mop );
return 1;
}
/* mindlessly copying logic from unu make; unsure of the value */
if ( '\"' == _dirStr[0] && '\"' == _dirStr[strlen( _dirStr )-1] ) {
_dirStr[strlen( _dirStr )-1] = 0;
dirStr = _dirStr + 1;
} else {
dirStr = _dirStr;
}
if ( nrrdSpaceVectorParse( sdir, dirStr, nin->spaceDim, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: couldn't parse space vector:\n%s", me, err );
airMopError( mop );
return 1;
}
} else {
dirStr = NULL;
}
for ( axi=0; axi<axesLen; axi++ ) {
unsigned int axis;
axis = axes[axi];
if ( strlen( label ) ) {
nout->axis[axis].label = ( char * )airFree( nout->axis[axis].label );
nout->axis[axis].label = airStrdup( label );
}
if ( strlen( units ) ) {
nout->axis[axis].units = ( char * )airFree( nout->axis[axis].units );
nout->axis[axis].units = airStrdup( units );
}
if ( hestSourceUser == opt[mmIdx].source ) {
/* if it came from user, set the value, even if its nan. Actually,
especially if its nan: that is the purpose of this extra logic */
nout->axis[axis].min = mm[0];
nout->axis[axis].max = mm[1];
} else {
if ( AIR_EXISTS( mm[0] ) ) {
nout->axis[axis].min = mm[0];
}
if ( AIR_EXISTS( mm[1] ) ) {
nout->axis[axis].max = mm[1];
}
}
if ( AIR_EXISTS( spc ) ) {
nout->axis[axis].spacing = spc;
}
/* see above
if ( nrrdCenterUnknown != cent ) {
nout->axis[axis].center = cent;
}
*/
if ( airStrlen( centerStr ) ) {
if ( !strcmp( "none", centerStr )
|| !strcmp( "???", centerStr ) ) {
center = nrrdCenterUnknown;
} else {
if ( !( center = airEnumVal( nrrdCenter, centerStr ) ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as %s\n", me,
centerStr, nrrdCenter->name );
airMopError( mop );
return 1;
}
}
nout->axis[axis].center = center;
}
if ( airStrlen( kindStr ) ) {
if ( !strcmp( "none", kindStr )
|| !strcmp( "???", kindStr ) ) {
kind = nrrdKindUnknown;
} else {
if ( !( kind = airEnumVal( nrrdKind, kindStr ) ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as %s\n", me,
kindStr, nrrdKind->name );
airMopError( mop );
return 1;
}
}
nout->axis[axis].kind = kind;
}
if ( dirStr ) {
nrrdSpaceVecCopy( nout->axis[axis].spaceDirection, sdir );
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
209 UNRRDU_CMD( axinfo, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Add a \"stub\" ( length 1 ) axis to a nrrd"
static const char *_unrrdu_axinsertInfoL =
( INFO
". The underlying linear ordering of the samples is "
"unchanged, and the information about the other axes is "
"shifted upwards as needed.\n "
"* Uses nrrdAxesInsert, and with \"-s\", nrrdPad_nva" );
int
36 unrrdu_axinsertMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *label;
Nrrd *nin, *nout;
int pret, kind, center;
unsigned int axis, size, opi, centOptIdx;
double mm[2];
airArray *mop;
NrrdBoundarySpec *bspec;
hparm->elideSingleOtherDefault = AIR_FALSE;
OPT_ADD_AXIS( axis, "dimension ( axis index ) at which to insert the new axis" );
hestOptAdd( &opt, "l, label", "label", airTypeString, 1, 1, &label, "",
"label to associate with new axis" );
opi = hestOptAdd( &opt, "k, kind", "kind", airTypeEnum, 1, 1, &kind, "stub",
"axis kind to associate with new axis", NULL, nrrdKind );
hestOptAdd( &opt, "mm, minmax", "min max", airTypeDouble, 2, 2, mm, "nan nan",
"min and max values along new axis" );
centOptIdx =
hestOptAdd( &opt, "c, center", "center", airTypeEnum, 1, 1, ¢er, "cell",
"centering of inserted axis: \"cell\" or \"node\"",
NULL, nrrdCenter );
hestOptAdd( &opt, "s, size", "size", airTypeUInt, 1, 1, &size, "1",
"after inserting stub axis, also pad out to some length, "
"according to the \"-b\" option" );
hestOptAdd( &opt, "b, boundary", "behavior", airTypeOther, 1, 1, &bspec,
"bleed",
"How to handle samples beyond the input bounds:\n "
"\b\bo \"pad:<val>\": use specified value\n "
"\b\bo \"bleed\": extend border values outward\n "
"\b\bo \"mirror\": repeated reflections\n "
"\b\bo \"wrap\": wrap-around to other side",
NULL, NULL, nrrdHestBoundarySpec );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_axinsertInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesInsert( nout, nin, axis ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error inserting axis:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( hestSourceUser == opt[centOptIdx].source ) {
nout->axis[axis].center = center;
}
if ( 1 < size ) {
/* we also do padding here */
ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
unsigned int ai;
Nrrd *npad;
for ( ai=0; ai<nout->dim; ai++ ) {
min[ai] = 0;
max[ai] = nout->axis[ai].size - 1;
}
max[axis] = size-1;
npad = nrrdNew( );
airMopAdd( mop, npad, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdPad_nva( npad, nout, min, max,
bspec->boundary, bspec->padValue ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error padding:\n%s", me, err );
airMopError( mop );
return 1;
}
/* sneaky, but ok; nothing changes in the mops */
nout = npad;
/* only set output kind if explicitly requested
( since the default is not appropriate ) */
if ( hestSourceUser == opt[opi].source ) {
nout->axis[axis].kind = kind;
}
} else {
/* no request to pad; setting the default "stub" kind is sensible */
nout->axis[axis].kind = kind;
}
if ( strlen( label ) ) {
nout->axis[axis].label = ( char * )airFree( nout->axis[axis].label );
nout->axis[axis].label = airStrdup( label );
}
if ( AIR_EXISTS( mm[0] ) ) {
nout->axis[axis].min = mm[0];
}
if ( AIR_EXISTS( mm[1] ) ) {
nout->axis[axis].max = mm[1];
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
139 UNRRDU_CMD( axinsert, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Merge two adjacent axes into one"
static const char *_unrrdu_axmergeInfoL =
( INFO
". A more general version of \"unu axdelete\". "
"The underlying linear ordering of the samples is "
"unchanged, and the information about the other axes is "
"shifted downwards as needed.\n "
"* Uses nrrdAxesMerge" );
int
37 unrrdu_axmergeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout[2];
int *axes, pret, ni;
unsigned int ii, jj, axesLen;
airArray *mop;
hestOptAdd( &opt, "a, axis", "ax0", airTypeInt, 1, -1, &axes, NULL,
"axis ( or axes ) to merge. Each axis index identified is the "
"lower of the pair of axes that will be merged. Saying \"-a 2\" "
"means to merge axis 2 and axis 3 into axis 2. If multiple "
"merges are to be done, the indices listed here are for "
"the axes prior to any merging.", &axesLen );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_axmergeInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
airMopAdd( mop, nout[0]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nout[1]=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( axesLen > 1 ) {
/* sort merge axes into ascending order */
qsort( axes, axesLen, sizeof( *axes ), nrrdValCompare[nrrdTypeInt] );
}
ni = 0;
for ( ii=0; ii<axesLen; ii++ ) {
if ( nrrdAxesMerge( nout[ni], !ii ? nin : nout[1-ni], axes[ii] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error merging axes:\n%s", me, err );
airMopError( mop );
return 1;
}
for ( jj=ii+1; jj<axesLen; jj++ ) {
axes[jj] -= 1;
}
ni = 1-ni;
}
SAVE( out, nout[1-ni], NULL );
airMopOkay( mop );
return 0;
}
90 UNRRDU_CMD( axmerge, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Split one axis into two axes"
static const char *_unrrdu_axsplitInfoL =
( INFO
". More general version of \"unu axinsert\", since a given axis can "
"be split into fast and slow axes of arbitrary size, as long as the "
"product of the fast and slow sizes is the same as the original size.\n "
"* Uses nrrdAxesSplit" );
int
36 unrrdu_axsplitMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
size_t size[2];
unsigned int axis;
airArray *mop;
OPT_ADD_AXIS( axis, "dimension ( axis index ) to split at" );
hestOptAdd( &opt, "s, size", "fast, slow sizes", airTypeSize_t, 2, 2,
size, NULL,
"fast and slow axis sizes to produce as result of splitting "
"given axis." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_axsplitInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesSplit( nout, nin, axis, size[0], size[1] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error splitting axis:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
77 UNRRDU_CMD( axsplit, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Modify whole-array attributes ( not per-axis )"
static const char *_unrrdu_basinfoInfoL =
( INFO
", which is called \"basic info\" in Nrrd terminology. "
"The only attributes which are set are those for which command-line "
"options are given.\n "
"* Uses no particular function; just sets fields in the Nrrd" );
int
36 unrrdu_basinfoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
/* these are stock for unrrdu */
hestOpt *opt = NULL;
airArray *mop;
int pret;
char *err;
/* these are stock for things using the usual -i and -o */
char *out;
Nrrd *nin, *nout;
/* these are specific to this command */
NrrdIoState *nio;
char *spcStr, *_origStr, *origStr, *content;
int space;
unsigned int spaceDim, cIdx;
hestOptAdd( &opt, "spc, space", "space", airTypeString, 1, 1, &spcStr, "",
"identify the space ( e.g. \"RAS\", \"LPS\" ) in which the array "
"conceptually lives, from the nrrdSpace airEnum, which in turn "
"determines the dimension of the space. Or, use an integer>0 to"
"give the dimension of a space that nrrdSpace doesn't know about. "
"By default ( not using this option ), the enclosing space is "
"set as unknown." );
hestOptAdd( &opt, "orig, origin", "origin", airTypeString, 1, 1, &_origStr, "",
"( NOTE: must quote vector ) the origin in space of the array: "
"the location of the center "
"of the first sample, of the form \"( x, y, z )\" ( or however "
"many coefficients are needed for the chosen space ). Quoting the "
"vector is needed to stop interpretation from the shell" );
cIdx =
hestOptAdd( &opt, "c, content", "content", airTypeString, 1, 1, &content, "",
"Specifies the content string of the nrrd, which is built upon "
"by many nrrd function to record a history of operations" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
USAGE( _unrrdu_basinfoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error copying input:\n%s", me, err );
airMopError( mop );
return 1;
}
/* HEY: copy and paste from unrrdu/make.c */
if ( airStrlen( spcStr ) ) {
space = airEnumVal( nrrdSpace, spcStr );
if ( !space ) {
/* couldn't parse it as space, perhaps its a uint */
if ( 1 != sscanf( spcStr, "%u", &spaceDim ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a nrrdSpace "
"or as a uint", me, spcStr );
airMopError( mop ); return 1;
}
/* else we did parse it as a uint */
nout->space = nrrdSpaceUnknown;
nout->spaceDim = spaceDim;
} else {
/* we did parse a known space */
nrrdSpaceSet( nout, space );
}
}
/* HEY: copy and paste from unrrdu/make.c */
if ( airStrlen( content ) ) { /* must have come from user */
if ( nout->content ) {
free( nout->content );
}
nout->content = airStrdup( content );
} else if ( hestSourceUser == opt[cIdx].source ) {
/* else user actually said: -c "" */
nout->content = ( char * )airFree( nout->content );
} /* else option not used */
/* HEY: copy and paste from unrrdu/make.c */
if ( airStrlen( _origStr ) ) {
/* why this is necessary is a bit confusing to me, both the check for
enclosing quotes, and the need to use to a separate variable ( isn't
hest doing memory management of addresses, not variables? ) */
if ( '\"' == _origStr[0] && '\"' == _origStr[strlen( _origStr )-1] ) {
_origStr[strlen( _origStr )-1] = 0;
origStr = _origStr + 1;
} else {
origStr = _origStr;
}
/* same hack about using NrrdIoState->line as basis for parsing */
nio->line = origStr;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_space_origin]( NULL, nout,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with origin \"%s\":\n%s",
me, origStr, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
150 UNRRDU_CMD( basinfo, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Condense axis-0 scanlines into \"blocks\""
static const char *_unrrdu_blockInfoL =
( INFO
". Output nrrd will be of type \"block\": the type "
"for an opaque chunk of "
"memory. Block samples can be sliced, cropped, shuffled, "
"permuted, etc., but there is no scalar value associated "
"with them, so they can not be histogrammed, quantized, "
"resampled, converted, etc. The output nrrd will have "
"one less dimension than input; axis N information will "
"be shifted down to axis N-1. Underlying data "
"is unchanged." );
int
41 unrrdu_blockMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
airArray *mop;
int pret;
/* if we gave a default for this, then there it would fine to have
no command-line arguments whatsoever, and then the user would not
know how to get the basic usage information */
hestOptAdd( &opt, "i", "nin", airTypeOther, 1, 1, &nin, NULL, "input nrrd",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_blockInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdBlock( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error blocking nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
79 UNRRDU_CMD( block, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Form adjecency matrix of connected components"
static const char *_unrrdu_ccadjInfoL =
( INFO
". This operates on the output of \"ccfind\". Output is unsigned char "
"array containing 1 at locations ( I, J ) and ( J, I ) if CCs with ids I and J are "
"adjacent, according to the chosen style of adjacency.\n "
"* Uses nrrdCCAdjacency" );
int
36 unrrdu_ccadjMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
airArray *mop;
int pret;
unsigned int conny;
hestOptAdd( &opt, "c, connect", "connectivity", airTypeUInt, 1, 1,
&conny, NULL,
"what kind of connectivity to use: the number of coordinates "
"that vary in order to traverse the neighborhood of a given "
"sample. In 2D: \"1\": 4-connected, \"2\": 8-connected" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_ccadjInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCCAdjacency( nout, nin, conny ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error finding adjacencies:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
76 UNRRDU_CMD( ccadj, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Find connected components ( CCs )"
static const char *_unrrdu_ccfindInfoL =
( INFO
". This works on 1-byte and 2-byte integral values, as well as "
"4-byte ints.\n "
"* Uses nrrdCCFind" );
int
35 unrrdu_ccfindMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *valS;
Nrrd *nin, *nout, *nval=NULL;
airArray *mop;
int type, pret;
unsigned int conny;
hestOptAdd( &opt, "v, values", "filename", airTypeString, 1, 1, &valS, "",
"Giving a filename here allows you to save out the values "
"associated with each connect component. This can be used "
"later with \"ccmerge -d\". By default, no record of the "
"original CC values is kept." );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to use for output, to store the CC ID values. By default "
"( not using this option ), the type used will be the smallest of "
"uchar, ushort, or int, that can represent all the CC ID values. "
"Using this option allows one to specify the integral type to "
"be used.",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &opt, "c, connect", "connectivity", airTypeUInt, 1, 1,
&conny, NULL,
"what kind of connectivity to use: the number of coordinates "
"that vary in order to traverse the neighborhood of a given "
"sample. In 2D: \"1\": 4-connected, \"2\": 8-connected" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_ccfindInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCCFind( nout, airStrlen( valS ) ? &nval : NULL, nin, type, conny ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing connected components:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( nval ) {
airMopAdd( mop, nval, ( airMopper )nrrdNuke, airMopAlways );
}
if ( airStrlen( valS ) ) {
SAVE( valS, nval, NULL );
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
93 UNRRDU_CMD( ccfind, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Merge CCs with their neighbors, under various constraints"
static const char *_unrrdu_ccmergeInfoL =
( INFO
". This operates on the output of \"ccfind\". "
"Merging of a CC is always done into its largest neighbor. "
"Whether or not to merge can be constrained by one or more of: "
"CC size ( \"-s\" ), original CC value being brighter or darker ( \"-d\" ), "
"and number of neighbors ( \"-n\" ).\n "
"* Uses nrrdCCMerge" );
int
38 unrrdu_ccmergeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *nout2, *nval;
airArray *mop;
int pret, maxSize, dir, maxNeigh, revalue;
unsigned int conny;
hestOptAdd( &opt, "d, directed", "dir", airTypeInt, 1, 1, &dir, "0",
"do value-driven merging. Using ( positive ) \"1\" says that "
"dark islands get merged with bright surrounds, while \"-1\" "
"says the opposite. By default, merging can go either way. " );
hestOptAdd( &opt, "s, size", "max size", airTypeInt, 1, 1, &maxSize, "0",
"a cap on the CC size that will be absorbed into its "
"surround. CCs larger than this are deemed too significant "
"to mess with. Or, use \"0\" to remove any such restriction "
"on merging." );
hestOptAdd( &opt, "n, neighbor", "max # neigh", airTypeInt, 1, 1,
&maxNeigh, "1",
"a cap on the number of neighbors that a CC may have if it is "
"to be be merged. \"1\" allows only islands to be merged, "
"\"2\" does merging with bigger of two neighbors, etc, while "
"\"0\" says that number of neighbors is no constraint" );
hestOptAdd( &opt, "c, connect", "connectivity", airTypeUInt, 1, 1,
&conny, NULL,
"what kind of connectivity to use: the number of coordinates "
"that vary in order to traverse the neighborhood of a given "
"sample. In 2D: \"1\": 4-connected, \"2\": 8-connected" );
hestOptAdd( &opt, "revalue", NULL, airTypeInt, 0, 0, &revalue, NULL,
"If this option is given, then after the merging, the CCs "
"are re-assigned their original datavalues, as given by "
"the \"-v\" option" );
OPT_ADD_NIN( nin, "input nrrd" );
hestOptAdd( &opt, "v, values", "values", airTypeOther, 1, 1, &nval, "",
"result of using \"ccfind -v\", the record of which values "
"were originally associated with each CC. This is required "
"for value-directed merging ( with non-zero \"-d\" option ), "
"or if the \"-revalue\" option is given, "
"but is not needed otherwise",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_ccmergeInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
airMopAdd( mop, nout=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
airMopAdd( mop, nout2=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCCMerge( nout, nin, nval, dir, maxSize, maxNeigh, conny ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing merging:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( revalue && nrrdCCRevalue( nout2, nout, nval ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing CC revalue:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, revalue ? nout2 : nout, NULL );
airMopOkay( mop );
return 0;
}
110 UNRRDU_CMD( ccmerge, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Remap CC values down to lowest contiguous values"
static const char *_unrrdu_ccsettleInfoL =
( INFO
".\n "
"* Uses nrrdCCSettle" );
int
34 unrrdu_ccsettleMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *valS;
Nrrd *nin, *nout, *nval=NULL;
airArray *mop;
int pret;
mop = airMopNew( );
hestOptAdd( &opt, "i, input", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "v, values", "filename", airTypeString, 1, 1, &valS, "",
"Giving a filename here allows you to save out the mapping "
"from new ( settled ) values to old values, in the form of a "
"1-D lookup table" );
OPT_ADD_NOUT( out, "output nrrd" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_ccsettleInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCCSettle( nout, airStrlen( valS ) ? &nval : NULL, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error settling connected components:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( nval ) {
airMopAdd( mop, nval, ( airMopper )nrrdNuke, airMopAlways );
}
if ( airStrlen( valS ) ) {
SAVE( valS, nval, NULL );
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
79 UNRRDU_CMD( ccsettle, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Compute 32-bit CRC of nrrd data ( same as via \"cksum\" )"
static const char *_unrrdu_cksumInfoL =
( INFO ". Unlike other commands, this doesn't produce a nrrd. It only "
"prints to standard out the CRC and byte counts for the input nrrd( s ), "
"seeking to emulate the formatting of cksum output.\n "
"* Uses nrrdCRC32" );
int
35 unrrdu_cksumDoit( const char *me, char *inS, int endian,
int printendian, FILE *fout ) {
Nrrd *nrrd;
airArray *mop;
unsigned int crc;
char stmp[AIR_STRLEN_SMALL], ends[AIR_STRLEN_SMALL];
size_t nn;
mop = airMopNew( );
airMopAdd( mop, nrrd=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nrrd, inS, NULL ) ) {
biffMovef( me, NRRD, "%s: trouble loading \"%s\"", me, inS );
airMopError( mop ); return 1;
}
crc = nrrdCRC32( nrrd, endian );
nn = nrrdElementNumber( nrrd )*nrrdElementSize( nrrd );
sprintf( ends, "( %s )", airEnumStr( airEndian, endian ) );
fprintf( fout, "%u%s %s%s%s\n", crc,
printendian ? ends : "",
airSprintSize_t( stmp, nn ),
strcmp( "-", inS ) ? " " : "",
strcmp( "-", inS ) ? inS : "" );
airMopOkay( mop );
return 0;
}
int
63 unrrdu_cksumMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err, **inS;
airArray *mop;
int pret, endian, printend;
unsigned int ni, ninLen;
mop = airMopNew( );
hestOptAdd( &opt, "en, endian", "end", airTypeEnum, 1, 1, &endian,
airEnumStr( airEndian, airMyEndian( ) ),
"Endianness in which to compute CRC; \"little\" for Intel and "
"friends; \"big\" for everyone else. "
"Defaults to endianness of this machine",
NULL, airEndian );
hestOptAdd( &opt, "pen, printendian", "bool", airTypeBool, 1, 1, &printend,
"false",
"whether or not to indicate after the CRC value the endianness "
"with which the CRC was computed; doing so clarifies "
"that the CRC result depends on endianness and may remove "
"confusion in comparing results on platforms of different "
"endianness" );
hestOptAdd( &opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL,
"input nrrd( s )", &ninLen );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_cksumInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
for ( ni=0; ni<ninLen; ni++ ) {
if ( unrrdu_cksumDoit( me, inS[ni], endian, printend, stdout ) ) {
airMopAdd( mop, err = biffGetDone( me ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with \"%s\":\n%s",
me, inS[ni], err );
/* continue working on the remaining files */
}
}
airMopOkay( mop );
return 0;
}
106 UNRRDU_CMD( cksum, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Cheap histogram-based median/mode filtering"
static const char *_unrrdu_cmedianInfoL =
( INFO
". Only works on 1, 2, or 3 dimensions. The window "
"over which filtering is done is always square, and "
"only a simplistic weighting scheme is available. "
"The method is cheap because it does the median "
"or mode based on a histogram, which enforces a quantization to the number "
"of bins in the histogram, which probably means a loss of precision for "
"anything except 8-bit data. Also, integral values can be recovered "
"exactly only when the number of bins is exactly min-max+1 ( as reported "
"by \"unu minmax\" ).\n "
"* Uses nrrdCheapMedian, plus nrrdSlice and nrrdJoin in "
"case of \"-c\"" );
int
43 unrrdu_cmedianMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *ntmp, **mnout;
int pad, pret, mode, chan, ni, nsize;
unsigned int bins, radius;
airArray *mop;
float wght;
hestOptAdd( &opt, "r, radius", "radius", airTypeUInt, 1, 1, &radius, NULL,
"how big a window to filter over. \"-r 1\" leads to a "
"3x3 window in an image, and a 3x3x3 window in a volume" );
hestOptAdd( &opt, "mode", NULL, airTypeInt, 0, 0, &mode, NULL,
"By default, median filtering is done. Using this option "
"enables mode filtering, in which the most common value is "
"used as output" );
hestOptAdd( &opt, "b, bins", "num", airTypeUInt, 1, 1, &bins, "256",
"# of bins in histogram. It is in your interest to minimize "
"this number, since big histograms mean slower execution "
"times. 8-bit data needs at most 256 bins." );
hestOptAdd( &opt, "w, weight", "weight", airTypeFloat, 1, 1, &wght, "1.0",
"How much higher to preferentially weight samples that are "
"closer to the center of the window. \"1.0\" weight means that "
"all samples are uniformly weighted over the window, which "
"facilitates a simple speed-up. " );
/* FYI: these are weights which are just high enough to preserve
an island of N contiguous high pixels in a row:
1: 7.695
2: 6.160
3: 4.829 ( actually only the middle pixel remains */
hestOptAdd( &opt, "p, pad", NULL, airTypeInt, 0, 0, &pad, NULL,
"Pad the input ( with boundary method \"bleed\" ), "
"and crop the output, so as to "
"overcome our cheapness and correctly "
"handle the border. Obviously, this takes more memory." );
hestOptAdd( &opt, "c, channel", NULL, airTypeInt, 0, 0, &chan, NULL,
"Slice the input along axis 0, run filtering on all slices, "
"and join the results back together. This is the way you'd "
"want to process color ( multi-channel ) images or volumes." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_cmedianInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( chan ) {
nsize = AIR_UINT( nin->axis[0].size );
mnout = AIR_CALLOC( nsize, Nrrd* );
airMopAdd( mop, mnout, airFree, airMopAlways );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
for ( ni=0; ni<nsize; ni++ ) {
if ( nrrdSlice( ntmp, nin, 0, ni ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error slicing input at pos = %d:\n%s",
me, ni, err );
airMopError( mop );
return 1;
}
airMopAdd( mop, mnout[ni] = nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCheapMedian( mnout[ni], ntmp, pad, mode, radius, wght, bins ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing cheap median:\n%s", me, err );
airMopError( mop );
return 1;
}
}
if ( nrrdJoin( nout, ( const Nrrd*const* )mnout, nsize, 0, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing final join:\n%s", me, err );
airMopError( mop );
return 1;
}
nrrdAxisInfoCopy( nout, nin, NULL, NRRD_AXIS_INFO_NONE );
if ( nrrdBasicInfoCopy( nout, nin,
NRRD_BASIC_INFO_DATA_BIT
| NRRD_BASIC_INFO_TYPE_BIT
| NRRD_BASIC_INFO_BLOCKSIZE_BIT
| NRRD_BASIC_INFO_DIMENSION_BIT
| NRRD_BASIC_INFO_CONTENT_BIT
| NRRD_BASIC_INFO_COMMENTS_BIT
| ( nrrdStateKeyValuePairsPropagate
? 0
: NRRD_BASIC_INFO_KEYVALUEPAIRS_BIT ) ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error copying basic info:\n%s", me, err );
airMopError( mop );
return 1;
}
} else {
if ( nrrdCheapMedian( nout, nin, pad, mode, radius, wght, bins ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing cheap median:\n%s", me, err );
airMopError( mop );
return 1;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
155 UNRRDU_CMD( cmedian, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Convert to another type ( as if by cast, w/ optional clamp )"
static const char *_unrrdu_convertInfoL =
( INFO ". By default this does not transform, scale, or intelligently "
"quantize values; it just copies them from one type to another, which "
"replicates exactly what you'd get in C when you assign from a variable "
"of one type to another, or when you cast to a different type. However, "
"clamping values to the representable range of the output type is possible. "
"with \"-clamp\". "
"See also \"unu quantize\", "
"\"unu 2op x\", and \"unu 3op clamp\".\n "
"* Uses nrrdConvert or nrrdClampConvert" );
int
40 unrrdu_convertMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int type, pret, E, doClamp;
airArray *mop;
OPT_ADD_TYPE( type, "type to convert to", NULL );
OPT_ADD_NIN( nin, "input nrrd" );
hestOptAdd( &opt, "clamp", NULL, airTypeInt, 0, 0, &doClamp, NULL,
"clamp input values to representable range of values of "
"output type, to avoid wrap-around problems" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_convertInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( doClamp ) {
E = nrrdClampConvert( nout, nin, type );
} else {
E = nrrdConvert( nout, nin, type );
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
83 UNRRDU_CMD( convert, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Crop along each axis to make a smaller nrrd"
static const char *_unrrdu_cropInfoL =
( INFO ".\n "
"* Uses nrrdCrop" );
int
33 unrrdu_cropMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int ai;
int minLen, maxLen, pret;
long int *minOff, *maxOff;
size_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
airArray *mop;
Nrrd *_nbounds;
OPT_ADD_BOUND( "min, minimum", 0, minOff, "0",
"low corner of bounding box.\n "
"\b\bo <int> gives 0-based index\n "
"\b\bo M, M+<int>, M-<int> give index relative "
"to the last sample on the axis ( M == #samples-1 ).",
minLen );
OPT_ADD_BOUND( "max, maximum", 0, maxOff, "0",
"high corner of bounding box. "
"Besides the specification styles described above, "
"there's also:\n "
"\b\bo m+<int> give index relative to minimum.",
maxLen );
hestOptAdd( &opt, "b, bounds", "filename", airTypeOther, 1, 1,
&_nbounds, "",
"a filename given here overrides the -min and -max "
"options ( they don't need to be used ) and provides the "
"cropping bounds as a 2-D array; first scanline is for "
"-min, second is for -max. Unfortunately the "
"\"m\" and \"M\" semantics ( above ) are currently not "
"supported in the bounds file.",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_cropInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !_nbounds ) {
if ( !( minLen == ( int )nin->dim && maxLen == ( int )nin->dim ) ) {
fprintf( stderr,
"%s: # min coords ( %d ) or max coords ( %d ) != nrrd dim ( %d )\n",
me, minLen, maxLen, nin->dim );
airMopError( mop );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
if ( -1 == minOff[0 + 2*ai] ) {
fprintf( stderr, "%s: can't use m+<int> specification for axis %d min\n",
me, ai );
airMopError( mop );
return 1;
}
}
for ( ai=0; ai<nin->dim; ai++ ) {
min[ai] = minOff[0 + 2*ai]*( nin->axis[ai].size-1 ) + minOff[1 + 2*ai];
if ( -1 == maxOff[0 + 2*ai] ) {
max[ai] = min[ai] + maxOff[1 + 2*ai];
} else {
max[ai] = maxOff[0 + 2*ai]*( nin->axis[ai].size-1 ) + maxOff[1 + 2*ai];
}
/*
fprintf( stderr, "%s: ai %2d: min = %4d, max = %4d\n",
me, ai, min[ai], max[ai] );
*/
}
} else {
Nrrd *nbounds;
airULLong *bounds;
unsigned int axi;
if ( !( 2 == _nbounds->dim
&& nin->dim == AIR_CAST( unsigned int, _nbounds->axis[0].size )
&& 2 == _nbounds->axis[1].size ) ) {
char stmp1[AIR_STRLEN_SMALL], stmp2[AIR_STRLEN_SMALL];
if ( _nbounds->dim >= 2 ) {
airSprintSize_t( stmp1, _nbounds->axis[1].size );
} else {
strcpy( stmp1, "" );
}
fprintf( stderr, "%s: expected 2-D %u-by-2 array of cropping bounds, "
"not %u-D %s%s%s%s\n", me, nin->dim, _nbounds->dim,
airSprintSize_t( stmp2, _nbounds->axis[0].size ),
_nbounds->dim >= 2 ? "-by-" : "-long",
_nbounds->dim >= 2 ? stmp1 : "",
_nbounds->dim > 2 ? "-by-X" : "" );
airMopError( mop );
return 1;
}
nbounds = nrrdNew( );
airMopAdd( mop, nbounds, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdConvert( nbounds, _nbounds, nrrdTypeULLong ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting bounds array:\n%s", me, err );
airMopError( mop );
return 1;
}
bounds = AIR_CAST( airULLong*, nbounds->data );
for ( axi=0; axi<nin->dim; axi++ ) {
min[axi] = AIR_CAST( size_t, bounds[axi + 0*( nin->dim )] );
max[axi] = AIR_CAST( size_t, bounds[axi + 1*( nin->dim )] );
}
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdCrop( nout, nin, min, max ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error cropping nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
157 UNRRDU_CMD( crop, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Write data segment of a nrrd file"
static const char *_unrrdu_dataInfoL =
( INFO ". The value of this is to pass the data segment in isolation to a "
"stand-alone decoder, in case this Teem build lacks an optional "
"data encoding required for a given nrrd file. Caveats: "
"Will start copying characters from the datafile "
"to output file until EOF is hit, so this won't work "
"correctly if the datafile has extraneous content at the end. Will "
"skip lines ( as per \"line skip:\" header field ) if needed, but can only "
"skip bytes ( as per \"byte skip:\" ) if the encoding is NOT a compression. "
"\n \n "
"To make vol.raw contain the uncompressed data from vol.nrrd "
"which uses \"gz\" encoding: \"unu data vol.nrrd | gunzip > vol.raw\"\n "
"\n "
"* Uses nrrdLoad with nio->skipData and nio->keepNrrdDataFileOpen both "
"true in the NrrdIoState nio." );
int
45 unrrdu_dataMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err, *inS=NULL;
Nrrd *nin;
NrrdIoState *nio;
airArray *mop;
int car, pret;
mop = airMopNew( );
hestOptAdd( &opt, NULL, "nin", airTypeString, 1, 1, &inS, NULL,
"input nrrd" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_dataInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nio->skipData = AIR_TRUE;
nio->keepNrrdDataFileOpen = AIR_TRUE;
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nin, inS, nio ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error reading header:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( _nrrdDataFNNumber( nio ) > 1 ) {
fprintf( stderr, "%s: sorry, currently can't operate with multiple "
"detached datafiles\n", me );
airMopError( mop );
return 1;
}
if ( !( nrrdFormatNRRD == nio->format ) ) {
fprintf( stderr, "%s: can only print data of NRRD format files\n", me );
airMopError( mop ); return 1;
}
car = fgetc( nio->dataFile );
#ifdef _MSC_VER
/* needed because otherwise printing a carraige return will
automatically also produce a newline */
_setmode( _fileno( stdout ), _O_BINARY );
#endif
while ( EOF != car ) {
fputc( car, stdout );
car = fgetc( nio->dataFile );
}
airFclose( nio->dataFile );
airMopOkay( mop );
return 0;
}
102 UNRRDU_CMD( data, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Ring removal for CT"
static const char *_unrrdu_deringInfoL =
( INFO
". Should be considered a work-in-progress. " );
int
33 unrrdu_deringMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
airArray *mop;
int pret;
Nrrd *nmask;
NrrdDeringContext *drc;
double center[2], radScale, clampPerc[2], backval;
int verbose, linterp, vertSeam;
unsigned int thetaNum;
NrrdKernelSpec *rkspec, *tkspec;
/* HEY is this needed? ( to display -rk and -tk kernels ) */
hparm->elideSingleOtherDefault = AIR_FALSE;
hestOptAdd( &opt, "c, center", "x y", airTypeDouble, 2, 2, center, NULL,
"center of rings, in index space of fastest two axes" );
hestOptAdd( &opt, "v, verbose", "v", airTypeInt, 1, 1, &verbose, "0",
"verbosity level" );
hestOptAdd( &opt, "li, linterp", "bool", airTypeBool, 1, 1, &linterp, "false",
"whether to use linear interpolation during polar transform" );
hestOptAdd( &opt, "vs, vertseam", "bool", airTypeBool, 1, 1, &vertSeam, "false",
"whether to dering left and right sides separately "
"( requires an even value for -tn thetanum )" );
hestOptAdd( &opt, "tn, thetanum", "# smpls", airTypeUInt, 1, 1, &thetaNum,
"20", "# of theta samples" );
hestOptAdd( &opt, "rs, radscale", "scale", airTypeDouble, 1, 1, &radScale,
"1.0", "scaling on radius in polar transform" );
hestOptAdd( &opt, "rk, radiuskernel", "kern", airTypeOther, 1, 1, &rkspec,
"gauss:3, 4",
"kernel for high-pass filtering along radial direction",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "tk, thetakernel", "kern", airTypeOther, 1, 1, &tkspec,
"box",
"kernel for blurring along theta direction.",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "cp, clampperc", "lo hi", airTypeDouble, 2, 2, clampPerc,
"0.0 0.0",
"when clamping values as part of ring estimation, the "
"clamping range is set to exclude this percent of values "
"from the low and high end of the data range" );
hestOptAdd( &opt, "m, mask", "mask", airTypeOther, 1, 1, &nmask, "",
"optional: after deringing, output undergoes a lerp, "
"parameterized by this array, from the background value "
"( via \"-b\" ) where mask=0 to the original deringing "
"output where mask=1. This lerp is effectively the same "
"as a \"unu 3op lerp\", so this should either be match the "
"input in size, or match its slices along the slowest axis.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "b, back", "val", airTypeDouble, 1, 1, &backval, "0.0",
"when using a mask ( \"-m\" ), the background value to "
"lerp with." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_deringInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nmask ) {
if ( !( 2 == nmask->dim
&& nrrdTypeBlock != nmask->type
&& nmask->axis[0].size == nin->axis[0].size
&& nmask->axis[1].size == nin->axis[1].size ) ) {
fprintf( stderr, "%s: given mask not 2-D %u-by-%u array of scalar type",
me, AIR_CAST( unsigned int, nin->axis[0].size ),
AIR_CAST( unsigned int, nin->axis[1].size ) );
airMopError( mop );
return 1;
}
}
drc = nrrdDeringContextNew( );
airMopAdd( mop, drc, ( airMopper )nrrdDeringContextNix, airMopAlways );
if ( nrrdDeringVerboseSet( drc, verbose )
|| nrrdDeringLinearInterpSet( drc, linterp )
|| nrrdDeringVerticalSeamSet( drc, vertSeam )
|| nrrdDeringInputSet( drc, nin )
|| nrrdDeringCenterSet( drc, center[0], center[1] )
|| nrrdDeringRadiusScaleSet( drc, radScale )
|| nrrdDeringThetaNumSet( drc, thetaNum )
|| nrrdDeringRadialKernelSet( drc, rkspec->kernel, rkspec->parm )
|| nrrdDeringThetaKernelSet( drc, tkspec->kernel, tkspec->parm )
|| nrrdDeringClampPercSet( drc, clampPerc[0], clampPerc[1] )
|| nrrdDeringExecute( drc, nout ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error deringing:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( nmask ) {
NrrdIter *nitout, *nitmask, *nitback;
Nrrd *ntmp;
nitout = nrrdIterNew( );
airMopAdd( mop, nitout, ( airMopper )nrrdIterNix, airMopAlways );
nitmask = nrrdIterNew( );
airMopAdd( mop, nitmask, ( airMopper )nrrdIterNix, airMopAlways );
nitback = nrrdIterNew( );
airMopAdd( mop, nitback, ( airMopper )nrrdIterNix, airMopAlways );
nrrdIterSetValue( nitback, backval );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
nrrdIterSetNrrd( nitout, nout );
nrrdIterSetNrrd( nitmask, nmask );
if ( nrrdArithIterTernaryOpSelect( ntmp, nrrdTernaryOpLerp,
nitmask, nitback, nitout, 2 )
|| nrrdCopy( nout, ntmp ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error masking:\n%s", me, err );
airMopError( mop );
return 1;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
165 UNRRDU_CMD_HIDE( dering, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Create image of 1-D value histogram"
static const char *_unrrdu_dhistoInfoL =
( INFO
". With \"-nolog\", this becomes a quick & dirty way of plotting a function.\n "
"* Uses nrrdHistoDraw" );
int
34 unrrdu_dhistoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret, nolog, notick;
unsigned int size;
airArray *mop;
double max;
hestOptAdd( &opt, "h, height", "height", airTypeUInt, 1, 1, &size, NULL,
"height of output image ( horizontal size is determined by "
"number of bins in input histogram )." );
hestOptAdd( &opt, "nolog", NULL, airTypeInt, 0, 0, &nolog, NULL,
"do not show the log-scaled histogram with decade tick-marks" );
hestOptAdd( &opt, "notick", NULL, airTypeInt, 0, 0, ¬ick, NULL,
"do not draw the log decade tick marks" );
hestOptAdd( &opt, "max, maximum", "max # hits", airTypeDouble, 1, 1,
&max, "nan",
"constrain the top of the drawn histogram to be at this "
"number of hits. This will either scale the drawn histogram "
"downward or clip its top, depending on whether the given max "
"is higher or lower than the actual maximum bin count. By "
"not using this option ( the default ), the actual maximum bin "
"count is used" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_dhistoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdHistoDraw( nout, nin, size,
nolog ? AIR_FALSE : ( notick ? 2 : AIR_TRUE ),
max ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error drawing histogram nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
87 UNRRDU_CMD( dhisto, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Save all slices along one axis into separate files"
static const char *_unrrdu_diceInfoL =
( INFO
". Calls \"unu slice\" for each position "
"along the indicated axis, and saves out a different "
"file for each sample along that axis.\n "
"* Uses repeated calls to nrrdSlice and nrrdSave" );
int
36 unrrdu_diceMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *base, *err, fnout[AIR_STRLEN_MED], /* file name out */
fffname[AIR_STRLEN_MED], /* format for filename */
*ftmpl; /* format template */
Nrrd *nin, *nout;
int pret, fit;
unsigned int axis, start, pos, top, size, sanity;
airArray *mop;
OPT_ADD_AXIS( axis, "axis to slice along" );
OPT_ADD_NIN( nin, "input nrrd" );
hestOptAdd( &opt, "s, start", "start", airTypeUInt, 1, 1, &start, "0",
"integer value to start numbering with" );
hestOptAdd( &opt, "ff, format", "form", airTypeString, 1, 1, &ftmpl, "",
"a printf-style format to use for generating all "
"filenames. Use this to override the number of characters "
"used to represent the slice position, or the file format "
"of the output, e.g. \"-ff %03d.ppm\" for 000.ppm, "
"001.ppm, etc. By default ( not using this option ), slices "
"are saved in NRRD format ( or PNM or PNG where possible ) "
"with shortest possible filenames." );
/* the fact that we're using unsigned int instead of size_t is
its own kind of sanity check */
hestOptAdd( &opt, "l, limit", "max#", airTypeUInt, 1, 1, &sanity, "9999",
"a sanity check on how many slice files should be saved "
"out, to prevent accidentally dicing the wrong axis "
"or the wrong array. Can raise this value if needed." );
hestOptAdd( &opt, "o, output", "prefix", airTypeString, 1, 1, &base, NULL,
"output filename prefix ( excluding info set via \"-ff\" ), "
"basically to set path of output files ( so be sure to end "
"with \"/\"." );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_diceInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !( axis < nin->dim ) ) {
fprintf( stderr, "%s: given axis ( %u ) outside range [0, %u]\n",
me, axis, nin->dim-1 );
airMopError( mop );
return 1;
}
if ( nin->axis[axis].size > sanity ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: axis %u size %s > sanity limit %u; "
"increase via \"-l\"\n", me,
axis, airSprintSize_t( stmp, nin->axis[axis].size ), sanity );
airMopError( mop );
return 1;
}
size = AIR_UINT( nin->axis[axis].size );
/* HEY: this should use nrrdSaveMulti( ), and if there's additional
smarts here, they should be moved into nrrdSaveMulti( ) */
if ( airStrlen( ftmpl ) ) {
if ( !( _nrrdContainsPercentThisAndMore( ftmpl, 'd' )
|| _nrrdContainsPercentThisAndMore( ftmpl, 'u' ) ) ) {
fprintf( stderr, "%s: given filename format \"%s\" doesn't seem to "
"have the converstion specification to print an integer\n",
me, ftmpl );
airMopError( mop );
return 1;
}
sprintf( fffname, "%%s%s", ftmpl );
} else {
unsigned int dignum=0, tmps;
tmps = top = start + size - 1;
do {
dignum++;
tmps /= 10;
} while ( tmps );
/* sprintf the number of digits into the string that will be used
to sprintf the slice number into the filename */
sprintf( fffname, "%%s%%0%uu.nrrd", dignum );
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
for ( pos=0; pos<size; pos++ ) {
if ( nrrdSlice( nout, nin, axis, pos ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error slicing nrrd:%s\n", me, err );
airMopError( mop );
return 1;
}
if ( 0 == pos && !airStrlen( ftmpl ) ) {
/* See if these slices would be better saved as PNG or PNM images.
Altering the file name will tell nrrdSave( ) to use a different
file format. We wait till now to check this so that we can
work from the actual slice */
if ( nrrdFormatPNG->fitsInto( nout, nrrdEncodingRaw, AIR_FALSE ) ) {
strcpy( fffname + strlen( fffname ) - 4, "png" );
} else {
fit = nrrdFormatPNM->fitsInto( nout, nrrdEncodingRaw, AIR_FALSE );
if ( 2 == fit ) {
strcpy( fffname + strlen( fffname ) - 4, "pgm" );
} else if ( 3 == fit ) {
strcpy( fffname + strlen( fffname ) - 4, "ppm" );
}
}
}
sprintf( fnout, fffname, base, pos+start );
if ( nrrdStateVerboseIO > 0 ) {
fprintf( stderr, "%s: %s ...\n", me, fnout );
}
if ( nrrdSave( fnout, nout, NULL ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error writing nrrd to \"%s\":%s\n",
me, fnout, err );
airMopError( mop );
return 1;
}
}
airMopOkay( mop );
return 0;
}
159 UNRRDU_CMD( dice, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Sees if two nrrds are different in any way"
static const char *_unrrdu_diffInfoL =
( INFO
". Looks through all fields to see if two given nrrds contain the "
"same information. Or, array meta-data can be excluded, and comparison "
"only on the data values is done with the -od flag.\n "
"* Uses nrrdCompare" );
int
36 unrrdu_diffMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err;
airArray *mop;
int pret;
Nrrd *ninA, *ninB;
int onlyData, differ;
double epsilon;
char explain[AIR_STRLEN_LARGE];
mop = airMopNew( );
hestOptAdd( &opt, NULL, "ninA", airTypeOther, 1, 1, &ninA, NULL,
"First input nrrd.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, NULL, "ninB", airTypeOther, 1, 1, &ninB, NULL,
"Second input nrrd.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "eps, epsilon", "eps", airTypeDouble, 1, 1, &epsilon, "0.0",
"threshold for allowable difference in values in "
"data values" );
hestOptAdd( &opt, "od, onlydata", NULL, airTypeInt, 0, 0, &onlyData, NULL,
"Compare data values only, excluding array meta-data" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_diffInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( nrrdCompare( ninA, ninB, onlyData, epsilon, &differ, explain ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing compare:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( differ ) {
printf( "%s: %s differ: %s\n", me, onlyData ? "data values" : "nrrds",
explain );
} else {
if ( 0 == epsilon ) {
printf( "%s: %s are the same\n", me, onlyData ? "data values" : "nrrds" );
} else {
printf( "%s: %s are same or within %g of each other\n", me,
onlyData ? "data values" : "nrrds", epsilon );
}
}
airMopOkay( mop );
return 0;
}
88 UNRRDU_CMD( diff, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Euclidean distance transform"
static const char *_unrrdu_distInfoL =
( INFO
". Based on \"Distance Transforms of Sampled Functions\" by "
"Pedro F. Felzenszwalb and Daniel P. Huttenlocher, "
"Cornell Computing and Information Science TR2004-1963. "
"This function first thresholds at the specified value and then "
"does the distance transform of the resulting binary image. "
"The signed distance ( negative values inside object ) is also available. "
"Distances between non-isotropic samples are handled correctly.\n "
"* Uses nrrdDistanceL2 or nrrdDistanceL2Signed" );
int
40 unrrdu_distMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
int E, typeOut, invert, sign;
double thresh, bias;
airArray *mop;
hestOptAdd( &opt, "th, thresh", "val", airTypeDouble, 1, 1, &thresh, NULL,
"threshold value to separate inside from outside" );
hestOptAdd( &opt, "b, bias", "val", airTypeDouble, 1, 1, &bias, "0.0",
"if non-zero, bias the distance transform by this amount "
"times the difference in value from the threshold" );
hestOptAdd( &opt, "t, type", "type", airTypeEnum, 1, 1, &typeOut, "float",
"type to save output in", NULL, nrrdType );
hestOptAdd( &opt, "sgn", NULL, airTypeInt, 0, 0, &sign, NULL,
"also compute signed ( negative ) distances inside objects, "
"instead of leaving them as zero" );
hestOptAdd( &opt, "inv", NULL, airTypeInt, 0, 0, &invert, NULL,
"values *below* threshold are considered interior to object. "
"By default ( not using this option ), values above threshold "
"are considered interior. " );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_distInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( bias && sign ) {
fprintf( stderr, "%s: sorry, signed and biased transform not "
"yet implemented\n", me );
airMopError( mop );
return 1;
}
if ( sign ) {
E = nrrdDistanceL2Signed( nout, nin, typeOut, NULL, thresh, !invert );
} else {
if ( bias ) {
E = nrrdDistanceL2Biased( nout, nin, typeOut, NULL, thresh, bias, !invert );
} else {
E = nrrdDistanceL2( nout, nin, typeOut, NULL, thresh, !invert );
}
}
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing distance transform:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
107 UNRRDU_CMD( dist, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Normalizes array orientation and meta-data"
static const char *_unrrdu_dnormInfoL =
( INFO
". Forces information about kind and orientation into "
"a consistent form, and nixes various other fields. This was "
"originally created as a utility for the Diderot project "
"( http://diderot-language.cs.uchicago.edu ), hence the name, "
"but it has proven useful in other contexts ( uses of gage ) in which "
"it is important to have standardized orientation information.\n "
"* Uses nrrdMetaDataNormalize" );
int
39 unrrdu_dnormMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
char *outS;
int pret;
Nrrd *nin, *nout;
NrrdIoState *nio;
int version, lostmf, headerOnly, trivialOrient, recenter;
double newSpacing;
hestOpt *opt = NULL;
char *err;
airArray *mop;
hestOptAdd( &opt, "h, header", NULL, airTypeInt, 0, 0, &headerOnly, NULL,
"output header of nrrd file only, not the data itself" );
hestOptAdd( &opt, "v, version", "version", airTypeEnum, 1, 1, &version, "alpha",
"what version of canonical meta-data to convert to; "
"\"alpha\" is what has been used for Diderot until at least "
"2016", NULL, nrrdMetaDataCanonicalVersion );
hestOptAdd( &opt, "to", NULL, airTypeInt, 0, 0, &trivialOrient, NULL,
"( *t*rivial *o*rientation ) "
"even if the input nrrd comes with full orientation or "
"per-axis min-max info, ignore it and instead assert the "
"identity mapping between index and world space" );
hestOptAdd( &opt, "rc, recenter", NULL, airTypeInt, 0, 0, &recenter, NULL,
"re-locate output spaceOrigin so that field is centered "
"around origin of space coordinates" );
hestOptAdd( &opt, "sp, spacing", "scl", airTypeDouble, 1, 1, &newSpacing,
"1.0",
"when having to contrive orientation information and there's "
"no per-axis min/max or spacing, this is the sample spacing "
"to assert" );
OPT_ADD_NIN( nin, "input image" );
OPT_ADD_NOUT( outS, "output filename" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_dnormInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( headerOnly ) {
/* no reason to duplicate data */
nout = nin;
} else {
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
}
if ( nrrdMetaDataNormalize( nout, nin,
version, trivialOrient,
AIR_FALSE /* permuteComponentAxisFastest */,
recenter,
newSpacing,
&lostmf ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( lostmf ) {
fprintf( stderr, "%s: WARNING: input array measurement frame "
"will be erased on output.\n", me );
}
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
/* disable printing comments about NRRD format URL */
nio->skipFormatURL = AIR_TRUE;
if ( headerOnly ) {
nio->skipData = AIR_TRUE;
}
if ( nrrdSave( outS, nout, nio ) ) {
airMopAdd( mop, err = biffGet( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble saving \"%s\":\n%s",
me, outS, err );
airMopError( mop ); return 1;
}
airMopOkay( mop );
return 0;
}
123 UNRRDU_CMD_HIDE( dnorm, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "List relevant environment variables and their values"
static const char *_unrrdu_envInfoL =
( INFO
". These environment variables provide a way of "
"setting global variables that can affect"
" the way Nrrd ( and unu ) operates.\n "
"* Uses nrrdGetenvBool, nrrdGetenvEnum, "
"nrrdGetenvInt, and nrrdGetenvUInt" );
void
37 _unrrdu_envBool( FILE *file, const char *envKey, int currVal,
const char *varName, const char *desc, int columns ) {
int val, ret;
char *envVal;
fprintf( file, "%s ( bool ): ", envKey );
ret = nrrdGetenvBool( &val, &envVal, envKey );
switch( ret ) {
case -1:
fprintf( file, "not set\n" );
break;
case AIR_TRUE:
fprintf( file, "set to \"%s\"\n", envVal );
break;
case AIR_FALSE:
fprintf( file, "set to \"%s\"? ( invalid ) \n", envVal );
break;
}
switch( ret ) {
case -1:
case AIR_FALSE:
fprintf( file, " ( %s == %s; unchanged )\n",
varName, airEnumStr( airBool, currVal ) );
break;
case AIR_TRUE:
fprintf( file, " ==> %s = %s **********************\n",
varName, airEnumStr( airBool, currVal ) );
break;
}
_hestPrintStr( file, 0, 0, columns, desc, AIR_FALSE );
fprintf( file, "\n" );
}
void
71 _unrrdu_envEnum( FILE *file, const airEnum *enm, const char *envKey,
int currVal, const char *varName,
const char *desc, int columns ) {
int val, ret;
char *envVal;
/* !!! HEY: CUT + PASTE !!! */
fprintf( file, "%s ( %s enum ): ", envKey, enm->name );
ret = nrrdGetenvEnum( &val, &envVal, enm, envKey );
switch( ret ) {
case -1:
fprintf( file, "not set\n" );
break;
case AIR_TRUE:
fprintf( file, "set to \"%s\"\n", envVal );
break;
case AIR_FALSE:
fprintf( file, "set to \"%s\"? ( invalid ) \n", envVal );
break;
}
switch( ret ) {
case -1:
case AIR_FALSE:
fprintf( file, " ( %s == %s; unchanged )\n",
varName, airEnumStr( enm, currVal ) );
break;
case AIR_TRUE:
fprintf( file, " ==> %s = %s **********************\n",
varName, airEnumStr( enm, currVal ) );
break;
}
_hestPrintStr( file, 0, 0, columns, desc, AIR_FALSE );
fprintf( file, "\n" );
/* !!! HEY: CUT + PASTE !!! */
}
void
108 _unrrdu_envInt( FILE *file, const char *envKey,
int currVal, const char *varName,
const char *desc, int columns ) {
int val, ret;
char *envVal;
/* !!! HEY: CUT + PASTE !!! */
fprintf( file, "%s ( int ): ", envKey );
ret = nrrdGetenvInt( &val, &envVal, envKey );
switch( ret ) {
case -1:
fprintf( file, "not set\n" );
break;
case AIR_TRUE:
fprintf( file, "set to \"%s\"\n", envVal );
break;
case AIR_FALSE:
fprintf( file, "set to \"%s\"? ( invalid ) \n", envVal );
break;
}
switch( ret ) {
case -1:
case AIR_FALSE:
fprintf( file, " ( %s == %d; unchanged )\n",
varName, currVal );
break;
case AIR_TRUE:
fprintf( file, " ==> %s = %d **********************\n",
varName, currVal );
break;
}
_hestPrintStr( file, 0, 0, columns, desc, AIR_FALSE );
fprintf( file, "\n" );
/* !!! HEY: CUT + PASTE !!! */
}
void
145 _unrrdu_envUInt( FILE *file, const char *envKey,
unsigned int currVal, const char *varName,
const char *desc, int columns ) {
int ret;
unsigned int val;
char *envVal;
/* !!! HEY: CUT + PASTE !!! */
fprintf( file, "%s ( unsigned int ): ", envKey );
ret = nrrdGetenvUInt( &val, &envVal, envKey );
switch( ret ) {
case -1:
fprintf( file, "not set\n" );
break;
case AIR_TRUE:
fprintf( file, "set to \"%s\"\n", envVal );
break;
case AIR_FALSE:
fprintf( file, "set to \"%s\"? ( invalid ) \n", envVal );
break;
}
switch( ret ) {
case -1:
case AIR_FALSE:
fprintf( file, " ( %s == %d; unchanged )\n",
varName, currVal );
break;
case AIR_TRUE:
fprintf( file, " ==> %s = %u **********************\n",
varName, currVal );
break;
}
_hestPrintStr( file, 0, 0, columns, desc, AIR_FALSE );
fprintf( file, "\n" );
/* !!! HEY: CUT + PASTE !!! */
}
int
183 unrrdu_envMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
FILE *out;
AIR_UNUSED( argc );
AIR_UNUSED( argv );
AIR_UNUSED( me );
out = stdout;
hestInfo( out, me, _unrrdu_envInfoL, hparm );
fprintf( out, "\n" );
_hestPrintStr( out, 0, 0, hparm->columns,
( "Each variable in the listing below starts with the name of "
"the environment variable ( \"NRRD_...\" ), what type of value "
"it represents ( e.g. \"int\", \"bool\" ), what the "
"environment variable is currently set to, what the "
"corresponding Nrrd global variable is set to, and a "
"description of the variable." ),
AIR_FALSE );
fprintf( out, "\n" );
_hestPrintStr( out, 0, 0, hparm->columns,
( "Bool variables may be set to true simply by setting the "
"environment variable; setting the value to \"true\" or "
"\"false\" sets the bool accordingly. Enum variables may "
"be set by setting the environment variable to any string "
"that parses as one of the enum values. Int and unsigned "
"int variables are set via a string parse-able as a numeric "
"value." ),
AIR_FALSE );
fprintf( out, "\n" );
/* UNRRDU_QUIET_QUIT functionality implemented in privateUnrrdu.h */
_hestPrintStr( out, 0, 0, hparm->columns,
( "In addition to the the \"NRRD_\" environment variables, "
"there is this one, " UNRRDU_QUIET_QUIT_ENV ", which "
"determines whether unu exits "
"quietly ( without error and usage info ) when it fails "
"because an input nrrd read immediately hit EOF ( as "
"happens when many unu invocations are piped together ). "
"This is currently detected by seeing if the error message "
"ends with \n \"" UNRRDU_QUIET_QUIT_STR "\"." ),
AIR_FALSE );
fprintf( out, "\n" );
fprintf( out, "%s: ", UNRRDU_QUIET_QUIT_ENV );
if ( getenv( UNRRDU_QUIET_QUIT_ENV ) ) {
fprintf( out, "is set ( to what doesn't matter ); quiet-quit enabled\n" );
} else {
fprintf( out, "is NOT set; quiet-quit NOT enabled\n" );
}
fprintf( out, "\n" );
_unrrdu_envBool( out,
nrrdEnvVarStateKeyValuePairsPropagate,
nrrdStateKeyValuePairsPropagate,
"nrrdStateKeyValuePairsPropagate",
"When true, key/value pairs are copied from input "
"nrrd to output nrrd just like other basic info that hasn't "
"just been modified ( e.g. type, dimension, block size ).",
hparm->columns );
_unrrdu_envEnum( out,
nrrdCenter, nrrdEnvVarDefaultCenter,
nrrdDefaultCenter,
"nrrdDefaultCenter",
"The type of sample centering to use when none has been "
"set but one has to be chosen for some operation "
"( e.g. resampling ).",
hparm->columns );
_unrrdu_envEnum( out,
nrrdEncodingType, nrrdEnvVarDefaultWriteEncodingType,
nrrdDefaultWriteEncodingType,
"nrrdDefaultWriteEncodingType",
"When writing nrrds, what encoding to use. Only "
"\"unu save\" affords explicit control of output encoding.",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarStateKindNoop,
nrrdStateKindNoop,
"nrrdStateKindNoop",
"When true, Nrrd makes not even the slightest effort to be "
"smart about setting the \"kind\" field of an axis after "
"some operation that modified its samples.",
hparm->columns );
_unrrdu_envInt( out,
nrrdEnvVarStateVerboseIO,
nrrdStateVerboseIO,
"nrrdStateVerboseIO",
"The verbosity level of Nrrd input/output operations.",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarStateBlind8BitRange,
nrrdStateBlind8BitRange,
"nrrdStateBlind8BitRange",
"When true, the determined range of 8-bit data will always "
"be [0, 255] ( for uchar ) or [-128, 127] ( for signed char ), "
"instead of actually looking into the data to find its "
"range.",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarDefaultWriteBareText,
nrrdDefaultWriteBareText,
"nrrdDefaultWriteBareText",
"When false, text files used for saving nrrds start with "
"comment ( \"# ...\" ) lines containing nrrd fields.",
hparm->columns );
_unrrdu_envEnum( out,
nrrdType, nrrdEnvVarStateMeasureType,
nrrdStateMeasureType,
"nrrdStateMeasureType",
"For measurements ( \"unu project\" ) like sum and product, "
"the type of the output result, when one hasn't been "
"explicitly requested.",
hparm->columns );
_unrrdu_envInt( out,
nrrdEnvVarStateMeasureModeBins,
nrrdStateMeasureModeBins,
"nrrdStateMeasureModeBins",
"When measuring mode but without a given histogram, how many "
"bins to use in the temporary internal histogram.",
hparm->columns );
_unrrdu_envEnum( out,
nrrdType, nrrdEnvVarStateMeasureHistoType,
nrrdStateMeasureHistoType,
"nrrdStateMeasureHistoType",
"Output type for most measurements of histograms, when one "
"hasn't been explicitly requested",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarStateAlwaysSetContent,
nrrdStateAlwaysSetContent,
"nrrdStateAlwaysSetContent",
"If true, the output content string is set even when the "
"input content string is not set.",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarStateDisableContent,
nrrdStateDisableContent,
"nrrdStateDisableContent",
"If true, output content is never set.",
hparm->columns );
_unrrdu_envUInt( out,
nrrdEnvVarDefaultWriteCharsPerLine,
nrrdDefaultWriteCharsPerLine,
"nrrdDefaultWriteCharsPerLine",
"When using text encoding, maximum # characters allowed "
"per line.",
hparm->columns );
_unrrdu_envUInt( out,
nrrdEnvVarDefaultWriteValsPerLine,
nrrdDefaultWriteValsPerLine,
"nrrdDefaultWriteValsPerLine",
"When using text encoding, maximum # values allowed "
"per line",
hparm->columns );
_unrrdu_envBool( out,
nrrdEnvVarStateGrayscaleImage3D,
nrrdStateGrayscaleImage3D,
"nrrdStateGrayscaleImage3D",
"If true, reading a 2-D grayscale image results in a "
"3-D image with a single sample ( size=1 ) on the first "
"( fastest ) axis.",
hparm->columns );
#if 0
/* GLK is ambivalent about the continued existence of these ... */
nrrdGetenvDouble( /**/ &nrrdDefaultKernelParm0,
nrrdEnvVarDefaultKernelParm0 );
nrrdGetenvDouble( /**/ &nrrdDefaultSpacing,
nrrdEnvVarDefaultSpacing );
#endif
/* NOTE: this is an exceptional unu command that doesn't rely on
privateUnrrdu.h USAGE( ) macro; so we determine our own return value */
return 0;
}
361 UNRRDU_CMD( env, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Fast Fourier Transform of selected axes"
static const char *_unrrdu_fftInfoL_yes =
( INFO
". Initial attempt at wrapping the FFTW3 library; options are "
"likely to change in Teem 2.0.\n "
"* Uses nrrdFFT" );
static const char *_unrrdu_fftInfoL_no =
( INFO
". This Teem has NOT been compiled with FFTW3 <http://www.fftw.org/>. "
"If it had been, "
"this would be a command-line interface to that functionality. "
"There is currently no non-FFTW implementation of the FFT available.\n "
"* Uses nrrdFFT" );
/* We create an airEnum to parse the "forward" and "backwards" values
needed to specify which kind of transform to run */
static const char *
_directionStr[] = {
"( unknown direction )",
"forward",
"backward"
};
static const char *
_directionDesc[] = {
"unknown direction",
"forward transform",
"backward ( inverse ) transform"
};
/* from fftw3.h
#define FFTW_FORWARD ( -1 )
#define FFTW_BACKWARD ( +1 )
*/
#define FORW ( -1 )
#define BACK ( +1 )
static const int
_directionVal[] = {
0,
FORW,
BACK
};
static const char *
_directionStrEqv[] = {
"f", "forw", "forward",
"b", "back", "backward", "i", "inv", "inverse",
""
};
static const int
_directionValEqv[] = {
FORW, FORW, FORW,
BACK, BACK, BACK, BACK, BACK, BACK
};
static const airEnum
_direction_enm = {
"direction",
2,
_directionStr,
_directionVal,
_directionDesc,
_directionStrEqv,
_directionValEqv,
AIR_FALSE
};
99 static const airEnum *const
direction_enm = &_direction_enm;
int
103 unrrdu_fftMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *_nin, *nout;
int pret;
airArray *mop;
int sign, rigor, rescale, realInput;
char *wispath;
FILE *fwise;
unsigned int *axes, axesLen;
hestOptAdd( &opt, NULL, "dir", airTypeEnum, 1, 1, &sign, NULL,
"forward ( \"forw\", \"f\" ) or backward/inverse "
"( \"back\", \"b\" ) transform ", NULL, direction_enm );
hestOptAdd( &opt, "a, axes", "ax0", airTypeUInt, 1, -1, &axes, NULL,
"the one or more axes that should be transformed", &axesLen );
hestOptAdd( &opt, "pr, planrigor", "pr", airTypeEnum, 1, 1, &rigor, "est",
"rigor with which fftw plan is constructed. Options include:\n "
"\b\bo \"e\", \"est\", \"estimate\": only an estimate\n "
"\b\bo \"m\", \"meas\", \"measure\": standard amount of "
"measurements of system properties\n "
"\b\bo \"p\", \"pat\", \"patient\": slower, more measurements\n "
"\b\bo \"x\", \"ex\", \"exhaustive\": slowest, most measurements",
NULL, nrrdFFTWPlanRigor );
hestOptAdd( &opt, "r, rescale", "bool", airTypeBool, 1, 1, &rescale, "true",
"scale fftw output ( by sqrt( 1/N ) ) so that forward and backward "
"transforms will get back to original values" );
hestOptAdd( &opt, "w, wisdom", "filename", airTypeString, 1, 1, &wispath, "",
"A filename here is used to read in fftw wisdom ( if the file "
"exists already ), and is used to save out updated wisdom "
"after the transform. By default ( not using this option ), "
"no wisdom is read or saved. Note: no wisdom is gained "
"( that is, learned by FFTW ) with planning rigor \"estimate\"." );
OPT_ADD_NIN( _nin, "input nrrd" );
hestOptAdd( &opt, "ri, realinput", NULL, airTypeInt, 0, 0, &realInput, NULL,
"input is real-valued, so insert new length-2 axis 0 "
"and set complex component to 0.0. Axes to transform "
"( indicated by \"-a\" ) will be incremented accordingly." );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
if ( nrrdFFTWEnabled ) {
USAGE( _unrrdu_fftInfoL_yes );
} else {
USAGE( _unrrdu_fftInfoL_no );
}
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( realInput ) {
ptrdiff_t minPad[NRRD_DIM_MAX], maxPad[NRRD_DIM_MAX];
unsigned int axi;
Nrrd *ntmp;
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesInsert( ntmp, _nin, 0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error creating complex axis:\n%s", me, err );
airMopError( mop );
return 1;
}
nin = nrrdNew( );
airMopAdd( mop, nin, ( airMopper )nrrdNuke, airMopAlways );
minPad[0] = 0;
maxPad[0] = 1;
for ( axi=1; axi<ntmp->dim; axi++ ) {
minPad[axi] = 0;
maxPad[axi] = AIR_CAST( ptrdiff_t, ntmp->axis[axi].size-1 );
}
if ( nrrdPad_nva( nin, ntmp, minPad, maxPad, nrrdBoundaryPad, 0.0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error padding out complex axis:\n%s", me, err );
airMopError( mop );
return 1;
}
/* increment specified axes to transform */
for ( axi=0; axi<axesLen; axi++ ) {
axes[axi]++;
}
/* ntmp is really done with, we can free up the space now; this
is one of the rare times we want airMopSub */
airMopSub( mop, ntmp, ( airMopper )nrrdNuke );
nrrdNuke( ntmp );
} else {
/* input is apparently already complex */
nin = _nin;
}
if ( airStrlen( wispath ) && nrrdFFTWEnabled ) {
fwise = fopen( wispath, "r" );
if ( fwise ) {
if ( nrrdFFTWWisdomRead( fwise ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with fft wisdom:\n%s", me, err );
airMopError( mop );
return 1;
}
fclose( fwise );
} else {
fprintf( stderr, "%s: ( \"%s\" couldn't be opened, will try to save "
"wisdom afterwards )", me, wispath );
}
}
if ( nrrdFFT( nout, nin, axes, axesLen, sign, rescale, rigor ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with fft:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( airStrlen( wispath ) && nrrdFFTWEnabled ) {
if ( !( fwise = fopen( wispath, "w" ) ) ) {
fprintf( stderr, "%s: couldn't open %s for writing: %s\n",
me, wispath, strerror( errno ) );
airMopError( mop );
return 1;
}
if ( nrrdFFTWWisdomWrite( fwise ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with fft wisdom:\n%s", me, err );
airMopError( mop );
return 1;
}
fclose( fwise );
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
243 UNRRDU_CMD( fft, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Reverse order of slices along one axis"
static const char *_unrrdu_flipInfoL =
( INFO
". Special case of \"unu\tshuffle\".\n "
"* Uses nrrdFlip" );
int
34 unrrdu_flipMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
unsigned int axis;
airArray *mop;
OPT_ADD_AXIS( axis, "axis to flip along" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_flipInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdFlip( nout, nin, axis ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error flipping nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
70 UNRRDU_CMD( flip, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#include <ctype.h>
const int
unrrduPresent = 42;
const char *
unrrduBiffKey = "unrrdu";
/* number of columns that hest will used */
unsigned int
unrrduDefNumColumns = 78;
/*
******** unrrduCmdList[]
**
** NULL-terminated array of unrrduCmd pointers, as ordered by UNRRDU_MAP macro
*/
unrrduCmd *
unrrduCmdList[] = {
UNRRDU_MAP( UNRRDU_LIST )
NULL
};
/*
******** unrrduCmdMain
**
** A "main" function for unu-like programs, which is very similar to
** teem/src/bin/unu.c:main( ), and
** teem/src/bin/tend.c:main( ), and
** teem/src/limn/test/lpu.c:main( ).
** With more time ( and a major Teem release ), this function may change,
** and those programs may use this.
**
** A sneaky but basic issue is the const-correctness of how the hestParm
** is used; we'd like to take a const hestParm* to communicate parameters
** the caller has set, but the show-stopper is that unrrduCmd->main( )
** takes a non-const hestParm, and it has to be that way, because some
** unu commands alter the given hparm ( which probably shouldn't happen ).
** Until that's fixed, we have a non-const hestParm* coming in here.
*/
int
68 unrrduCmdMain( int argc, const char **argv,
const char *cmd, const char *title,
const unrrduCmd *const *cmdList,
hestParm *_hparm, FILE *fusage ) {
int i, ret;
const char *me;
char *argv0 = NULL;
hestParm *hparm;
airArray *mop;
me = argv[0];
/* parse environment variables first, in case they break nrrdDefault*
or nrrdState* variables in a way that nrrdSanity( ) should see */
nrrdDefaultGetenv( );
nrrdStateGetenv( );
/* unu does some unu-specific environment-variable handling here */
nrrdSanityOrDie( me );
mop = airMopNew( );
if ( _hparm ) {
hparm = _hparm;
} else {
hparm = hestParmNew( );
airMopAdd( mop, hparm, ( airMopper )hestParmFree, airMopAlways );
hparm->elideSingleEnumType = AIR_TRUE;
hparm->elideSingleOtherType = AIR_TRUE;
hparm->elideSingleOtherDefault = AIR_FALSE;
hparm->elideSingleNonExistFloatDefault = AIR_TRUE;
hparm->elideMultipleNonExistFloatDefault = AIR_TRUE;
hparm->elideSingleEmptyStringDefault = AIR_TRUE;
hparm->elideMultipleEmptyStringDefault = AIR_TRUE;
hparm->cleverPluralizeOtherY = AIR_TRUE;
/* learning columns from current window; if ioctl is available
if ( 1 ) {
struct winsize ws;
ioctl( 1, TIOCGWINSZ, &ws );
hparm->columns = ws.ws_col - 1;
}
*/
hparm->columns = 78;
}
/* if there are no arguments, then we give general usage information */
if ( 1 >= argc ) {
/* this is like unrrduUsageUnu( ) */
unsigned int ii, maxlen = 0;
char *buff, *fmt, tdash[] = "--- %s ---";
for ( ii=0; cmdList[ii]; ii++ ) {
if ( cmdList[ii]->hidden ) {
continue;
}
maxlen = AIR_MAX( maxlen, AIR_UINT( strlen( cmdList[ii]->name ) ) );
}
if ( !maxlen ) {
fprintf( fusage, "%s: problem: maxlen = %u\n", me, maxlen );
airMopError( mop ); return 1;
}
buff = AIR_CALLOC( strlen( tdash ) + strlen( title ) + 1, char );
airMopAdd( mop, buff, airFree, airMopAlways );
sprintf( buff, tdash, title );
fmt = AIR_CALLOC( hparm->columns + strlen( buff ) + 1, char ); /* generous */
airMopAdd( mop, buff, airFree, airMopAlways );
sprintf( fmt, "%%%us\n",
AIR_UINT( ( hparm->columns-strlen( buff ) )/2 + strlen( buff ) - 1 ) );
fprintf( fusage, fmt, buff );
for ( ii=0; cmdList[ii]; ii++ ) {
unsigned int cc, len;
if ( cmdList[ii]->hidden ) {
continue;
}
len = AIR_UINT( strlen( cmdList[ii]->name ) );
strcpy( buff, "" );
for ( cc=len; cc<maxlen; cc++ )
strcat( buff, " " );
strcat( buff, cmd );
strcat( buff, " " );
strcat( buff, cmdList[ii]->name );
strcat( buff, " ... " );
len = strlen( buff );
fprintf( fusage, "%s", buff );
_hestPrintStr( fusage, len, len, hparm->columns,
cmdList[ii]->info, AIR_FALSE );
}
airMopError( mop );
return 1;
}
/* else, we see if its --version */
if ( !strcmp( "--version", argv[1] ) ) {
char vbuff[AIR_STRLEN_LARGE];
airTeemVersionSprint( vbuff );
printf( "%s\n", vbuff );
exit( 0 );
}
/* else, we should see if they're asking for a command we know about */
for ( i=0; cmdList[i]; i++ ) {
if ( !strcmp( argv[1], cmdList[i]->name ) ) {
break;
}
/* if user typed "prog --help" we treat it as "prog about",
but only if there is an "about" command */
if ( !strcmp( "--help", argv[1] )
&& !strcmp( "about", cmdList[i]->name ) ) {
break;
}
}
if ( cmdList[i] ) {
/* yes, we have that command */
/* initialize variables used by the various commands */
argv0 = AIR_CALLOC( strlen( cmd ) + strlen( argv[1] ) + 2, char );
airMopMem( mop, &argv0, airMopAlways );
sprintf( argv0, "%s %s", cmd, argv[1] );
/* run the individual command, saving its exit status */
ret = cmdList[i]->main( argc-2, argv+2, argv0, hparm );
} else {
fprintf( stderr, "%s: unrecognized command: \"%s\"; type \"%s\" for "
"complete list\n", me, argv[1], me );
ret = 1;
}
airMopDone( mop, ret );
return ret;
}
/*
******** unrrduUsageUnu
**
** prints out a little banner, and a listing of all available commands
** with their one-line descriptions
*/
void
204 unrrduUsageUnu( const char *me, hestParm *hparm ) {
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE];
unsigned int cmdi, chi, len, maxlen;
maxlen = 0;
for ( cmdi=0; unrrduCmdList[cmdi]; cmdi++ ) {
maxlen = AIR_MAX( maxlen, AIR_UINT( strlen( unrrduCmdList[cmdi]->name ) ) );
}
sprintf( buff, "--- unu: Utah Nrrd Utilities command-line interface ---" );
len = AIR_UINT( strlen( buff ) );
sprintf( fmt, "%%%us\n", ( hparm->columns > len
? hparm->columns-len
: 0 )/2 + len - 1 );
fprintf( stdout, fmt, buff );
for ( cmdi=0; unrrduCmdList[cmdi]; cmdi++ ) {
int nofft;
if ( unrrduCmdList[cmdi]->hidden ) {
/* nothing to see here! */
continue;
}
nofft = !strcmp( unrrduCmdList[cmdi]->name, "fft" ) && !nrrdFFTWEnabled;
len = AIR_UINT( strlen( unrrduCmdList[cmdi]->name ) );
len += !!nofft;
strcpy( buff, "" );
for ( chi=len; chi<maxlen; chi++ )
strcat( buff, " " );
if ( nofft ) {
strcat( buff, "( " );
}
strcat( buff, me );
strcat( buff, " " );
strcat( buff, unrrduCmdList[cmdi]->name );
strcat( buff, " ... " );
len = AIR_UINT( strlen( buff ) );
fprintf( stdout, "%s", buff );
if ( nofft ) {
char *infop;
/* luckily, still fits within 80 columns */
fprintf( stdout, "Not Enabled: " );
infop = AIR_CALLOC( strlen( unrrduCmdList[cmdi]->info ) + 2, char );
sprintf( infop, "%s )", unrrduCmdList[cmdi]->info );
_hestPrintStr( stdout, len, len, hparm->columns,
infop, AIR_FALSE );
free( infop );
} else {
_hestPrintStr( stdout, len, len, hparm->columns,
unrrduCmdList[cmdi]->info, AIR_FALSE );
}
}
return;
}
/*
******** unrrduUsage
**
** A generic version of the usage command, which can be used by other
** programs that are leveraging the unrrduCmd infrastructure.
**
** does not use biff
*/
int
266 unrrduUsage( const char *me, hestParm *hparm,
const char *title, unrrduCmd **cmdList ) {
char buff[AIR_STRLEN_LARGE], fmt[AIR_STRLEN_LARGE];
unsigned int cmdi, chi, len, maxlen;
if ( !( title && cmdList ) ) {
/* got NULL pointer */
return 1;
}
maxlen = 0;
for ( cmdi=0; cmdList[cmdi]; cmdi++ ) {
maxlen = AIR_MAX( maxlen, AIR_UINT( strlen( cmdList[cmdi]->name ) ) );
}
sprintf( buff, "--- %s ---", title );
len = AIR_UINT( strlen( buff ) );
sprintf( fmt, "%%%us\n", ( hparm->columns > len
? hparm->columns-len
: 0 )/2 + len - 1 );
fprintf( stdout, fmt, buff );
for ( cmdi=0; cmdList[cmdi]; cmdi++ ) {
len = AIR_UINT( strlen( cmdList[cmdi]->name ) );
strcpy( buff, "" );
for ( chi=len; chi<maxlen; chi++ )
strcat( buff, " " );
strcat( buff, me );
strcat( buff, " " );
strcat( buff, cmdList[cmdi]->name );
strcat( buff, " ... " );
len = AIR_UINT( strlen( buff ) );
fprintf( stdout, "%s", buff );
_hestPrintStr( stdout, len, len, hparm->columns,
cmdList[cmdi]->info, AIR_FALSE );
}
return 0;
}
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduHestPosCB
**
** For parsing position along an axis. Can be a simple ( long ) integer,
** or M to signify last position along axis ( #samples-1 ), or
** M+<int> or M-<int> to signify some position relative to the end.
**
** It can also be m+<int> to signify some position relative to some
** "minimum", assuming that a minimum position is being specified
** at the same time as this one. Obviously, there has to be some
** error handling to make sure that no one is trying to define a
** minimum position with respect to itself. And, the ability to
** specify a position as "m+<int>" shouldn't be advertised in situations
** ( unu slice ) where you only have one position, rather than an interval
** between two positions ( unu crop and unu pad ).
**
** This information is represented with two integers, pos[0] and pos[1]:
** pos[0] == 0: pos[1] gives the absolute position
** pos[0] == 1: pos[1] gives the position relative to the last index
** pos[0] == -1: pos[1] gives the position relative to a "minimum" position
*/
int
329 unrrduParsePos( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParsePos";
long int *pos;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
pos = ( long int* )ptr;
if ( !strcmp( "M", str ) ) {
pos[0] = 1;
pos[1] = 0;
return 0;
}
if ( 'M' == str[0] ) {
if ( !( '-' == str[1] || '+' == str[1] ) ) {
sprintf( err, "%s: \'M\' can be followed only by \'+\' or \'-\'", me );
return 1;
}
pos[0] = 1;
if ( 1 != sscanf( str+1, "%ld", &( pos[1] ) ) ) {
sprintf( err, "%s: can't parse \"%s\" as M+<int> or M-<int>", me, str );
return 1;
}
return 0;
}
if ( 'm' == str[0] ) {
if ( '+' != str[1] ) {
sprintf( err, "%s: \'m\' can only be followed by \'+\'", me );
return 1;
}
pos[0] = -1;
if ( 1 != sscanf( str+1, "%ld", &( pos[1] ) ) ) {
sprintf( err, "%s: can't parse \"%s\" as m+<int>", me, str );
return 1;
}
if ( pos[1] < 0 ) {
sprintf( err, "%s: int in m+<int> must be non-negative ( not %ld )",
me, pos[1] );
return 1;
}
return 0;
}
/* else its just a plain unadorned integer */
pos[0] = 0;
if ( 1 != sscanf( str, "%ld", &( pos[1] ) ) ) {
sprintf( err, "%s: can't parse \"%s\" as int", me, str );
return 1;
}
return 0;
}
hestCB unrrduHestPosCB = {
2*sizeof( long int ),
"position",
unrrduParsePos,
NULL
};
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduHestMaybeTypeCB
**
** although nrrdType is an airEnum that hest already knows how
** to parse, we want the ability to have "unknown" be a valid
** parsable value, contrary to how airEnums usually work with hest.
** For instance, we might want to use "unknown" to represent
** "same type as the input, whatever that is".
**
** 18 July 03: with new nrrdTypeDefault, this function becomes
** less of a hack, and more necessary, because the notion of an
** unknown but valid type ( as a default type is ) falls squarely
** outside the nrrdType airEnum framework. Added a separate test
** for "default", even though currently nrrdTypeUnknown is the same
** value as nrrdTypeDefault.
*/
int
409 unrrduParseMaybeType( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParseMaybeType";
int *typeP;
/* fprintf( stderr, "!%s: str = \"%s\"\n", me, str ); */
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
typeP = ( int* )ptr;
if ( !strcmp( "unknown", str ) ) {
*typeP = nrrdTypeUnknown;
} else if ( !strcmp( "default", str ) ) {
*typeP = nrrdTypeDefault;
} else {
*typeP = airEnumVal( nrrdType, str );
if ( nrrdTypeUnknown == *typeP ) {
sprintf( err, "%s: can't parse \"%s\" as type", me, str );
return 1;
}
}
/* fprintf( stderr, "!%s: *typeP = %d\n", me, *typeP ); */
return 0;
}
hestCB unrrduHestMaybeTypeCB = {
sizeof( int ),
"type",
unrrduParseMaybeType,
NULL
};
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduHestBitsCB
**
** for parsing an int that can be 8, 16, or 32
*/
int
451 unrrduParseBits( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParseBits";
unsigned int *bitsP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
bitsP = ( unsigned int* )ptr;
if ( 1 != sscanf( str, "%u", bitsP ) ) {
sprintf( err, "%s: can't parse \"%s\" as int", me, str );
return 1;
}
if ( !( 8 == *bitsP || 16 == *bitsP || 32 == *bitsP ) ) {
sprintf( err, "%s: bits ( %d ) not 8, 16, or 32", me, *bitsP );
return 1;
}
return 0;
}
hestCB unrrduHestBitsCB = {
sizeof( int ),
"quantization bits",
unrrduParseBits,
NULL
};
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduParseScale
**
** parse the strings used with "unu resample -s" to indicate
** the new number of samples.
** = : unrrduScaleNothing
** a : unrrduScaleAspectRatio
** x<float> : unrrduScaleMultiply
** x=<float> : unrrduScaleMultiply
** /<float> : unrrduScaleDivide
** /=<float> : unrrduScaleDivide
** +=<uint> : unrrduScaleAdd
** -=<uint> : unrrduScaleSubstract
** <uint> : unrrduScaleExact
** s<float> : unrrduScaleSpacingTarget
*/
int
499 unrrduParseScale( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParseScale";
double *scale;
unsigned int num;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
scale = AIR_CAST( double *, ptr );
if ( !strcmp( "=", str ) ) {
scale[0] = AIR_CAST( double, unrrduScaleNothing );
scale[1] = 0.0;
} else if ( !strcmp( "a", str ) ) {
scale[0] = AIR_CAST( double, unrrduScaleAspectRatio );
scale[1] = 0.0;
} else if ( strlen( str ) > 2
&& ( 'x' == str[0] || '/' == str[0] )
&& '=' == str[1] ) {
if ( 1 != sscanf( str+2, "%lf", scale+1 ) ) {
sprintf( err, "%s: can't parse \"%s\" as x=<float> or /=<float>",
me, str );
return 1;
}
if ( !( scale[1] > 0 ) ) {
sprintf( err, "%s: need positive float from \"%s\" ( not %g )",
me, str, scale[1] );
return 1;
}
scale[0] = AIR_CAST( double, ( 'x' == str[0]
? unrrduScaleMultiply
: unrrduScaleDivide ) );
} else if ( strlen( str ) > 1
&& ( 'x' == str[0] || '/' == str[0] || 's' == str[0] ) ) {
if ( 1 != sscanf( str+1, "%lf", scale+1 ) ) {
sprintf( err, "%s: can't parse \"%s\" as x<float>, /<float>, "
"or s<float>", me, str );
return 1;
}
if ( !( scale[1] > 0 ) ) {
sprintf( err, "%s: need positive float from \"%s\" ( not %g )",
me, str, scale[1] );
return 1;
}
scale[0] = AIR_CAST( double, ( 'x' == str[0]
? unrrduScaleMultiply
: ( '/' == str[0]
? unrrduScaleDivide
: unrrduScaleSpacingTarget ) ) );
} else if ( strlen( str ) > 2
&& ( '+' == str[0] || '-' == str[0] )
&& '=' == str[1] ) {
if ( 1 != sscanf( str+2, "%u", &num ) ) {
sprintf( err, "%s: can't parse \"%s\" as +=<uint> or -=<uint>",
me, str );
return 1;
}
scale[0] = AIR_CAST( double, ( '+' == str[0]
? unrrduScaleAdd
: unrrduScaleSubtract ) );
scale[1] = AIR_CAST( double, num );
} else {
if ( 1 != sscanf( str, "%u", &num ) ) {
sprintf( err, "%s: can't parse \"%s\" as uint", me, str );
return 1;
}
scale[0] = AIR_CAST( double, unrrduScaleExact );
scale[1] = AIR_CAST( double, num );
}
return 0;
}
hestCB unrrduHestScaleCB = {
2*sizeof( double ),
"sampling specification",
unrrduParseScale,
NULL
};
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduHestFileCB
**
** for parsing a filename, which means opening it in "rb" mode and
** getting a FILE *. "-" is interpreted as stdin, which is not
** fclose( )ed at the end, unlike all other files.
*/
void *
590 unrrduMaybeFclose( void *_file ) {
FILE *file;
file = ( FILE * )_file;
if ( stdin != file ) {
file = airFclose( file );
}
return NULL;
}
int
601 unrrduParseFile( void *ptr, char *str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParseFile";
FILE **fileP;
if ( !( ptr && str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
fileP = ( FILE ** )ptr;
if ( !( *fileP = airFopen( str, stdin, "rb" ) ) ) {
sprintf( err, "%s: fopen( \"%s\", \"rb\" ) failed: %s",
me, str, strerror( errno ) );
return 1;
}
return 0;
}
hestCB unrrduHestFileCB = {
sizeof( FILE * ),
"filename",
unrrduParseFile,
unrrduMaybeFclose,
};
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/*
******** unrrduHestEncodingCB
**
** for parsing output encoding, including compression flags
** enc[0]: which encoding, from nrrdEncodingType* enum
** enc[1]: for compressions: zlib "level" and bzip2 "blocksize"
** enc[2]: for zlib: strategy, from nrrdZlibStrategy* enum
*/
int
638 unrrduParseEncoding( void *ptr, char *_str, char err[AIR_STRLEN_HUGE] ) {
char me[]="unrrduParseEncoding", *str, *opt;
int *enc;
airArray *mop;
if ( !( ptr && _str ) ) {
sprintf( err, "%s: got NULL pointer", me );
return 1;
}
enc = ( int * )ptr;
/* these are the defaults, they may not get over-written */
enc[1] = -1;
enc[2] = nrrdZlibStrategyDefault;
enc[0] = airEnumVal( nrrdEncodingType, _str );
if ( nrrdEncodingTypeUnknown != enc[0] ) {
/* we're done; encoding was simple: "raw" or "gz" */
return 0;
}
mop = airMopNew( );
str = airStrdup( _str );
airMopMem( mop, &str, airMopAlways );
opt = strchr( str, ':' );
if ( !opt ) {
/* couldn't parse string as nrrdEncodingType, but there wasn't a colon */
sprintf( err, "%s: didn't recognize \"%s\" as an encoding", me, str );
airMopError( mop ); return 1;
} else {
*opt = '\0';
opt++;
enc[0] = airEnumVal( nrrdEncodingType, str );
if ( nrrdEncodingTypeUnknown == enc[0] ) {
sprintf( err, "%s: didn't recognize \"%s\" as an encoding", me, str );
airMopError( mop ); return 1;
}
if ( !nrrdEncodingArray[enc[0]]->isCompression ) {
sprintf( err, "%s: only compression encodings have parameters", me );
airMopError( mop ); return 1;
}
while ( *opt ) {
int opti = AIR_INT( *opt );
if ( isdigit( opti ) ) {
enc[1] = *opt - '0';
} else if ( 'd' == tolower( opti ) ) {
enc[2] = nrrdZlibStrategyDefault;
} else if ( 'h' == tolower( opti ) ) {
enc[2] = nrrdZlibStrategyHuffman;
} else if ( 'f' == tolower( opti ) ) {
enc[2] = nrrdZlibStrategyFiltered;
} else {
sprintf( err, "%s: parameter char \"%c\" not a digit or 'd', 'h', 'f'",
me, *opt );
airMopError( mop ); return 1;
}
opt++;
}
}
airMopOkay( mop );
return 0;
}
hestCB unrrduHestEncodingCB = {
3*sizeof( int ),
"encoding",
unrrduParseEncoding,
NULL
};
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Brighten or darken values with a gamma"
static const char *_unrrdu_gammaInfoL =
( INFO
". Just as in xv, the gamma value here is actually the "
"reciprocal of the exponent actually used to transform "
"the values.\n "
"* Uses nrrdArithGamma" );
int
36 unrrdu_gammaMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
double min, max, Gamma;
airArray *mop;
int pret, blind8BitRange;
NrrdRange *range;
hestOptAdd( &opt, "g, gamma", "gamma", airTypeDouble, 1, 1, &Gamma, NULL,
"gamma > 1.0 brightens; gamma < 1.0 darkens. "
"Negative gammas invert values ( like in xv ). " );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Value to implicitly map to 0.0 prior to calling pow( ). "
"Defaults to lowest value found in input nrrd." );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"Value to implicitly map to 1.0 prior to calling pow( ). "
"Defaults to highest value found in input nrrd." );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] )." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_gammaInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
if ( nrrdArithGamma( nout, nin, range, Gamma ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing gamma:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
88 UNRRDU_CMD( gamma, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "generate list of oriented grid locations"
static const char *_unrrdu_gridInfoL =
( INFO ". For a N-D grid, the output is a 2-D M-by-S array of grid sample "
"locations, where M is the space dimension of the oriented grid, and S "
"is the total number of real samples in the grid. "
"Implementation currently incomplete, because of the number of "
"unresolved design questions.\n "
"* ( not based on any particular nrrd function )" );
static int
37 gridGen( Nrrd *nout, int typeOut, const Nrrd *nin ) {
static const char me[]="gridGen";
size_t II, NN, size[NRRD_DIM_MAX], coord[NRRD_DIM_MAX];
double loc[NRRD_SPACE_DIM_MAX],
sdir[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX],
( *ins )( void *v, size_t I, double d );
unsigned int axi, dim, sdim, base;
void *out;
if ( nrrdTypeBlock == typeOut ) {
biffAddf( UNRRDU, "%s: can't use type %s", me,
airEnumStr( nrrdType, nrrdTypeBlock ) );
return 1;
}
if ( !( nin->spaceDim ) ) {
biffAddf( UNRRDU, "%s: can currently only work on arrays "
"with space directions and space origin", me );
return 1;
}
dim = nin->dim;
sdim = nin->spaceDim;
if ( !nrrdSpaceVecExists( sdim, nin->spaceOrigin ) ) {
biffAddf( UNRRDU, "%s: space origin didn't exist", me );
return 1;
}
if ( !( sdim <= dim ) ) {
biffAddf( UNRRDU, "%s: can't handle space dimension %u > dimension %u",
me, sdim, dim );
return 1;
}
base = dim - sdim;
NN = 1;
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSize, size );
nrrdAxisInfoGet_nva( nin, nrrdAxisInfoSpaceDirection, sdir );
for ( axi=base; axi<dim; axi++ ) {
if ( !nrrdSpaceVecExists( sdim, sdir[axi] ) ) {
biffAddf( UNRRDU, "%s: axis %u space dir didn't exist", me, axi );
return 1;
}
NN *= size[axi];
}
ins = nrrdDInsert[typeOut];
if ( nrrdMaybeAlloc_va( nout, typeOut, 2,
AIR_CAST( size_t, sdim ),
NN ) ) {
biffMovef( UNRRDU, NRRD, "%s: couldn't allocate output", me );
return 1;
}
out = AIR_CAST( void *, nout->data );
for ( axi=0; axi<dim; axi++ ) {
coord[axi] = 0;
}
for ( II=0; II<NN; II++ ) {
nrrdSpaceVecCopy( loc, nin->spaceOrigin );
for ( axi=base; axi<dim; axi++ ) {
nrrdSpaceVecScaleAdd2( loc, 1, loc, coord[axi], sdir[axi] );
}
/*
fprintf( stderr, "!%s: ( %u ) %u %u %u: %g %g\n", me,
AIR_CAST( unsigned int, II ),
AIR_CAST( unsigned int, coord[0] ),
AIR_CAST( unsigned int, coord[1] ),
AIR_CAST( unsigned int, coord[2] ),
loc[0], loc[1] );
*/
for ( axi=0; axi<sdim; axi++ ) {
ins( out, axi + sdim*II, loc[axi] );
}
NRRD_COORD_INCR( coord, size, dim, base );
}
return 0;
}
int
113 unrrdu_gridMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
airArray *mop;
int typeOut;
hestOptAdd( &opt, "i, input", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd. That this argument is required instead of "
"optional, as with most unu commands, is a quirk caused by the "
"need to have \"unu grid\" generate usage info, combined "
"with the fact that the other arguments have sensible "
"defaults",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_TYPE( typeOut, "type of output", "double" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_gridInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( gridGen( nout, typeOut, nin ) ) {
airMopAdd( mop, err = biffGetDone( UNRRDU ), airFree, airMopAlways );
fprintf( stderr, "%s: error generating output:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
156 UNRRDU_CMD_HIDE( grid, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "a hack of some kind"
static const char *_unrrdu_hackInfoL =
( INFO ". This is used as a place to put whatever one-off code "
"you want to try, with the whatever benefits come with being a "
"unu command.\n "
"* ( not based on any particular nrrd function )" );
int
35 unrrdu_hackMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
airArray *mop;
char *what;
hestOptAdd( &opt, NULL, "what", airTypeString, 1, 1, &what, NULL,
"what hack to do" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_hackInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( !strcmp( what, "sdincr" ) ) {
unsigned int sdim, axi;
sdim = nin->spaceDim;
if ( !sdim ) {
fprintf( stderr, "%s: need non-zero space dimension", me );
airMopError( mop );
return 1;
}
if ( nrrdCopy( nout, nin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error converting nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
nout->spaceDim = sdim + 1;
nout->space = nrrdSpaceUnknown;
nout->spaceOrigin[sdim] = 0.0;
for ( axi=0; axi<nout->dim; axi++ ) {
if ( nrrdSpaceVecExists( sdim, nout->axis[axi].spaceDirection ) ) {
nout->axis[axi].spaceDirection[sdim] = 0.0;
}
}
} else {
fprintf( stderr, "%s: no \"%s\" hack implemented", me, what );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
94 UNRRDU_CMD_HIDE( hack, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Print header of one or more nrrd files"
static const char *_unrrdu_headInfoL =
( INFO ". The value of this is simply to print the contents of nrrd "
"headers. This avoids the use of \"head -N\", where N has to be "
"determined manually, which always risks printing raw binary data "
"( following the header ) to screen, which tends to clobber terminal "
"settings, make pointless beeps, and be annoying.\n "
"* Uses _nrrdOneLine" );
int
37 unrrdu_headDoit( const char *me, NrrdIoState *nio, char *inS, FILE *fout ) {
airArray *mop;
unsigned int len;
FILE *fin;
mop = airMopNew( );
if ( !( fin = airFopen( inS, stdin, "rb" ) ) ) {
biffAddf( me, "%s: couldn't fopen( \"%s\", \"rb\" ): %s\n",
me, inS, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, fin, ( airMopper )airFclose, airMopAlways );
if ( _nrrdOneLine( &len, nio, fin ) ) {
biffAddf( me, "%s: error getting first line of file \"%s\"", me, inS );
airMopError( mop ); return 1;
}
if ( !len ) {
biffAddf( me, "%s: immediately hit EOF\n", me );
airMopError( mop ); return 1;
}
if ( !( nrrdFormatNRRD->contentStartsLike( nio ) ) ) {
biffAddf( me, "%s: first line ( \"%s\" ) isn't a nrrd magic\n",
me, nio->line );
airMopError( mop ); return 1;
}
while ( len > 1 ) {
fprintf( fout, "%s\n", nio->line );
_nrrdOneLine( &len, nio, fin );
};
/* experience has shown that on at least windows and darwin, the writing
process's fwrite( ) to stdout will fail if we exit without consuming
everything from stdin */
if ( stdin == fin ) {
int c = getc( fin );
while ( EOF != c ) {
c = getc( fin );
}
}
airMopOkay( mop );
return 0;
}
int
83 unrrdu_headMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err, **inS;
NrrdIoState *nio;
airArray *mop;
int pret;
unsigned int ni, ninLen;
mop = airMopNew( );
hestOptAdd( &opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL,
"input nrrd( s )", &ninLen );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_headInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
for ( ni=0; ni<ninLen; ni++ ) {
if ( ninLen > 1 ) {
fprintf( stdout, "==> %s <==\n", inS[ni] );
}
if ( unrrdu_headDoit( me, nio, inS[ni], stdout ) ) {
airMopAdd( mop, err = biffGetDone( me ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble reading from \"%s\":\n%s",
me, inS[ni], err );
/* continue working on the remaining files */
}
if ( ninLen > 1 && ni < ninLen-1 ) {
fprintf( stdout, "\n" );
}
}
airMopOkay( mop );
return 0;
}
123 UNRRDU_CMD( head, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Perform histogram equalization"
static const char *_unrrdu_heqInfoL =
( INFO
". If this seems to be doing nothing, try increasing the "
"number of histograms bins by an order of magnitude or "
"two ( or more ). Or, use \"unu gamma\" to warp the values "
"in the direction you know they need to go. Either of "
"these might work because extremely tall and narrow peaks "
"in the equalization histogram will produce poor results.\n "
"* Uses nrrdHistoEq" );
int
39 unrrdu_heqMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *mapS;
Nrrd *nin, *nout, *nmap;
int smart, pret;
unsigned int bins;
airArray *mop;
float amount;
/* we want to facilitate saving out the mapping as a text file,
but with the domain included */
/* this is commented out with the 8 Aug 2003 advent of nrrdDefGetenv
nrrdDefWriteBareTable = AIR_FALSE;
*/
hestOptAdd( &opt, "b, bin", "bins", airTypeInt, 1, 1, &bins, NULL,
"# bins to use in histogram that is created in order to "
"calculate the mapping that achieves the equalization." );
hestOptAdd( &opt, "s, smart", "bins", airTypeInt, 0, 1, &smart, "0",
"# bins in value histogram to ignore in calculating the mapping. "
"Bins are ignored when they get more hits than other bins, and "
"when the values that fall in them are constant. This is an "
"effective way to prevent large regions of background value "
"from distorting the equalization mapping." );
hestOptAdd( &opt, "a, amount", "amount", airTypeFloat, 1, 1, &amount, "1.0",
"extent to which the histogram equalizing mapping should be "
"applied; 0.0: no change, 1.0: full equalization" );
hestOptAdd( &opt, "m, map", "filename", airTypeString, 1, 1, &mapS, "",
"The value mapping used to achieve histogram equalization is "
"represented by a univariate regular map. By giving a filename "
"here, that map can be saved out and applied to other nrrds "
"with \"unu rmap\"" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_heqInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdHistoEq( nout, nin, airStrlen( mapS ) ? &nmap : NULL,
bins, smart, amount ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble histogram equalizing:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( airStrlen( mapS ) ) {
SAVE( mapS, nmap, NULL );
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
102 UNRRDU_CMD( heq, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Replace each scanline along an axis with its histogram"
static const char *_unrrdu_histaxInfoL =
( INFO
".\n "
"* Uses nrrdHistoAxis" );
int
34 unrrdu_histaxMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
char *minStr, *maxStr;
int type, pret, blind8BitRange;
unsigned int axis, bins;
airArray *mop;
NrrdRange *range;
OPT_ADD_AXIS( axis, "axis to histogram along" );
hestOptAdd( &opt, "b, bin", "bins", airTypeUInt, 1, 1, &bins, NULL,
"# of bins in histogram" );
OPT_ADD_TYPE( type, "output type", "uchar" );
/* HEY copy and paste from unrrdu/quantize.c */
hestOptAdd( &opt, "min, minimum", "value", airTypeString, 1, 1,
&minStr, "nan",
"The value to map to zero, given explicitly as a regular number, "
"*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF
"\" suffix, this "
"minimum is specified in terms of the percentage of samples in "
"input that are lower. "
"\"0" NRRD_MINMAX_PERC_SUFF "\" means the "
"lowest input value is used, "
"\"1" NRRD_MINMAX_PERC_SUFF "\" means that the "
"1% of the lowest values are all mapped to zero. "
"By default ( not using this option ), the lowest input value is "
"used." );
hestOptAdd( &opt, "max, maximum", "value", airTypeString, 1, 1,
&maxStr, "nan",
"The value to map to the highest unsigned integral value, given "
"explicitly as a regular number, "
"*or*, if the number is given with "
"a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, "
"this maximum is specified "
"in terms of the percentage of samples in input that are higher. "
"\"0" NRRD_MINMAX_PERC_SUFF "\" means the highest input value is "
"used, which is also the default "
"behavior ( same as not using this option )." );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] )." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_histaxInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
range = nrrdRangeNew( AIR_NAN, AIR_NAN );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdRangePercentileFromStringSet( range, nin, minStr, maxStr,
10*bins /* HEY magic */,
blind8BitRange )
|| nrrdHistoAxis( nout, nin, range, axis, bins, type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing axis histogramming:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
108 UNRRDU_CMD( histax, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Create 1-D histogram of values in a nrrd"
static const char *_unrrdu_histoInfoL =
( INFO
". Can explicitly set bounds of histogram domain or can learn these "
"from the data.\n "
"* Uses nrrdHisto" );
int
35 unrrdu_histoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *nwght;
char *minStr, *maxStr;
int type, pret, blind8BitRange;
unsigned int bins;
NrrdRange *range;
airArray *mop;
hestOptAdd( &opt, "b, bins", "num", airTypeUInt, 1, 1, &bins, NULL,
"# of bins in histogram" );
hestOptAdd( &opt, "w, weight", "nweight", airTypeOther, 1, 1, &nwght, "",
"how to weigh contributions to histogram. By default "
"( not using this option ), the increment is one bin count per "
"sample, but by giving a nrrd, the value in the nrrd at the "
"corresponding location will be the bin count increment ",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "min, minimum", "value", airTypeString, 1, 1,
&minStr, "nan",
"Value at low end of histogram, given explicitly as a "
"regular number, "
"*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF
"\" suffix, this "
"minimum is specified in terms of the percentage of samples in "
"input that are lower. "
"By default ( not using this option ), the lowest value "
"found in input nrrd." );
hestOptAdd( &opt, "max, maximum", "value", airTypeString, 1, 1,
&maxStr, "nan",
"Value at high end of histogram, given "
"explicitly as a regular number, "
"*or*, if the number is given with "
"a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, "
"this maximum is specified "
"in terms of the percentage of samples in input that are higher. "
"Defaults to highest value found in input nrrd." );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] )." );
OPT_ADD_TYPE( type, "type to use for bins in output histogram", "uint" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_histoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
range = nrrdRangeNew( AIR_NAN, AIR_NAN );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdRangePercentileFromStringSet( range, nin, minStr, maxStr,
10*bins /* HEY magic */,
blind8BitRange )
|| nrrdHisto( nout, nin, range, nwght, bins, type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with range or quantizing:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
108 UNRRDU_CMD( histo, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "converts from 1-D index to world position"
static const char *_unrrdu_i2wInfoL =
( INFO ", given the centering of the data ( cell vs. node ), "
"the range of positions, and the number of intervals into "
"which position has been quantized. "
"This is a demo/utility, which does not actually operate on any nrrds. "
"Previously available as the stand-alone idx2pos binary.\n "
"* Uses NRRD_POS macro" );
int
37 unrrdu_i2wMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
airArray *mop;
int pret;
char *err;
int center;
double minPos, maxPos, pos, indx, size;
mop = airMopNew( );
hestOptAdd( &opt, NULL, "center", airTypeEnum, 1, 1, ¢er, NULL,
"which centering applies to the quantized position.\n "
"Possibilities are:\n "
"\b\bo \"cell\": for histogram bins, quantized values, and "
"pixels-as-squares\n "
"\b\bo \"node\": for non-trivially interpolated "
"sample points", NULL, nrrdCenter );
hestOptAdd( &opt, NULL, "minPos", airTypeDouble, 1, 1, &minPos, NULL,
"smallest position associated with index 0" );
hestOptAdd( &opt, NULL, "maxPos", airTypeDouble, 1, 1, &maxPos, NULL,
"highest position associated with highest index" );
hestOptAdd( &opt, NULL, "num", airTypeDouble, 1, 1, &size, NULL,
"number of intervals into which position has been quantized" );
hestOptAdd( &opt, NULL, "index", airTypeDouble, 1, 1, &indx, NULL,
"the input index position, to be converted to world" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_i2wInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
pos = NRRD_POS( center, minPos, maxPos, size, indx );
printf( "%g\n", pos );
airMopOkay( mop );
return 0;
}
75 UNRRDU_CMD( i2w, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through *irregular* univariate map ( \"colormap\" )"
static const char *_unrrdu_imapInfoL =
( INFO
". A map is irregular if the control points are not evenly "
"spaced along the domain, and hence their position must be "
"explicitly represented in the map. As nrrds, these maps "
"are necessarily 2D. Along axis 0, the first value is the "
"location of the control point, and the remaining values "
"give are the range of the map for that control point. "
"The output value( s ) is the result of linearly "
"interpolating between value( s ) from the map.\n "
"* Uses nrrdApply1DIrregMap" );
int
41 unrrdu_imapMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nmap, *nacl, *nout;
airArray *mop;
NrrdRange *range=NULL;
unsigned int aclLen;
int typeOut, rescale, pret, blind8BitRange;
double min, max;
hestOptAdd( &opt, "m, map", "map", airTypeOther, 1, 1, &nmap, NULL,
"irregular map to map input nrrd through",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "l, length", "aclLen", airTypeUInt, 1, 1, &aclLen, "0",
"length of accelerator array, used to try to speed-up "
"task of finding between which pair of control points "
"a given value lies. Not terribly useful for small maps "
"( about 10 points or less ). Use 0 to turn accelorator off. " );
hestOptAdd( &opt, "r, rescale", NULL, airTypeInt, 0, 0, &rescale, NULL,
"rescale the input values from the input range to the "
"map domain" );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Low end of input range. Defaults to lowest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"High end of input range. Defaults to highest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the map's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_imapInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( aclLen ) {
nacl = nrrdNew( );
airMopAdd( mop, nacl, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrd1DIrregAclGenerate( nacl, nmap, aclLen ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble generating accelerator:\n%s", me, err );
airMopError( mop );
return 1;
}
} else {
nacl = NULL;
}
if ( rescale ) {
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nmap->type;
}
/* some very non-exhaustive tests seemed to indicate that the
accelerator does not in fact reliably speed anything up.
This of course depends on the size of the imap ( # points ),
but chances are most imaps will have only a handful of points,
in which case the binary search in _nrrd1DIrregFindInterval( )
will finish quickly ... */
if ( nrrdApply1DIrregMap( nout, nin, range, nmap, nacl, typeOut, rescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying map:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
134 UNRRDU_CMD( imap, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Replace a sub-region with a different nrrd"
static const char *_unrrdu_insetInfoL =
( INFO ". This is functionally the opposite of \"crop\".\n "
"* Uses nrrdInset" );
int
33 unrrdu_insetMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *nsub;
unsigned int ai, minLen;
int pret;
long int *minOff;
size_t min[NRRD_DIM_MAX];
airArray *mop;
OPT_ADD_BOUND( "min, minimum", 1, minOff, NULL,
"coordinates of where to locate sub-volume within "
"input nrrd.\n "
"\b\bo <int> gives 0-based index\n "
"\b\bo M, M+<int>, M-<int> give index relative "
"to the last sample on the axis ( M == #samples-1 ).",
minLen );
hestOptAdd( &opt, "s, subset", "nsub", airTypeOther, 1, 1, &( nsub ), NULL,
"sub-region nrrd. This the data to be inset in \"nin\"",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_insetInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !( minLen == nin->dim ) ) {
fprintf( stderr,
"%s: # min coords ( %d ) != nrrd dim ( %d )\n",
me, minLen, nin->dim );
airMopError( mop );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
if ( -1 == minOff[0 + 2*ai] ) {
fprintf( stderr, "%s: can't use m+<int> specification for axis %u min\n",
me, ai );
airMopError( mop );
return 1;
}
}
for ( ai=0; ai<=nin->dim-1; ai++ ) {
min[ai] = minOff[0 + 2*ai]*( nin->axis[ai].size-1 ) + minOff[1 + 2*ai];
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdInset( nout, nin, nsub, min ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error insetting nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
99 UNRRDU_CMD( inset, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Create joint histogram of two or more nrrds"
static const char *_unrrdu_jhistoInfoL =
( INFO
". Each axis of the output corresponds to one of the "
"input nrrds, and each bin in the output records the "
"number of corresponding positions in the inputs with "
"a combination of values represented by the coordinates "
"of the bin.\n "
"* Uses nrrdHistoJoint" );
int
38 unrrdu_jhistoMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd **nin, **npass;
Nrrd *nout, *nwght;
size_t *bin;
int type, clamp[NRRD_DIM_MAX], pret;
unsigned int minLen, maxLen, ninLen, binLen, ai, diceax;
airArray *mop;
double *min, *max;
NrrdRange **range;
hestOptAdd( &opt, "b, bin", "bins0 bins1", airTypeSize_t, 2, -1, &bin, NULL,
"bins<i> is the number of bins to use along axis i ( of joint "
"histogram ), which represents the values of nin<i> ",
&binLen );
hestOptAdd( &opt, "w, weight", "nweight", airTypeOther, 1, 1, &nwght, "",
"how to weigh contributions to joint histogram. By default "
"( not using this option ), the increment is one bin count per "
"sample, but by giving a nrrd, the value in the nrrd at the "
"corresponding location will be the bin count increment ",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "min, minimum", "min0 min1", airTypeDouble, 2, -1,
&min, "nan nan",
"min<i> is the low range of values to be quantized along "
"axis i; use \"nan\" to represent lowest value present ",
&minLen );
hestOptAdd( &opt, "max, maximum", "max0 max1", airTypeDouble, 2, -1,
&max, "nan nan",
"max<i> is the high range of values to be quantized along "
"axis i; use \"nan\" to represent highest value present ",
&maxLen );
OPT_ADD_TYPE( type, "type to use for output ( the type used to store hit "
"counts in the joint histogram ). Clamping is done on hit "
"counts so that they never overflow a fixed-point type",
"uint" );
hestOptAdd( &opt, "i, input", "nin0 [nin1]", airTypeOther, 1, -1, &nin, "-",
"list of nrrds ( one for each axis of joint histogram ), "
"or, single nrrd that will be sliced along specified axis.",
&ninLen, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "a, axis", "axis", airTypeUInt, 1, 1, &diceax, "0",
"axis to slice along when working with single nrrd. " );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_jhistoInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( ninLen == 1 ) {
/* Slice a nrrd on the fly */
size_t asize;
if ( !( diceax <= nin[0]->dim-1 ) ) {
fprintf( stderr, "%s: slice axis %u not valid for single %u-D nrrd",
me, diceax, nin[0]->dim );
airMopError( mop );
return 1;
}
asize = nin[0]->axis[diceax].size;
if ( asize != binLen ) {
fprintf( stderr,
"%s: size ( %u ) of slice axis %u != # bins given ( %u )\n", me,
AIR_CAST( unsigned int, asize ), diceax,
AIR_CAST( unsigned int, binLen ) );
airMopError( mop );
return 1;
}
/* create buffer for slices */
if ( !( npass = AIR_CALLOC( binLen, Nrrd* ) ) ) {
fprintf( stderr, "%s: couldn't allocate nrrd array ( size %u )\n",
me, binLen );
airMopError( mop ); return 1;
}
airMopMem( mop, &npass, airMopAlways );
/* slice this nrrd, allocate new nrrds, and store the slices in nin */
for ( ai=0; ai<binLen; ai++ ) {
/* Allocate each slice nrrd */
if ( !( npass[ai] = nrrdNew( ) ) ) {
fprintf( stderr, "%s: couldn't allocate npass[%u]\n", me, ai );
airMopError( mop ); return 1;
}
airMopAdd( mop, npass[ai], ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSlice( npass[ai], nin[0], diceax, ai ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error slicing:\n%s", me, err );
airMopError( mop ); return 1;
}
}
} else {
/* we were given multiple nrrds */
if ( ninLen != binLen ) {
fprintf( stderr,
"%s: # input nrrds ( %u ) != # bin specifications ( %u )\n", me,
AIR_CAST( unsigned int, ninLen ), AIR_CAST( unsigned int, binLen ) );
airMopError( mop );
return 1;
}
/* create buffer for slices ( HEY copy and paste ) */
if ( !( npass = AIR_CALLOC( binLen, Nrrd* ) ) ) {
fprintf( stderr, "%s: couldn't allocate nrrd array ( size %u )\n",
me, binLen );
airMopError( mop ); return 1;
}
for ( ai=0; ai<binLen; ai++ ) {
npass[ai] = nin[ai];
}
}
range = AIR_CALLOC( binLen, NrrdRange* );
airMopAdd( mop, range, airFree, airMopAlways );
for ( ai=0; ai<binLen; ai++ ) {
range[ai] = nrrdRangeNew( AIR_NAN, AIR_NAN );
airMopAdd( mop, range[ai], ( airMopper )nrrdRangeNix, airMopAlways );
}
if ( 2 != minLen || ( AIR_EXISTS( min[0] ) || AIR_EXISTS( min[1] ) ) ) {
if ( minLen != binLen ) {
fprintf( stderr, "%s: # mins ( %d ) != # input nrrds ( %d )\n", me,
minLen, binLen );
airMopError( mop ); return 1;
}
for ( ai=0; ai<binLen; ai++ ) {
range[ai]->min = min[ai];
}
}
if ( 2 != maxLen || ( AIR_EXISTS( max[0] ) || AIR_EXISTS( max[1] ) ) ) {
if ( maxLen != binLen ) {
fprintf( stderr, "%s: # maxs ( %d ) != # input nrrds ( %d )\n", me,
maxLen, binLen );
airMopError( mop ); return 1;
}
for ( ai=0; ai<binLen; ai++ ) {
range[ai]->max = max[ai];
}
}
for ( ai=0; ai<binLen; ai++ ) {
clamp[ai] = 0;
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdHistoJoint( nout, ( const Nrrd*const* )npass,
( const NrrdRange*const* )range,
binLen, nwght, bin, type, clamp ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error doing joint histogram:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
196 UNRRDU_CMD( jhisto, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Connect slices and/or slabs into a bigger nrrd"
static const char *_unrrdu_joinInfoL =
( INFO
". Can stich images into volumes, or tile images side "
"by side, or attach images onto volumes. If there are many many "
"files to name in the \"-i\" option, and using wildcards won't work, "
"consider putting the list of "
"filenames into a separate text file ( e.g. \"slices.txt\" ), and then "
"name this file as a response file ( e.g. \"-i @slices.txt\" ). "
"This command now allows you to set the same pieces of information that "
"previously had to be set with \"unu axinfo\": label, spacing, and min/max. "
"These can be use whether the join axis is new ( because of \"-incr\" ) or "
"not.\n "
"* Uses nrrdJoin" );
int
43 unrrdu_joinMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *label, *kindStr;
Nrrd **nin;
Nrrd *nout;
int incrDim, pret, kind;
unsigned int ninLen, axis;
double mm[2], spc;
airArray *mop;
hparm->respFileEnable = AIR_TRUE;
hestOptAdd( &opt, "i, input", "nin0", airTypeOther, 1, -1, &nin, NULL,
"everything to be joined together",
&ninLen, NULL, nrrdHestNrrd );
OPT_ADD_AXIS( axis, "axis to join along" );
hestOptAdd( &opt, "incr", NULL, airTypeInt, 0, 0, &incrDim, NULL,
"in situations where the join axis is *not* among the existing "
"axes of the input nrrds, then this flag signifies that the join "
"axis should be *inserted*, and the output dimension should "
"be one greater than input dimension. Without this flag, the "
"nrrds are joined side-by-side, along an existing axis." );
hestOptAdd( &opt, "l, label", "label", airTypeString, 1, 1, &label, "",
"label to associate with join axis" );
hestOptAdd( &opt, "k, kind", "kind", airTypeString, 1, 1, &kindStr, "",
"kind to set on join axis. "
"Not using this option leaves the kind as is" );
hestOptAdd( &opt, "mm, minmax", "min max", airTypeDouble, 2, 2, mm, "nan nan",
"min and max values along join axis" );
hestOptAdd( &opt, "sp, spacing", "spc", airTypeDouble, 1, 1, &spc, "nan",
"spacing between samples along join axis" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_joinInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdJoin( nout, AIR_CAST( const Nrrd*const*, nin ), ninLen,
axis, incrDim ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error joining nrrds:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( strlen( label ) ) {
nout->axis[axis].label = ( char * )airFree( nout->axis[axis].label );
nout->axis[axis].label = airStrdup( label );
}
if ( airStrlen( kindStr ) ) {
if ( !strcmp( "none", kindStr )
|| !strcmp( "???", kindStr ) ) {
kind = nrrdKindUnknown;
} else {
if ( !( kind = airEnumVal( nrrdKind, kindStr ) ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as %s\n", me,
kindStr, nrrdKind->name );
airMopError( mop );
return 1;
}
}
nout->axis[axis].kind = kind;
}
if ( AIR_EXISTS( mm[0] ) ) {
nout->axis[axis].min = mm[0];
}
if ( AIR_EXISTS( mm[1] ) ) {
nout->axis[axis].max = mm[1];
}
if ( AIR_EXISTS( spc ) ) {
nout->axis[axis].spacing = spc;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
128 UNRRDU_CMD( join, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through one univariate lookup table"
static const char *_unrrdu_lutInfoL =
( INFO
" ( itself represented as a nrrd ). The lookup table "
"can be 1D, in which case the output "
"has the same dimension as the input, or 2D, in which case "
"the output has one more dimension than the input, and each "
"value is mapped to a scanline ( along axis 0 ) from the "
"lookup table.\n "
"* Uses nrrdApply1DLut" );
int
39 unrrdu_lutMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nlut, *nout;
airArray *mop;
int typeOut, rescale, pret, blind8BitRange;
double min, max;
NrrdRange *range=NULL;
hestOptAdd( &opt, "m, map", "lut", airTypeOther, 1, 1, &nlut, NULL,
"lookup table to map input nrrd through",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "r, rescale", NULL, airTypeInt, 0, 0, &rescale, NULL,
"rescale the input values from the input range to the "
"lut domain. The lut domain is either explicitly "
"defined by the axis min, max along axis 0 or 1, or, it "
"is implicitly defined as zero to the length of that axis "
"minus one." );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Low end of input range. Defaults to lowest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"High end of input range. Defaults to highest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the lut's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_lutInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* see comment rmap.c */
if ( !( AIR_EXISTS( nlut->axis[nlut->dim - 1].min ) &&
AIR_EXISTS( nlut->axis[nlut->dim - 1].max ) ) ) {
rescale = AIR_TRUE;
}
if ( rescale ) {
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nlut->type;
}
if ( nrrdApply1DLut( nout, nin, range, nlut, typeOut, rescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying LUT:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
117 UNRRDU_CMD( lut, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through a bivariate lookup table"
static const char *_unrrdu_lut2InfoL =
( INFO
" ( itself represented as a nrrd ). The lookup table "
"can be 2D, in which case the output "
"has the same dimension as the input, or 3D, in which case "
"the output has one more dimension than the input, and each "
"pair of values is mapped to a scanline ( along axis 0 ) from the "
"lookup table. In any case, axis 0 of the input must have length two.\n "
"* Uses nrrdApply2DLut" );
int
39 unrrdu_lut2Main( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nlut, *nout, *ntmp[2];
airArray *mop;
int typeOut, rescale[2], pret, blind8BitRange;
double min[2], max[2];
NrrdRange *range[2]={NULL, NULL};
unsigned int mapAxis, rai;
hestOptAdd( &opt, "m, map", "lut", airTypeOther, 1, 1, &nlut, NULL,
"lookup table to map input nrrd through",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "r, rescale", "bool bool", airTypeBool, 2, 2, rescale,
"false false",
"rescale one or both of the input values from the "
"input range to the lut domain. The lut domain is either "
"explicitly defined by the axis min, max along axis 0 or 1, "
"or, it is implicitly defined as zero to the length of that axis "
"minus one." );
hestOptAdd( &opt, "min, minimum", "min0 min1", airTypeDouble, 2, 2, min,
"nan nan",
"Low ends of input range. Defaults to lowest values "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "max, maximum", "max0 max1", airTypeDouble, 2, 2, max,
"nan nan",
"High end of input range. Defaults to highest values "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the lut's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_lut2InfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( !( nin->dim > 1 && 2 == nin->axis[0].size ) ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: input nrrd dim must be > 1, and axis[0].size "
"must be 2 ( not %s )\n", me,
airSprintSize_t( stmp, nin->axis[0].size ) );
airMopError( mop );
return 1;
}
mapAxis = nlut->dim - 2;
if ( !( 0 == mapAxis || 1 == mapAxis ) ) {
fprintf( stderr, "%s: dimension of lut should be 2 or 3, not %d",
me, nlut->dim );
airMopError( mop );
return 1;
}
/* see comment in rmap.c */
for ( rai=0; rai<=1; rai++ ) {
if ( !( AIR_EXISTS( nlut->axis[mapAxis + rai].min ) &&
AIR_EXISTS( nlut->axis[mapAxis + rai].max ) ) ) {
rescale[rai] = AIR_TRUE;
}
if ( rescale[rai] ) {
ntmp[rai] = nrrdNew( );
airMopAdd( mop, ntmp[rai], AIR_CAST( airMopper, nrrdNuke ), airMopAlways );
if ( nrrdSlice( ntmp[rai], nin, 0, rai ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble slicing input value %u:\n%s",
me, rai, err );
airMopError( mop );
return 1;
}
range[rai] = nrrdRangeNew( min[rai], max[rai] );
airMopAdd( mop, range[rai], ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range[rai], ntmp[rai], blind8BitRange );
}
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nlut->type;
}
if ( nrrdApply2DLut( nout, nin, 0, range[0], range[1], nlut, typeOut,
rescale[0], rescale[1] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying 2-D LUT:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
148 UNRRDU_CMD( lut2, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
/* learned: some header file must declare private functions
** within extern "C" so that others can link with them
*/
#define NO_STRING "."
#define INFO "Create a nrrd ( or nrrd header ) from scratch"
static const char *_unrrdu_makeInfoL =
( INFO
". The data can be in one or more files, or coming from stdin. "
"This provides an easy way of specifying the information about some "
"data as to wrap it in a NRRD file, either to pass on for further "
"unu processing, or to save to disk. Note that with \"-h\", this creates "
"a detached nrrd header file, without ever reading or writing data files. "
"\n \n "
"When using multiple datafiles, the data from each is simply "
"concatenated in memory ( as opposed to interleaving along a faster axis ). "
"Keep in mind that all the options below refer to the finished data segment "
"resulting from joining all the data pieces together, "
"except for \"-ls\", \"-bs\", and \"-e\", which apply ( uniformly ) to the "
"individual data files. Use the \"-fd\" option when the things being joined "
"together are not slices of the final result, but slabs or scanlines. "
"It may be easier to put multiple filenames in a response file; "
"there can be one or more filenames per line of the response file. "
"You can also use a sprintf-style format to identify a numbered "
"range of files, so for example \"-i I.%03d 1 90 1\" "
"refers to I.001, I.002, ... I.090, using the inclusive range from the first "
"to the second integer ( following the sprintf-style format ), in steps of "
"the third. Can optionally give a fourth integer to serve same role as "
"\"-fd\"."
"\n \n "
"NOTE: for the \"-l\" ( labels ), \"-u\" ( units ), and \"-spu\" ( space units ) "
"options below, you can use a single unquoted period ( . ) to signify "
"an empty string. This creates a convenient way to convey something that "
"the shell doesn't make it easy to convey. Shell expansion weirdness "
"also requires the use of quotes around the arguments to \"-orig\" ( space "
"origin ), \"-dirs\" ( space directions ), and \"-mf\" ( measurement frame ).\n "
"\n "
"* Uses various components of file and data IO, but currently there is no "
"library function that encapsulates the functionality here." );
int
69 unrrdu_makeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *outData, *err,
**dataFileNames, **kvp, *content, encInfo[AIR_STRLEN_LARGE];
Nrrd *nrrd;
size_t *size, bufLen;
int headerOnly, pret, lineSkip, endian, type,
encodingType, gotSpacing, gotThickness, space,
spaceSet;
long int byteSkip;
unsigned int ii, kindsLen, thicknessLen, spacingLen, sizeLen, nameLen,
centeringsLen, unitsLen, labelLen, kvpLen, spunitsLen, dataFileDim,
spaceDim;
double *spacing, *thickness;
airArray *mop;
NrrdIoState *nio;
FILE *fileOut;
char **label, **units, **spunits, **kinds, **centerings, *parseBuf,
*spcStr, *_origStr, *origStr, *_dirStr, *dirStr, *_mframeStr, *mframeStr;
const NrrdEncoding *encoding;
/* so that long lists of filenames can be read from file */
hparm->respFileEnable = AIR_TRUE;
hparm->greedySingleString = AIR_TRUE;
mop = airMopNew( );
hestOptAdd( &opt, "h", NULL, airTypeBool, 0, 0, &headerOnly, NULL,
"Generate header ONLY: don't write out the whole nrrd, "
"don't even bother reading the input data, just output the "
"detached nrrd header file ( usually with a \".nhdr\" "
"extension ) determined by the options below. The single "
"constraint is that detached headers are incompatible with "
"using stdin as the data source." );
hestOptAdd( &opt, "i, input", "file", airTypeString, 1, -1,
&dataFileNames, "-",
"Filename( s ) of data file( s ); use \"-\" for stdin. *OR*, can "
"use sprintf-style format for identifying a range of numbered "
"files, see above for details.", &nameLen );
hestOptAdd( &opt, "t, type", "type", airTypeEnum, 1, 1, &type, NULL,
"type of data ( e.g. \"uchar\", \"int\", \"float\", "
"\"double\", etc. )",
NULL, nrrdType );
hestOptAdd( &opt, "s, size", "sz0 sz1", airTypeSize_t, 1, -1, &size, NULL,
"number of samples along each axis ( and implicit indicator "
"of dimension of nrrd )", &sizeLen );
hestOptAdd( &opt, "fd, filedim", "dim", airTypeUInt, 1, 1,
&dataFileDim, "0",
"When using *multiple* input data files ( to \"-i\" ), what is "
"the dimension of the array data in each individual file. By "
"default ( not using this option ), this dimension is assumed "
"to be one less than the whole data dimension. " );
hestOptAdd( &opt, "sp, spacing", "sp0 sp1", airTypeDouble, 1, -1,
&spacing, "nan",
"spacing between samples on each axis. Use \"nan\" for "
"any non-spatial axes ( e.g. spacing between red, green, and blue "
"along axis 0 of interleaved RGB image data )", &spacingLen );
hestOptAdd( &opt, "th, thickness", "th0 th1", airTypeDouble, 1, -1,
&thickness, "nan",
"thickness of region represented by one sample along each axis. "
" As with spacing, use \"nan\" for "
"any non-spatial axes.", &thicknessLen );
hestOptAdd( &opt, "k, kind", "k0 k1", airTypeString, 1, -1, &kinds, "",
"what \"kind\" is each axis, from the nrrdKind airEnum "
"( e.g. space, time, 3-vector, 3D-masked-symmetric-matrix, "
"or \"none\" to signify no kind )", &kindsLen );
hestOptAdd( &opt, "cn, centering", "c0 c1", airTypeString, 1, -1,
¢erings, "",
"kind of centering ( node or cell ) for each axis, or "
"\"none\" to signify no centering", ¢eringsLen );
hestOptAdd( &opt, "l, label", "lb0 lb1", airTypeString, 1, -1, &label, "",
"short string labels for each of the axes", &labelLen );
hestOptAdd( &opt, "u, unit", "un0 un1", airTypeString, 1, -1, &units, "",
"short strings giving units for each of the axes", &unitsLen );
hestOptAdd( &opt, "c, content", "content", airTypeString, 1, 1, &content, "",
"Specifies the content string of the nrrd, which is built upon "
"by many nrrd function to record a history of operations" );
hestOptAdd( &opt, "ls, lineskip", "num", airTypeInt, 1, 1, &lineSkip, "0",
"number of ascii lines to skip before reading data" );
hestOptAdd( &opt, "bs, byteskip", "num", airTypeLongInt, 1, 1, &byteSkip, "0",
"number of bytes to skip ( after skipping ascii lines, if any ) "
"before reading data. Can use \"-bs -1\" to skip a binary "
"header of unknown length in raw-encoded data" );
strcpy( encInfo,
"encoding of input data. Possibilities include:"
"\n \b\bo \"raw\": raw encoding"
"\n \b\bo \"ascii\": ascii values, one scanline per line of text, "
"values within line are delimited by space, tab, or comma"
"\n \b\bo \"hex\": two hex digits per byte" );
if ( nrrdEncodingGzip->available( ) ) {
strcat( encInfo,
"\n \b\bo \"gzip\", \"gz\": gzip compressed raw data" );
}
if ( nrrdEncodingBzip2->available( ) ) {
strcat( encInfo,
"\n \b\bo \"bzip2\", \"bz2\": bzip2 compressed raw data" );
}
hestOptAdd( &opt, "e, encoding", "enc", airTypeEnum, 1, 1,
&encodingType, "raw",
encInfo, NULL, nrrdEncodingType );
hestOptAdd( &opt, "en, endian", "end", airTypeEnum, 1, 1, &endian,
airEnumStr( airEndian, airMyEndian( ) ),
"Endianness of data; relevent for any data with value "
"representation bigger than 8 bits, with a non-ascii encoding: "
"\"little\" for Intel and friends "
"( least significant byte first, at lower address ); "
"\"big\" for everyone else ( most significant byte first ). "
"Defaults to endianness of this machine",
NULL, airEndian );
hestOptAdd( &opt, "kv, keyvalue", "key/val", airTypeString, 1, -1, &kvp, "",
"key/value string pairs to be stored in nrrd. Each key/value "
"pair must be a single string ( put it in \"\"s "
"if the key or the value contain spaces ). The format of each "
"pair is \"<key>:=<value>\", with no spaces before or after "
"\":=\".", &kvpLen );
hestOptAdd( &opt, "spc, space", "space", airTypeString, 1, 1, &spcStr, "",
"identify the space ( e.g. \"RAS\", \"LPS\" ) in which the array "
"conceptually lives, from the nrrdSpace airEnum, which in turn "
"determines the dimension of the space. Or, use an integer>0 to"
"give the dimension of a space that nrrdSpace doesn't know about. "
"By default ( not using this option ), the enclosing space is "
"set as unknown." );
hestOptAdd( &opt, "orig, origin", "origin", airTypeString, 1, 1, &_origStr, "",
"( NOTE: must quote vector ) the origin in space of the array: "
"the location of the center "
"of the first sample, of the form \"( x, y, z )\" ( or however "
"many coefficients are needed for the chosen space ). Quoting the "
"vector is needed to stop interpretation from the shell" );
hestOptAdd( &opt, "dirs, directions", "v0 v1 ...", airTypeString, 1, 1,
&_dirStr, "",
"( NOTE: must quote whole vector list ) The \"space directions\": "
"the vectors in space spanned by incrementing ( by one ) each "
"axis index ( the column vectors of the index-to-world "
"matrix transform ), OR, \"none\" for non-spatial axes. Give "
"one vector per axis. ( Quoting around whole vector list, not "
"individually, is needed because of limitations in the parser )" );
hestOptAdd( &opt, "mf, measurementframe", "v0 v1 ...", airTypeString, 1, 1,
&_mframeStr, "",
"( NOTE: must quote whole vector list ). Each vector is a *column* "
"vector of the matrix which transforms from coordinates in "
"measurement frame ( in which the coefficients of vectors and "
"tensors are given ) to coordinates of world space ( given with "
"\"-spc\" ). This is not a per-axis field: the column vectors "
"comprise a D-by-D square matrix, where D is the dimension of "
"world space." );
hestOptAdd( &opt, "spu, spaceunit", "su0 su1", airTypeString, 1, -1,
&spunits, "",
"short strings giving units with which the coefficients of the "
"space origin and direction vectors are measured.", &spunitsLen );
hestOptAdd( &opt, "o, output", "nout", airTypeString, 1, 1, &out, "-",
"output filename. If \"-h\" has been used, the output file is "
"always a detached header. Otherwise, use extension "
"\".nrrd\" to signal creation of self-contained nrrd, and "
"\".nhdr\" to signal creating of a detached header with "
"( single ) data file." );
hestOptAdd( &opt, "od, outputdata", "name", airTypeString, 1, 1, &outData, "",
"when *not* using \"-h\" and saving to a \".nhdr\" file, using "
"this option allows you to explicitly name the data file, "
"instead of ( by default, not using this option ) having it be "
"the same filename base as the header file." );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_makeInfoL );
airStrtokQuoting = AIR_TRUE;
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
encoding = nrrdEncodingArray[encodingType];
/* ---------------- BEGIN ERROR CHECKING --------------- */
if ( headerOnly ) {
for ( ii=0; ii<nameLen; ii++ ) {
if ( !strcmp( "-", dataFileNames[ii] ) ) {
fprintf( stderr, "%s: can't use detached headers ( \"-h\" ) "
"with stdin ( \"-\" ) as data source "
"( filename %d of %d )\n", me, ii+1, nameLen );
airMopError( mop ); return 1;
}
}
}
/* given the information we have, we set the fields in the nrrdIoState
so as to simulate having read the information from a header */
if ( !( AIR_IN_CL( 1, sizeLen, NRRD_DIM_MAX ) ) ) {
fprintf( stderr, "%s: # axis sizes ( %d ) not in valid nrrd dimension "
"range [1, NRRD_DIM_MAX] = [1, %d]\n", me, sizeLen, NRRD_DIM_MAX );
airMopError( mop );
return 1;
}
gotSpacing = ( spacingLen > 1 ||
( sizeLen == 1 && AIR_EXISTS( spacing[0] ) ) );
if ( gotSpacing && spacingLen != sizeLen ) {
fprintf( stderr,
"%s: number of spacings ( %d ) not same as dimension ( %d )\n",
me, spacingLen, sizeLen );
airMopError( mop );
return 1;
}
gotThickness = ( thicknessLen > 1 ||
( sizeLen == 1 && AIR_EXISTS( thickness[0] ) ) );
if ( gotThickness && thicknessLen != sizeLen ) {
fprintf( stderr,
"%s: number of thicknesses ( %d ) not same as dimension ( %d )\n",
me, thicknessLen, sizeLen );
airMopError( mop );
return 1;
}
if ( airStrlen( label[0] ) && sizeLen != labelLen ) {
fprintf( stderr,
"%s: number of labels ( %d ) not same as dimension ( %d )\n",
me, labelLen, sizeLen );
airMopError( mop );
return 1;
}
if ( airStrlen( units[0] ) && sizeLen != unitsLen ) {
fprintf( stderr,
"%s: number of units ( %d ) not same as dimension ( %d )\n",
me, unitsLen, sizeLen );
airMopError( mop );
return 1;
}
if ( airStrlen( kinds[0] ) && sizeLen != kindsLen ) {
fprintf( stderr,
"%s: number of kinds ( %d ) not same as dimension ( %d )\n",
me, kindsLen, sizeLen );
airMopError( mop );
return 1;
}
if ( airStrlen( centerings[0] ) && sizeLen != centeringsLen ) {
fprintf( stderr,
"%s: number of centerings ( %d ) not same as dimension ( %d )\n",
me, centeringsLen, sizeLen );
airMopError( mop );
return 1;
}
/* ----------------- END ERROR CHECKING ---------------- */
/* ----------------- BEGIN SETTING INFO ---------------- */
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
nrrd = nrrdNew( );
airMopAdd( mop, nrrd, ( airMopper )nrrdNuke, airMopAlways );
nrrd->type = type;
nrrd->dim = sizeLen;
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSize, size );
/* have to simulate having parsed this line for error checking in
_nrrdDataFNCheck( ) to not cause problems */
nio->seen[nrrdField_sizes] = AIR_TRUE;
if ( _nrrdContainsPercentThisAndMore( dataFileNames[0], 'd' ) ) {
/* trying to do a formatted filename list */
if ( nameLen < 4 || nameLen > 5 ) {
fprintf( stderr, "%s: formatted list of filenames needs between "
"3 and 4 ints after the format ( not %d )\n", me, nameLen-1 );
airMopError( mop );
return 1;
}
bufLen = 0;
for ( ii=0; ii<nameLen; ii++ ) {
bufLen += strlen( dataFileNames[ii] ) + 1;
}
parseBuf = AIR_CALLOC( bufLen+1, char );
airMopAdd( mop, parseBuf, airFree, airMopAlways );
strcpy( parseBuf, "" );
for ( ii=0; ii<nameLen; ii++ ) {
if ( ii ) {
strcat( parseBuf, " " );
}
strcat( parseBuf, dataFileNames[ii] );
}
nio->line = parseBuf;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_data_file]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with formatted filename list \"%s\":\n%s",
me, parseBuf, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
} else {
/* single or regular LIST of files */
if ( nameLen > 1 ) {
nio->dataFileDim = dataFileDim ? dataFileDim : nrrd->dim-1;
} else {
nio->dataFileDim = nrrd->dim;
}
airArrayLenSet( nio->dataFNArr, nameLen );
for ( ii=0; ii<nameLen; ii++ ) {
nio->dataFN[ii] = airStrdup( dataFileNames[ii] );
}
}
if ( _nrrdDataFNCheck( nio, nrrd, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with input datafiles:\n%s", me, err );
airMopError( mop ); return 1;
}
if ( gotSpacing ) {
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoSpacing, spacing );
}
if ( gotThickness ) {
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoThickness, thickness );
}
if ( airStrlen( label[0] ) ) {
for ( ii=0; ii<nrrd->dim; ii++ ) {
if ( !strcmp( NO_STRING, label[ii] ) ) {
strcpy( label[ii], "" );
}
}
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoLabel, label );
}
if ( airStrlen( units[0] ) ) {
for ( ii=0; ii<nrrd->dim; ii++ ) {
if ( !strcmp( NO_STRING, units[ii] ) ) {
strcpy( units[ii], "" );
}
}
nrrdAxisInfoSet_nva( nrrd, nrrdAxisInfoUnits, units );
}
if ( airStrlen( content ) ) {
nrrd->content = airStrdup( content );
}
if ( kvpLen ) {
for ( ii=0; ii<kvpLen; ii++ ) {
/* a hack: have to use NrrdIoState->line as the channel to communicate
the key/value pair, since we have to emulate it having been
read from a NRRD header. But because nio doesn't own the
memory, we must be careful to unset the pointer prior to
NrrdIoStateNix being called by the mop. */
nio->line = kvp[ii];
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_keyvalue]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with key/value %d \"%s\":\n%s",
me, ii, kvp[ii], err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
}
if ( airStrlen( kinds[0] ) ) {
/* have to allocate line then pass it to parsing */
bufLen = 0;
for ( ii=0; ii<sizeLen; ii++ ) {
bufLen += airStrlen( " " ) + airStrlen( kinds[ii] );
}
parseBuf = AIR_CALLOC( bufLen+1, char );
airMopAdd( mop, parseBuf, airFree, airMopAlways );
strcpy( parseBuf, "" );
for ( ii=0; ii<sizeLen; ii++ ) {
if ( ii ) {
strcat( parseBuf, " " );
}
strcat( parseBuf, kinds[ii] );
}
nio->line = parseBuf;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_kinds]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with kinds \"%s\":\n%s",
me, parseBuf, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( airStrlen( centerings[0] ) ) {
/* have to allocate line then pass it to parsing */
bufLen = 0;
for ( ii=0; ii<sizeLen; ii++ ) {
bufLen += airStrlen( " " ) + airStrlen( centerings[ii] );
}
parseBuf = AIR_CALLOC( bufLen+1, char );
airMopAdd( mop, parseBuf, airFree, airMopAlways );
strcpy( parseBuf, "" );
for ( ii=0; ii<sizeLen; ii++ ) {
if ( ii ) {
strcat( parseBuf, " " );
}
strcat( parseBuf, centerings[ii] );
}
nio->line = parseBuf;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_centers]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with centerings \"%s\":\n%s",
me, parseBuf, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( airStrlen( spcStr ) ) {
space = airEnumVal( nrrdSpace, spcStr );
if ( !space ) {
/* couldn't parse it as space, perhaps its a uint */
if ( 1 != sscanf( spcStr, "%u", &spaceDim ) ) {
fprintf( stderr, "%s: couldn't parse \"%s\" as a nrrdSpace "
"or as a uint", me, spcStr );
airMopError( mop ); return 1;
}
/* else we did parse it as a uint */
nrrd->space = nrrdSpaceUnknown;
nrrd->spaceDim = spaceDim;
} else {
/* we did parse a known space */
nrrdSpaceSet( nrrd, space );
}
spaceSet = AIR_TRUE;
} else {
/* we got no space information at all */
nrrdSpaceSet( nrrd, nrrdSpaceUnknown );
spaceSet = AIR_FALSE;
}
if ( airStrlen( _origStr ) ) {
/* why this is necessary is a bit confusing to me, both the check for
enclosing quotes, and the need to use to a separate variable ( isn't
hest doing memory management of addresses, not variables? ) */
if ( '\"' == _origStr[0] && '\"' == _origStr[strlen( _origStr )-1] ) {
_origStr[strlen( _origStr )-1] = 0;
origStr = _origStr + 1;
} else {
origStr = _origStr;
}
/* same hack about using NrrdIoState->line as basis for parsing */
nio->line = origStr;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_space_origin]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with origin \"%s\":\n%s",
me, origStr, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( airStrlen( _dirStr ) ) {
/* same confusion as above */
if ( '\"' == _dirStr[0] && '\"' == _dirStr[strlen( _dirStr )-1] ) {
_dirStr[strlen( _dirStr )-1] = 0;
dirStr = _dirStr + 1;
} else {
dirStr = _dirStr;
}
/* same hack about using NrrdIoState->line as basis for parsing */
nio->line = dirStr;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_space_directions]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with space directions \"%s\":\n%s",
me, dirStr, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( airStrlen( _mframeStr ) ) {
/* same confusion as above */
if ( '\"' == _mframeStr[0] && '\"' == _mframeStr[strlen( _mframeStr )-1] ) {
_mframeStr[strlen( _mframeStr )-1] = 0;
mframeStr = _mframeStr + 1;
} else {
mframeStr = _mframeStr;
}
/* same hack about using NrrdIoState->line as basis for parsing */
nio->line = mframeStr;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_measurement_frame]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with measurement frame \"%s\":\n%s",
me, mframeStr, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( airStrlen( spunits[0] ) ) {
if ( !spaceSet ) {
fprintf( stderr, "%s: can't have space units with no space set\n", me );
airMopError( mop ); return 1;
}
if ( nrrd->spaceDim != spunitsLen ) {
fprintf( stderr,
"%s: number of space units ( %d ) "
"not same as space dimension ( %d )\n",
me, spunitsLen, nrrd->spaceDim );
airMopError( mop );
return 1;
}
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
if ( !strcmp( NO_STRING, spunits[ii] ) ) {
strcpy( spunits[ii], "" );
}
}
/* have to allocate line then pass it to parsing */
bufLen = 0;
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
bufLen += airStrlen( " " ) + airStrlen( "\"\"" ) + airStrlen( spunits[ii] );
}
parseBuf = AIR_CALLOC( bufLen+1, char );
airMopAdd( mop, parseBuf, airFree, airMopAlways );
strcpy( parseBuf, "" );
for ( ii=0; ii<nrrd->spaceDim; ii++ ) {
if ( ii ) {
strcat( parseBuf, " " );
}
strcat( parseBuf, "\"" );
strcat( parseBuf, spunits[ii] );
strcat( parseBuf, "\"" );
}
nio->line = parseBuf;
nio->pos = 0;
if ( nrrdFieldInfoParse[nrrdField_space_units]( NULL, nrrd,
nio, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with space units \"%s\":\n%s",
me, parseBuf, err );
nio->line = NULL; airMopError( mop ); return 1;
}
nio->line = NULL;
}
if ( _nrrdCheck( nrrd, AIR_FALSE, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problems with nrrd as set up:\n%s", me, err );
airMopError( mop ); return 1;
}
/* ----------------- END SETTING INFO ---------------- */
/* -------------------- BEGIN I/O -------------------- */
nio->lineSkip = lineSkip;
nio->byteSkip = byteSkip;
nio->encoding = encoding;
nio->endian = endian;
/* for the sake of reading in data files, this is as good a guess
as any as to what the header-relative path to them is. This
assuages concerns that come up even with headerOnly */
nio->path = airStrdup( "." );
if ( headerOnly ) {
/* we open and hand off the output FILE* to the nrrd writer, which
will not write any data, because of nio->skipData = AIR_TRUE */
if ( !( fileOut = airFopen( out, stdout, "wb" ) ) ) {
fprintf( stderr, "%s: couldn't fopen( \"%s\", \"wb\" ): %s\n",
me, out, strerror( errno ) );
airMopError( mop ); return 1;
}
airMopAdd( mop, fileOut, ( airMopper )airFclose, airMopAlways );
/* whatever line and byte skipping is required will be simply
recorded in the header, and done by the next reader */
nio->detachedHeader = AIR_TRUE;
nio->skipData = AIR_TRUE;
if ( nrrdFormatNRRD->write( fileOut, nrrd, nio ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble writing header:\n%s", me, err );
airMopError( mop ); return 1;
}
} else {
/* all this does is read the data from the files. We up the verbosity
because of all places this is probably where we really want it */
nrrdStateVerboseIO++;
if ( nrrdFormatNRRD->read( NULL, nrrd, nio ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble reading data files:\n%s", me, err );
airMopError( mop ); return 1;
}
nrrdStateVerboseIO--;
/* then save normally */
nrrdIoStateInit( nio );
if ( strlen( outData ) ) {
airArrayLenSet( nio->dataFNArr, 1 );
nio->dataFN[0] = airStrdup( outData );
}
SAVE( out, nrrd, nio );
}
airMopOkay( mop );
return 0;
}
651 UNRRDU_CMD( make, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Print out min and max values in one or more nrrds"
static const char *_unrrdu_minmaxInfoL =
( INFO ". Unlike other commands, this doesn't produce a nrrd. It only "
"prints to standard out the min and max values found in the input nrrd( s ), "
"and it also indicates if there are non-existent values.\n "
"* Uses nrrdRangeNewSet" );
int
35 unrrdu_minmaxDoit( const char *me, char *inS, int blind8BitRange, FILE *fout ) {
Nrrd *nrrd;
NrrdRange *range;
airArray *mop;
mop = airMopNew( );
airMopAdd( mop, nrrd=nrrdNew( ), ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdLoad( nrrd, inS, NULL ) ) {
biffMovef( me, NRRD, "%s: trouble loading \"%s\"", me, inS );
airMopError( mop ); return 1;
}
range = nrrdRangeNewSet( nrrd, blind8BitRange );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
airSinglePrintf( fout, NULL, "min: %.17g\n", range->min );
airSinglePrintf( fout, NULL, "max: %.17g\n", range->max );
if ( range->min == range->max ) {
if ( 0 == range->min ) {
fprintf( fout, "# min == max == 0.0 exactly\n" );
} else {
fprintf( fout, "# min == max\n" );
}
}
if ( range->hasNonExist ) {
fprintf( fout, "# has non-existent values\n" );
}
airMopOkay( mop );
return 0;
}
int
67 unrrdu_minmaxMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err, **inS;
airArray *mop;
int pret, blind8BitRange;
unsigned int ni, ninLen;
#define B8DEF "false"
mop = airMopNew( );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
B8DEF, /* NOTE: not using nrrdStateBlind8BitRange here
for consistency with previous behavior */
"whether to blindly assert the range of 8-bit data, "
"without actually going through the data values, i.e. "
"uchar is always [0, 255], signed char is [-128, 127]. "
"Note that even if you do not use this option, the default "
"( " B8DEF " ) is potentialy over-riding the effect of "
"environment variable NRRD_STATE_BLIND_8_BIT_RANGE; "
"see \"unu env\"" );
hestOptAdd( &opt, NULL, "nin1", airTypeString, 1, -1, &inS, NULL,
"input nrrd( s )", &ninLen );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_minmaxInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
for ( ni=0; ni<ninLen; ni++ ) {
if ( ninLen > 1 ) {
fprintf( stdout, "==> %s <==\n", inS[ni] );
}
if ( unrrdu_minmaxDoit( me, inS[ni], blind8BitRange, stdout ) ) {
airMopAdd( mop, err = biffGetDone( me ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble with \"%s\":\n%s",
me, inS[ni], err );
/* continue working on the remaining files */
}
if ( ninLen > 1 && ni < ninLen-1 ) {
fprintf( stdout, "\n" );
}
}
airMopOkay( mop );
return 0;
}
114 UNRRDU_CMD( minmax, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through whole nrrd of univariate lookup tables"
static const char *_unrrdu_mlutInfoL =
( INFO
", with one lookup table per element of input nrrd. The multiple "
"tables are stored in a nrrd with a dimension which is either 1 or 2 "
"more than the dimension of the input nrrd, resulting in an output "
"which has either the same or one more dimension than the input, "
"resptectively.\n "
"* Uses nrrdApplyMulti1DLut" );
int
38 unrrdu_mlutMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, **_nmlut, *nmlut, *nout;
airArray *mop;
int typeOut, rescale, pret, blind8BitRange;
unsigned int _nmlutLen, mapAxis;
double min, max;
NrrdRange *range=NULL;
hestOptAdd( &opt, "m, map", "mlut", airTypeOther, 1, -1, &_nmlut, NULL,
"one nrrd of lookup tables to map input nrrd through, or, "
"list of nrrds which contain the individual entries of "
"the lookup table at each voxel, which will be joined together.",
&_nmlutLen, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "r, rescale", NULL, airTypeInt, 0, 0, &rescale, NULL,
"rescale the input values from the input range to the "
"lut domain. The lut domain is either explicitly "
"defined by the axis min, max along axis 0 or 1, or, it "
"is implicitly defined as zero to the length of that axis "
"minus one." );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Low end of input range. Defaults to lowest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"High end of input range. Defaults to highest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful only with rescaling ( \"-r\" )" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the lut's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_mlutInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* by the end of this block we need to have nmlut and mapAxis */
if ( 1 == _nmlutLen ) {
/* we got the mlut as a single nrrd */
nmlut = _nmlut[0];
mapAxis = nmlut->dim - nin->dim - 1;
/* its not our job to do real error checking ... */
mapAxis = AIR_MIN( mapAxis, nmlut->dim - 1 );
} else {
/* we have to join together multiple nrrds to get the mlut */
nmlut = nrrdNew( );
airMopAdd( mop, nmlut, ( airMopper )nrrdNuke, airMopAlways );
/* assume that mlut component nrrds are all compatible sizes,
nrrdJoin will fail if they aren't */
mapAxis = _nmlut[0]->dim - nin->dim;
if ( nrrdJoin( nmlut, ( const Nrrd*const* )_nmlut, _nmlutLen,
mapAxis, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble joining mlut:\n%s", me, err );
airMopError( mop );
return 1;
}
/* set these if they were given, they'll be NaN otherwise */
nmlut->axis[mapAxis].min = min;
nmlut->axis[mapAxis].max = max;
}
if ( !( AIR_EXISTS( nmlut->axis[mapAxis].min ) &&
AIR_EXISTS( nmlut->axis[mapAxis].max ) ) ) {
rescale = AIR_TRUE;
}
if ( rescale ) {
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nmlut->type;
}
if ( nrrdApplyMulti1DLut( nout, nin, range, nmlut, typeOut, rescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying multi-LUT:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
144 UNRRDU_CMD( mlut, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through a whole nrrd of regular univariate maps"
static const char *_unrrdu_mrmapInfoL =
( INFO
", one map per sample in input. The \"mmap\" nrrd has the same dimensional "
"constraints as the \"mlut\" nrrd for \"unu mlut\". This functionality "
"is a generalization of \"unu 3op lerp\": it allows you to lerp through "
"multiple nrrds, instead of just two.\n "
"* Uses nrrdApplyMulti1DRegMap" );
int
37 unrrdu_mrmapMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, **_nmmap, *nmmap, *nout;
airArray *mop;
NrrdRange *range=NULL;
unsigned int mapAxis;
int typeOut, rescale, pret, blind8BitRange;
unsigned int _nmmapLen;
double min, max;
hestOptAdd( &opt, "m, map", "mmap", airTypeOther, 1, -1, &_nmmap, NULL,
"one nrrd of regular maps to map input nrrd through, or, "
"list of nrrds which contain the individual entries of the map "
"at each voxel, which will be joined together.",
&_nmmapLen, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "r, rescale", NULL, airTypeInt, 0, 0, &rescale, NULL,
"rescale the input values from the input range to the "
"map domain. The map domain is either explicitly "
"defined by the axis min, max along axis 0 or 1, or, it "
"is implicitly defined as zero to one minus the length of "
"that axis." );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Low end of input range. Defaults to lowest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"High end of input range. Defaults to highest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the map's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_mrmapInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* see comment rmap.c */
/* by the end of this block we need to have nmmap and mapAxis */
if ( 1 == _nmmapLen ) {
/* we got the mmap as a single nrrd */
nmmap = _nmmap[0];
mapAxis = nmmap->dim - nin->dim - 1;
/* its not our job to do real error checking ... */
mapAxis = AIR_MIN( mapAxis, nmmap->dim - 1 );
} else {
/* we have to join together multiple nrrds to get the mmap */
nmmap = nrrdNew( );
airMopAdd( mop, nmmap, ( airMopper )nrrdNuke, airMopAlways );
/* assume that mmap component nrrds are all compatible sizes,
nrrdJoin will fail if they aren't */
mapAxis = _nmmap[0]->dim - nin->dim;
if ( nrrdJoin( nmmap, ( const Nrrd*const* )_nmmap, _nmmapLen, mapAxis, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble joining mmap:\n%s", me, err );
airMopError( mop );
return 1;
}
/* set these if they were given, they'll be NaN otherwise */
nmmap->axis[mapAxis].min = min;
nmmap->axis[mapAxis].max = max;
}
if ( !( AIR_EXISTS( nmmap->axis[mapAxis].min ) &&
AIR_EXISTS( nmmap->axis[mapAxis].max ) ) ) {
rescale = AIR_TRUE;
}
if ( rescale ) {
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nmmap->type;
}
if ( nrrdApplyMulti1DRegMap( nout, nin, range, nmmap, typeOut, rescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying map:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
149 UNRRDU_CMD( mrmap, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Pad along each axis to make a bigger nrrd"
static const char *_unrrdu_padInfoL =
( INFO ".\n "
"* Uses nrrdPad_nva" );
int
33 unrrdu_padMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int ai;
int minLen, maxLen, bb, pret;
long int *minOff, *maxOff;
ptrdiff_t min[NRRD_DIM_MAX], max[NRRD_DIM_MAX];
double padVal;
airArray *mop;
OPT_ADD_BOUND( "min, minimum", 1, minOff, NULL,
"low corner of bounding box.\n "
"\b\bo <int> gives 0-based index\n "
"\b\bo M, M+<int>, M-<int> give index relative "
"to the last sample on the axis ( M == #samples-1 ).",
minLen );
OPT_ADD_BOUND( "max, maximum", 1, maxOff, NULL,
"high corner of bounding box. "
"Besides the specification styles described above, "
"there's also:\n "
"\b\bo m+<int> give index relative to minimum.",
maxLen );
hestOptAdd( &opt, "b, boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed",
"How to handle samples beyond the input bounds:\n "
"\b\bo \"pad\": use some specified value\n "
"\b\bo \"bleed\": extend border values outward\n "
"\b\bo \"mirror\": repeated reflections\n "
"\b\bo \"wrap\": wrap-around to other side",
NULL, nrrdBoundary );
hestOptAdd( &opt, "v, value", "val", airTypeDouble, 1, 1, &padVal, "0.0",
"for \"pad\" boundary behavior, pad with this value" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_padInfoL );
/* hammerhead problems were here */
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !( minLen == ( int )nin->dim && maxLen == ( int )nin->dim ) ) {
fprintf( stderr,
"%s: # min coords ( %d ) or max coords ( %d ) != nrrd dim ( %d )\n",
me, minLen, maxLen, nin->dim );
airMopError( mop );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
if ( -1 == minOff[0 + 2*ai] ) {
fprintf( stderr, "%s: can't use m+<int> specification for axis %d min\n",
me, ai );
airMopError( mop );
return 1;
}
}
for ( ai=0; ai<nin->dim; ai++ ) {
min[ai] = minOff[0 + 2*ai]*( nin->axis[ai].size-1 ) + minOff[1 + 2*ai];
if ( -1 == maxOff[0 + 2*ai] ) {
max[ai] = min[ai] + maxOff[1 + 2*ai];
} else {
max[ai] = maxOff[0 + 2*ai]*( nin->axis[ai].size-1 ) + maxOff[1 + 2*ai];
}
/*
fprintf( stderr, "%s: ai %2d: min = %4d, max = %4d\n",
me, ai, min[ai], mai[ai] );
*/
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdPad_nva( nout, nin, min, max, bb, padVal ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error padding nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
121 UNRRDU_CMD( pad, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Permute ordering of axes"
static const char *_unrrdu_permuteInfoL =
( INFO
". The permutation gives the new ordering of the old "
"axes ( in 0-based numbering ). For example, the "
"permutation 0->1, \t1->2, \t2->0 would be \"2 0 1\".\n "
"* Uses nrrdAxesPermute" );
int
36 unrrdu_permuteMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int *perm, permLen;
int pret;
airArray *mop;
hestOptAdd( &opt, "p, permute", "ax0 ax1", airTypeUInt, 1, -1, &perm, NULL,
"new axis ordering", &permLen );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_permuteInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( !( permLen == nin->dim ) ) {
fprintf( stderr, "%s: # axes in permutation ( %u ) != nrrd dim ( %d )\n",
me, permLen, nin->dim );
airMopError( mop );
return 1;
}
if ( nrrdAxesPermute( nout, nin, perm ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error permuting nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
80 UNRRDU_CMD( permute, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Collapse scanlines to scalars along some axis"
static const char *_unrrdu_projectInfoL =
( INFO
". The scanline is reduced to a single scalar by "
"\"measuring\" all the values in the scanline "
"with some measure. The output nrrd has dimension "
"one less than input ( except when the input is itself 1-D ); "
"the output type depends on "
"the measure in a non-trivial way, or it can be set explicitly "
"with the \"-t\" option. To save the overhead of multiple input data "
"reads if projections along different axes are needed, you can give "
"multiple axes to \"-a\" ( and a matching number of output filenames "
"to \"-o\" ), as well as multiple measures to \"-m\" ( and possibly a "
"specific type to \"-t\" to permit their joining on fastest axis ).\n "
"* Uses nrrdProject, and nrrdJoin if multiple measures" );
int
44 unrrdu_projectMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char **out, *err;
Nrrd *nin, *nout;
Nrrd **nslice;
unsigned int *axis, axisLen, outLen, measrLen, outIdx, measrIdx;
int *measr, pret, type;
airArray *mop;
hestOptAdd( &opt, "a, axis", "axis", airTypeUInt, 1, -1, &axis, NULL,
"axis or axes to project along", &axisLen );
hestOptAdd( &opt, "m, measure", "measr", airTypeEnum, 1, -1, &measr, NULL,
"How to \"measure\" a scanline, by summarizing all its values "
"with a single scalar. Multiple measures will be joined along "
"fastest axis if output, but you may need to set output type "
"explicitly via \"-t\" so that the join works. "
NRRD_MEASURE_DESC, &measrLen, nrrdMeasure );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to use for output. By default ( not using this option ), "
"the output type is determined auto-magically",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
hestOptAdd( &opt, "o, output", "nout", airTypeString, 1, -1, &out, "-",
"one or more output nrrd filenames. Number of names here "
"has to match number of axes specified.", &outLen );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_projectInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( axisLen != outLen ) {
fprintf( stderr, "%s: got %u \"-a\" axes but %u \"-o\" outputs\n", me,
axisLen, outLen );
airMopError( mop );
return 1;
}
if ( measrLen > 1 ) {
nslice = AIR_CALLOC( measrLen, Nrrd * );
airMopAdd( mop, nslice, airFree, airMopAlways );
for ( measrIdx=0; measrIdx<measrLen; measrIdx++ ) {
nslice[measrIdx] = nrrdNew( );
airMopAdd( mop, nslice[measrIdx], ( airMopper )nrrdNuke, airMopAlways );
}
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
for ( outIdx=0; outIdx<outLen; outIdx++ ) {
if ( measrLen > 1 ) {
/* first project into slices */
for ( measrIdx=0; measrIdx<measrLen; measrIdx++ ) {
if ( nrrdProject( nslice[measrIdx], nin, axis[outIdx],
measr[measrIdx], type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error projecting nrrd %u/%u:\n%s",
me, outIdx, measrIdx, err );
airMopError( mop );
return 1;
}
}
/* then join slices into output */
if ( nrrdJoin( nout, ( const Nrrd *const* )nslice, measrLen, 0, AIR_TRUE ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error joining nrrd %u; will have to use \"-t\" "
"option to make sure all projections have same type:\n%s",
me, outIdx, err );
airMopError( mop );
return 1;
}
} else {
if ( nrrdProject( nout, nin, axis[outIdx], measr[0], type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error projecting nrrd %u:\n%s", me, outIdx, err );
airMopError( mop );
return 1;
}
}
SAVE( out[outIdx], nout, NULL );
}
airMopOkay( mop );
return 0;
}
132 UNRRDU_CMD( project, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Quantize values to 8, 16, or 32 bits"
static const char *_unrrdu_quantizeInfoL =
( INFO ". Input values can be fixed point ( e.g. quantizing ushorts down to "
"uchars ) or floating point. Values are clamped to the min and max before "
"they are quantized, so there is no risk of getting 255 where you expect 0 "
"( with unsigned char output, for example ). The min and max can be specified "
"explicitly ( as a regular number ), or in terms of percentiles ( a number "
"suffixed with \"" NRRD_MINMAX_PERC_SUFF "\", no space in between ). "
"This does only linear quantization. "
"See also \"unu convert\", \"unu 2op x\", "
"and \"unu 3op clamp\".\n "
"* Uses nrrdQuantize" );
int
41 unrrdu_quantizeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
char *minStr, *maxStr;
int pret, blind8BitRange;
unsigned int bits, hbins;
double gamma;
NrrdRange *range;
airArray *mop;
hestOptAdd( &opt, "b, bits", "bits", airTypeOther, 1, 1, &bits, NULL,
"Number of bits to quantize down to; determines the type "
"of the output nrrd:\n "
"\b\bo \"8\": unsigned char\n "
"\b\bo \"16\": unsigned short\n "
"\b\bo \"32\": unsigned int",
NULL, NULL, &unrrduHestBitsCB );
hestOptAdd( &opt, "min, minimum", "value", airTypeString, 1, 1,
&minStr, "nan",
"The value to map to zero, given explicitly as a regular number, "
"*or*, if the number is given with a \"" NRRD_MINMAX_PERC_SUFF
"\" suffix, this "
"minimum is specified in terms of the percentage of samples in "
"input that are lower. "
"\"0" NRRD_MINMAX_PERC_SUFF "\" means the "
"lowest input value is used, "
"\"1" NRRD_MINMAX_PERC_SUFF "\" means that the "
"1% of the lowest values are all mapped to zero. "
"By default ( not using this option ), the lowest input value is "
"used." );
hestOptAdd( &opt, "max, maximum", "value", airTypeString, 1, 1,
&maxStr, "nan",
"The value to map to the highest unsigned integral value, given "
"explicitly as a regular number, "
"*or*, if the number is given with "
"a \"" NRRD_MINMAX_PERC_SUFF "\" suffix, "
"this maximum is specified "
"in terms of the percentage of samples in input that are higher. "
"\"0" NRRD_MINMAX_PERC_SUFF "\" means the highest input value is "
"used, which is also the default "
"behavior ( same as not using this option )." );
hestOptAdd( &opt, "g, gamma", "gamma", airTypeDouble, 1, 1, &gamma, "1.0",
"gamma > 1.0 brightens; gamma < 1.0 darkens. "
"Negative gammas invert values. " );
hestOptAdd( &opt, "hb, bins", "bins", airTypeUInt, 1, 1, &hbins, "5000",
"number of bins in histogram of values, for determining min "
"or max by percentiles. This has to be large enough so that "
"any errant very high or very low values do not compress the "
"interesting part of the histogram to an inscrutably small "
"number of bins." );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"if not using \"-min\" or \"-max\", whether to know "
"the range of 8-bit data blindly ( uchar is always [0, 255], "
"signed char is [-128, 127] )" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_quantizeInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
range = nrrdRangeNew( AIR_NAN, AIR_NAN );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdRangePercentileFromStringSet( range, nin, minStr, maxStr,
hbins, blind8BitRange )
|| ( 1 == gamma ? 0
: nrrdArithGamma( nin, nin, range, gamma ) )
|| nrrdQuantize( nout, nin, range, bits ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with range%s quantizing:\n%s", me,
( 1 == gamma ? " or" : ", gamma, or" ), err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
130 UNRRDU_CMD( quantize, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Filtering and {up, down}sampling with a separable kernel"
static const char *_unrrdu_resampleInfoL =
( INFO
". Simplifies access to the NrrdResampleContext functions "
"by assuming ( among other things ) that the same kernel "
"is used for resampling "
"every axis that is being resampled. Only required option is "
"\"-s\" to specify which axes to resample and how many "
"output samples to generate. Resampling kernel \"-k\" defaults "
"to an interpolating cubic, but many other choices are available. "
"By default, resampling an axis resamples the full extent of its "
"samples, but it is possible to offset this range via \"-off\", "
"or to crop and/or pad via \"-min\" and \"-max\". "
"The resampling respects the difference between cell- and "
"node-centered data, but you can over-ride known centering "
"with \"-co\".\n "
"* Uses the many nrrdResample* functions operating on a nrrdResampleContext" );
int
46 unrrdu_resampleMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int type, bb, pret, norenorm, neb, older, E, defaultCenter,
verbose, overrideCenter, minSet=AIR_FALSE, maxSet=AIR_FALSE,
offSet=AIR_FALSE;
unsigned int scaleLen, ai, samplesOut, minLen, maxLen, offLen,
aspRatNum, nonAspRatNum;
airArray *mop;
double *scale;
double padVal, *min, *max, *off, aspRatScl=AIR_NAN;
NrrdResampleInfo *info;
NrrdResampleContext *rsmc;
NrrdKernelSpec *unuk;
mop = airMopNew( );
info = nrrdResampleInfoNew( );
airMopAdd( mop, info, ( airMopper )nrrdResampleInfoNix, airMopAlways );
hparm->elideSingleOtherDefault = AIR_FALSE;
hestOptAdd( &opt, "old", NULL, airTypeInt, 0, 0, &older, NULL,
"instead of using the new nrrdResampleContext implementation, "
"use the old nrrdSpatialResample implementation" );
hestOptAdd( &opt, "s, size", "sz0", airTypeOther, 1, -1, &scale, NULL,
"For each axis, information about how many samples in output:\n "
"\b\bo \"=\": leave this axis completely untouched: no "
"resampling whatsoever\n "
"\b\bo \"x<float>\": multiply the number of input samples by "
"<float>, and round to the nearest integer, to get the number "
"of output samples. Use \"x1\" to resample the axis but leave "
"the number of samples unchanged\n "
"\b\bo \"/<float>\": divide number of samples by <float>\n "
"\b\bo \"+=<uint>\", \"-=<uint>\": add <uint> to or subtract "
"<uint> from number input samples to get number output samples\n "
"\b\bo \"s<float>\": assuming that some spacing information is "
"known on this input axis, then set the number of sample so that "
"the given float is the output axis spacing\n "
"\b\bo \"<uint>\": exact number of output samples\n "
"\b\bo \"a\": resample this axis to whatever number of samples "
"preserves the aspect ratio of other resampled axes. Currently "
"needs to be used on all but one of the resampled axes, "
"if at all. ",
&scaleLen, NULL, &unrrduHestScaleCB );
hestOptAdd( &opt, "off, offset", "off0", airTypeDouble, 0, -1, &off, "",
"For each axis, an offset or shift to the position ( in index "
"space ) of the lower end of the sampling domain. "
"Either -off can be used, or -min and -max "
"together, or none of these ( so that, by default, the full "
"domain of the axis is resampled ).", &offLen );
hestOptAdd( &opt, "min, minimum", "min0", airTypeDouble, 0, -1, &min, "",
"For each axis, the lower end ( in index space ) of the domain "
"of the resampling. Either -off can be used, or -min and -max "
"together, or none of these ( so that, by default, the full "
"domain of the axis is resampled ).", &minLen );
hestOptAdd( &opt, "max, maximum", "max0", airTypeDouble, 0, -1, &max, "",
"For each axis, the upper end ( in index space ) of the domain "
"of the resampling. Either -off can be used, or -min and -max "
"together, or none of these, so that ( by default ), the full "
"domain of the axis is resampled.", &maxLen );
hestOptAdd( &opt, "k, kernel", "kern", airTypeOther, 1, 1, &unuk,
"cubic:0, 0.5",
"The kernel to use for resampling. "
"Kernels logically live in the input index space for upsampling, "
"and in the output index space for downsampling. "
"Possibilities include:\n "
"\b\bo \"box\": nearest neighbor interpolation on upsampling, "
"and uniform averaging on downsampling\n "
"\b\bo \"cheap\": nearest neighbor interpolation for upsampling, "
"and non-blurring sub-sampling ( pick subset of input samples ) "
"on downsampling\n "
"\b\bo \"tent\": linear interpolation\n "
"\b\bo \"cubic:B, C\": Mitchell/Netravali BC-family of "
"cubics:\n "
"\t\t\"cubic:1, 0\": B-spline; maximal blurring\n "
"\t\t\"cubic:0, 0.5\": Catmull-Rom; good interpolating kernel\n "
"\b\bo \"c4h\": 6-sample-support, C^4 continuous, accurate\n "
"\b\bo \"c4hai\": discrete pre-filter to make c4h interpolate\n "
"\b\bo \"bspl3\", \"bspl5\", \"bspl7\": cubic ( same as cubic:1, 0 ), "
"quintic, and 7th order B-spline\n "
"\b\bo \"bspl3ai\", \"bspl5ai\", \"bspl7ai\": discrete pre-filters to make "
"bspl3, bspl5, bspl7 interpolate\n "
"\b\bo \"hann:R\": Hann ( cosine bell ) windowed sinc, radius R\n "
"\b\bo \"black:R\": Blackman windowed sinc, radius R\n "
"\b\bo \"gauss:S, C\": Gaussian blurring, with standard deviation "
"S and cut-off at C standard deviations\n "
"\b\bo \"dgauss:S, C\": Lindeberg's discrete Gaussian.",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "nrn", NULL, airTypeInt, 0, 0, &norenorm, NULL,
"do NOT do per-pass kernel weight renormalization. "
"Doing the renormalization is not a performance hit ( hence is "
"enabled by default ), and the renormalization is sometimes "
"needed to avoid \"grating\" on non-integral "
"down-sampling. Disabling the renormalization is needed for "
"correct results with artificially narrow kernels. " );
hestOptAdd( &opt, "ne, nonexistent", "behavior", airTypeEnum, 1, 1,
&neb, "noop",
"When resampling floating-point values, how to handle "
"non-existent values within kernel support:\n "
"\b\bo \"noop\": do nothing; let them pollute result\n "
"\b\bo \"renorm\": ignore them and renormalize weights of "
"existent values\n "
"\b\bo \"wght\": ignore them and simply use weights of "
"existent values",
NULL, nrrdResampleNonExistent );
hestOptAdd( &opt, "b, boundary", "behavior", airTypeEnum, 1, 1, &bb, "bleed",
"How to handle samples beyond the input bounds:\n "
"\b\bo \"pad\": use some specified value\n "
"\b\bo \"bleed\": extend border values outward\n "
"\b\bo \"mirror\": repeated reflections\n "
"\b\bo \"wrap\": wrap-around to other side",
NULL, nrrdBoundary );
hestOptAdd( &opt, "v, value", "value", airTypeDouble, 1, 1, &padVal, "0.0",
"for \"pad\" boundary behavior, pad with this value" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &type, "default",
"type to save OUTPUT as. By default ( not using this option ), "
"the output type is the same as the input type",
NULL, NULL, &unrrduHestMaybeTypeCB );
hestOptAdd( &opt, "cheap", NULL, airTypeInt, 0, 0, &( info->cheap ), NULL,
"[DEPRECATED: the \"-k cheap\" option is the new ( and more "
"reliable ) way to access this functionality. \"-cheap\" is "
"only here for legacy use in combination with \"-old\".]\n "
"When downsampling ( reducing number of samples ), don't "
"try to do correct filtering by scaling kernel to match "
"new ( stretched ) index space; keep it in old index space. "
"When used in conjunction with \"-k box\", this can implement "
"subsampling which chooses every Nth value. " );
hestOptAdd( &opt, "c, center", "center", airTypeEnum, 1, 1, &defaultCenter,
( nrrdCenterCell == nrrdDefaultCenter
? "cell"
: "node" ),
"( not available with \"-old\" ) "
"default centering of axes when input nrrd "
"axes don't have a known centering: \"cell\" or \"node\" ",
NULL, nrrdCenter );
hestOptAdd( &opt, "co, center-override", NULL, airTypeInt, 0, 0,
&overrideCenter, NULL,
"( not available with \"-old\" ) "
"centering info specified via \"-c\" should *over-ride* "
"known centering, rather than simply be used when centering "
"is unknown." );
hestOptAdd( &opt, "verbose", "v", airTypeInt, 1, 1, &verbose, "0",
"( not available with \"-old\" ) verbosity level" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_resampleInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( scaleLen != nin->dim ) {
fprintf( stderr, "%s: # sampling sizes %d != input nrrd dimension %d\n",
me, scaleLen, nin->dim );
airMopError( mop );
return 1;
}
if ( !older ) {
if ( offLen >= 1 ) {
/* seems to want to set off[] */
if ( offLen != scaleLen ) {
fprintf( stderr, "%s: offLen %u != scaleLen %u\n", me,
offLen, scaleLen );
airMopError( mop );
return 1;
}
for ( ai=0; ai<offLen; ai++ ) {
if ( unrrduScaleNothing != ( int )( scale[0 + 2*ai] )
&& !AIR_EXISTS( off[ai] ) ) {
fprintf( stderr, "%s: off[%u] %g doesn't exist\n", me,
ai, off[ai] );
airMopError( mop );
return 1;
}
}
offSet = AIR_TRUE;
} else {
offSet = AIR_FALSE;
}
if ( minLen >= 1 && AIR_EXISTS( min[0] ) ) { /* HEY copy and paste */
/* seems to want to set min[] */
if ( minLen != scaleLen ) {
fprintf( stderr, "%s: minLen %u != scaleLen %u\n", me,
minLen, scaleLen );
airMopError( mop );
return 1;
}
for ( ai=0; ai<minLen; ai++ ) {
if ( unrrduScaleNothing != ( int )( scale[0 + 2*ai] )
&& !AIR_EXISTS( min[ai] ) ) {
fprintf( stderr, "%s: min[%u] %g doesn't exist\n", me,
ai, min[ai] );
airMopError( mop );
return 1;
}
}
minSet = AIR_TRUE;
} else {
minSet = AIR_FALSE;
}
if ( maxLen >= 1 && AIR_EXISTS( max[0] ) ) { /* HEY copy and paste */
/* seems to want to set max[] */
if ( maxLen != scaleLen ) {
fprintf( stderr, "%s: maxLen %u != scaleLen %u\n", me,
maxLen, scaleLen );
airMopError( mop );
return 1;
}
for ( ai=0; ai<maxLen; ai++ ) {
if ( unrrduScaleNothing != ( int )( scale[0 + 2*ai] )
&& !AIR_EXISTS( max[ai] ) ) {
fprintf( stderr, "%s: max[%u] %g doesn't exist\n", me,
ai, max[ai] );
airMopError( mop );
return 1;
}
}
maxSet = AIR_TRUE;
} else {
maxSet = AIR_FALSE;
}
if ( !( ( minSet && maxSet ) || ( !minSet && !maxSet ) ) ) {
fprintf( stderr, "%s: need -min and -max to be set consistently\n", me );
airMopError( mop );
return 1;
}
if ( minSet && offSet ) {
fprintf( stderr, "%s: can't use -off with -min and -max\n", me );
airMopError( mop );
return 1;
}
aspRatNum = nonAspRatNum = 0;
for ( ai=0; ai<nin->dim; ai++ ) {
int dowhat = AIR_CAST( int, scale[0 + 2*ai] );
if ( !( unrrduScaleNothing == dowhat ) ) {
if ( unrrduScaleAspectRatio == dowhat ) {
aspRatNum++;
} else {
nonAspRatNum++;
}
}
}
if ( aspRatNum ) {
if ( 1 != nonAspRatNum ) {
fprintf( stderr, "%s: sorry, aspect-ratio-preserving "
"resampling must currently be used on all but one "
"( not %u ) resampled axis, if any\n", me,
nonAspRatNum );
airMopError( mop );
return 1;
}
}
rsmc = nrrdResampleContextNew( );
rsmc->verbose = verbose;
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
E = AIR_FALSE;
if ( !E ) E |= nrrdResampleDefaultCenterSet( rsmc, defaultCenter );
if ( !E ) E |= nrrdResampleInputSet( rsmc, nin );
for ( ai=0; ai<nin->dim; ai++ ) {
double spin, spout, svec[NRRD_SPACE_DIM_MAX];
int spstat;
int dowhat = AIR_CAST( int, scale[0 + 2*ai] );
switch( dowhat ) {
case unrrduScaleNothing:
/* no resampling */
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, NULL, NULL );
break;
case unrrduScaleMultiply:
case unrrduScaleDivide:
case unrrduScaleAdd:
case unrrduScaleSubtract:
/* scaling of input # samples */
if ( defaultCenter && overrideCenter ) {
if ( !E ) E |= nrrdResampleOverrideCenterSet( rsmc, ai, defaultCenter );
}
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, unuk->kernel, unuk->parm );
switch( dowhat ) {
unsigned int incr;
char stmp[AIR_STRLEN_SMALL];
case unrrduScaleMultiply:
samplesOut = AIR_ROUNDUP( nin->axis[ai].size*scale[1 + 2*ai] );
break;
case unrrduScaleDivide:
samplesOut = AIR_ROUNDUP( nin->axis[ai].size/scale[1 + 2*ai] );
break;
case unrrduScaleAdd:
samplesOut = nin->axis[ai].size + AIR_CAST( unsigned int, scale[1 + 2*ai] );
break;
case unrrduScaleSubtract:
incr = AIR_CAST( unsigned int, scale[1 + 2*ai] );
if ( nin->axis[ai].size - 1 < incr ) {
fprintf( stderr, "%s: can't subtract %u from axis size %s\n",
me, incr, airSprintSize_t( stmp, nin->axis[ai].size ) );
airMopError( mop );
return 1;
}
samplesOut = nin->axis[ai].size - incr;
break;
}
aspRatScl = AIR_CAST( double, samplesOut )/nin->axis[ai].size;
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, samplesOut );
break;
case unrrduScaleSpacingTarget:
/* wants the output spacing to be something particular */
spstat = nrrdSpacingCalculate( nin, ai, &spin, svec );
spout = scale[1 + 2*ai];
switch ( spstat ) {
case nrrdSpacingStatusUnknown:
case nrrdSpacingStatusNone:
fprintf( stderr, "%s: want to set output axis %u spacing ( to %g ), "
"but can't find input axis spacing\n",
me, ai, spout );
airMopError( mop );
return 1;
break;
case nrrdSpacingStatusScalarNoSpace:
case nrrdSpacingStatusScalarWithSpace:
case nrrdSpacingStatusDirection:
if ( !AIR_EXISTS( spin ) ) {
fprintf( stderr, "%s: want to set output axis %u spacing ( to %g ), "
"but can't input axis spacing was %g\n",
me, ai, spout, spin );
airMopError( mop );
return 1;
}
samplesOut = AIR_ROUNDUP( nin->axis[ai].size*spin/spout );
break;
}
aspRatScl = AIR_CAST( double, samplesOut )/nin->axis[ai].size;
if ( defaultCenter && overrideCenter ) {
if ( !E ) E |= nrrdResampleOverrideCenterSet( rsmc, ai, defaultCenter );
}
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, unuk->kernel, unuk->parm );
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, samplesOut );
break;
case unrrduScaleExact:
/* explicit # of samples */
if ( defaultCenter && overrideCenter ) {
if ( !E ) E |= nrrdResampleOverrideCenterSet( rsmc, ai, defaultCenter );
}
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, unuk->kernel, unuk->parm );
samplesOut = ( size_t )scale[1 + 2*ai];
aspRatScl = AIR_CAST( double, samplesOut )/nin->axis[ai].size;
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, samplesOut );
break;
case unrrduScaleAspectRatio:
/* wants aspect-ratio preserving, but may not know # samples yet */
if ( defaultCenter && overrideCenter ) {
if ( !E ) E |= nrrdResampleOverrideCenterSet( rsmc, ai, defaultCenter );
}
if ( !E ) E |= nrrdResampleKernelSet( rsmc, ai, unuk->kernel, unuk->parm );
/* will set samples later, after aspRatScl has been set */
break;
default:
fprintf( stderr, "%s: sorry, unrecognized unrrduScale value %d\n",
me, dowhat );
airMopError( mop );
return 1;
}
if ( minSet && maxSet ) {
if ( !E ) E |= nrrdResampleRangeSet( rsmc, ai, min[ai], max[ai] );
} else {
if ( !E ) E |= nrrdResampleRangeFullSet( rsmc, ai );
if ( offSet ) {
/* HEY: this is a hack; We're reading out the information from
determined by nrrdResampleRangeFullSet, and benefitting from
the fact that it set one of the flags that are processed by
nrrdResampleExecute( ) */
rsmc->axis[ai].min += off[ai];
rsmc->axis[ai].max += off[ai];
}
}
}
if ( !E && aspRatNum ) {
if ( !AIR_EXISTS( aspRatScl ) ) {
fprintf( stderr, "%s: confusion, should have learned scaling "
"of aspect-ratio-preserving resampling by now", me );
airMopError( mop );
return 1;
}
for ( ai=0; ai<nin->dim; ai++ ) {
int dowhat = AIR_CAST( int, scale[0 + 2*ai] );
if ( unrrduScaleAspectRatio == dowhat ) {
samplesOut = AIR_ROUNDUP( nin->axis[ai].size*aspRatScl );
if ( !E ) E |= nrrdResampleSamplesSet( rsmc, ai, samplesOut );
}
}
}
if ( !E ) E |= nrrdResampleBoundarySet( rsmc, bb );
if ( !E ) E |= nrrdResampleTypeOutSet( rsmc, type );
if ( !E ) E |= nrrdResamplePadValueSet( rsmc, padVal );
if ( !E ) E |= nrrdResampleRenormalizeSet( rsmc, !norenorm );
if ( !E ) E |= nrrdResampleNonExistentSet( rsmc, neb );
if ( !E ) E |= nrrdResampleExecute( rsmc, nout );
if ( E ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error resampling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
} else {
for ( ai=0; ai<nin->dim; ai++ ) {
int dowhat = AIR_CAST( int, scale[0 + 2*ai] );
/* this may be over-written below */
info->kernel[ai] = unuk->kernel;
switch( dowhat ) {
case unrrduScaleNothing:
/* no resampling */
info->kernel[ai] = NULL;
break;
case unrrduScaleMultiply:
/* scaling of input # samples */
info->samples[ai] = AIR_ROUNDUP( scale[1 + 2*ai]*nin->axis[ai].size );
break;
case unrrduScaleExact:
/* explicit # of samples */
info->samples[ai] = ( size_t )scale[1 + 2*ai];
break;
default:
fprintf( stderr, "%s: sorry, unrecognized unrrduScale value %d\n",
me, dowhat );
airMopError( mop );
return 1;
}
memcpy( info->parm[ai], unuk->parm,
NRRD_KERNEL_PARMS_NUM*sizeof( *unuk->parm ) );
if ( info->kernel[ai] &&
( !( AIR_EXISTS( nin->axis[ai].min )
&& AIR_EXISTS( nin->axis[ai].max ) ) ) ) {
nrrdAxisInfoMinMaxSet( nin, ai,
( nin->axis[ai].center
? nin->axis[ai].center
: nrrdDefaultCenter ) );
}
info->min[ai] = nin->axis[ai].min;
info->max[ai] = nin->axis[ai].max;
}
info->boundary = bb;
info->type = type;
info->padValue = padVal;
info->renormalize = !norenorm;
if ( nrrdSpatialResample( nout, nin, info ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error resampling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
507 UNRRDU_CMD( resample, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Superficially change dimension and/or axes sizes"
static const char *_unrrdu_reshapeInfoL =
( INFO
". The underlying linear ordering of the samples is "
"unchanged, but the reported dimension or axes sizes "
"are changed. Identical in concept to Matlab's "
"\"reshape\" command.\n "
"* Uses nrrdReshape_nva" );
int
37 unrrdu_reshapeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
size_t *size;
unsigned int sizeLen;
airArray *mop;
hestOptAdd( &opt, "s, size", "sz0 sz1 ", airTypeSize_t, 1, -1, &size, NULL,
"new axes sizes", &sizeLen );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_reshapeInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdReshape_nva( nout, nin, sizeLen, size ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error reshaping nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
75 UNRRDU_CMD( reshape, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through one *regular* univariate map ( \"colormap\" )"
static const char *_unrrdu_rmapInfoL =
( INFO
". A map is regular if the control points are evenly "
"spaced along the domain, and hence their position isn't "
"explicitly represented in the map; the axis min, axis "
"max, and number of points determine their location. "
"The map can be a 1D nrrd ( for \"grayscale\" ), "
"in which case the "
"output has the same dimension as the input, "
"or a 2D nrrd ( for \"color\" ), in which case "
"the output has one more dimension than the input. In "
"either case, the output is the result of linearly "
"interpolating between map points, either scalar values "
"( \"grayscale\" ), or scanlines along axis 0 "
"( \"color\" ).\n "
"* Uses nrrdApply1DRegMap" );
int
46 unrrdu_rmapMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nmap, *nout;
airArray *mop;
NrrdRange *range=NULL;
int typeOut, rescale, pret, blind8BitRange;
double min, max;
hestOptAdd( &opt, "m, map", "map", airTypeOther, 1, 1, &nmap, NULL,
"regular map to map input nrrd through",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "r, rescale", NULL, airTypeInt, 0, 0, &rescale, NULL,
"rescale the input values from the input range to the "
"map domain. The map domain is either explicitly "
"defined by the axis min, max along axis 0 or 1, or, it "
"is implicitly defined as zero to the length of "
"that axis minus one." );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &min, "nan",
"Low end of input range. Defaults to lowest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &max, "nan",
"High end of input range. Defaults to highest value "
"found in input nrrd. Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "blind8", "bool", airTypeBool, 1, 1, &blind8BitRange,
nrrdStateBlind8BitRange ? "true" : "false",
"Whether to know the range of 8-bit data blindly "
"( uchar is always [0, 255], signed char is [-128, 127] ). "
"Explicitly setting this is useful "
"only with rescaling ( \"-r\" ) or if the map domain is only "
"implicitly defined" );
hestOptAdd( &opt, "t, type", "type", airTypeOther, 1, 1, &typeOut, "default",
"specify the type ( \"int\", \"float\", etc. ) of the "
"output nrrd. "
"By default ( not using this option ), the output type "
"is the map's type.",
NULL, NULL, &unrrduHestMaybeTypeCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_rmapInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* here is a big difference between unu and nrrd: we enforce
rescaling any time that the map domain is implicit. This
is how the pre-1.6 functionality is recreated. Also, whenever
there is rescaling we pass a NrrdRange to reflect the ( optional )
user range specification, instead of letting nrrdApply1DRegMap
find the input range itself ( by passing a NULL NrrdRange ).
*/
if ( !( AIR_EXISTS( nmap->axis[nmap->dim - 1].min ) &&
AIR_EXISTS( nmap->axis[nmap->dim - 1].max ) ) ) {
rescale = AIR_TRUE;
}
if ( rescale ) {
range = nrrdRangeNew( min, max );
airMopAdd( mop, range, ( airMopper )nrrdRangeNix, airMopAlways );
nrrdRangeSafeSet( range, nin, blind8BitRange );
}
if ( nrrdTypeDefault == typeOut ) {
typeOut = nmap->type;
}
if ( nrrdApply1DRegMap( nout, nin, range, nmap, typeOut, rescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying map:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
134 UNRRDU_CMD( rmap, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Write nrrd with specific format, encoding, or endianness"
static const char *_unrrdu_saveInfoL =
( INFO
". Use \"unu\tsave\t-f\tpnm\t|\txv\t-\" to view PPM- or "
"PGM-compatible nrrds on unix. EPS output is a EPSF-3.0 file with "
"BoundingBox and HiResBoundingBox DSC comments, and is suitable for "
"inclusion into other PostScript documents. As a stand-alone file, the "
"image is conveniently centered on an 8.5x11 inch page, with 0.5 "
"inch margins.\n "
"* Uses various fields in the NrrdIOState passed to nrrdSave" );
int
39 unrrdu_saveMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err, *outData,
encInfo[AIR_STRLEN_HUGE], fmtInfo[AIR_STRLEN_HUGE];
Nrrd *nin, *nout;
airArray *mop;
NrrdIoState *nio;
int pret, enc[3], formatType;
mop = airMopNew( );
nio = nrrdIoStateNew( );
airMopAdd( mop, nio, ( airMopper )nrrdIoStateNix, airMopAlways );
strcpy( fmtInfo,
"output file format. Possibilities include:\n "
"\b\bo \"nrrd\": standard nrrd format\n "
"\b\bo \"pnm\": PNM image; PPM for color, PGM for grayscale\n "
"\b\bo \"text\": plain ASCII text for 1-D and 2-D data\n "
"\b\bo \"vtk\": VTK \"STRUCTURED_POINTS\" dataset" );
if ( nrrdFormatPNG->available( ) ) {
strcat( fmtInfo,
"\n \b\bo \"png\": PNG image" );
}
strcat( fmtInfo,
"\n \b\bo \"eps\": EPS file" );
hestOptAdd( &opt, "f, format", "form", airTypeEnum, 1, 1, &formatType, NULL,
fmtInfo, NULL, nrrdFormatType );
strcpy( encInfo,
"encoding of data in file. Not all encodings are supported in "
"a given format. Possibilities include:"
"\n \b\bo \"raw\": raw encoding"
"\n \b\bo \"ascii\": print data in ascii"
"\n \b\bo \"hex\": two hex digits per byte" );
if ( nrrdEncodingGzip->available( ) ) {
strcat( encInfo,
"\n \b\bo \"gzip\", \"gz\": gzip compressed raw data" );
}
if ( nrrdEncodingBzip2->available( ) ) {
strcat( encInfo,
"\n \b\bo \"bzip2\", \"bz2\": bzip2 compressed raw data" );
}
if ( nrrdEncodingGzip->available( ) || nrrdEncodingBzip2->available( ) ) {
strcat( encInfo,
"\n The specifiers for compressions may be followed by a colon "
"\":\", followed by an optional digit giving compression \"level\" "
"( for gzip ) or \"block size\" ( for bzip2 ). For gzip, this can be "
"followed by an optional character for a compression strategy:\n "
"\b\bo \"d\": default, Huffman with string match\n "
"\b\bo \"h\": Huffman alone\n "
"\b\bo \"f\": specialized for filtered data\n "
"For example, \"gz\", \"gz:9\", \"gz:9f\" are all valid" );
}
hestOptAdd( &opt, "e, encoding", "enc", airTypeOther, 1, 1, enc, "raw",
encInfo, NULL, NULL, &unrrduHestEncodingCB );
hestOptAdd( &opt, "en, endian", "end", airTypeEnum, 1, 1, &( nio->endian ),
airEnumStr( airEndian, airMyEndian( ) ),
"Endianness to save data out as; \"little\" for Intel and "
"friends; \"big\" for everyone else. "
"Defaults to endianness of this machine",
NULL, airEndian );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
hestOptAdd( &opt, "od, ouputdata", "name", airTypeString, 1, 1, &outData, "",
"when saving to a \".nhdr\" file, "
"this option allows you to explicitly name the data file, "
"instead of ( by default, not using this option ) having it be "
"the same filename base as the header file." );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_saveInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
nrrdCopy( nout, nin );
nio->encoding = nrrdEncodingArray[enc[0]];
nio->format = nrrdFormatArray[formatType];
if ( nrrdEncodingTypeGzip == enc[0] ) {
nio->zlibLevel = enc[1];
nio->zlibStrategy = enc[2];
} else if ( nrrdEncodingTypeBzip2 == enc[0] ) {
nio->bzip2BlockSize = enc[1];
}
if ( airMyEndian( ) != nio->endian ) {
nrrdSwapEndian( nout );
}
if ( airEndsWith( out, NRRD_EXT_NHDR ) ) {
if ( nio->format != nrrdFormatNRRD ) {
fprintf( stderr, "%s: WARNING: will use %s format\n", me,
nrrdFormatNRRD->name );
nio->format = nrrdFormatNRRD;
}
if ( strlen( outData ) ) {
airArrayLenSet( nio->dataFNArr, 1 );
nio->dataFN[0] = airStrdup( outData );
}
}
SAVE( out, nout, nio );
airMopOkay( mop );
return 0;
}
148 UNRRDU_CMD( save, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Permute slices along one axis"
static const char *_unrrdu_shuffleInfoL =
( INFO
". Slices along one axis are re-arranged as units "
"according to the given permutation ( or its inverse ). "
"The permutation tells which old slice to put at each "
"new position. For example, the shuffle "
"0->1, \t1->2, \t2->0 would be \"2 0 1\". Obviously, "
"if you have to rearrange the many slices of a large "
"dataset, you should probably store the permutation "
"in a plain text file and use it as a "
"\"response file\".\n "
"* Uses nrrdShuffle" );
int
42 unrrdu_shuffleMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int di, axis, permLen, *perm, *iperm, *whichperm;
size_t *realperm;
int inverse, pret;
airArray *mop;
/* so that long permutations can be read from file */
hparm->respFileEnable = AIR_TRUE;
hestOptAdd( &opt, "p, permute", "slc0 slc1", airTypeUInt, 1, -1, &perm, NULL,
"new slice ordering", &permLen );
hestOptAdd( &opt, "inv, inverse", NULL, airTypeInt, 0, 0, &inverse, NULL,
"use inverse of given permutation" );
OPT_ADD_AXIS( axis, "axis to shuffle along" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_shuffleInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
/* we have to do error checking on axis in order to do error
checking on length of permutation */
if ( !( axis < nin->dim ) ) {
fprintf( stderr, "%s: axis %d not in valid range [0, %d]\n",
me, axis, nin->dim-1 );
airMopError( mop );
return 1;
}
if ( !( permLen == nin->axis[axis].size ) ) {
char stmp[AIR_STRLEN_SMALL];
fprintf( stderr, "%s: permutation length ( %u ) != axis %d's size ( %s )\n",
me, permLen, axis,
airSprintSize_t( stmp, nin->axis[axis].size ) );
airMopError( mop );
return 1;
}
if ( inverse ) {
iperm = AIR_CALLOC( permLen, unsigned int );
airMopAdd( mop, iperm, airFree, airMopAlways );
if ( nrrdInvertPerm( iperm, perm, permLen ) ) {
fprintf( stderr,
"%s: couldn't compute inverse of given permutation\n", me );
airMopError( mop );
return 1;
}
whichperm = iperm;
} else {
whichperm = perm;
}
realperm = AIR_CALLOC( permLen, size_t );
airMopAdd( mop, realperm, airFree, airMopAlways );
for ( di=0; di<permLen; di++ ) {
realperm[di] = whichperm[di];
}
if ( nrrdShuffle( nout, nin, axis, realperm ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error shuffling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
121 UNRRDU_CMD( shuffle, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Slice along one or more axes at given positions"
static const char *_unrrdu_sliceInfoL =
( INFO
". Output nrrd dimension is less than input nrrd "
"dimension by the number of slice axes ( except when the "
"input is or gets down to 1-D ). Can slice on all axes "
"in order to sample a single value from the array. "
"Per-axis information is preserved.\n "
"* Uses nrrdSlice ( possibly called multiple times )" );
int
38 unrrdu_sliceMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int *axis;
int pret, axi, axisNum, posNum;
size_t pos[NRRD_DIM_MAX];
long int *_pos;
airArray *mop;
hestOptAdd( &opt, "a, axis", "axis", airTypeUInt, 1, -1, &axis, NULL,
"single axis or multiple axes to slice along. "
"Giving multiple axes here leads to doing multiple slices "
"( at the corresponding positions "
"given with \"-p\" ). Multiple axes should be identified "
"in terms of the axis numbering of the original nrrd; as "
"the slices are done ( in the given ordering ) the actual "
"slice axis will be different if previous slices were on "
"lower-numbered ( faster ) axes.", &axisNum );
hestOptAdd( &opt, "p, position", "pos", airTypeOther, 1, -1, &_pos, NULL,
"position( s ) to slice at:\n "
"\b\bo <int> gives 0-based index\n "
"\b\bo M-<int> give index relative "
"to the last sample on the axis ( M == #samples-1 ).",
&posNum, NULL, &unrrduHestPosCB );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_sliceInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( axisNum != posNum ) {
fprintf( stderr, "%s: # axes %u != # positions %u\n", me, axisNum, posNum );
airMopError( mop ); return 1;
}
if ( axisNum > NRRD_DIM_MAX ) {
fprintf( stderr, "%s: got more slice axes %u than max nrrd dimension %u\n",
me, axisNum, NRRD_DIM_MAX );
airMopError( mop ); return 1;
}
for ( axi=0; axi<axisNum; axi++ ) {
char stmp[AIR_STRLEN_SMALL];
if ( axisNum > 1 ) {
sprintf( stmp, "[%d]", axi );
} else {
strcpy( stmp, "" );
}
if ( !( axis[axi] < nin->dim ) ) {
fprintf( stderr, "%s: axis%s %d not in range [0, %d]\n",
me, stmp, axis[axi], nin->dim-1 );
airMopError( mop ); return 1;
}
if ( _pos[0 + 2*axi] == -1 ) {
fprintf( stderr, "%s: pos%s m+<int> spec not meaningful here\n",
me, stmp );
airMopError( mop ); return 1;
}
pos[axi] = ( _pos[0 + 2*axi]*( nin->axis[axis[axi]].size-1 )
+ _pos[1 + 2*axi] );
/*
printf( "%s: [%d] axis = %u, pos = %u\n", me, axi, axis[axi],
AIR_CAST( unsigned int, pos[axi] ) );
*/
}
/* check on possibly adjust slice axes downward */
if ( axisNum > 1 ) {
int axj;
for ( axi=0; axi<axisNum-1; axi++ ) {
for ( axj=axi+1; axj<axisNum; axj++ ) {
if ( axis[axi] == axis[axj] ) {
fprintf( stderr, "%s: can't repeat axis: axis[%d] = axis[%d] = %u\n",
me, axi, axj, axis[axj] );
airMopError( mop ); return 1;
}
}
}
for ( axi=0; axi<axisNum-1; axi++ ) {
for ( axj=axi+1; axj<axisNum; axj++ ) {
axis[axj] -= ( axis[axj] > axis[axi] );
}
}
/*
for ( axi=0; axi<axisNum; axi++ ) {
printf( "%s: axis[%d] = %u\n", me, axi, axis[axi] );
}
*/
}
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( 1 == axisNum ) {
/* old single axis code */
if ( nrrdSlice( nout, nin, axis[0], pos[0] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error slicing nrrd:\n%s", me, err );
airMopError( mop ); return 1;
}
} else {
/* have to do multiple slices */
Nrrd *ntmp[2];
unsigned int tidx = 0;
ntmp[0] = nrrdNew( );
airMopAdd( mop, ntmp[0], ( airMopper )nrrdNuke, airMopAlways );
ntmp[1] = nrrdNew( );
airMopAdd( mop, ntmp[1], ( airMopper )nrrdNuke, airMopAlways );
for ( axi=0; axi<axisNum; axi++ ) {
if ( nrrdSlice( ( axi < axisNum-1
? ntmp[1-tidx] /* use an ntmp for all but last output */
: nout ), /* and use nout for last output */
( 0 == axi
? nin /* use nin for only first input */
: ntmp[tidx] ), /* use an ntmp for all but first input */
axis[axi], pos[axi] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error with slice %d of %d:\n%s", me,
axi+1, axisNum, err );
airMopError( mop ); return 1;
}
tidx = 1 - tidx;
}
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
171 UNRRDU_CMD( slice, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Replace a slice with a different nrrd"
static const char *_unrrdu_spliceInfoL =
( INFO ". This is functionally the opposite of \"slice\".\n "
"* Uses nrrdSplice" );
int
33 unrrdu_spliceMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout, *nslice;
unsigned int axis;
int pret;
long int _pos[2];
size_t pos;
airArray *mop;
OPT_ADD_AXIS( axis, "axis to splice along" );
hestOptAdd( &opt, "p, position", "pos", airTypeOther, 1, 1, _pos, NULL,
"position to splice at:\n "
"\b\bo <int> gives 0-based index\n "
"\b\bo M-<int> give index relative "
"to the last sample on the axis ( M == #samples-1 ).",
NULL, NULL, &unrrduHestPosCB );
hestOptAdd( &opt, "s, slice", "nslice", airTypeOther, 1, 1, &( nslice ), NULL,
"slice nrrd. This is the slice to insert into \"nin\"",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NIN( nin, "input nrrd. This is the nrrd into which the slice is "
"inserted" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_spliceInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !( axis < nin->dim ) ) {
fprintf( stderr, "%s: axis %u not in range [0, %u]\n", me, axis, nin->dim-1 );
return 1;
}
if ( _pos[0] == -1 ) {
fprintf( stderr, "%s: m+<int> specification format meaningless here\n", me );
return 1;
}
pos = _pos[0]*( nin->axis[axis].size-1 ) + _pos[1];
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdSplice( nout, nin, nslice, axis, pos ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error splicing nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
90 UNRRDU_CMD( splice, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Select subset of slices along an axis"
static const char *_unrrdu_sselectInfoL =
( INFO ". The choice to keep or nix a slice is determined by whether the "
"values in a given 1-D line of values is above or below a given "
"threshold.\n "
"* Uses nrrdSliceSelect" );
int
35 unrrdu_sselectMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *err;
Nrrd *nin, *noutAbove, *noutBelow, *nline;
unsigned int axis;
double thresh;
int pret;
airArray *mop;
char *outS[2];
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_AXIS( axis, "axis to slice along" );
hestOptAdd( &opt, "s, selector", "nline", airTypeOther, 1, 1, &nline, NULL,
"the 1-D nrrd of values to compare with threshold",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "th", "thresh", airTypeDouble, 1, 1, &thresh,
NULL, "threshold on selector line" );
hestOptAdd( &opt, "o, output", "above below", airTypeString, 2, 2,
outS, "- x",
"outputs for slices corresponding to values "
"above ( first ) and below ( second ) given threshold. "
"Use \"x\" to say that no output is desired." );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_sselectInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
if ( !strcmp( outS[0], "x" ) && !strcmp( outS[1], "x" ) ) {
fprintf( stderr, "%s: need to save either above or below slices "
"( can't use \"x\" for both )\n", me );
airMopError( mop );
return 1;
}
if ( strcmp( outS[0], "x" ) ) {
noutAbove = nrrdNew( );
airMopAdd( mop, noutAbove, ( airMopper )nrrdNuke, airMopAlways );
} else {
noutAbove = NULL;
}
if ( strcmp( outS[1], "x" ) ) {
noutBelow = nrrdNew( );
airMopAdd( mop, noutBelow, ( airMopper )nrrdNuke, airMopAlways );
} else {
noutBelow = NULL;
}
if ( nrrdSliceSelect( noutAbove, noutBelow, nin, axis,
nline, thresh ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error selecting slices:\n%s", me, err );
airMopError( mop );
return 1;
}
if ( noutAbove ) {
SAVE( outS[0], noutAbove, NULL );
}
if ( noutBelow ) {
SAVE( outS[1], noutBelow, NULL );
}
airMopOkay( mop );
return 0;
}
104 UNRRDU_CMD( sselect, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Map nrrd through a univariate substitution table"
static const char *_unrrdu_substInfoL =
( INFO
" ( itself represented as a nrrd ). The substitution table "
"is a 2-by-N table: a list of pairs giving the old value ( to replace ) "
"and the the new value ( to change to ), in that order. A plain text file "
"makes this easy. Unlike with \"unu lut\", \"unu rmap\", and \"unu imap\", "
"the output type is the same as the input type, rather than the type of "
"the lut or map.\n "
"* Uses nrrdApply1DSubstitution" );
int
39 unrrdu_substMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
int pret;
Nrrd *nin, *nsubst, *nout;
airArray *mop;
hestOptAdd( &opt, "s, subst", "subst", airTypeOther, 1, 1, &nsubst, NULL,
"substition table to map input nrrd through",
NULL, NULL, nrrdHestNrrd );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_substInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdApply1DSubstitution( nout, nin, nsubst ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble applying SUBST:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
76 UNRRDU_CMD( subst, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Interchange ordering of two axes"
static const char *_unrrdu_swapInfoL =
( INFO
". Special case of \"unu\tpermute\".\n "
"* Uses nrrdAxesSwap" );
int
34 unrrdu_swapMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
unsigned int ax[2];
airArray *mop;
hestOptAdd( &opt, "a, axis", "axisA axisB", airTypeUInt, 2, 2, ax, NULL,
"the two axes to switch ( 0-based numbering )" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_swapInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdAxesSwap( nout, nin, ax[0], ax[1] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error swapping nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
71 UNRRDU_CMD( swap, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Tile slices of one axis into two other axes"
static const char *_unrrdu_tileInfoL =
( INFO
". Tiling an array means splitting one axis into fast and slow parts, "
"and then interleaving those parts into other ( existing ) axes by doing "
"two axis merges, which combine an existing axis with part of the split "
"axis. This reduces the dimension by one. The three axis arguments all "
"identify axes in the input array as is. This provides, for example, "
"a simple way of viewing the 128 slices along the slow axis of a 3-D volume "
"as a 16x8 tiled array of 2-D slices, as with \"-a 2 0 1 -s 16 8\".\n "
"* Uses nrrdTile2D" );
int
40 unrrdu_tileMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
size_t size[2];
unsigned int axes[3];
airArray *mop;
hestOptAdd( &opt, "a, axis", "axSplit ax0 ax1", airTypeUInt, 3, 3, axes, NULL,
"axSplit is divided and merged with ax0 and ax1" );
hestOptAdd( &opt, "s, size", "fast slow", airTypeSize_t, 2, 2, size, NULL,
"fast and slow axis sizes to produce as result of splitting "
"the axSplit axis." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_tileInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdTile2D( nout, nin, axes[1], axes[2], axes[0], size[0], size[1] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error tiling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
81 UNRRDU_CMD( tile, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Expand \"blocks\" into scanlines on axis 0"
static const char *
_unrrdu_unblockInfoL =
( INFO
". Based on the requested output type, the number of "
"samples along axis 0 will be determined automatically. "
"Axis N information will be bumped up to axis N+1. "
"Underlying data is unchanged." );
int
37 unrrdu_unblockMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int type, pret;
size_t blockSize;
airArray *mop;
OPT_ADD_TYPE( type, "type to unblock to", NULL );
hestOptAdd( &opt, "bs", "blocksize", airTypeSize_t, 1, 1, &blockSize, "0",
"Useful only if *output* type is also block: the size of "
"blocks in output nrrd" );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_unblockInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
nout->blockSize = blockSize;
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdUnblock( nout, nin, type ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error unblocking nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
77 UNRRDU_CMD( unblock, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Converts DOS text files to normal, and more"
static const char *_unrrdu_undosInfoL =
( INFO
". Normally, it converts LF-CR pairs to just CR, or, with the \"-r\" "
"option, convert back to DOS, for whatever sick and "
"twisted reason you'd have to do that. Can also handle legacy MAC "
"text files ( only LF ). Unlike the simple sed or perl scripts for "
"this purpose, this program is careful to be idempotent. Also, this "
"makes an effort to not meddle with binary files ( on which this may be "
"mistakenly invoked ). A message is printed to stderr for all the files "
"actually modified. Does various things, some more justified than "
"others.\n "
"* ( not actually based on Nrrd )" );
#define CR 10
#define LF 13
#define BAD_PERC 5.0
static void
46 undosConvert( const char *me, char *name, int reverse, int mac,
int quiet, int noAction ) {
airArray *mop;
FILE *fin, *fout;
char *data=NULL;
airArray *dataArr;
unsigned int ci;
int car, numBad, willConvert;
airPtrPtrUnion appu;
mop = airMopNew( );
if ( !airStrlen( name ) ) {
fprintf( stderr, "%s: empty filename\n", me );
airMopError( mop ); return;
}
/* -------------------------------------------------------- */
/* open input file */
fin = airFopen( name, stdin, "rb" );
if ( !fin ) {
if ( !quiet ) {
fprintf( stderr, "%s: couldn't open \"%s\" for reading: \"%s\"\n",
me, name, strerror( errno ) );
}
airMopError( mop ); return;
}
airMopAdd( mop, fin, ( airMopper )airFclose, airMopOnError );
/* -------------------------------------------------------- */
/* create buffer */
appu.c = &data;
dataArr = airArrayNew( appu.v, NULL, sizeof( char ), AIR_STRLEN_HUGE );
if ( !dataArr ) {
if ( !quiet ) {
fprintf( stderr, "%s: internal allocation error #1\n", me );
}
airMopError( mop ); return;
}
airMopAdd( mop, dataArr, ( airMopper )airArrayNuke, airMopAlways );
/* -------------------------------------------------------- */
/* read input file, testing for binary-ness along the way */
numBad = 0;
car = getc( fin );
if ( EOF == car ) {
if ( !quiet ) {
fprintf( stderr, "%s: \"%s\" is empty, skipping ...\n", me, name );
}
airMopError( mop ); return;
}
do {
ci = airArrayLenIncr( dataArr, 1 );
if ( !dataArr->data ) {
if ( !quiet ) {
fprintf( stderr, "%s: internal allocation error #2\n", me );
}
airMopError( mop ); return;
}
data[ci] = car;
numBad += !( isprint( car ) || isspace( car ) );
car = getc( fin );
} while ( EOF != car && BAD_PERC > 100.0*numBad/dataArr->len );
if ( EOF != car ) {
if ( !quiet ) {
fprintf( stderr, "%s: more than %g%% of \"%s\" is non-printing, "
"skipping ...\n", me, BAD_PERC, name );
}
airMopError( mop ); return;
}
fin = airFclose( fin );
/* -------------------------------------------------------- */
/* see if we really need to do anything */
willConvert = AIR_FALSE;
if ( !strcmp( "-", name ) ) {
willConvert = AIR_TRUE;
} else if ( reverse ) {
for ( ci=0; ci<dataArr->len; ci++ ) {
if ( mac ) {
if ( CR == data[ci] ) {
willConvert = AIR_TRUE;
break;
}
} else {
if ( CR == data[ci] && ( ci && LF != data[ci-1] ) ) {
willConvert = AIR_TRUE;
break;
}
}
}
} else {
for ( ci=0; ci<dataArr->len; ci++ ) {
if ( mac ) {
if ( LF == data[ci] ) {
willConvert = AIR_TRUE;
break;
}
} else {
if ( LF == data[ci] && ( ci+1<dataArr->len && CR == data[ci+1] ) ) {
willConvert = AIR_TRUE;
break;
}
}
}
}
if ( !willConvert ) {
/* no, we don't need to do anything; quietly quit */
airMopOkay( mop );
return;
} else {
if ( !quiet ) {
fprintf( stderr, "%s: %s \"%s\" %s %s ... \n", me,
noAction ? "would convert" : "converting",
name,
reverse ? "to" : "from",
mac ? "MAC" : "DOS" );
}
}
if ( noAction ) {
/* just joking, we won't actually write anything.
( yes, even if input was stdin ) */
airMopOkay( mop );
return;
}
/* -------------------------------------------------------- */
/* open output file */
fout = airFopen( name, stdout, "wb" );
if ( !fout ) {
if ( !quiet ) {
fprintf( stderr, "%s: couldn't open \"%s\" for writing: \"%s\"\n",
me, name, strerror( errno ) );
}
airMopError( mop ); return;
}
airMopAdd( mop, fout, ( airMopper )airFclose, airMopOnError );
/* -------------------------------------------------------- */
/* write output file */
car = 'a';
if ( reverse ) {
for ( ci=0; ci<dataArr->len; ci++ ) {
if ( ( mac && CR == data[ci] )
|| ( CR == data[ci] && ( ci && LF != data[ci-1] ) ) ) {
car = putc( LF, fout );
if ( !mac && EOF != car ) {
car = putc( CR, fout );
}
} else {
car = putc( data[ci], fout );
}
}
} else {
for ( ci=0; EOF != car && ci<dataArr->len; ci++ ) {
if ( ( mac && LF == data[ci] )
|| ( LF == data[ci] && ( ci+1<dataArr->len && CR == data[ci+1] ) ) ) {
car = putc( CR, fout );
ci += !mac;
} else {
car = putc( data[ci], fout );
}
}
}
if ( EOF == car ) {
if ( !quiet ) {
fprintf( stderr, "%s: ERROR writing \"%s\" possible data loss !!! "
"( sorry )\n", me, name );
}
}
fout = airFclose( fout );
airMopOkay( mop );
return;
}
int
222 unrrdu_undosMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
/* these are stock for unrrdu */
hestOpt *opt = NULL;
airArray *mop;
int pret;
char *err;
/* these are specific to this command */
char **name;
int lenName, ni, reverse, quiet, noAction, mac;
hestOptAdd( &opt, "r", NULL, airTypeInt, 0, 0, &reverse, NULL,
"convert back to DOS, instead of converting from DOS to normal" );
hestOptAdd( &opt, "q", NULL, airTypeInt, 0, 0, &quiet, NULL,
"never print anything to stderr, even for errors." );
hestOptAdd( &opt, "m", NULL, airTypeInt, 0, 0, &mac, NULL,
"deal with legacy MAC text files." );
hestOptAdd( &opt, "n", NULL, airTypeInt, 0, 0, &noAction, NULL,
"don't actually write converted files, just pretend to. "
"This is useful to see which files WOULD be converted. " );
hestOptAdd( &opt, NULL, "file", airTypeString, 1, -1, &name, NULL,
"all the files to convert. Each file will be over-written "
"with its converted contents. Use \"-\" to read from stdin "
"and write to stdout", &lenName );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_undosInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
for ( ni=0; ni<lenName; ni++ ) {
undosConvert( me, name[ni], reverse, mac, quiet, noAction );
}
airMopOkay( mop );
return 0;
}
261 UNRRDU_CMD_HIDE( undos, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Make image orientation be axis-aligned"
static const char *_unrrdu_unorientInfoL =
( INFO
". Does various tricks.\n "
"* Uses nrrdOrientationReduce" );
int
34 unrrdu_unorientMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int pret;
int setMinsFromOrigin;
airArray *mop;
/* if we gave a default for this, then there it would fine to have
no command-line arguments whatsoever, and then the user would not
know how to get the basic usage information */
hestOptAdd( &opt, "i, input", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd "
"( sorry, can't use usual default of \"-\" for stdin "
"because of hest quirk )",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "smfo", NULL, airTypeInt, 0, 0,
&setMinsFromOrigin, NULL,
"set some axis mins based on space origin ( hack )" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_unorientInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdOrientationReduce( nout, nin, setMinsFromOrigin ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error unorienting nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
79 UNRRDU_CMD( unorient, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Recover floating point values from quantized data"
static const char *_unrrdu_unquantizeInfoL =
( INFO ". Uses the oldMin and oldMax fields in the Nrrd of quantized values "
"to regenerate approximate versions of the original unquantized values. "
"Can also override these with \"-min\" and \"-max\".\n "
"* Uses nrrdUnquantize" );
int
35 unrrdu_unquantizeMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
int dbl, pret;
double oldMin, oldMax;
airArray *mop;
/* mandatory arg so that "unu unquantize" produces usage info */
hestOptAdd( &opt, "i, input", "nin", airTypeOther, 1, 1, &nin, NULL,
"input nrrd. That this argument is required instead of "
"optional, as with most unu commands, is a quirk caused by the "
"need to have \"unu unquantize\" generate usage info, combined "
"with the fact that all the other arguments have sensible "
"defaults",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "min, minimum", "value", airTypeDouble, 1, 1, &oldMin, "nan",
"Lowest value prior to quantization. Defaults to "
"nin->oldMin if it exists, otherwise 0.0" );
hestOptAdd( &opt, "max, maximum", "value", airTypeDouble, 1, 1, &oldMax, "nan",
"Highest value prior to quantization. Defaults to "
"nin->oldMax if it exists, otherwise 1.0" );
hestOptAdd( &opt, "double", NULL, airTypeBool, 0, 0, &dbl, NULL,
"Use double for output type, instead of float" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_unquantizeInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( AIR_EXISTS( oldMin ) )
nin->oldMin = oldMin;
if ( AIR_EXISTS( oldMax ) )
nin->oldMax = oldMax;
if ( nrrdUnquantize( nout, nin, dbl ? nrrdTypeDouble : nrrdTypeFloat ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error unquantizing nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
89 UNRRDU_CMD( unquantize, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Undo \"unu tile\": merge slow parts of two axis splits"
static const char *_unrrdu_untileInfoL =
( INFO
". Untiling an array means spliting two axes, permuting the slow parts "
"of those axes to be adjecent in the axis ordering, and then merging "
"them. This increases the dimension by one. Undoing a \"unu tile\" "
"uses the same \"-s\" argument, and sometimes a different \"-a\" argument, "
"as demonstrated here for a 3-D array:\n "
"\"unu untile -a 2 0 1\" undoes \"unu tile -a 2 0 1\"\n "
"\"unu untile -a 1 0 1\" undoes \"unu tile -a 1 0 2\"\n "
"\"unu untile -a 0 0 1\" undoes \"unu tile -a 0 1 2\".\n "
"* Uses nrrdUntile2D" );
int
41 unrrdu_untileMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
char *out, *err;
Nrrd *nin, *nout;
unsigned int axes[3];
int pret;
size_t size[2];
airArray *mop;
hestOptAdd( &opt, "a, axis", "axMerge ax0 ax1", airTypeUInt, 3, 3, axes, NULL,
"the slow parts of axes ax0 and ax1 are merged into a ( new ) "
"axis axMerge, with the axis ax0 part being faster than ax1." );
hestOptAdd( &opt, "s, size", "size0 size1", airTypeSize_t, 2, 2, size, NULL,
"the slow parts of axes ax0 and ax1 are taken to have size "
"size0 and size1, respectively, and axis axMerge will have "
"size size0*size1." );
OPT_ADD_NIN( nin, "input nrrd" );
OPT_ADD_NOUT( out, "output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_untileInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdUntile2D( nout, nin, axes[1], axes[2], axes[0], size[0], size[1] ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: error tiling nrrd:\n%s", me, err );
airMopError( mop );
return 1;
}
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
84 UNRRDU_CMD( untile, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "Try to create the look of early 80s analog B+W video"
static const char *_unrrdu_vidiconInfoL =
( INFO
". Does various things, some more justified than others.\n "
"* ( as yet there's no single nrrd function which does all this )" );
int
34 unrrdu_vidiconMain( int argc, const char **argv, const char *me,
hestParm *hparm ) {
hestOpt *opt = NULL;
airArray *mop, *submop=NULL;
unsigned int vsize[2], vpadding[2], rpadding[2];
double rescale, rperc;
Nrrd *nin, *nrescale, *npad, *nvbase, *ntmp, *nout;
char *out, *err, *stpfx, stname[AIR_STRLEN_SMALL];
int pret;
NrrdResampleContext *rsmc;
NrrdKernelSpec *rescaleKsp, *vdsmp[2];
NrrdRange *b8range;
hparm->elideSingleOtherDefault = AIR_FALSE;
hestOptAdd( &opt, "i", "input", airTypeOther, 1, 1, &nin, NULL,
"input image. Should be grayscale PNG.",
NULL, NULL, nrrdHestNrrd );
hestOptAdd( &opt, "rs", "rescale", airTypeDouble, 1, 1, &rescale, "0.75",
"how to rescale ( downsample ) the image prior to processing, "
"just to get a better representation of the floating-point "
"range of image values ( overcoming 8-bit quantization effects )" );
hestOptAdd( &opt, "rsk", "kern", airTypeOther, 1, 1, &rescaleKsp, "hann:5",
"kernel for rescaling.",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "rsp", "percentile", airTypeDouble, 1, 1, &rperc, "1.5",
"after rescaling, the highest and lowest percentiles are mapped "
"to 0.0 and 255.0, just to have a uniform range of intensities "
"in subsequent processing. This option determines how big those "
"percentiles are." );
hestOptAdd( &opt, "vs", "sx sy", airTypeUInt, 2, 2, vsize, "550 525",
"the lowest ( \"video\" ) resolution to which the image is "
"down-sampled, reflecting the limited resolution of the "
"vidicon tubes" );
hestOptAdd( &opt, "pad", "padX padY", airTypeUInt, 2, 2, vpadding, "10 10",
"at the lowest resolution, there should be this much padding "
"by black, to reflect the fact the signal outside the tube "
"( e.g. between scanlines is black )" );
hestOptAdd( &opt, "vk", "kernX kernY", airTypeOther, 2, 2, vdsmp,
"hann:1, 4 cubic:0, 0.5",
"kernels for downsampling to video resolution; the horizontal "
"and vertical kernels are different",
NULL, NULL, nrrdHestKernelSpec );
hestOptAdd( &opt, "stp", "prefix", airTypeString, 1, 1, &stpfx, "",
"if a string is given here, a series of images are saved, "
"representing the various stages of processing" );
hestOptAdd( &opt, "o", "output", airTypeString, 1, 1, &out, NULL,
"output nrrd" );
mop = airMopNew( );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_vidiconInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
ntmp = nrrdNew( );
airMopAdd( mop, ntmp, ( airMopper )nrrdNuke, airMopAlways );
nout = nrrdNew( );
airMopAdd( mop, nout, ( airMopper )nrrdNuke, airMopAlways );
b8range = nrrdRangeNew( 0.0, 255.0 );
airMopAdd( mop, b8range, ( airMopper )nrrdRangeNix, airMopAlways );
if ( !( 2 == nin->dim && nrrdTypeBlock != nin->type ) ) {
fprintf( stderr, "%s: need input as 2D grayscale image ( not %u-d %s )\n",
me, nin->dim, airEnumStr( nrrdType, nin->type ) );
airMopError( mop ); return 1;
}
nrescale = nrrdNew( );
airMopAdd( mop, nrescale, ( airMopper )nrrdNuke, airMopAlways );
fprintf( stderr, "%s: rescaling by %g ... \n", me, rescale );
rsmc = nrrdResampleContextNew( );
airMopAdd( mop, rsmc, ( airMopper )nrrdResampleContextNix, airMopAlways );
if ( nrrdResampleDefaultCenterSet( rsmc, nrrdCenterCell )
|| nrrdResampleInputSet( rsmc, nin )
|| nrrdResampleKernelSet( rsmc, 0, rescaleKsp->kernel, rescaleKsp->parm )
|| nrrdResampleKernelSet( rsmc, 1, rescaleKsp->kernel, rescaleKsp->parm )
|| nrrdResampleSamplesSet( rsmc, 0, AIR_CAST( size_t,
rescale*nin->axis[0].size ) )
|| nrrdResampleSamplesSet( rsmc, 1, AIR_CAST( size_t,
rescale*nin->axis[1].size ) )
|| nrrdResampleRangeFullSet( rsmc, 0 )
|| nrrdResampleRangeFullSet( rsmc, 1 )
|| nrrdResampleTypeOutSet( rsmc, nrrdTypeFloat )
|| nrrdResampleRenormalizeSet( rsmc, AIR_TRUE )
|| nrrdResampleExecute( rsmc, nrescale ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem rescaling:\n%s", me, err );
airMopError( mop ); return 1;
}
#define SAVE_TMP( name, nrrd ) \
if ( airStrlen( stpfx ) ) { \
sprintf( stname, "%s-" #name ".png", stpfx ); \
if ( nrrdQuantize( ntmp, nrrd, b8range, 8 ) \
|| nrrdSave( stname, ntmp, 0 ) ) { \
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways ); \
fprintf( stderr, "%s: problem saving %s:\n%s", me, stname, err ); \
airMopError( mop ); return 1; \
} \
}
SAVE_TMP( rescale, nrescale );
/* rescaling values to 0.0 -- 255.0 based on percentile rperc */
{
Nrrd *nhist;
double *hist, sum, total, minval, maxval;
unsigned int hi, hbins;
float *rescaled;
size_t ii, nn;
submop = airMopNew( );
nhist = nrrdNew( );
hbins = 3000;
airMopAdd( submop, nhist, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdHisto( nhist, nrescale, NULL, NULL, hbins, nrrdTypeDouble ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: trouble making histogram:\n%s", me, err );
airMopError( submop ); airMopError( mop ); return 1;
}
hist = AIR_CAST( double *, nhist->data );
total = AIR_CAST( double, nrrdElementNumber( nrescale ) );
minval = AIR_NAN;
sum = 0;
for ( hi=0; hi<hbins; hi++ ) {
sum += hist[hi];
if ( sum >= rperc*total/100.0 ) {
minval = AIR_AFFINE( 0, hi, hbins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( hi == hbins || !AIR_EXISTS( minval ) ) {
fprintf( stderr, "%s: failed to find lower %g-percentile value",
me, rperc );
airMopError( submop ); airMopError( mop ); return 1;
}
maxval = AIR_NAN;
sum = 0;
for ( hi=hbins; hi; hi-- ) {
sum += hist[hi-1];
if ( sum >= rperc*total/100.0 ) {
maxval = AIR_AFFINE( 0, hi-1, hbins-1,
nhist->axis[0].min, nhist->axis[0].max );
break;
}
}
if ( !hi || !AIR_EXISTS( maxval ) ) {
fprintf( stderr, "%s: failed to find upper %g-percentile value",
me, rperc );
airMopError( submop ); airMopError( mop ); return 1;
}
fprintf( stderr, "%s: min %g --> 0, max %g --> 255\n", me, minval, maxval );
nn = nrrdElementNumber( nrescale );
rescaled = AIR_CAST( float *, nrescale->data );
for ( ii=0; ii<nn; ii++ ) {
rescaled[ii] = AIR_CAST( float, AIR_AFFINE( minval, rescaled[ii],
maxval, 0.0, 255.0 ) );
}
airMopOkay( submop );
submop = NULL;
}
/* padding rescaled image with black */
rpadding[0] = AIR_ROUNDUP( AIR_CAST( double, vpadding[0] )
* nrescale->axis[0].size / vsize[0] );
rpadding[1] = AIR_ROUNDUP( AIR_CAST( double, vpadding[1] )
* nrescale->axis[1].size / vsize[1] );
fprintf( stderr, "%s: padding in rescaled image: %u x %u\n",
me, rpadding[0], rpadding[1] );
npad = nrrdNew( );
airMopAdd( mop, npad, ( airMopper )nrrdNuke, airMopAlways );
{
ptrdiff_t pmin[2], pmax[2];
pmin[0] = -AIR_CAST( ptrdiff_t, rpadding[0] );
pmin[1] = -AIR_CAST( ptrdiff_t, rpadding[1] );
pmax[0] = nrescale->axis[0].size + rpadding[0];
pmax[1] = nrescale->axis[1].size + rpadding[1];
if ( nrrdPad_nva( npad, nrescale, pmin, pmax, nrrdBoundaryPad, 0.0 ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem padding:\n%s", me, err );
airMopError( mop ); return 1;
}
}
/* rescaling down to "video" resolution */
fprintf( stderr, "%s: downsampling to %u x %u\n", me,
AIR_CAST( unsigned int, vsize[0] + 2*vpadding[0] ),
AIR_CAST( unsigned int, vsize[1] + 2*vpadding[1] ) );
nvbase = nrrdNew( );
airMopAdd( mop, nvbase, ( airMopper )nrrdNuke, airMopAlways );
if ( nrrdResampleDefaultCenterSet( rsmc, nrrdCenterCell )
|| nrrdResampleInputSet( rsmc, npad )
|| nrrdResampleKernelSet( rsmc, 0, vdsmp[0]->kernel, vdsmp[0]->parm )
|| nrrdResampleKernelSet( rsmc, 1, vdsmp[1]->kernel, vdsmp[1]->parm )
|| nrrdResampleSamplesSet( rsmc, 0, vsize[0] + 2*vpadding[0] )
|| nrrdResampleSamplesSet( rsmc, 1, vsize[1] + 2*vpadding[1] )
|| nrrdResampleRangeFullSet( rsmc, 0 )
|| nrrdResampleRangeFullSet( rsmc, 1 )
|| nrrdResampleTypeOutSet( rsmc, nrrdTypeFloat )
|| nrrdResampleRenormalizeSet( rsmc, AIR_TRUE )
|| nrrdResampleExecute( rsmc, nvbase ) ) {
airMopAdd( mop, err = biffGetDone( NRRD ), airFree, airMopAlways );
fprintf( stderr, "%s: problem downsampling to video resolution:\n%s", me, err );
airMopError( mop ); return 1;
}
241 /* halo, lowfilt, windowing, noise, filt, interlace, noise, fuzz, upsample */
nrrdCopy( nout, nvbase );
SAVE( out, nout, NULL );
airMopOkay( mop );
return 0;
}
UNRRDU_CMD_HIDE( vidicon, INFO );
1 /*
Teem: Tools to process and visualize scientific data and images .
Copyright ( C ) 2013, 2012, 2011, 2010, 2009 University of Chicago
Copyright ( C ) 2008, 2007, 2006, 2005 Gordon Kindlmann
Copyright ( C ) 2004, 2003, 2002, 2001, 2000, 1999, 1998 University of Utah
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
( LGPL ) as published by the Free Software Foundation; either
version 2.1 of the License, or ( at your option ) any later version.
The terms of redistributing and/or modifying this software also
include exceptions to the LGPL that facilitate static linking.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "unrrdu.h"
#include "privateUnrrdu.h"
#define INFO "converts from 1-D world to index position"
static const char *_unrrdu_w2iInfoL =
( INFO ", given the centering of the data ( cell vs. node ), "
"the range of positions, and the number of intervals into "
"which position has been quantized. "
"This is a demo/utility, which does not actually operate on any nrrds. "
"Previously available as the stand-alone pos2idx binary.\n "
"* Uses NRRD_IDX macro" );
int
37 unrrdu_w2iMain( int argc, const char **argv, const char *me, hestParm *hparm ) {
hestOpt *opt = NULL;
airArray *mop;
int pret;
char *err;
int center;
double minPos, maxPos, pos, indx, size;
mop = airMopNew( );
hestOptAdd( &opt, NULL, "center", airTypeEnum, 1, 1, ¢er, NULL,
"which centering applies to the quantized position.\n "
"Possibilities are:\n "
"\b\bo \"cell\": for histogram bins, quantized values, and "
"pixels-as-squares\n "
"\b\bo \"node\": for non-trivially interpolated "
"sample points", NULL, nrrdCenter );
hestOptAdd( &opt, NULL, "minPos", airTypeDouble, 1, 1, &minPos, NULL,
"smallest position associated with index 0" );
hestOptAdd( &opt, NULL, "maxPos", airTypeDouble, 1, 1, &maxPos, NULL,
"highest position associated with highest index" );
hestOptAdd( &opt, NULL, "num", airTypeDouble, 1, 1, &size, NULL,
"number of intervals into which position has been quantized" );
hestOptAdd( &opt, NULL, "world", airTypeDouble, 1, 1, &pos, NULL,
"the input world position, to be converted to index" );
airMopAdd( mop, opt, ( airMopper )hestOptFree, airMopAlways );
USAGE( _unrrdu_w2iInfoL );
PARSE( );
airMopAdd( mop, opt, ( airMopper )hestParseFree, airMopAlways );
indx = NRRD_IDX( center, minPos, maxPos, size, pos );
printf( "%g\n", indx );
airMopOkay( mop );
return 0;
}
74 UNRRDU_CMD( w2i, INFO );