/***********************************************************************/
/* Open Visualization Data Explorer                                    */
/* (C) Copyright IBM Corp. 1989,1999                                   */
/* ALL RIGHTS RESERVED                                                 */
/* This code licensed under the                                        */
/*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
/***********************************************************************/
/* XXX - equivalence loop, prisms, efficient degenerate removal */
/* XXX - the diagonal isn't preserved with band quad->tri decomp */
/* XXX - color map component */

#include <dxconfig.h>


/* XXX - traverse once to get values and once to set them (attributes). merge */

#ifndef _ISOSURFACE_PASS_NUMBER


/*
 * The following commands may be useful in rebuilding isosurface in
 *     the form it had before the large change (rewrite):
 *
 *     rlog -d'<93/02/06' ${file} | grep '^revis' | head -1
 *     co   -d'93/02/06'  ${file}
 *
 * for the files:
 *     isosurface.m _isosurface.h _isosurface.c _contour.c _irriso.c _regiso.c
 */

/***************************************************\
 *   Generic instantiation system:                 *
 *       for each of the base types: float, char   *
 *           create a copy of those routines that  *
 *             handle those specific types         *
\***************************************************/

extern int _dxd_isosurface_task_counter;

#define  _ISOSURFACE_PASS_NUMBER  1  /*------------------------------*/

#define ERROR_SECTION if ( DXGetError() == ERROR_NONE ) \
                  DXDebug ( "I0D", "No error set at %s:%d", __FILE__,__LINE__)

#define  VALUE_COUNT(a)   (  (a)->number )
#define  STRUCT_COUNT(a)  ( ((a)->module==BAND) ? 1      : (a)->number )
#define  MOD_NAME(a)      ( ((a)->module==BAND) ? "Band" : "Isosurface")

#define  ATTR_NAME(a) ( ((a)->module==BAND) ? "Band value" : "Isosurface value")


#endif /* ( not defined _ISOSURFACE_PASS_NUMBER ) */


#if ( _ISOSURFACE_PASS_NUMBER == 1 )


/*
** -------- Include Files -------------------------------------------------
** -------- Type Information ----------------------------------------------
** -------- Constants -----------------------------------------------------
** -------- storage areas -------------------------------------------------
** -------- Inline Macros -------------------------------------------------
** -------- Function Prototypes -------------------------------------------
*/


#include <math.h> 
#include <dx/dx.h> 
#include "_isosurface.h"
#include "_getfield.h"

#ifdef DXD_LONG_HASHKEY
#endif
#define TST_HASH_INTS if(sizeof(PointId)!=sizeof(int32))\
                      DXErrorGoto(ERROR_UNEXPECTED,"64 bit porting error")

/* the following function resides in the file: showboundary.m */
extern
Array _dxf_resample_array
          ( component_info comp,  int dep_ref,
            int Nin,   int Nout,
            int *push, int *pull );


static RGBColor DEFAULT_SURFACE_COLOR = { 0.5, 0.7, 1.0 }; /*bluish slate grey*/
static RGBColor DEFAULT_LINE_COLOR    = { 0.7, 0.7, 0.0 };

                        /* sizeof unsigned int is 4 bytes, 32 bits */
#define  NEWBIT(n)      ((Pointer)DXAllocateLocalZero((((n)+31)/32)*4))
#define  SETBIT(a,i,v)  (((unsigned int*)(a))[((i)/32)]|=((v)<<((i)&31)))
#define  GETBIT(a,i)    (1&(((unsigned int*)(a))[((i)/32)]>>((i)&31)))

#define  NOWHITEBOX_TEST
#define  INCLUDES_BAND


#define ABS(x) (((x)<0.0)?-(x):(x))

typedef struct
{
    Line  edge;
    int   seq;
#if 0
    float t; /* XXX - intrusive in the contour connection table ? */
#endif
}
iso_edge_hash_rec;

typedef iso_edge_hash_rec *iso_edge_hash_ptr;


#ifdef INCLUDES_BAND

typedef enum { DEP_CONNECTIONS, DEP_POSITIONS } data_dep;

#define  NEITHER_VALUE   -1

typedef  int  band_value_sel;

typedef struct
{
    Line           edge;
    band_value_sel select;
    int            seq;
}
band_edge_hash_rec;

typedef band_edge_hash_rec *band_edge_hash_ptr;
#endif



typedef struct
{
    int       count;
    HashTable table;   
    Pointer   set_vertices;
}
hash_table_rec;

typedef hash_table_rec *hash_table_ptr;



typedef struct
{
    Pointer   set_connections;
    Vector  **index;
    SegList  *list;
}
grad_table_rec;

typedef grad_table_rec *grad_table_ptr;



typedef struct
{
    int     count;
    SegList *conn;
    SegList *conn_id;
}
conn_list_rec;

typedef conn_list_rec *conn_list_ptr;



#ifdef WHITEBOX_TEST
struct
{
    int case_signatures   [ 256 ];
    int polygonalizations [ 8 ];
}
histograms;
#endif


Field remove_degenerates ( Field in );


#endif /* ( _ISOSURFACE_PASS_NUMBER == 1 ) */



/*---------------------------------------------*\
 | Begin generic templates.
\*---------------------------------------------*/

    /* Moved to _getfield.c */

/*---------------------------------------------*\
 | End generic templates.
\*---------------------------------------------*/



#if ( _ISOSURFACE_PASS_NUMBER == 1 )



static
Pointer copy_seglist_to_memory
            ( SegList *list, Pointer mem, int itemsize, int count )
{
    SegListSegment *sublist  = NULL;
    int            items     = 0;
    char           *location = NULL;
    int            segsize   = 0;

    DXASSERTGOTO ( list != ERROR );
    DXASSERTGOTO ( mem  != ERROR );
    DXASSERTGOTO ( count == DXGetSegListItemCount ( list ) );

    if ( count == 0 ) 
        return mem;

    if ( !DXInitGetNextSegListSegment ( list ) ) goto error;

    for ( items = 0,        location = (char *) mem;
          ( NULL != ( sublist = DXGetNextSegListSegment ( list ) ) );
          items += segsize, location += ( segsize * itemsize ) )
    {
        if ( ERROR == ( segsize = DXGetSegListSegmentItemCount ( sublist ) ) )
            goto error;

        if ( !memcpy ( location,
                       (char *) DXGetSegListSegmentPointer ( sublist ),
                       ( segsize * itemsize ) ) )
            DXErrorGoto2
                ( ERROR_INTERNAL,
                  "#11800", /* C standard library call, %s, returns error */
                  "memcpy()" )
    }

    DXASSERTGOTO ( items == count );

    return mem;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
array_info copy_seglist_to_array ( SegList *list, array_info array )
{
    DXASSERT ( array != ERROR );

    if ( copy_seglist_to_memory
             ( list, array->data, array->itemsize, array->items ) )
        return array;
    else
        return ERROR;
}



static
Error tally_dependers ( field_info input_info, int *posi, int *conn )
{
    component_info  comp;
    int             i;

    DXASSERTGOTO ( posi != ERROR );
    DXASSERTGOTO ( conn != ERROR );

    for ( i=0, *posi=0, *conn=0, comp=input_info->comp_list;
          i<input_info->comp_count;
          i++, comp++ )
    {
        if ( ( 0 == strcmp ( comp->name, "positions" ) ) ||
             ( ( NULL  != comp->std_attribs[(int)DEP]        ) &&
               ( ERROR != comp->std_attribs[(int)DEP]->value ) &&
               ( 0 == strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            (*posi)++;
        else
        if ( ( 0 == strcmp ( comp->name, "connections" ) ) ||
             ( ( NULL  != comp->std_attribs[(int)DEP]        ) &&
               ( ERROR != comp->std_attribs[(int)DEP]->value ) &&
               ( 0 == strcmp ( comp->std_attribs[(int)DEP]->value,
                               "connections" ) ) ) )
            (*conn)++;
    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
SegList *set_c_ID ( SegList *list, int index, int ID )
{
    int  length     = DXGetSegListItemCount ( list );
    int  *list_item;

    DXASSERT ( length <= index );

    while ( length < index )
        if ( ERROR == ( list_item = (int*) DXNewSegListItem ( list ) ) )
            return ERROR;
        else
            { *list_item = ID; length++; }

    DXASSERT ( length == DXGetSegListItemCount ( list ) );

    return list;
}



static
SegList *set_cval ( SegList *list, int index, float val )
{
    int   length     = DXGetSegListItemCount ( list );
    float *list_item;

    DXASSERT ( length <= index );

    while ( length < index )
        if ( ERROR == ( list_item = (float*) DXNewSegListItem ( list ) ) )
            return ERROR;
        else
            { *list_item = val; length++; }

    DXASSERT ( length == DXGetSegListItemCount ( list ) );

    return list;
}



static
Error interp ( array_info info, Pointer scratch,
               int I, int J, double t, Pointer dest, int tot, int out_size )
{
    typedef unsigned int    unsigned_int;
    typedef unsigned char   unsigned_char;
    typedef unsigned short  unsigned_short;

    Pointer si = scratch;
    Pointer sj = (Pointer)&(((char*)scratch)[out_size]);
    int i;

    if (t == 0) {
	if (!info->get_item(I, info, dest))
	    goto error;
    } else if (t == 1) {
	if (!info->get_item(J, info, dest))
	    goto error;
    } else {
	if ( !info->get_item ( I, info, si ) || !info->get_item ( J, info, sj ) )
	    goto error;

	for(i=0; i<tot; i++)
	    ((float*)dest)[i] = (double)((float*)si)[i] + ( (double)((float*)sj)[i] - (double)((float*)si)[i] ) * (double)t;
    }

#if 0
    for(i=0; i<tot; i++)
	printf("i t si sj dest %d %16.10f %16.10f %16.10f %16.10f\n",
	       i, t, ((float*)si)[i], ((float*)sj)[i], ((float*)dest)[i]);
#endif

#if 0
    /* The following interpolation method didn't make sense because get_item  */
    /* always returns float arrays, and the destination array for all cases   */
    /* is also going to be float.  suits 9/5/96                               */

#define INTERP(CTYPE) tot=info->itemsize/sizeof(CTYPE);\
    for(i=0;i<tot;i++) ((CTYPE*)dest)[i] = ((CTYPE*)si)[i] +\
    ( ( (float)(((CTYPE*)sj)[i]) - (float)(((CTYPE*)si)[i]) ) * t );

    switch ( info->type )
    {
        case TYPE_FLOAT:  INTERP ( float          );  break;
        case TYPE_DOUBLE: INTERP ( double         );  break;
        case TYPE_INT:    INTERP ( int            );  break;
        case TYPE_BYTE:   INTERP ( char           );  break;
        case TYPE_UINT:   INTERP ( unsigned_int   );  break;
        case TYPE_UBYTE:  INTERP ( unsigned_char  );  break;
        case TYPE_USHORT: INTERP ( unsigned_char  );  break;
        case TYPE_SHORT:  INTERP ( unsigned_short );  break;
    }

#undef INTERP
#endif

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
Field create_output_arrays
          ( Field     output,  /* handle                  */
            int       nP,      /* N positions             */
            int       Pd,      /* positions dim           */
            int       nC,      /* N connections           */
            int       Cd,      /* connections dim         */
            int       nN,      /* N normals               */
            RGBColor  color,   /* default color           */
            float     Dv,      /* data value (isos. only) */
            data_dep  Dd,      /* data dependency         */
            iso_module_types module   /* isosurface/band */ )
{
    Array  array  =  NULL;

    DXASSERTGOTO ( output != ERROR );
    DXASSERTGOTO ( DXGetObjectClass ( (Object) output ) == CLASS_FIELD );

    /* 
     * Basic assumptions for isosurface output: 
     *    Colors and Data are regular (constant) dep positions.
     *    Positions and Connections are always irregular.
     *    Normals are for surfaces.
     *    Connections are used for surfaces and contours only.
     *    Connections are 1d (Lines) or 2d (Triangles) or nonexistant.
     *    int 0 is usable as float, char, ... 0.
     *          (int[2] for double), (int[3] for RBGColor)
     *
     * XXX - libdx
     *   assume that DXSetAttribute(array) and DXSetComponentAttribute 
     *    are interchangeable.
     */ 

    if ( !_dxf_MakeFieldEmpty ( output ) )
        goto error;


    if ( nP == 0 ) 

        return output;

    else
    { 
        /* POSITIONS */
        if ( ( ERROR == ( array
                   = DXNewArray ( TYPE_FLOAT, CATEGORY_REAL, 1, Pd ) ) )
             ||
             !DXAllocateArray ( array, nP )
             ||
             !DXAddArrayData ( array, 0, nP, NULL )
             ||
             !DXTrim ( array )
             ||
             !DXSetAttribute
                  ( (Object)array,
                    "dep", (Object) DXNewString ( "positions" ) )
             ||
             !DXSetComponentValue ( output, "positions", (Object) array ) )
            goto error;

        array = NULL;

        /* COLORS */
/* XXX - conditional */

        if ( ( ERROR == ( array
                   = (Array)DXNewConstantArray
                                ( ((Dd==DEP_POSITIONS)? nP: nC),
                                  (Pointer)&color, TYPE_FLOAT,
                                  CATEGORY_REAL, 1, 3 ) ) )
             ||
             !DXSetAttribute
                 ( (Object)array, "dep",
                   (Object) DXNewString
                            ( (Dd==DEP_POSITIONS)?"positions" :"connections" ) )
             ||
             !DXSetComponentValue ( output, "colors", (Object) array ) )
            goto error;

        array = NULL;

        /* DATA */
        switch ( module )
        {
            case ISOSURFACE:
                DXASSERTGOTO ( Dd == DEP_POSITIONS );
                if ( ERROR == ( array
                         = (Array)DXNewConstantArray
                                      ( nP, (Pointer)&Dv, TYPE_FLOAT,
                                        CATEGORY_REAL, 0, 0 ) ) )
                    goto error;
                break;

            case BAND:
                if ( ( ERROR == ( array
                           = DXNewArray ( TYPE_FLOAT, CATEGORY_REAL, 0, 0 ) ) )
                     ||
                     !DXAllocateArray ( array, ((Dd==DEP_POSITIONS)? nP: nC) )
                     ||
                     !DXAddArrayData
                         ( array, 0, ((Dd==DEP_POSITIONS)? nP: nC), NULL )
                     ||
                     !DXTrim ( array ) )
                    goto error;
                break;
        }

        if ( !DXSetComponentValue ( output, "data", (Object) array )
             ||
             !DXSetComponentAttribute
                  ( output, "data", "dep",
                    (Object) DXNewString
                                ( ( (Dd==DEP_POSITIONS) ?
                                       "positions" : "connections" ) ) ) )
            goto error;

        array = NULL;

        /* NORMALS */
        if ( ( nN != 0 ) && (
             ( ERROR == ( array
                           = DXNewArray ( TYPE_FLOAT, CATEGORY_REAL, 1, 3 ) ) )
             ||
             !DXAllocateArray ( array, nP )
             ||
             !DXAddArrayData ( array, 0, nP, NULL )
             ||
             !DXTrim ( array )
             ||
             !DXSetAttribute
                 ( (Object)array, "dep", (Object) DXNewString ( "positions" ) )
             ||
             !DXSetComponentValue ( output, "normals", (Object) array ) ) )
        goto error;

        array = NULL;
    } 

    if ( nC != 0 ) 
    { 
        char *element_type;

        switch ( Cd )
        {
            case 1: if ( !_dxf_SetFuzzAttribute ( output, 3 ) ) goto error;
                    element_type = "lines";     break;
            case 2: element_type = "triangles"; break;
            default:
                DXASSERTGOTO ( ( Cd == 1 ) || ( Cd == 2 ) );
        }

        /* CONNECTIONS */
        if ( ( ERROR == ( array =
                    DXNewArray ( TYPE_INT, CATEGORY_REAL, 1, ( Cd + 1 ) ) ) )
             ||
             !DXAllocateArray ( array, nC )
             ||
             !DXAddArrayData ( array, 0, nC, (Pointer) NULL )
             ||
             !DXTrim ( array )
             ||
             !DXSetAttribute
                  ( (Object)array, "ref", (Object) DXNewString ( "positions" ) )
             ||
             !DXSetAttribute
                  ( (Object)array,
                    "element type", (Object) DXNewString ( element_type ) )
             ||
             !DXSetComponentValue ( output, "connections", (Object) array ) )
        goto error;

        array = NULL;
    } 

    return ( output );

    error:
        ERROR_SECTION;

        DXDelete ( (Object) array );  array = NULL;

        return ERROR;
}



static
int iso_edge_compare_func ( Key i, Element j )
{
    /* XXX - requires pre-sorting! */
    return ( ( ( ((iso_edge_hash_ptr)i)->edge.p
                 == 
                 ((iso_edge_hash_ptr)j)->edge.p )
               &&
               ( ((iso_edge_hash_ptr)i)->edge.q
                 == 
                 ((iso_edge_hash_ptr)j)->edge.q ) ) ? 0 : 1 );
}



#ifdef INCLUDES_BAND
static
int band_edge_compare_func ( Key i, Element j )
{
    /* XXX - requires pre-sorting! */
    return ( ( ( ((band_edge_hash_ptr)i)->edge.p
                 == 
                 ((band_edge_hash_ptr)j)->edge.p )
               &&
               ( ((band_edge_hash_ptr)i)->edge.q
                 == 
                 ((band_edge_hash_ptr)j)->edge.q )
               &&
               ( ((band_edge_hash_ptr)i)->select
                 == 
                 ((band_edge_hash_ptr)j)->select ) ) ? 0 : 1 );
}
#endif



static
PseudoKey iso_edge_hash_func ( Key i )
{
#define  k1       ((int)((iso_edge_hash_ptr)i)->edge.p)
#define  k2       ((int)((iso_edge_hash_ptr)i)->edge.q)
#define  PRIME_1  10607 
#define  PRIME_2  18913   

    PseudoKey address = ( ( k1 * PRIME_1 ) + ( k2 * PRIME_2 ) );

#if 0
    DXDebug ( "I", "Address(%d,%d) = %d", k1, k2, address );
#endif

    return address;

#undef  PRIME_1
#undef  PRIME_2
#undef  k1
#undef  k2
}



#ifdef INCLUDES_BAND
static
PseudoKey band_edge_hash_func ( Key i )
{
#define  k1       ((int)((band_edge_hash_ptr)i)->edge.p)
#define  k2       ((int)((band_edge_hash_ptr)i)->edge.q)
#define  k3       ((int)((band_edge_hash_ptr)i)->select)
#define  PRIME_1  10607 
#define  PRIME_2  18913   
#define  PRIME_3  28001

    PseudoKey address = (( k1 * PRIME_1 )+ ( k2 * PRIME_2 )+ ( k3 * PRIME_3 ));

#if 0
    DXDebug ( "I", "Address(%d,%d,%d) = %d", k1, k2, k3, address );
#endif

    return address;

#undef  PRIME_1
#undef  PRIME_2
#undef  PRIME_3
#undef  k1
#undef  k2
#undef  k3
}
#endif



static
int iso_hash_enter ( Line *edge, hash_table_ptr hash, int *o )
{
    iso_edge_hash_rec  element;
    iso_edge_hash_ptr  element_ptr;

    if ( edge->p <= edge->q )
        element.edge = *edge;
    else
        { element.edge.p = edge->q; element.edge.q = edge->p; }

    if ( NULL != ( element_ptr
              = (iso_edge_hash_ptr) DXQueryHashElement
                                    ( hash->table, (Key)&element ) ) )
        *o = element_ptr->seq;
    else
    {
        *o = element.seq = (hash->count)++;

        if ( hash->set_vertices != NULL )
        {
            SETBIT ( hash->set_vertices, element.edge.p, 1 );
            SETBIT ( hash->set_vertices, element.edge.q, 1 );
        }

        if ( !DXInsertHashElement ( hash->table, (Element)&element ) )
            goto error;
    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
int band_hash_enter
        ( Line *edge, band_value_sel select, hash_table_ptr hash, int *o )
{
    band_edge_hash_rec  element;
    band_edge_hash_ptr  element_ptr;

   /* DXASSERTGOTO ( ( edge->p == edge->q ) == ( select == NEITHER_VALUE ) ); */

    if ( edge->p <= edge->q )
        element.edge = *edge;
    else
        { element.edge.p = edge->q; element.edge.q = edge->p; }

    element.select = select;

    if ( NULL != ( element_ptr
              = (band_edge_hash_ptr) DXQueryHashElement
                                    ( hash->table, (Key)&element ) ) )
        *o = element_ptr->seq;
    else
    {
        *o = element.seq = (hash->count)++;

        if ( !DXInsertHashElement ( hash->table, (Element)&element ) )
            goto error;
    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
int iso_hash_enter_indirect
      ( int *conn, Line *edge, hash_table_ptr hash, int *o, float *data_values )
{
    Line lookup;

    if ( data_values [ edge->p ] == 0.0 )
    {
        lookup.p = conn [ edge->p ];
        lookup.q = conn [ edge->p ];
    }
    else if ( data_values [ edge->q ] == 0.0 )
    {
        lookup.p = conn [ edge->q ];
        lookup.q = conn [ edge->q ];
    }
    else
    {
        lookup.p = conn [ edge->p ];
        lookup.q = conn [ edge->q ];
    }

    return iso_hash_enter ( &lookup, hash, o );
}



#ifdef INCLUDES_BAND
static
int band_hash_enter_indirect
        ( int *conn, Line *edge, band_value_sel select,
          hash_table_ptr hash, int *o, float *data_values, float *band_values )
{
    Line lookup;

    if ( select == NEITHER_VALUE )
    {
        lookup.p = conn [ edge->p ];
        lookup.q = conn [ edge->q ];
    }
    else
    {
        if ( data_values [ edge->p ] == band_values [ select ] )
        {
            lookup.p = conn [ edge->p ];
            lookup.q = conn [ edge->p ];
        }
        else if ( data_values [ edge->q ] == band_values [ select ] )
        {
            lookup.p = conn [ edge->q ];
            lookup.q = conn [ edge->q ];
        }
        else
        {
            lookup.p = conn [ edge->p ];
            lookup.q = conn [ edge->q ];
        }
    }

    return band_hash_enter ( &lookup, select, hash, o );
}
#endif




#define IVTX(E,N)    iso_hash_enter_indirect   \
                        ( conn, E,    vtab, &poly[N], data_values )
#define BVTX(E,S,N)  band_hash_enter_indirect  \
                        ( conn, E, S, vtab, &poly[N], data_values, band_values )
#define BCHK(E,S)    DXASSERT \
                        ( ( E->p == E->q ) == ( S == NEITHER_VALUE ) )

#define TRI(A,B,C) \
    if (( poly[A] != poly[B] )&&( poly[B] != poly[C] )&&( poly[C] != poly[A] ))\
        if (ERROR == ( tp = (Triangle*)DXNewSegListItem ( tlis ))) goto error;\
        else { tp->p = poly[A];  tp->q = poly[B];  tp->r = poly[C]; }


static
Error make_iso_line
          ( int *conn, Line *e0, Line *e1,
            hash_table_ptr vtab, hash_table_ptr ltab, float *data_values )
{
    int  poly[2];
    int  XXX;

#ifdef WHITEBOX_TEST
histograms.polygonalizations[2]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 )   )
      goto error;

    if ( poly[0] != poly[1] )
        if ( !iso_hash_enter ( (Line*)poly, ltab, &XXX ) )
            goto error;

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
Error make_band_line
          ( int *conn,
            Line          *e0,
            Line          *e1,
            band_value_sel s0,
            band_value_sel s1,
            hash_table_ptr vtab, SegList *llis, float *data_values,
            float *band_values )
{
    int   poly[2];
    Line  *lp;

#ifdef WHITEBOX_TEST
histograms.polygonalizations[2]++;
#endif
BCHK(e0,s0);BCHK(e1,s1);

    if ( !BVTX ( e0, s0, 0 ) ||
         !BVTX ( e1, s1, 1 )   )
      goto error;

    if ( poly[0] != poly[1] )
        if ( ERROR == ( lp = (Line*) DXNewSegListItem ( llis ) ) )
            goto error;
        else
        {
            lp->p = poly[0];
            lp->q = poly[1];
        }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error make_iso_poly_3
          ( int *conn, 
            Line *e0, Line *e1, Line *e2,
            hash_table_ptr vtab, SegList *tlis, float *data_values )
{
    int      poly[3];
    Triangle *tp;

#ifdef WHITEBOX_TEST
histograms.polygonalizations[3]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 ) ||
         !IVTX ( e2, 2 )   )
      goto error;

    TRI ( 0, 1, 2 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
Error make_band_poly_3
          ( int *conn, 
            Line *e0,
            Line *e1,
            Line *e2,
            band_value_sel s0,
            band_value_sel s1,
            band_value_sel s2,
            hash_table_ptr vtab, SegList *tlis, float *data_values,
            float *band_values )
{
    int      poly[3];
    Triangle *tp;

#ifdef WHITEBOX_TEST
histograms.polygonalizations[3]++;
#endif
BCHK(e0,s0);BCHK(e1,s1);BCHK(e2,s2);

    if ( !BVTX ( e0, s0, 0 ) ||
         !BVTX ( e1, s1, 1 ) ||
         !BVTX ( e2, s2, 2 )   )
      goto error;

    TRI ( 0, 1, 2 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif


static
Error make_iso_poly_4
          ( int *conn, 
            Line *e0, Line *e1, Line *e2, Line *e3,
            hash_table_ptr vtab, SegList *tlis, float *data_values )
{
    Triangle *tp;
    int      poly[4];


#ifdef WHITEBOX_TEST
histograms.polygonalizations[4]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 ) ||
         !IVTX ( e2, 2 ) ||
         !IVTX ( e3, 3 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
Error make_band_poly_4
          ( int *conn, 
            Line *e0,
            Line *e1,
            Line *e2,
            Line *e3,
            band_value_sel s0,
            band_value_sel s1,
            band_value_sel s2,
            band_value_sel s3,
            hash_table_ptr vtab, SegList *tlis, float *data_values,
            float *band_values )
{
    Triangle *tp;
    int      poly[4];


#ifdef WHITEBOX_TEST
histograms.polygonalizations[4]++;
#endif
BCHK(e0,s0);BCHK(e1,s1);BCHK(e2,s2);BCHK(e3,s3);

    if ( !BVTX ( e0, s0, 0 ) ||
         !BVTX ( e1, s1, 1 ) ||
         !BVTX ( e2, s2, 2 ) ||
         !BVTX ( e3, s3, 3 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error make_iso_poly_5
          ( int *conn, 
            Line *e0, Line *e1, Line *e2, Line *e3, Line *e4,
            hash_table_ptr vtab, SegList *tlis, float *data_values )
{
    Triangle *tp;
    int      poly[5];

#ifdef WHITEBOX_TEST
histograms.polygonalizations[5]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 ) ||
         !IVTX ( e2, 2 ) ||
         !IVTX ( e3, 3 ) ||
         !IVTX ( e4, 4 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );
    TRI ( 0, 3, 4 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
Error make_band_poly_5
          ( int *conn, 
            Line *e0,
            Line *e1,
            Line *e2,
            Line *e3,
            Line *e4,
            band_value_sel s0,
            band_value_sel s1,
            band_value_sel s2,
            band_value_sel s3,
            band_value_sel s4,
            hash_table_ptr vtab, SegList *tlis, float *data_values,
            float *band_values )
{
    Triangle *tp;
    int      poly[5];

#ifdef WHITEBOX_TEST
histograms.polygonalizations[5]++;
#endif
BCHK(e0,s0);BCHK(e1,s1);BCHK(e2,s2);BCHK(e3,s3);BCHK(e4,s4);

    if ( !BVTX ( e0, s0, 0 ) ||
         !BVTX ( e1, s1, 1 ) ||
         !BVTX ( e2, s2, 2 ) ||
         !BVTX ( e3, s3, 3 ) ||
         !BVTX ( e4, s4, 4 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );
    TRI ( 0, 3, 4 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error make_iso_poly_6
          ( int *conn, 
            Line *e0, Line *e1, Line *e2, Line *e3, Line *e4, Line *e5,
            hash_table_ptr vtab, SegList *tlis, float *data_values )
{
    Triangle *tp;
    int      poly[6];

#ifdef WHITEBOX_TEST
histograms.polygonalizations[6]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 ) ||
         !IVTX ( e2, 2 ) ||
         !IVTX ( e3, 3 ) ||
         !IVTX ( e4, 4 ) ||
         !IVTX ( e5, 5 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );
    TRI ( 0, 3, 4 );
    TRI ( 0, 4, 5 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}


#ifdef INCLUDES_BAND
static
Error make_band_poly_6
          ( int *conn, 
            Line *e0,
            Line *e1,
            Line *e2,
            Line *e3,
            Line *e4,
            Line *e5,
            band_value_sel s0,
            band_value_sel s1,
            band_value_sel s2,
            band_value_sel s3,
            band_value_sel s4,
            band_value_sel s5,
            hash_table_ptr vtab, SegList *tlis, float *data_values,
            float *band_values )
{
    Triangle *tp;
    int      poly[6];

#ifdef WHITEBOX_TEST
histograms.polygonalizations[6]++;
#endif
BCHK(e0,s0);BCHK(e1,s1);BCHK(e2,s2);BCHK(e3,s3);BCHK(e4,s4);BCHK(e5,s5);

    if ( !BVTX ( e0, s0, 0 ) ||
         !BVTX ( e1, s1, 1 ) ||
         !BVTX ( e2, s2, 2 ) ||
         !BVTX ( e3, s3, 3 ) ||
         !BVTX ( e4, s4, 4 ) ||
         !BVTX ( e5, s5, 5 )   )
      goto error;

    TRI ( 0, 1, 2 );
    TRI ( 0, 2, 3 );
    TRI ( 0, 3, 4 );
    TRI ( 0, 4, 5 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif




static
Error make_iso_poly_7
         ( int *conn, 
           Line *e0, Line *e1, Line *e2,
           Line *e3, Line *e4, Line *e5, Line *e6,
           hash_table_ptr vtab, SegList *tlis, float *data_values )
{
    Triangle *tp;
    int      poly[7];

#ifdef WHITEBOX_TEST
histograms.polygonalizations[7]++;
#endif

    if ( !IVTX ( e0, 0 ) ||
         !IVTX ( e1, 1 ) ||
         !IVTX ( e2, 2 ) ||
         !IVTX ( e3, 3 ) ||
         !IVTX ( e4, 4 ) ||
         !IVTX ( e5, 5 ) ||
         !IVTX ( e6, 6 )   )
      goto error;

    TRI ( 0, 1, 3 );
    TRI ( 0, 3, 4 );
    TRI ( 0, 4, 6 );
    TRI ( 6, 4, 5 );
    TRI ( 1, 2, 3 );

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}

#undef  IVTX
#undef  BVTX
#undef  POLY



#ifdef INCLUDES_BAND
static
Error band_convert_line
          ( Line            *line,
            float           data_values[2],
            float           *value_p,
            int             index,
            hash_table_ptr  vtx_hash,
            SegList         *line_list,
            int             equivalence )
{
    float value_low  = value_p [ index ];
    float value_high = value_p [ index + 1 ];

    static Line line_edge[3] = { { 0, 1 }, { 0, 0 }, { 1, 1 } };

    int  signature = 0;

    if ( equivalence == 1 );

    if ( data_values[0] > value_low  ) signature += 1;
    if ( data_values[0] > value_high ) signature += 2;
    if ( data_values[1] > value_low  ) signature += 4;
    if ( data_values[1] > value_high ) signature += 8;

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

#define LINE(A,B,C,D)  if ( !make_band_line ( (int *)line, \
                   &line_edge[A], \
                   &line_edge[B], \
                   ((C==0)?-1:(index+C-1)), \
                   ((D==0)?-1:(index+D-1)), \
               vtx_hash, line_list, data_values, value_p ) ) goto error;

        case  0:
        case 15: break;

        case  1: LINE ( 1,0,  0,1 );  break;
        case  3: LINE ( 0,0,  2,1 );  break;
        case  4: LINE ( 0,2,  1,0 );  break;
        case  5: LINE ( 1,2,  0,0 );  break;
        case  7: LINE ( 0,2,  2,0 );  break;
        case 12: LINE ( 0,0,  1,2 );  break;
        case 13: LINE ( 1,0,  0,2 );  break;

        default:
            DXSetError ( ERROR_ASSERTION, "case signature %d", signature );
            goto error;

#undef  LINE

    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error iso_convert_triangle
          ( Triangle        *tri,
            float           data_values[3],
            hash_table_ptr  vtx_hash,
            hash_table_ptr  edge_hash,
            int             equivalence )
{
    static Line tri_edge[3] = { { 0, 1 }, { 1, 2 }, { 2, 0 } };

    int  signature = 0;

    if ( equivalence == 1 )
    {
        if ( data_values[0] > 0.0 ) signature += 1;
        if ( data_values[1] > 0.0 ) signature += 2;
        if ( data_values[2] > 0.0 ) signature += 4;
    }
    else
    {
        if ( data_values[0] == 0.0 ) signature += 1;
        if ( data_values[1] == 0.0 ) signature += 2;
        if ( data_values[2] == 0.0 ) signature += 4;
    }

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

#define LINE(A,B)  if ( !make_iso_line ( (int *)tri, &tri_edge[A], \
                     &tri_edge[B], vtx_hash, edge_hash, data_values ) ) \
                   goto error;

        case 0: break;
        case 1: LINE ( 0, 2 );  break;
        case 2: LINE ( 0, 1 );  break;
        case 3: LINE ( 1, 2 );  break;
        case 4: LINE ( 1, 2 );  break;
        case 5: LINE ( 0, 1 );  break;
        case 6: LINE ( 0, 2 );  break;
        case 7: break;

#undef  LINE

    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}


#ifdef INCLUDES_BAND
static
Error band_convert_triangle
          ( Triangle        *tri,
            float           data_values[3],
            float           *value_p,
            int             index,
            hash_table_ptr  vtx_hash,
            SegList         *tri_list,
            int             equivalence )
{
    float value_low  = value_p [ index ];
    float value_high = value_p [ index + 1 ];

    static Line tri_edge[6] = { { 0, 1 }, { 1, 2 }, { 2, 0 },
                                { 0, 0 }, { 1, 1 }, { 2, 2 } };

    int  signature = 0;

    if ( equivalence == 1 );

    if ( data_values[0] > value_low  ) signature += 1;
    if ( data_values[0] > value_high ) signature += 2;
    if ( data_values[1] > value_low  ) signature += 4;
    if ( data_values[1] > value_high ) signature += 8;
    if ( data_values[2] > value_low  ) signature += 16;
    if ( data_values[2] > value_high ) signature += 32;

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */


#define POLY3(A,B,C,D,E,F)  if(!make_band_poly_3((int*)tri, \
                      &tri_edge[A], \
                      &tri_edge[B], \
                      &tri_edge[C], \
                      ((D==0)?-1:(index+D-1)), \
                      ((E==0)?-1:(index+E-1)), \
                      ((F==0)?-1:(index+F-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

#define POLY4(A,B,C,D,E,F,G,H)  if(!make_band_poly_4((int*)tri, \
                      &tri_edge[A], \
                      &tri_edge[B], \
                      &tri_edge[C], \
                      &tri_edge[D], \
                      ((E==0)?-1:(index+E-1)), \
                      ((F==0)?-1:(index+F-1)), \
                      ((G==0)?-1:(index+G-1)), \
                      ((H==0)?-1:(index+H-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

#define POLY5(A,B,C,D,E,F,G,H,I,J)  if(!make_band_poly_5((int*)tri, \
                      &tri_edge[A], \
                      &tri_edge[B], \
                      &tri_edge[C], \
                      &tri_edge[D], \
                      &tri_edge[E], \
                      ((F==0)?-1:(index+F-1)), \
                      ((G==0)?-1:(index+G-1)), \
                      ((H==0)?-1:(index+H-1)), \
                      ((I==0)?-1:(index+I-1)), \
                      ((J==0)?-1:(index+J-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

        case  0:
        case 63: break;

        case  1: POLY3 ( 0,2,3,      1,1,0     );  break;
        case  3: POLY4 ( 0,0,2,2,    2,1,1,2   );  break;
        case  4: POLY3 ( 4,1,0,      0,1,1     );  break;
        case  5: POLY4 ( 3,4,1,2,    0,0,1,1   );  break;
        case  7: POLY5 ( 0,4,1,2,2,  2,0,1,1,2 );  break;
        case 12: POLY4 ( 0,0,1,1,    1,2,2,1   );  break;
        case 13: POLY5 ( 3,0,1,1,2,  0,2,2,1,1 );  break;
        case 15: POLY4 ( 2,1,1,2,    2,2,1,1   );  break;
        case 16: POLY3 ( 2,1,5,      1,1,0     );  break;
        case 17: POLY4 ( 3,0,1,5,    0,1,1,0   );  break;
        case 19: POLY5 ( 0,0,1,5,2,  2,1,1,0,2 );  break;
        case 20: POLY4 ( 0,4,5,2,    1,0,0,1   );  break;
        case 21: POLY3 ( 3,4,5,      0,0,0     );  break;
        case 23: POLY4 ( 0,4,5,2,    2,0,0,2   );  break;
        case 28: POLY5 ( 0,0,1,5,2,  1,2,2,0,1 );  break;
        case 29: POLY4 ( 3,0,1,5,    0,2,2,0   );  break;
        case 31: POLY3 ( 2,1,5,      2,2,0     );  break;
        case 48: POLY4 ( 2,1,1,2,    1,1,2,2   );  break;
        case 49: POLY5 ( 3,0,1,1,2,  0,1,1,2,2 );  break;
        case 51: POLY4 ( 0,0,1,1,    2,1,1,2   );  break;
        case 52: POLY5 ( 0,4,1,2,2,  1,0,2,2,1 );  break;
        case 53: POLY4 ( 3,4,1,2,    0,0,2,2   );  break;
        case 55: POLY3 ( 0,4,1,      2,0,2     );  break;
        case 60: POLY4 ( 0,0,2,2,    1,2,2,1   );  break;
        case 61: POLY3 ( 3,0,2,      0,2,2     );  break;

        default:
            DXSetError ( ERROR_ASSERTION, "case signature %d", signature );
            goto error;

#undef  POLY3
#undef  POLY4
#undef  POLY5

    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error iso_convert_quadrilateral
          ( Quadrilateral   *quad,
            float           data_values[4],
            hash_table_ptr  vtx_hash,
            hash_table_ptr  edge_hash,
            int             equivalence )
{

#if 0

    Triangle n_tri;
    float    n_data_values[3];
    int      i, j;

    static int cvt[2][3] = { { 0, 2, 3 }, { 0, 3, 1 } };

    for ( i=0; i<2; i++ )
    {
        for ( j=0; j<3; j++ )
        {
            n_data_values[j]   = data_values   [ cvt[i][j] ];
            ((int *)&n_tri)[j] = ((int *)quad) [ cvt[i][j] ];
        }

        if ( !iso_convert_triangle
                  ( &n_tri, n_data_values,
                    vtx_hash, edge_hash, equivalence ) )
            goto error;
    }

#else

    static Line quad_edge[4] = { { 0, 1 }, { 1, 3 }, { 2, 3 }, { 0, 2 } };

    int  signature = 0;

    if ( equivalence == 1 )
    {
        if ( data_values[0] > 0.0 ) signature += 1;
        if ( data_values[1] > 0.0 ) signature += 2;
        if ( data_values[2] > 0.0 ) signature += 4;
        if ( data_values[3] > 0.0 ) signature += 8;
    }
    else
    {
        if ( data_values[0] == 0.0 ) signature += 1;
        if ( data_values[1] == 0.0 ) signature += 2;
        if ( data_values[2] == 0.0 ) signature += 4;
        if ( data_values[3] == 0.0 ) signature += 8;
    }

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

#define LINE(A,B)  if ( !make_iso_line ( (int *)quad, &quad_edge[A], \
                           &quad_edge[B], vtx_hash, edge_hash, data_values ) ) \
                   goto error;

        case  0: break;
        case  1: LINE ( 0,3 );               break;
        case  2: LINE ( 0,1 );               break;
        case  3: LINE ( 1,3 );               break;
        case  4: LINE ( 2,3 );               break;
        case  5: LINE ( 0,2 );               break;
        case  6: LINE ( 0,1 ); LINE ( 2,3 ); break;
        case  7: LINE ( 1,2 );               break;
        case  8: LINE ( 1,2 );               break;
        case  9: LINE ( 0,3 ); LINE ( 1,2 ); break;
        case 10: LINE ( 0,2 );               break;
        case 11: LINE ( 2,3 );               break;
        case 12: LINE ( 1,3 );               break;
        case 13: LINE ( 0,1 );               break;
        case 14: LINE ( 0,3 );               break;
        case 15: break;

#undef  LINE

    }

#endif

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



#ifdef INCLUDES_BAND
static
Error band_convert_quadrilateral
          ( Quadrilateral   *quad,
            float           data_values[4],
            float           *value_p,
            int             index,
            hash_table_ptr  vtx_hash,
            SegList         *tri_list,
            int             equivalence )
{

#if 0

    Triangle n_tri;
    float    n_data_values[3];
    int      i, j;

    static int cvt[2][3] = { { 0, 2, 3 }, { 0, 3, 1 } };

    for ( i=0; i<2; i++ )
    {
        for ( j=0; j<3; j++ )
        {
            n_data_values[j]   = data_values   [ cvt[i][j] ];
            ((int *)&n_tri)[j] = ((int *)quad) [ cvt[i][j] ];
        }

        if ( !band_convert_triangle
                  ( &n_tri, n_data_values, value_p, index,
                     vtx_hash, tri_list, equivalence ) )
            goto error;
    }

#else

    float value_low  = value_p [ index ];
    float value_high = value_p [ index + 1 ];

    static Line quad_edge[8] = { { 0, 1 }, { 1, 3 }, { 2, 3 }, { 0, 2 },
                                 { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 } };

    int  signature = 0;

    if ( equivalence == 1 );

    if ( data_values[0] > value_low  ) signature += 1;
    if ( data_values[0] > value_high ) signature += 2;
    if ( data_values[1] > value_low  ) signature += 4;
    if ( data_values[1] > value_high ) signature += 8;
    if ( data_values[2] > value_low  ) signature += 16;
    if ( data_values[2] > value_high ) signature += 32;
    if ( data_values[3] > value_low  ) signature += 64;
    if ( data_values[3] > value_high ) signature += 128;

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {

#define POLY3(A,B,C,D,E,F)  if(!make_band_poly_3((int*)quad, \
                      &quad_edge[A], \
                      &quad_edge[B], \
                      &quad_edge[C], \
                      ((D==0)?-1:(index+D-1)), \
                      ((E==0)?-1:(index+E-1)), \
                      ((F==0)?-1:(index+F-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

#define POLY4(A,B,C,D,E,F,G,H)  if(!make_band_poly_4((int*)quad, \
                      &quad_edge[A], \
                      &quad_edge[B], \
                      &quad_edge[C], \
                      &quad_edge[D], \
                      ((E==0)?-1:(index+E-1)), \
                      ((F==0)?-1:(index+F-1)), \
                      ((G==0)?-1:(index+G-1)), \
                      ((H==0)?-1:(index+H-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

#define POLY5(A,B,C,D,E,F,G,H,I,J)  if(!make_band_poly_5((int*)quad, \
                      &quad_edge[A], \
                      &quad_edge[B], \
                      &quad_edge[C], \
                      &quad_edge[D], \
                      &quad_edge[E], \
                      ((F==0)?-1:(index+F-1)), \
                      ((G==0)?-1:(index+G-1)), \
                      ((H==0)?-1:(index+H-1)), \
                      ((I==0)?-1:(index+I-1)), \
                      ((J==0)?-1:(index+J-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

#define POLY6(A,B,C,D,E,F,G,H,I,J,K,L)  if(!make_band_poly_6((int*)quad, \
                      &quad_edge[A], \
                      &quad_edge[B], \
                      &quad_edge[C], \
                      &quad_edge[D], \
                      &quad_edge[E], \
                      &quad_edge[F], \
                      ((G==0)?-1:(index+G-1)), \
                      ((H==0)?-1:(index+H-1)), \
                      ((I==0)?-1:(index+I-1)), \
                      ((J==0)?-1:(index+J-1)), \
                      ((K==0)?-1:(index+K-1)), \
                      ((L==0)?-1:(index+L-1)), \
                vtx_hash, tri_list, data_values, value_p ) ) goto error;

        case   0:
        case 255: break;

        case   1: POLY3 ( 4,0,3,        0,1,1       );  break;
        case   3: POLY4 ( 0,0,3,3,      2,1,1,2     );  break;
        case   4: POLY3 ( 0,5,1,        1,0,1       );  break;
        case   5: POLY4 ( 4,5,1,3,      0,0,1,1     );  break;
        case   7: POLY5 ( 0,5,1,3,3,    2,0,1,1,2   );  break;
        case  12: POLY4 ( 0,0,1,1,      1,2,2,1     );  break;
        case  13: POLY5 ( 4,0,1,1,3,    0,2,2,1,1   );  break;
        case  15: POLY4 ( 3,1,1,3,      2,2,1,1     );  break;
        case  16: POLY3 ( 3,2,6,        1,1,0       );  break;
        case  17: POLY4 ( 4,0,2,6,      0,1,1,0     );  break;
        case  19: POLY5 ( 0,0,2,6,3,    2,1,1,0,2   );  break;
        case  20: POLY3 ( 0,5,1,        1,0,1       );
                  POLY3 ( 3,2,6,        1,1,0       );  break;
        case  21: POLY5 ( 4,5,1,2,6,    0,0,1,1,0   );  break;
        case  23: POLY6 ( 0,5,1,2,6,3,  2,0,1,1,0,2 );  break;
        case  28: POLY3 ( 3,2,6,        1,1,0       );
                  POLY4 ( 0,0,1,1,      1,2,2,1     );  break;
        case  29: POLY6 ( 4,0,1,1,2,6,  0,2,2,1,1,0 );  break;
        case  31: POLY5 ( 3,1,1,2,6,    2,2,1,1,0   );  break;
        case  48: POLY4 ( 3,2,2,3,      1,1,2,2     );  break;
        case  49: POLY5 ( 4,0,2,2,3,    0,1,1,2,2   );  break;
        case  51: POLY4 ( 0,0,2,2,      2,1,1,2     );  break;
        case  52: POLY3 ( 0,5,1,        1,0,1       ); 
                  POLY4 ( 3,2,2,3,      1,1,2,2     );  break;
        case  53: POLY6 ( 4,5,1,2,2,3,  0,0,1,1,2,2 );  break;
        case  55: POLY5 ( 0,5,1,2,2,    2,0,1,1,2   );  break;
        case  60: POLY4 ( 3,2,2,3,      1,1,2,2     );
                  POLY4 ( 0,0,1,1,      1,2,2,1     );  break;
        case  61: POLY3 ( 4,0,3,        0,2,2       );
                  POLY6 ( 0,1,1,2,2,3,  2,2,1,1,2,2 );  break;
        case  63: POLY4 ( 2,1,1,2,      2,2,1,1     );  break;
        case  64: POLY3 ( 1,7,2,        1,0,1       );  break;
        case  65: POLY3 ( 4,0,3,        0,1,1       );
                  POLY3 ( 1,7,2,        1,0,1       );  break;
        case  67: POLY3 ( 2,1,7,        1,1,0       ); 
                  POLY4 ( 3,0,0,3,      2,2,1,1     );  break;
        case  68: POLY4 ( 0,5,7,2,      1,0,0,1     );  break;
        case  69: POLY5 ( 4,5,7,2,3,    0,0,0,1,1   );  break;
        case  71: POLY6 ( 0,5,7,2,3,3,  2,0,0,1,1,2 );  break;
        case  76: POLY5 ( 0,0,1,7,2,    1,2,2,0,1   );  break;
        case  77: POLY6 ( 4,0,1,7,2,3,  0,2,2,0,1,1 );  break;
        case  79: POLY5 ( 3,1,7,2,3,    2,2,0,1,1   );  break;
        case  80: POLY4 ( 3,1,7,6,      1,1,0,0     );  break;
        case  81: POLY5 ( 4,0,1,7,6,    0,1,1,0,0   );  break;
        case  83: POLY6 ( 0,0,1,7,6,3,  2,1,1,0,0,2 );  break;
        case  84: POLY5 ( 0,5,7,6,3,    1,0,0,0,1   );  break;
        case  85: POLY4 ( 4,5,7,6,      0,0,0,0     );  break;
        case  87: POLY5 ( 0,5,7,6,3,    2,0,0,0,2   );  break;
        case  92: POLY6 ( 0,0,1,7,6,3,  1,2,2,0,0,1 );  break;
        case  93: POLY5 ( 4,0,1,7,6,    0,2,2,0,0   );  break;
        case  95: POLY4 ( 3,1,7,6,      2,2,0,0     );  break;
        case 112: POLY5 ( 3,1,7,2,3,    1,1,0,2,2   );  break;
        case 113: POLY6 ( 4,0,1,7,2,3,  0,1,1,0,2,2 );  break;
        case 115: POLY5 ( 0,0,1,7,2,    2,1,1,0,2   );  break;
        case 116: POLY6 ( 0,5,7,2,3,3,  1,0,0,2,2,1 );  break;
        case 117: POLY5 ( 4,5,7,2,3,    0,0,0,2,2   );  break;
        case 119: POLY4 ( 0,5,7,2,      2,0,0,2     );  break;
        case 124: POLY3 ( 1,7,2,        2,0,2       );
                  POLY6 ( 0,0,1,2,3,3,  1,2,2,2,2,1 );  break;
        case 125: POLY6 ( 4,0,1,7,2,3,  0,2,2,0,2,2 );  break;
        case 127: POLY3 ( 1,7,2,        2,0,2       );  break;
        case 192: POLY4 ( 1,1,2,2,      1,2,2,1     );  break;
        case 193: POLY3 ( 4,0,3,        0,1,1       );
                  POLY4 ( 1,1,2,2,      1,2,2,1     );  break;
        case 195: POLY4 ( 0,0,3,3,      2,1,1,2     );
                  POLY4 ( 1,1,2,2,      1,2,2,1     );  break;
        case 196: POLY5 ( 0,5,1,2,2,    1,0,2,2,1   );  break;
        case 197: POLY6 ( 4,5,1,2,2,3,  0,0,2,2,1,1 );  break;
        case 199: POLY3 ( 0,5,1,        2,0,2       ); 
                  POLY6 ( 0,1,2,2,3,3,  2,2,2,1,1,2 );  break;
        case 204: POLY4 ( 0,0,2,2,      1,2,2,1     );  break;
        case 205: POLY5 ( 4,0,2,2,3,    0,2,2,1,1   );  break;
        case 207: POLY4 ( 3,2,2,3,      2,2,1,1     );  break;
        case 208: POLY5 ( 3,1,1,2,6,    1,1,2,2,0   );  break;
        case 209: POLY6 ( 4,0,1,1,2,6,  0,1,1,2,2,0 );  break;
        case 211: POLY3 ( 3,2,6,        2,2,0       ); 
                  POLY6 ( 0,0,1,1,2,3,  2,1,1,2,2,2 );  break;
        case 212: POLY6 ( 0,5,1,2,6,3,  1,0,2,2,0,1 );  break;
        case 213: POLY5 ( 4,5,1,2,6,    0,0,2,2,0   );  break;
        case 215: POLY6 ( 3,0,5,1,2,6,  2,2,0,2,2,0 );  break;
        case 220: POLY5 ( 0,0,2,6,3,    1,2,2,0,1   );  break;
        case 221: POLY4 ( 4,0,2,6,      0,2,2,0     );  break;
        case 223: POLY3 ( 3,2,6,        2,2,0       );  break;
        case 240: POLY4 ( 3,1,1,3,      1,1,2,2     );  break;
        case 241: POLY5 ( 4,0,1,1,3,    0,1,1,2,2   );  break;
        case 243: POLY4 ( 0,0,1,1,      2,1,1,2     );  break;
        case 244: POLY5 ( 0,5,1,3,3,    1,0,2,2,1   );  break;
        case 245: POLY4 ( 4,5,1,3,      0,0,2,2     );  break;
        case 247: POLY3 ( 0,5,1,        2,0,2       );  break;
        case 252: POLY4 ( 0,0,3,3,      1,2,2,1     );  break;
        case 253: POLY3 ( 4,0,3,        0,2,2       );  break;

        default:
            DXSetError ( ERROR_ASSERTION, "case signature %d", signature );
            goto error;

#undef  POLY3
#undef  POLY4
#undef  POLY5
#undef  POLY6

    }

#endif

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



static
Error iso_convert_tetrahedron
          ( Tetrahedron     *tet,
            float           data_values[4],
            hash_table_ptr  vtx_hash,
            SegList         *tri_list,
            int             equivalence )
{
    static Line tetra_edge[6]
        = { { 0, 1 }, { 1, 2 }, { 0, 2 }, { 0, 3 }, { 1, 3 }, { 2, 3 } };

    int  signature = 0;

    if ( equivalence == 1 )
    {
        if ( data_values[0] > 0.0 ) signature += 1;
        if ( data_values[1] > 0.0 ) signature += 2;
        if ( data_values[2] > 0.0 ) signature += 4;
        if ( data_values[3] > 0.0 ) signature += 8;
    }
    else
    {
        if ( data_values[0] == 0.0 ) signature += 1;
        if ( data_values[1] == 0.0 ) signature += 2;
        if ( data_values[2] == 0.0 ) signature += 4;
        if ( data_values[3] == 0.0 ) signature += 8;
    }

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

        /* coded in inversions imply that the table is reversed */

#define POLY3(A,B,C)  if(!make_iso_poly_3((int*)tet, \
                         &tetra_edge[C], &tetra_edge[B], &tetra_edge[A], \
                         vtx_hash, tri_list, data_values ) ) goto error;

#define POLY4(A,B,C,D)  if(!make_iso_poly_4((int*)tet, \
                         &tetra_edge[D], &tetra_edge[C], \
                         &tetra_edge[B], &tetra_edge[A], \
                         vtx_hash, tri_list, data_values ) ) goto error;

            case  0:  break;
            case  1:  POLY3 ( 0, 2, 3 );     break;
            case  2:  POLY3 ( 0, 4, 1 );     break;
            case  3:  POLY4 ( 1, 2, 3, 4 );  break;
            case  4:  POLY3 ( 1, 5, 2 );     break;
            case  5:  POLY4 ( 0, 1, 5, 3 );  break;
            case  6:  POLY4 ( 0, 4, 5, 2 );  break;
            case  7:  POLY3 ( 3, 4, 5 );     break;
            case  8:  POLY3 ( 3, 5, 4 );     break;
            case  9:  POLY4 ( 0, 2, 5, 4 );  break;
            case 10:  POLY4 ( 0, 3, 5, 1 );  break;
            case 11:  POLY3 ( 1, 2, 5 );     break;
            case 12:  POLY4 ( 1, 4, 3, 2 );  break;
            case 13:  POLY3 ( 0, 1, 4 );     break;
            case 14:  POLY3 ( 0, 3, 2 );     break;
            case 15:  break;

#undef  POLY3
#undef  POLY4

        default: break;
    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
Error iso_convert_cube
          ( Cube            *cube,
            float           data_values[8],
            hash_table_ptr  vtx_hash,
            SegList         *tri_list,
            int             equivalence )
{
    static Line cube_edge[12]
        = { { 0, 1 }, { 1, 3 }, { 0, 2 }, { 2, 3 },
            { 4, 5 }, { 5, 7 }, { 4, 6 }, { 6, 7 },
            { 0, 4 }, { 1, 5 }, { 2, 6 }, { 3, 7 } };

    int  signature = 0;

    if ( equivalence == 1 )
    {
        if ( data_values[0] > 0.0 ) signature += 1;
        if ( data_values[1] > 0.0 ) signature += 2;
        if ( data_values[2] > 0.0 ) signature += 4;
        if ( data_values[3] > 0.0 ) signature += 8;
        if ( data_values[4] > 0.0 ) signature += 16;
        if ( data_values[5] > 0.0 ) signature += 32;
        if ( data_values[6] > 0.0 ) signature += 64;
        if ( data_values[7] > 0.0 ) signature += 128;
    }
    else
    {
        if ( data_values[0] == 0.0 ) signature += 1;
        if ( data_values[1] == 0.0 ) signature += 2;
        if ( data_values[2] == 0.0 ) signature += 4;
        if ( data_values[3] == 0.0 ) signature += 8;
        if ( data_values[4] == 0.0 ) signature += 16;
        if ( data_values[5] == 0.0 ) signature += 32;
        if ( data_values[6] == 0.0 ) signature += 64;
        if ( data_values[7] == 0.0 ) signature += 128;
    }

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

#define POLY3(A,B,C)  if(!make_iso_poly_3((int*)cube, \
                         &cube_edge[A], &cube_edge[B], &cube_edge[C], \
                         vtx_hash, tri_list, data_values ) ) goto error;

#define POLY4(A,B,C,D)  if(!make_iso_poly_4((int*)cube, \
                         &cube_edge[A], &cube_edge[B], &cube_edge[C], \
                         &cube_edge[D], \
                         vtx_hash, tri_list, data_values ) ) goto error;

#define POLY5(A,B,C,D,E)  if(!make_iso_poly_5((int*)cube, \
                         &cube_edge[A], &cube_edge[B], &cube_edge[C], \
                         &cube_edge[D], &cube_edge[E], \
                         vtx_hash, tri_list, data_values ) ) goto error;

#define POLY6(A,B,C,D,E,F)  if(!make_iso_poly_6((int*)cube, \
                         &cube_edge[A], &cube_edge[B], &cube_edge[C], \
                         &cube_edge[D], &cube_edge[E], &cube_edge[F], \
                         vtx_hash, tri_list, data_values ) ) goto error;

#define POLY7(A,B,C,D,E,F,G)  if(!make_iso_poly_7((int*)cube, \
                         &cube_edge[A], &cube_edge[B], &cube_edge[C], \
                         &cube_edge[D], &cube_edge[E], &cube_edge[F], \
                         &cube_edge[G], \
                         vtx_hash, tri_list, data_values ) ) goto error;

        case   0:                                   break;
        case   1: POLY3 ( 0, 2, 8 );                break;
        case   2: POLY3 ( 0, 9, 1 );                break;
        case   3: POLY4 ( 1, 2, 8, 9 );             break;
        case   4: POLY3 ( 2, 3, 10 );               break;
        case   5: POLY4 ( 0, 3, 10, 8 );            break;
        case   6: POLY3 ( 0, 9, 1 );  
                  POLY3 ( 2, 3, 10 );               break;
        case   7: POLY5 ( 1, 3, 10, 8, 9 );         break;
        case   8: POLY3 ( 1, 11, 3 );               break;
        case   9: POLY3 ( 1, 11, 3 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  10: POLY4 ( 0, 9, 11, 3 );            break;
        case  11: POLY5 ( 2, 8, 9, 11, 3 );         break;
        case  12: POLY4 ( 1, 11, 10, 2 );           break;
        case  13: POLY5 ( 0, 1, 11, 10, 8 );        break;
        case  14: POLY5 ( 0, 9, 11, 10, 2 );        break;
        case  15: POLY4 ( 8, 9, 11, 10 );           break;
        case  16: POLY3 ( 4, 8, 6 );                break;
        case  17: POLY4 ( 0, 2, 6, 4 );             break;
        case  18: POLY3 ( 4, 8, 6 );  
                  POLY3 ( 0, 9, 1 );                break;
        case  19: POLY5 ( 1, 2, 6, 4, 9 );          break;
        case  20: POLY3 ( 4, 8, 6 );  
                  POLY3 ( 2, 3, 10 );               break;
        case  21: POLY5 ( 0, 3, 10, 6, 4 );         break;
        case  22: POLY3 ( 4, 8, 6 );  
                  POLY3 ( 2, 3, 10 );  
                  POLY3 ( 0, 9, 1 );                break;
        case  23: POLY6 ( 1, 3, 10, 6, 4, 9 );      break;
        case  24: POLY3 ( 4, 8, 6 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  25: POLY4 ( 0, 2, 6, 4 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  26: POLY3 ( 4, 8, 6 );  
                  POLY4 ( 0, 9, 11, 3 );            break;
        case  27: POLY6 ( 2, 6, 4, 9, 11, 3 );      break;
        case  28: POLY3 ( 4, 8, 6 );  
                  POLY4 ( 1, 11, 10, 2 );           break;
        case  29: POLY6 ( 0, 1, 11, 10, 6, 4 );     break;
        case  30: POLY3 ( 4, 8, 6 );  
                  POLY5 ( 0, 9, 11, 10, 2 );        break;
        case  31: POLY5 ( 4, 9, 11, 10, 6 );        break;
        case  32: POLY3 ( 4, 5, 9 );                break;
        case  33: POLY3 ( 4, 5, 9 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  34: POLY4 ( 0, 4, 5, 1 );             break;
        case  35: POLY5 ( 1, 2, 8, 4, 5 );          break;
        case  36: POLY3 ( 2, 3, 10 );  
                  POLY3 ( 4, 5, 9 );                break;
        case  37: POLY4 ( 0, 3, 10, 8 );  
                  POLY3 ( 4, 5, 9 );                break;
        case  38: POLY4 ( 0, 4, 5, 1 );  
                  POLY3 ( 2, 3, 10 );               break;
        case  39: POLY6 ( 1, 3, 10, 8, 4, 5 );      break;
        case  40: POLY3 ( 4, 5, 9 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  41: POLY3 ( 4, 5, 9 );  
                  POLY3 ( 1, 11, 3 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  42: POLY5 ( 0, 4, 5, 11, 3 );         break;
        case  43: POLY6 ( 2, 8, 4, 5, 11, 3 );      break;
        case  44: POLY3 ( 4, 5, 9 );  
                  POLY4 ( 1, 11, 10, 2 );           break;
        case  45: POLY3 ( 4, 5, 9 );  
                  POLY5 ( 0, 1, 11, 10, 8 );        break;
        case  46: POLY6 ( 0, 4, 5, 11, 10, 2 );     break;
        case  47: POLY5 ( 4, 5, 11, 10, 8 );        break;
        case  48: POLY4 ( 5, 9, 8, 6 );             break;
        case  49: POLY5 ( 0, 2, 6, 5, 9 );          break;
        case  50: POLY5 ( 0, 8, 6, 5, 1 );          break;
        case  51: POLY4 ( 1, 2, 6, 5 );             break;
        case  52: POLY4 ( 5, 9, 8, 6 );  
                  POLY3 ( 2, 3, 10 );               break;
        case  53: POLY6 ( 0, 3, 10, 6, 5, 9 );      break;
        case  54: POLY3 ( 2, 3, 10 );  
                  POLY5 ( 0, 8, 6, 5, 1 );          break;
        case  55: POLY5 ( 1, 3, 10, 6, 5 );         break;
        case  56: POLY3 ( 1, 11, 3 );  
                  POLY4 ( 5, 9, 8, 6 );             break;
        case  57: POLY3 ( 1, 11, 3 );  
                  POLY5 ( 0, 2, 6, 5, 9 );          break;
        case  58: POLY6 ( 0, 8, 6, 5, 11, 3 );      break;
        case  59: POLY5 ( 2, 6, 5, 11, 3 );         break;
        case  60: POLY4 ( 5, 9, 8, 6 );  
                  POLY4 ( 1, 11, 10, 2 );           break;
        case  61: POLY7 ( 0, 1, 11, 10, 6, 5, 9 );  break;
        case  62: POLY7 ( 0, 8, 6, 5, 11, 10, 2 );  break;
        case  63: POLY4 ( 5, 11, 10, 6 );           break;
        case  64: POLY3 ( 6, 10, 7 );               break;
        case  65: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  66: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 0, 9, 1 );                break;
        case  67: POLY3 ( 6, 10, 7 );  
                  POLY4 ( 1, 2, 8, 9 );             break;
        case  68: POLY4 ( 2, 3, 7, 6 );             break;
        case  69: POLY5 ( 0, 3, 7, 6, 8 );          break;
        case  70: POLY3 ( 0, 9, 1 );  
                  POLY4 ( 2, 3, 7, 6 );             break;
        case  71: POLY6 ( 1, 3, 7, 6, 8, 9 );       break;
        case  72: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  73: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 1, 11, 3 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  74: POLY3 ( 6, 10, 7 );  
                  POLY4 ( 0, 9, 11, 3 );            break;
        case  75: POLY3 ( 6, 10, 7 );  
                  POLY5 ( 2, 8, 9, 11, 3 );         break;
        case  76: POLY5 ( 1, 11, 7, 6, 2 );         break;
        case  77: POLY6 ( 0, 1, 11, 7, 6, 8 );      break;
        case  78: POLY6 ( 0, 9, 11, 7, 6, 2 );      break;
        case  79: POLY5 ( 7, 6, 8, 9, 11 );         break;
        case  80: POLY4 ( 4, 8, 10, 7 );            break;
        case  81: POLY5 ( 0, 2, 10, 7, 4 );         break;
        case  82: POLY4 ( 4, 8, 10, 7 )
                  POLY3 ( 0, 9, 1 );                break;
        case  83: POLY6 ( 1, 2, 10, 7, 4, 9 );      break;
        case  84: POLY5 ( 2, 3, 7, 4, 8 );          break;
        case  85: POLY4 ( 0, 3, 7, 4 );             break;
        case  86: POLY5 ( 2, 3, 7, 4, 8 );  
                  POLY3 ( 0, 9, 1 );                break;
        case  87: POLY5 ( 1, 3, 7, 4, 9 );          break;
        case  88: POLY4 ( 4, 8, 10, 7 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  89: POLY5 ( 0, 2, 10, 7, 4 );  
                  POLY3 ( 1, 11, 3 );               break;
        case  90: POLY4 ( 4, 8, 10, 7 );  
                  POLY4 ( 0, 9, 11, 3 );            break;
        case  91: POLY7 ( 2, 10, 7, 4, 9, 11, 3 );  break;
        case  92: POLY6 ( 1, 11, 7, 4, 8, 2 );      break;
        case  93: POLY5 ( 0, 1, 11, 7, 4 );         break;
        case  94: POLY7 ( 2, 0, 9, 11, 7, 4, 8 );   break;
        case  95: POLY4 ( 4, 9, 11, 7 );            break;
        case  96: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 4, 5, 9 );                break;
        case  97: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 4, 5, 9 );  
                  POLY3 ( 0, 2, 8 );                break;
        case  98: POLY3 ( 6, 10, 7 );  
                  POLY4 ( 0, 4, 5, 1 );             break;
        case  99: POLY3 ( 6, 10, 7 );  
                  POLY5 ( 1, 2, 8, 4, 5 );          break;
        case 100: POLY4 ( 2, 3, 7, 6 );  
                  POLY3 ( 4, 5, 9 );                break;
        case 101: POLY5 ( 0, 3, 7, 6, 8 );  
                  POLY3 ( 4, 5, 9 );                break;
        case 102: POLY4 ( 0, 4, 5, 1 );  
                  POLY4 ( 2, 3, 7, 6 );             break;
        case 103: POLY7 ( 8, 4, 5, 1, 3, 7, 6 );    break;
        case 104: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 4, 5, 9 ); 
                  POLY3 ( 1, 11, 3 );               break;
        case 105: POLY3 ( 6, 10, 7 );  
                  POLY3 ( 4, 5, 9 );  
                  POLY3 ( 1, 11, 3 );  
                  POLY3 ( 0, 2, 8 );                break;
        case 106: POLY3 ( 6, 10, 7 );  
                  POLY5 ( 0, 4, 5, 11, 3 );         break;
        case 107: POLY3 ( 6, 10, 7 );  
                  POLY6 ( 2, 8, 4, 5, 11, 3 );      break;
        case 108: POLY3 ( 4, 5, 9 );  
                  POLY5 ( 1, 11, 7, 6, 2 );         break;
        case 109: POLY3 ( 4, 5, 9 );  
                  POLY6 ( 0, 1, 11, 7, 6, 8 );      break;
        case 110: POLY7 ( 11, 7, 6, 2, 0, 4, 5 );   break;
        case 111: POLY6 ( 11, 7, 6, 8, 4, 5 );      break;
        case 112: POLY5 ( 5, 9, 8, 10, 7 );         break;
        case 113: POLY6 ( 0, 2, 10, 7, 5, 9 );      break;
        case 114: POLY6 ( 0, 8, 10, 7, 5, 1 );      break;
        case 115: POLY5 ( 1, 2, 10, 7, 5 );         break;
        case 116: POLY6 ( 2, 3, 7, 5, 9, 8 );       break;
        case 117: POLY5 ( 0, 3, 7, 5, 9 );          break;
        case 118: POLY7 ( 8, 2, 3, 7, 5, 1, 0 );    break;
        case 119: POLY4 ( 1, 3, 7, 5 );             break;
        case 120: POLY3 ( 1, 11, 3 );  
                  POLY5 ( 5, 9, 8, 10, 7 );         break;
        case 121: POLY3 ( 1, 11, 3 );  
                  POLY6 ( 0, 2, 10, 7, 5, 9 );      break;
        case 122: POLY7 ( 5, 11, 3, 0, 8, 10, 7 );  break;
        case 123: POLY6 ( 2, 10, 7, 5, 11, 3 );     break;
        case 124: POLY7 ( 7, 5, 9, 8, 2, 1, 11 );   break;
        case 125: POLY6 ( 0, 1, 11, 7, 5, 9 );      break;
        case 126: POLY3 ( 5, 11, 7 );  
                  POLY3 ( 0, 8, 2 );                break;
        case 127: POLY3 ( 5, 11, 7 );               break;
        case 128: POLY3 ( 5, 7, 11 );               break;
        case 129: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 0, 2, 8 );                break;
        case 130: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 0, 9, 1 );                break;
        case 131: POLY3 ( 5, 7, 11 );  
                  POLY4 ( 1, 2, 8, 9 );             break;
        case 132: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 2, 3, 10 );               break;
        case 133: POLY3 ( 5, 7, 11 );  
                  POLY4 ( 0, 3, 10, 8 );            break;
        case 134: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 0, 9, 1 );  
                  POLY3 ( 2, 3, 10 );               break;
        case 135: POLY3 ( 5, 7, 11 );  
                  POLY5 ( 1, 3, 10, 8, 9 );         break;
        case 136: POLY4 ( 1, 5, 7, 3 );             break;
        case 137: POLY3 ( 0, 2, 8 );  
                  POLY4 ( 1, 5, 7, 3 );             break;
        case 138: POLY5 ( 0, 9, 5, 7, 3 );          break;
        case 139: POLY6 ( 2, 8, 9, 5, 7, 3 );       break;
        case 140: POLY5 ( 1, 5, 7, 10, 2 );         break;
        case 141: POLY6 ( 0, 1, 5, 7, 10, 8 );      break;
        case 142: POLY6 ( 0, 9, 5, 7, 10, 2 );      break;
        case 143: POLY5 ( 5, 7, 10, 8, 9 );         break;
        case 144: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 4, 8, 6 );                break;
        case 145: POLY3 ( 5, 7, 11 );  
                  POLY4 ( 0, 2, 6, 4 );             break;
        case 146: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 4, 8, 6 );  
                  POLY3 ( 0, 9, 1 );                break;
        case 147: POLY3 ( 5, 7, 11 );  
                  POLY5 ( 1, 2, 6, 4, 9 );          break;
        case 148: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 4, 8, 6 );  
                  POLY3 ( 2, 3, 10 );               break;
        case 149: POLY3 ( 5, 7, 11 );  
                  POLY5 ( 0, 3, 10, 6, 4 );         break;
        case 150: POLY3 ( 5, 7, 11 );  
                  POLY3 ( 4, 8, 6 );  
                  POLY3 ( 2, 3, 10 );  
                  POLY3 ( 0, 9, 1 );                break;
        case 151: POLY3 ( 5, 7, 11 );  
                  POLY6 ( 1, 3, 10, 6, 4, 9 );      break;
        case 152: POLY3 ( 4, 8, 6 );  
                  POLY4 ( 1, 5, 7, 3 );             break;
        case 153: POLY4 ( 0, 2, 6, 4 );  
                  POLY4 ( 1, 5, 7, 3 );             break;
        case 154: POLY3 ( 4, 8, 6 );  
                  POLY5 ( 0, 9, 5, 7, 3 );          break;
        case 155: POLY7 ( 9, 5, 7, 3, 2, 6, 4 );    break;
        case 156: POLY3 ( 4, 8, 6 );  
                  POLY5 ( 1, 5, 7, 10, 2 );         break;
        case 157: POLY7 ( 10, 6, 4, 0, 1, 5, 7 );   break;
        case 158: POLY3 ( 4, 8, 6 );  
                  POLY6 ( 0, 9, 5, 7, 10, 2 );      break;
        case 159: POLY6 ( 9, 5, 7, 10, 6, 4 );      break;
        case 160: POLY4 ( 4, 7, 11, 9 );            break;
        case 161: POLY4 ( 4, 7, 11, 9 );  
                  POLY3 ( 0, 2, 8 );                break;
        case 162: POLY5 ( 0, 4, 7, 11, 1 );         break;
        case 163: POLY6 ( 1, 2, 8, 4, 7, 11 );      break;
        case 164: POLY4 ( 4, 7, 11, 9 );  
                  POLY3 ( 2, 3, 10 );               break;
        case 165: POLY4 ( 4, 7, 11, 9 );  
                  POLY4 ( 0, 3, 10, 8 );            break;
        case 166: POLY3 ( 2, 3, 10 );  
                  POLY5 ( 0, 4, 7, 11, 1 );         break;
        case 167: POLY7 ( 1, 3, 10, 8, 4, 7, 11 );  break;
        case 168: POLY5 ( 1, 9, 4, 7, 3 );          break;
        case 169: POLY5 ( 1, 9, 4, 7, 3 )
                  POLY3 ( 0, 2, 8 );                break;
        case 170: POLY4 ( 0, 4, 7, 3 );             break;
        case 171: POLY5 ( 2, 8, 4, 7, 3 );          break;
        case 172: POLY6 ( 1, 9, 4, 7, 10, 2 );      break;
        case 173: POLY7 ( 1, 9, 4, 7, 10, 8, 0 );   break;
        case 174: POLY5 ( 0, 4, 7, 10, 2 );         break;
        case 175: POLY4 ( 4, 7, 10, 8 );            break;
        case 176: POLY5 ( 6, 7, 11, 9, 8 );         break;
        case 177: POLY6 ( 0, 2, 6, 7, 11, 9 );      break;
        case 178: POLY6 ( 0, 8, 6, 7, 11, 1 );      break;
        case 179: POLY5 ( 1, 2, 6, 7, 11 );         break;
        case 180: POLY3 ( 2, 3, 10 );  
                  POLY5 ( 6, 7, 11, 9, 8 );         break;
        case 181: POLY7 ( 6, 7, 11, 9, 0, 3, 10 );  break;
        case 182: POLY3 ( 2, 3, 10 );  
                  POLY6 ( 0, 8, 6, 7, 11, 1 );      break;
        case 183: POLY6 ( 1, 3, 10, 6, 7, 11 );     break;
        case 184: POLY6 ( 1, 9, 8, 6, 7, 3 );       break;
        case 185: POLY7 ( 9, 0, 2, 6, 7, 3, 1 );    break;
        case 186: POLY5 ( 0, 8, 6, 7, 3 );          break;
        case 187: POLY4 ( 2, 6, 7, 3 );             break;
        case 188: POLY7 ( 7, 10, 2, 1, 9, 8, 6 );   break;
        case 189: POLY3 ( 6, 7, 10 );  
                  POLY3 ( 0, 1, 9 );                break;
        case 190: POLY6 ( 0, 8, 6, 7, 10, 2 );      break;
        case 191: POLY3 ( 6, 7, 10 );               break;
        case 192: POLY4 ( 5, 6, 10, 11 );           break;
        case 193: POLY4 ( 5, 6, 10, 11 );  
                  POLY3 ( 0, 2, 8 );                break;
        case 194: POLY4 ( 5, 6, 10, 11 );  
                  POLY3 ( 0, 9, 1 );                break;
        case 195: POLY4 ( 5, 6, 10, 11 );  
                  POLY4 ( 1, 2, 8, 9 );             break;
        case 196: POLY5 ( 2, 3, 11, 5, 6 );         break;
        case 197: POLY6 ( 0, 3, 11, 5, 6, 8 );      break;
        case 198: POLY3 ( 0, 9, 1 );  
                  POLY5 ( 2, 3, 11, 5, 6 );         break;
        case 199: POLY7 ( 3, 11, 5, 6, 8, 9, 1 );   break;
        case 200: POLY5 ( 1, 5, 6, 10, 3 );         break;
        case 201: POLY5 ( 0, 1, 5, 6, 8 );  
                  POLY3 ( 2, 10, 3 );               break;
        case 202: POLY6 ( 0, 9, 5, 6, 10, 3 );      break;
        case 203: POLY7 ( 3, 2, 8, 9, 5, 6, 10 );   break;
        case 204: POLY4 ( 1, 5, 6, 2 );             break;
        case 205: POLY5 ( 0, 1, 5, 6, 8 );          break;
        case 206: POLY5 ( 0, 9, 5, 6, 2 );          break;
        case 207: POLY4 ( 5, 6, 8, 9 );             break;
        case 208: POLY5 ( 4, 8, 10, 11, 5 );        break;
        case 209: POLY6 ( 0, 2, 10, 11, 5, 4 );     break;
        case 210: POLY3 ( 0, 9, 1 );  
                  POLY5 ( 4, 8, 10, 11, 5 );        break;
        case 211: POLY7 ( 4, 9, 1, 2, 10, 11, 5 );  break;
        case 212: POLY6 ( 2, 3, 11, 5, 4, 8 );      break;
        case 213: POLY5 ( 0, 3, 11, 5, 4 );         break;
        case 214: POLY3 ( 0, 9, 1 );  
                  POLY6 ( 2, 3, 11, 5, 4, 8 );      break;
        case 215: POLY6 ( 3, 11, 5, 4, 9, 1 );      break;
        case 216: POLY6 ( 1, 5, 4, 8, 10, 3 );      break;
        case 217: POLY7 ( 10, 3, 1, 5, 4, 0, 2 );   break;
        case 218: POLY7 ( 5, 4, 8, 10, 3, 0, 9 );   break;
        case 219: POLY3 ( 4, 9, 5 );  
                  POLY3 ( 2, 10, 3 );               break;
        case 220: POLY5 ( 1, 5, 4, 8, 2 );          break;
        case 221: POLY4 ( 0, 1, 5, 4 );             break;
        case 222: POLY6 ( 5, 4, 8, 2, 0, 9 );       break;
        case 223: POLY3 ( 4, 9, 5 );                break;
        case 224: POLY5 ( 4, 6, 10, 11, 9 );        break;
        case 225: POLY3 ( 0, 2, 8 );  
                  POLY5 ( 4, 6, 10, 11, 9 );        break;
        case 226: POLY6 ( 0, 4, 6, 10, 11, 1 );     break;
        case 227: POLY7 ( 4, 6, 10, 11, 1, 2, 8 );  break;
        case 228: POLY6 ( 2, 3, 11, 9, 4, 6 );      break;
        case 229: POLY7 ( 6, 8, 0, 3, 11, 9, 4 );   break;
        case 230: POLY7 ( 11, 1, 0, 4, 6, 2, 3 );   break;
        case 231: POLY3 ( 1, 3, 11 );  
                  POLY3 ( 4, 6, 8 );                break;
        case 232: POLY6 ( 1, 9, 4, 6, 10, 3 );      break;
        case 233: POLY3 ( 0, 2, 8 );  
                  POLY6 ( 1, 9, 4, 6, 10, 3 );      break;
        case 234: POLY5 ( 0, 4, 6, 10, 3 );         break;
        case 235: POLY6 ( 4, 6, 10, 3, 2, 8 );      break;
        case 236: POLY5 ( 1, 9, 4, 6, 2 );          break;
        case 237: POLY6 ( 1, 9, 4, 6, 8, 0 );       break;
        case 238: POLY4 ( 0, 4, 6, 2 );             break;
        case 239: POLY3 ( 4, 6, 8 );                break;
        case 240: POLY4 ( 8, 10, 11, 9 );           break;
        case 241: POLY5 ( 0, 2, 10, 11, 9 );        break;
        case 242: POLY5 ( 0, 8, 10, 11, 1 );        break;
        case 243: POLY4 ( 1, 2, 10, 11 );           break;
        case 244: POLY5 ( 2, 3, 11, 9, 8 );         break;
        case 245: POLY4 ( 0, 3, 11, 9 );            break;
        case 246: POLY6 ( 8, 2, 3, 11, 1, 0 );      break;
        case 247: POLY3 ( 1, 3, 11 );               break;
        case 248: POLY5 ( 1, 9, 8, 10, 3 );         break;
        case 249: POLY6 ( 10, 3, 1, 9, 0, 2 );      break;
        case 250: POLY4 ( 0, 8, 10, 3 );            break;
        case 251: POLY3 ( 2, 10, 3 );               break;
        case 252: POLY4 ( 1, 9, 8, 2 );             break;
        case 253: POLY3 ( 0, 1, 9 );                break;
        case 254: POLY3 ( 0, 8, 2 );                break;
        case 255:                                   break;

        default: break;

#undef  POLY3
#undef  POLY4
#undef  POLY5
#undef  POLY6
#undef  POLY7

    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
Error iso_convert_prism
          ( Prism           *prism,
            float           data_values[6],
            hash_table_ptr  vtx_hash,
            SegList         *tri_list,
            int             equivalence )
{
    int  signature = 0;

    DXErrorGoto2
        ( ERROR_NOT_IMPLEMENTED,
          "#11380", /* %s is an invalid connections type */
          "\"prism\"" );

    if ( equivalence == 1 )
    {
        if ( data_values[0] > 0.0 ) signature += 1;
        if ( data_values[1] > 0.0 ) signature += 2;
        if ( data_values[2] > 0.0 ) signature += 4;
        if ( data_values[3] > 0.0 ) signature += 8;
        if ( data_values[4] > 0.0 ) signature += 16;
        if ( data_values[5] > 0.0 ) signature += 32;
    }
    else
    {
        if ( data_values[0] == 0.0 ) signature += 1;
        if ( data_values[1] == 0.0 ) signature += 2;
        if ( data_values[2] == 0.0 ) signature += 4;
        if ( data_values[3] == 0.0 ) signature += 8;
        if ( data_values[4] == 0.0 ) signature += 16;
        if ( data_values[5] == 0.0 ) signature += 32;
    }

#ifdef WHITEBOX_TEST
    DXDebug ( "I", "Signature = %d", signature );
    histograms.case_signatures[signature]++;
#endif

    switch ( signature )
    {
        /* 
         * This table was entered symbolically into a file called table.DB
         * and then converted to 'C' code in a file 'table.C' by a script
         * named 'table.MAK'.  
         * All files currently reside in the directory ~allain/kalvin.
         */

        default: break;

#undef  LINE

    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}





static
Field create_iso_points
          ( field_info     input_info, 
            float          *value_ptr,
            int            is_grown,
            Error (*convert_connection)
                        (void *, float *, hash_table_ptr, hash_table_ptr, int),
                     /* (void *, float *, hash_table_ptr, SegList *,    int),*/
            Field out )

{
    float           value = value_ptr [ 0 ];
    array_info      pi_info, ci_info, di_info  = NULL;
    array_info      po_info = NULL;
    hash_table_rec  vtx_hash;
    Line            line;
    Point           point[2];
    float           data[2];
    double          t;
    iso_edge_hash_ptr   element_ptr = NULL;
    int             i, j;
    field_info      output_info = NULL;
    int             position_dim;
    int             connection_shape;
    int             XXX;

    array_info      a_info;
    component_info  comp;
    Array           array    = NULL;
    Pointer         scratch  = NULL;
    Pointer         out_data = NULL;

    InvalidComponentHandle  i_handle = NULL;

    DXASSERTGOTO ( ERROR != input_info );
    DXASSERTGOTO ( ERROR != value_ptr );
    DXASSERTGOTO ( ERROR != out );

    vtx_hash.count        = 0;
    vtx_hash.table        = NULL;
    vtx_hash.set_vertices = NULL;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    if ( ( pi_info->shape[0] != 1 ) &&
         ( pi_info->shape[0] != 2 ) &&
         ( pi_info->shape[0] != 3 ) )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#11400", /* %s has invalid dimensionality; does not match %s */
              "\"positions\" component", "\"connections\"" );

    position_dim     = pi_info->shape[0];
    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( connection_shape == 2 );
    DXASSERTGOTO ( ci_info->items >= ci_info->original_items );

    TST_HASH_INTS;

    if ( ( ERROR == ( vtx_hash.table
             = DXCreateHash
                 ( sizeof(iso_edge_hash_rec),
                   iso_edge_hash_func, iso_edge_compare_func ) ) ) )
        goto error;

    if ( ci_info->get_item == _dxf_get_conn_grid_LINES )
    {
        array_info t0;
        int        II;
        int        I0;

        t0 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[0]);

        DXASSERTGOTO ( ((grid_connections_info)ci_info)->counts != NULL );
        DXASSERTGOTO
          ( t0->items == ( ((grid_connections_info)ci_info)->counts[0] - 1 ) );

        if ( ((mesh_array_info)ci_info)->grow_offsets != NULL )
        {
            DXASSERTGOTO ( is_grown );

            I0 = ((mesh_array_info)ci_info)->grow_offsets[0];
        }
        else
            I0 = 0;

        if ( !ci_info->get_item ( I0, ci_info, &line )
             ||
             !di_info->get_item ( line.p, di_info, &data[0] ) )
            goto error;

        data[0] -= value;

        for ( II=I0;
              II<( I0 + t0->original_items );
              II++,
                  line.p++, line.q++, data[0] = data[1] )
            {
                if ( !di_info->get_item ( line.q, di_info, &data[1] ) )
                    goto error;

                data[1] -= value;

                /* iso_convert_line */
               if ( ( NULL != i_handle ) && DXIsElementInvalid (i_handle, II) )
                    continue;
                else if ( ( ( data[0] <= 0.0 ) && ( data[1] > 0.0 ) )
                          ||
                          ( ( data[0] > 0.0 ) && ( data[1] <= 0.0 ) ) )
                    /* XXX indirect with dummy line */
                    if ( !iso_hash_enter ( &line, &vtx_hash, &XXX ) )
                        goto error;
            }
    }
    else
    {
        for ( i=0;
              i<ci_info->original_items;
              i++ )
            if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
                continue;
            else if ( !ci_info->get_item ( i, ci_info, &line ) )
                goto error;
            else
            {
                if ( !di_info->get_item ( line.p, di_info, &data[0] ) ||
                     !di_info->get_item ( line.q, di_info, &data[1] )   )
                    goto error;

                data[0] -= value;
                data[1] -= value;

                /* iso_convert_line */
                if ( ( ( data[0] <= 0.0 ) && ( data[1]  > 0.0 ) )
                     ||
                     ( ( data[0]  > 0.0 ) && ( data[1] <= 0.0 ) ) )
                    /* XXX indirect with dummy line */
                    if ( !iso_hash_enter ( &line, &vtx_hash, &XXX ) )
                        goto error;
            }
    }

    if ( !create_output_arrays
              ( out,
                vtx_hash.count,     position_dim,
                0,                  0,
                0,
                DEFAULT_LINE_COLOR, 
                value,              DEP_POSITIONS,
                ISOSURFACE ) )
        goto error;

    if ( vtx_hash.count == 0 ) 
        goto done;

    if ( ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
         ||
         !DXInitGetNextHashElement ( vtx_hash.table ) )
        goto error;

    DXASSERTGOTO ( output_info->std_comps[(int)POSITIONS] != NULL );

    po_info = (array_info) &(output_info->std_comps[(int)POSITIONS]->array);


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( 0 != strcmp ( comp->name, "positions" ) ) &&
             ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
               ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
               ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "data"        ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )   )
            continue;

        a_info = (array_info) &(comp->array);

    if ( CLASS_CONSTANTARRAY == a_info->class )
    {
        if ( ( ERROR == ( array = (Array)DXNewConstantArrayV
                                     ( vtx_hash.count,
                                       DXGetConstantArrayData ( a_info->array ),
                                       a_info->type,
                                       a_info->category,
                                       a_info->rank,
                                       a_info->shape ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) ) )

            goto error;

        continue;
    }

        if ( ( ERROR == ( array
                   = DXNewArrayV
                         ( a_info->type, a_info->category,
                           a_info->rank, &a_info->shape[0] ) ) )
             ||
             !DXAllocateArray     ( array, vtx_hash.count )           ||
             !DXAddArrayData      ( array, 0, vtx_hash.count, NULL )  ||
             !DXTrim              ( array )                           ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) )
             ||
             !_dxf_SetIterator    ( comp )
             ||
             ( ERROR == ( out_data = DXGetArrayData  ( array )))             ||
             ( ERROR == ( scratch  = DXAllocateLocal ( 2 * a_info->itemsize )))
             ||
             !DXInitGetNextHashElement ( vtx_hash.table ) )
            goto error;

        array = NULL;

        for ( i = 0;
              ( NULL != ( element_ptr
                              = (iso_edge_hash_ptr) DXGetNextHashElement
                                                    ( vtx_hash.table ) ) );
              i++ )
        {
            DXASSERTGOTO ( element_ptr->seq < po_info->items );

            if ( element_ptr->edge.p == element_ptr->edge.q )
                t = 0.0;
            else
            {
                if ( !di_info->get_item
                          ( element_ptr->edge.p, di_info, &data[0] )||
                     !di_info->get_item
                          ( element_ptr->edge.q, di_info, &data[1] ) )
                    goto error;

                data[0] -= value;
                data[1] -= value;

                if      ( data[0] == 0.0 ) t = 0.0;
                else if ( data[1] == 0.0 ) t = 1.0;
                else
                    t = (double)data[0] / ( (double)data[0] - (double)data[1] );

                DXASSERTGOTO ( ( t >= 0.0 ) && ( t <= 1.0 ) );
            }

            if ( !interp ( a_info, scratch,
                           element_ptr->edge.p, element_ptr->edge.q, t, 
                           &(((char*)out_data)
                               [ a_info->itemsize * element_ptr->seq ]),
			       a_info->itemsize/sizeof(float),
			       a_info->itemsize) )
                goto error;
        }

        DXFree ( scratch );  scratch = NULL;
    }

    done:
        /* This is create_iso_points */
        if ( !DXSetIntegerAttribute ( (Object) out, "shade", 0 ) )
            goto error;

        DXFreeInvalidComponentHandle ( i_handle );

        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( scratch );
        _dxf_FreeInMemory ( output_info );

        i_handle       = NULL;
        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        output_info    = NULL;
        scratch        = NULL;

        return DXEndField ( out );

    error:
        ERROR_SECTION;

        DXFreeInvalidComponentHandle ( i_handle );

        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( scratch );
        _dxf_FreeInMemory ( output_info );

        i_handle       = NULL;
        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        output_info    = NULL;
        scratch        = NULL;

        return ERROR;
}



#ifdef INCLUDES_BAND


static
float *band_value_list
           ( Field    field,
             float    *vp,
             int      *number,
             float    *min_max,
             float    *old_min,
             float    *old_max )
{
    float *value2_ptr = NULL;
    int   i;

    *number += 2;

    if ( ( ERROR == ( value2_ptr
                         = (float *) DXAllocate ( (*number) * sizeof (float) )))
         ||
         ! memcpy ( (char *)(&value2_ptr[1]), (char *)vp,
                    ( ((*number)-2) * sizeof (float) ) ) )
        goto error;

    if ( min_max != NULL )
    {
        value2_ptr[0]           = min_max[0];
        value2_ptr[(*number)-1] = min_max[1];
    }
    else
        if ( !DXStatistics
                  ( (Object)field, "data",
                    &value2_ptr[0], &value2_ptr[(*number)-1], NULL, NULL ) )
            goto error;

    /* If values in the input list are outside min,max: prune them off */

    while ( ( value2_ptr[1] < value2_ptr[0] ) && ( (*number) > 2 ) )
    {
        for ( i=2; i<(*number); i++ ) value2_ptr[i-1] = value2_ptr[i];
        (*number)--;
    }

    while ( ( value2_ptr[(*number)-2] > value2_ptr[(*number)-1] )
            &&
            ( (*number) > 2 ) )
    {
        value2_ptr[(*number)-2] = value2_ptr[(*number)-1];
        (*number)--;
    }

    DXASSERTGOTO ( value2_ptr[0] <= value2_ptr[(*number)-1] );

    *old_min = value2_ptr [ 0 ];
    *old_max = value2_ptr [ (*number) - 1 ];

#if 0
    /* The following "fudge" can result in band values slightly outside the    */
    /* range of the input data.  For example: if you use AutoColor prior to    */
    /* band and then use the calculated colormap to color the band output, the */
    /* colormap may not encompass the data.  Thus I'm removing it now and will */
    /* check if it introduces problems.  suits 9/5/96                          */

    /* Ensure floating point inequality with min and max */

    value2_ptr[0]
        = ( value2_ptr[0] == 0 )? -DXD_MIN_FLOAT :
          ( value2_ptr[0]  > 0 )?
              value2_ptr[0] * ( 1.0 - DXD_FLOAT_EPSILON ) :
              value2_ptr[0] * ( 1.0 + DXD_FLOAT_EPSILON );

    value2_ptr[*number-1]
        = ( value2_ptr[(*number)-1] == 0 )? DXD_MIN_FLOAT :
          ( value2_ptr[(*number)-1]  > 0 )?
              value2_ptr[(*number)-1] * ( 1.0 + DXD_FLOAT_EPSILON ) :
              value2_ptr[(*number)-1] * ( 1.0 - DXD_FLOAT_EPSILON );
#endif

    return value2_ptr;

    error:
        ERROR_SECTION;

        DXFree ( (Pointer) value2_ptr );
        return ERROR;
}



static
Field create_band_lines
          ( field_info     input_info, 
            float          *value_ptr,
            int            number,
            band_remap_types remap,
            int            is_grown,
            float          *min_max,
            Error (*convert_connection)
                  (void *,float *,float *,int,hash_table_ptr,SegList *,int),
            Field out )
{
    array_info      pi_info, ci_info, di_info = NULL;
    array_info      po_info, co_info, do_info = NULL;
    hash_table_rec  vtx_hash;
    SegList         *line_list = NULL;
    SegList         *cval_list = NULL;
    Line            line;
    Point           point[2];
    float           data[2];
    double          t;
    band_edge_hash_ptr  element_ptr = NULL;
    int             D;
    int             i, j;
    field_info      output_info = NULL;
    int             position_dim;
    int             connection_shape;
    int             connection_count;
    float           value_low, value_high;
    float           old_min, old_max;
    float           *value2_ptr = NULL;

    SegList         *c_ID_list = NULL;
    int             *c_ID_pull = NULL;
    int             n_c_dep, n_p_dep;
    array_info      a_info;
    component_info  comp;
    Array           array    = NULL;
    Pointer         scratch  = NULL;
    Pointer         out_data = NULL;
    int             itemsize;

    InvalidComponentHandle  i_handle = NULL;

    DXASSERTGOTO ( ERROR != convert_connection );
    DXASSERTGOTO ( ERROR != input_info );
    DXASSERTGOTO ( ERROR != value_ptr );
    DXASSERTGOTO ( ERROR != out );

    vtx_hash.count        = 0;
    vtx_hash.table        = NULL;
    vtx_hash.set_vertices = NULL;

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    if ( ERROR == ( value2_ptr = band_value_list
                                     ( input_info->field,
                                       value_ptr, &number, min_max,
                                       &old_min, &old_max ) ) )
        goto error;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    if ( ( pi_info->shape[0] != 1 ) &&
         ( pi_info->shape[0] != 2 ) &&
         ( pi_info->shape[0] != 3 ) )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#11400", /* %s has invalid dimensionality; does not match %s */
              "\"positions\" component", "\"connections\"" );

    position_dim     = pi_info->shape[0];
    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( connection_shape == 2 );
    DXASSERTGOTO ( ci_info->items >= ci_info->original_items );

    TST_HASH_INTS;

    if ( ( ERROR == ( vtx_hash.table
             = DXCreateHash
                 ( sizeof(band_edge_hash_rec),
                   band_edge_hash_func,
                   band_edge_compare_func ) ) )
         ||
         ( ERROR == ( line_list
                          = DXNewSegList ( sizeof(Line), 1000, 1000 ) ) ) )
        goto error;

    if ( remap != ORIGINAL_DATA )
       if ( ERROR == ( cval_list = DXNewSegList ( sizeof(float), 1000, 1000 )))
            goto error;

    if ( !tally_dependers ( input_info, &n_c_dep, &n_p_dep ) )
        goto error;

    if ( ( n_c_dep > 1 )
         &&
         ( ERROR == ( c_ID_list = DXNewSegList ( sizeof(int), 1000, 1000 ) ) ) )
        goto error;

    if ( ci_info->get_item == _dxf_get_conn_grid_LINES )
    {
        array_info t0;
        int        II;
        int        I0;

        t0 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[0]);

        DXASSERTGOTO ( ((grid_connections_info)ci_info)->counts != NULL );
        DXASSERTGOTO
          ( t0->items == ( ((grid_connections_info)ci_info)->counts[0] - 1 ) );

        if ( ((mesh_array_info)ci_info)->grow_offsets != NULL )
        {
            DXASSERTGOTO ( is_grown );

            I0 = ((mesh_array_info)ci_info)->grow_offsets[0];
        }
        else
            I0 = 0;

        if ( !ci_info->get_item ( I0, ci_info, &line )
             ||
             !di_info->get_item ( line.p, di_info, &data[0] ) )
            goto error;

        for ( II=I0;
              II<( I0 + t0->original_items );
              II++,
                  line.p++, line.q++, data[0] = data[1] )
            {
                if ( !di_info->get_item ( line.q, di_info, &data[1] ) )
                    goto error;

               if ( ( NULL != i_handle ) && DXIsElementInvalid (i_handle, II) )
                    continue;
                else
                    for ( D=0; D<number-1; D++ )
                    {
                        if ( !convert_connection 
                                 ( &line, data, value2_ptr, D,
                                   &vtx_hash, line_list, 1 ) )
                            goto error;
                        else if ( ( NULL != c_ID_list )
                                  &&
                                  !set_c_ID
                                       ( c_ID_list,
                                         DXGetSegListItemCount ( line_list ),
                                         II ) )
                            goto error;

                        switch ( remap )
                        {
                            case ORIGINAL_DATA: break;

                            case DATA_LOW:
                                if ( !set_cval
                                         ( cval_list,
                                           DXGetSegListItemCount ( line_list ),
                                           ( ( D == 0 ) ?
                                               old_min : value2_ptr[D] ) ) )
                                    goto error;
                                break;

                            case DATA_HIGH:
                                if ( !set_cval
                                         ( cval_list,
                                           DXGetSegListItemCount ( line_list ),
                                           ( ( (D+1) == number ) ?
                                               old_max : value2_ptr[D+1] ) ) )
                                    goto error;
                                break;
                        }
                    }
            }
    }
    else
    {
        for ( i=0;
              i<ci_info->original_items;
              i++ )
            if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
                continue;
            else if ( !ci_info->get_item ( i, ci_info, &line ) )
                goto error;
            else
            {
                if ( !di_info->get_item ( line.p, di_info, &data[0] ) ||
                     !di_info->get_item ( line.q, di_info, &data[1] )   )
                    goto error;

                for ( D=0; D<number-1; D++ )
                {
                    if ( !convert_connection
                             ( &line, data, value2_ptr, D,
                               &vtx_hash, line_list, 1 ) )
                        goto error;
                    else if ( ( NULL != c_ID_list )
                              &&
                              !set_c_ID
                                  ( c_ID_list,
                                    DXGetSegListItemCount ( line_list ),
                                    i ) )
                        goto error;

                    switch ( remap )
                    {
                        case ORIGINAL_DATA: break;

                        case DATA_LOW:
                            if ( !set_cval
                                     ( cval_list,
                                       DXGetSegListItemCount ( line_list ),
                                       ( ( D == 0 ) ?
                                           old_min : value2_ptr[D] ) ) )
                                goto error;
                            break;

                        case DATA_HIGH:
                            if ( !set_cval
                                     ( cval_list,
                                       DXGetSegListItemCount ( line_list ),
                                       ( ( (D+1) == number ) ?
                                           old_max : value2_ptr[D+1] ) ) )
                                goto error;
                            break;
                    }
                }
            }
    }

    connection_count = DXGetSegListItemCount ( line_list );

    if ( connection_count == 0 )
        if ( !_dxf_MakeFieldEmpty ( out ) )
            goto error;
        else
            goto done;

    if ( !create_output_arrays
              ( out,
                vtx_hash.count,    position_dim,
                connection_count,  1,
                0,
                DEFAULT_LINE_COLOR, 
                0.0, ( (remap==ORIGINAL_DATA)? DEP_POSITIONS: DEP_CONNECTIONS ),
                BAND ) )
        goto error;

    if ( vtx_hash.count == 0 ) 
        goto done;

    if ( ( connection_count > 0 ) && ( c_ID_list != NULL ) )
    {
        if ( ( ERROR == ( c_ID_pull = DXAllocateLocal
                                          ( connection_count * sizeof(int) ) ) )
             ||
             !copy_seglist_to_memory
                  ( c_ID_list, c_ID_pull, sizeof(int), connection_count ) )
            goto error;
    
        DXDeleteSegList ( c_ID_list );  c_ID_list = NULL;
    }

    if ( ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
         ||
         !DXInitGetNextHashElement ( vtx_hash.table ) )
        goto error;

    DXASSERTGOTO ( output_info->std_comps[(int)POSITIONS] != NULL );

    po_info = (array_info) &(output_info->std_comps[(int)POSITIONS]->array);
    co_info = (array_info) &(output_info->std_comps[(int)CONNECTIONS]->array);
    do_info = (array_info) &(output_info->std_comps[(int)DATA]->array);

    if ( !copy_seglist_to_array ( line_list, co_info ) )
        goto error;

    if ( remap != ORIGINAL_DATA )
    {
        if ( !copy_seglist_to_array ( cval_list, do_info ) )
            goto error;
    }

    DXDeleteSegList ( line_list );  line_list = NULL;
    DXDeleteSegList ( cval_list );  cval_list = NULL;

    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( 0 != strcmp ( comp->name, "positions" ) ) &&
             ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
               ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
               ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            continue;

        if ( ( ( 0 == strcmp ( comp->name, "data" ) )
               &&
               ( remap != ORIGINAL_DATA ) )
             ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

    if ( CLASS_CONSTANTARRAY == a_info->class )
    {
        if ( ( ERROR == ( array = (Array)DXNewConstantArrayV
                                     ( vtx_hash.count,
                                       DXGetConstantArrayData ( a_info->array ),
                                       a_info->type,
                                       a_info->category,
                                       a_info->rank,
                                       a_info->shape ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) ) )

            goto error;

        continue;
    }

        if ( ( ERROR == ( array
                   = DXNewArrayV
                         ( TYPE_FLOAT, a_info->category,
                           a_info->rank, &a_info->shape[0] ) ) )
             ||
             !DXAllocateArray     ( array, vtx_hash.count )           ||
             !DXAddArrayData      ( array, 0, vtx_hash.count, NULL )  ||
             !DXTrim              ( array )                           ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) )
             ||
             !_dxf_SetIterator    ( comp )
             ||
             ( ERROR == ( out_data = DXGetArrayData  ( array )))
	     ||
	     !(itemsize = DXGetItemSize(array))
	     ||
             ( ERROR == ( scratch  = DXAllocateLocal ( 2 * itemsize)))
             ||
             !DXInitGetNextHashElement ( vtx_hash.table ) )
            goto error;

        array = NULL;

        for ( i = 0;
              ( NULL != ( element_ptr
                              = (band_edge_hash_ptr) DXGetNextHashElement
                                                    ( vtx_hash.table ) ) );
              i++ )
        {
            DXASSERTGOTO ( element_ptr->seq < po_info->items );

            if ( element_ptr->edge.p == element_ptr->edge.q )
                t = 0.0;
            else
            {
                if ( !di_info->get_item
                          ( element_ptr->edge.p, di_info, &data[0] )||
                     !di_info->get_item
                          ( element_ptr->edge.q, di_info, &data[1] ) )
                    goto error;

                /* DXASSERTGOTO ( element_ptr->select != NEITHER_VALUE ); */

                value_low = value2_ptr [ element_ptr->select ];

                if      ( data[0] == value_low ) t = 0.0;
                else if ( data[1] == value_low ) t = 1.0;
                else
                    t = ( (double)data[0] - (double)value_low ) / ( (double)data[0] - (double)data[1] );

                DXASSERTGOTO ( ( t >= 0.0 ) && ( t <= 1.0 ) );
            }

            if ( !interp ( a_info, scratch,
                           element_ptr->edge.p, element_ptr->edge.q, t, 
                           &(((char*)out_data)
                               [ a_info->itemsize * element_ptr->seq ]),
			       itemsize/sizeof(float),
			       itemsize) )
                goto error;
        }

        DXFree ( scratch );  scratch = NULL;
    }


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
             ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
             ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                             "connections" ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "connections" ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
	     ( 0 == strcmp  ( comp->name, "invalid connections" ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

        if ( ( ERROR == ( array = _dxf_resample_array
                                      ( comp, 1,
                                        ((array_info) &(comp->array))->items,
                                        connection_count, NULL, c_ID_pull ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object)array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "connections" ) ) )
            goto error;
    }

    if ( !remove_degenerates ( out ) )
        goto error;

    done:
        /* This is create_band_lines */
        if ( DXGetAttribute ( (Object)input_info->field, "shade" ) )
            if ( !DXSetAttribute
                      ( (Object) out,
                        "shade",
                        DXGetAttribute
                            ( (Object)input_info->field, "shade" ) ) )
                goto error;

        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( (Pointer) value2_ptr  );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        value2_ptr     = NULL;
        output_info    = NULL;
        i_handle       = NULL;
        c_ID_list      = NULL;
        c_ID_pull      = NULL;
        scratch        = NULL;

        return DXEndField ( out );

    error:
        ERROR_SECTION;

        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( (Pointer) value2_ptr  );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        value2_ptr     = NULL;
        output_info    = NULL;
        i_handle       = NULL;
        c_ID_list      = NULL;
        c_ID_pull      = NULL;
        scratch        = NULL;

        return ERROR;
}
#endif



static
Field create_iso_contours
          ( field_info     input_info, 
            float          *value_ptr,
            int            is_grown,
            Error (*convert_connection)
                        (void *, float *, hash_table_ptr, hash_table_ptr, int),
                     /* (void *, float *, hash_table_ptr, SegList *,    int),*/
            Field out )

{
    float           value = value_ptr [ 0 ];
    array_info      pi_info, ci_info, di_info  = NULL;
    array_info      po_info, co_info = NULL;
    hash_table_rec  vtx_hash;
    hash_table_rec  edge_hash;
    Quadrilateral   quad;
    Point           point[2];
    float           data[4];
    double          t;
    iso_edge_hash_ptr   element_ptr = NULL;
    int             i, j;
    field_info      output_info = NULL;
    int             position_dim;
    int             connection_shape;

    SegList         *c_ID_list = NULL;
    int             *c_ID_pull = NULL;
    int             n_c_dep, n_p_dep;
    array_info      a_info;
    component_info  comp;
    Array           array    = NULL;
    Pointer         scratch  = NULL;
    Pointer         out_data = NULL;

    InvalidComponentHandle  i_handle = NULL;

    DXASSERTGOTO ( ERROR != convert_connection );
    DXASSERTGOTO ( ERROR != input_info );
    DXASSERTGOTO ( ERROR != value_ptr );
    DXASSERTGOTO ( ERROR != out );

    vtx_hash.count         = 0;
    vtx_hash.table         = NULL;
    vtx_hash.set_vertices  = NULL;
    edge_hash.count        = 0;
    edge_hash.table        = NULL;
    edge_hash.set_vertices = NULL;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    if ( ( pi_info->shape[0] != 2 ) &&
         ( pi_info->shape[0] != 3 ) )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#11400", /* %s has invalid dimensionality; does not match %s */
              "\"positions\" component", "\"connections\"" );

    position_dim     = pi_info->shape[0];
    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( ( connection_shape == 3 ) || ( connection_shape == 4 ) );
    DXASSERTGOTO ( ci_info->items >= ci_info->original_items );

    TST_HASH_INTS;

    if ( ( ERROR == ( vtx_hash.table
             = DXCreateHash
                 ( sizeof(iso_edge_hash_rec),
                   iso_edge_hash_func, iso_edge_compare_func ) ) )
         ||
         ( ERROR == ( edge_hash.table
             = DXCreateHash
                 ( sizeof(iso_edge_hash_rec),
                   iso_edge_hash_func, iso_edge_compare_func ) ) ) )
        goto error;

    if ( !tally_dependers ( input_info, &n_c_dep, &n_p_dep ) )
        goto error;

    if ( ( n_c_dep > 1 )
         &&
         ( ERROR == ( c_ID_list = DXNewSegList ( sizeof(int), 1000, 1000 ) ) ) )
        goto error;

    if ( ci_info->get_item == _dxf_get_conn_grid_QUADS )
    {
        array_info  t0, t1;
        int         II, JJ;
        int         I0, J0;

        t0 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[0]);

        t1 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[1]);

        DXASSERTGOTO ( ((grid_connections_info)ci_info)->counts != NULL );
        DXASSERTGOTO
           ( t0->items == ( ((grid_connections_info)ci_info)->counts[0] - 1 ) );
        DXASSERTGOTO
           ( t1->items == ( ((grid_connections_info)ci_info)->counts[1] - 1 ) );

        if ( ((mesh_array_info)ci_info)->grow_offsets != NULL )
        {
            DXASSERTGOTO ( is_grown );

            I0 = ((mesh_array_info)ci_info)->grow_offsets[0];
            J0 = ((mesh_array_info)ci_info)->grow_offsets[1];
        }
        else
            I0 = J0 = 0;

        for ( II=I0;
              II<( I0 + t0->original_items );
              II++ )
        {
            if ( !ci_info->get_item 
                      ( ( ( II * t1->items ) + J0 ),
                        ci_info, &quad )
                 ||
                 !di_info->get_item ( quad.p, di_info, &data[0] ) ||
                 !di_info->get_item ( quad.r, di_info, &data[2] )   )
                goto error;

            data[0] -= value;
            data[2] -= value;

            for ( JJ=J0;
                  JJ<( J0 + t1->original_items );
                  JJ++,
                        quad.p++, quad.q++, quad.r++, quad.s++,
                        data[0] = data[1],  data[2] = data[3] )
            {
                if ( !di_info->get_item ( quad.q, di_info, &data[1] ) ||
                     !di_info->get_item ( quad.s, di_info, &data[3] )   )
                    goto error;

                data[1] -= value;
                data[3] -= value;

                if ( ( NULL != i_handle )
                     &&
                     DXIsElementInvalid ( i_handle, ((II*t1->items)+JJ) ) )
                    continue;
                else if ( !convert_connection
                          ( &quad, data, &vtx_hash, &edge_hash,
                            /* equivalence */ 1 ) )
                    goto error;
                else if ( ( NULL != c_ID_list )
                          &&
                          !set_c_ID
                               ( c_ID_list, edge_hash.count,
                                 ((II*t1->items)+JJ) ) )
                    goto error;
            }
        }
    }
    else
    {
        for ( i=0;
              i<ci_info->original_items;
              i++ )
            if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
                continue;
            else if ( !ci_info->get_item ( i, ci_info, &quad ) )
                goto error;
            else
            {
                for ( j=0; j<connection_shape; j++ )
                {
                    if ( !di_info->get_item
                              ( ((int *)&quad)[j], di_info, &data[j] ) )
                        goto error;
                    data[j] -= value;
                }

                if ( !convert_connection
                          ( &quad, data, &vtx_hash, &edge_hash,
                            /* equivalence */ 1 ) )
                    goto error;
                else if ( ( NULL != c_ID_list )
                          &&
                          !set_c_ID ( c_ID_list, edge_hash.count, i ) )
                    goto error;
            }
    }

    if ( edge_hash.count == 0 )
        if ( !_dxf_MakeFieldEmpty ( out ) )
            goto error;
        else
            goto done;

    if ( !create_output_arrays
              ( out,
                vtx_hash.count,     position_dim,
                edge_hash.count,    1,
                0,
                DEFAULT_LINE_COLOR, 
                value,              DEP_POSITIONS,
                ISOSURFACE ) )
        goto error;

    if ( vtx_hash.count == 0 ) 
        goto done;

    if ( ( edge_hash.count > 0 ) && ( c_ID_list != NULL ) )
    {
        if ( ( ERROR == ( c_ID_pull = DXAllocateLocal
                                          ( edge_hash.count * sizeof(int) ) ) )
             ||
             !copy_seglist_to_memory
                  ( c_ID_list, c_ID_pull, sizeof(int), edge_hash.count ) )
            goto error;
    
        DXDeleteSegList ( c_ID_list );  c_ID_list = NULL;
    }

    if ( ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
         ||
         !DXInitGetNextHashElement ( vtx_hash.table )
         ||
         !DXInitGetNextHashElement ( edge_hash.table ) )
        goto error;

    DXASSERTGOTO ( output_info->std_comps[(int)POSITIONS]   != NULL );
    DXASSERTGOTO ( output_info->std_comps[(int)CONNECTIONS] != NULL );

    po_info = (array_info) &(output_info->std_comps[(int)POSITIONS]->array);
    co_info = (array_info) &(output_info->std_comps[(int)CONNECTIONS]->array);

    for ( i = 0;
          ( NULL != ( element_ptr
                          = (iso_edge_hash_ptr) DXGetNextHashElement
                                                ( edge_hash.table ) ) );
          i++ )
    {
        DXASSERTGOTO ( element_ptr->seq < co_info->items );

        ((Line *)co_info->data) [ element_ptr->seq ] = element_ptr->edge;
    }

    DXDestroyHash ( edge_hash.table );
        edge_hash.table = NULL;  edge_hash.count = 0;


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( 0 != strcmp ( comp->name, "positions" ) ) &&
             ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
               ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
               ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "data"        ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )   )
            continue;

        a_info = (array_info) &(comp->array);

    if ( CLASS_CONSTANTARRAY == a_info->class )
    {
        if ( ( ERROR == ( array = (Array)DXNewConstantArrayV
                                     ( vtx_hash.count,
                                       DXGetConstantArrayData ( a_info->array ),
                                       a_info->type,
                                       a_info->category,
                                       a_info->rank,
                                       a_info->shape ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) ) )

            goto error;

        continue;
    }

        if ( ( ERROR == ( array
                   = DXNewArrayV
                         ( a_info->type, a_info->category,
                           a_info->rank, &a_info->shape[0] ) ) )
             ||
             !DXAllocateArray     ( array, vtx_hash.count )           ||
             !DXAddArrayData      ( array, 0, vtx_hash.count, NULL )  ||
             !DXTrim              ( array )                           ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) )
             ||
             !_dxf_SetIterator    ( comp )
             ||
             ( ERROR == ( out_data = DXGetArrayData  ( array )))             ||
             ( ERROR == ( scratch  = DXAllocateLocal ( 2 * a_info->itemsize )))
             ||
             !DXInitGetNextHashElement ( vtx_hash.table ) )
            goto error;

        array = NULL;

        for ( i = 0;
              ( NULL != ( element_ptr
                              = (iso_edge_hash_ptr) DXGetNextHashElement
                                                    ( vtx_hash.table ) ) );
              i++ )
        {
            DXASSERTGOTO ( element_ptr->seq < po_info->items );

            if ( element_ptr->edge.p == element_ptr->edge.q )
                t = 0.0;
            else
            {
                if ( !di_info->get_item
                          ( element_ptr->edge.p, di_info, &data[0] )||
                     !di_info->get_item
                          ( element_ptr->edge.q, di_info, &data[1] ) )
                    goto error;

                data[0] -= value;
                data[1] -= value;

                if      ( data[0] == 0.0 ) t = 0.0;
                else if ( data[1] == 0.0 ) t = 1.0;
                else
                    t = (double)data[0] / ( (double)data[0] - (double)data[1] );

                DXASSERTGOTO ( ( t >= 0.0 ) && ( t <= 1.0 ) );
            }

            if ( !interp ( a_info, scratch,
                           element_ptr->edge.p, element_ptr->edge.q, t,
                           &(((char*)out_data)
                               [ a_info->itemsize * element_ptr->seq ]),
			       a_info->itemsize/sizeof(float),
			       a_info->itemsize) )
                goto error;
        }

        DXFree ( scratch );  scratch = NULL;
    }


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
             ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
             ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                             "connections" ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "connections" ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
	     ( 0 == strcmp  ( comp->name, "invalid connections" ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

        if ( ( ERROR == ( array = _dxf_resample_array
                                      ( comp, 1,
                                        ((array_info) &(comp->array))->items,
                                        edge_hash.count, NULL, c_ID_pull ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object)array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "connections" ) ) )
            goto error;
    }

    if ( !remove_degenerates ( out ) )
        goto error;

    done:
        /* This is create_iso_contours */
        if ( !DXSetIntegerAttribute ( (Object) out, "shade", 0 ) )
            goto error;

        DXDestroyHash     ( edge_hash.table );
        DXDestroyHash     ( vtx_hash.table );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        edge_hash.table = NULL;
        edge_hash.count = 0;
        vtx_hash.table  = NULL;
        vtx_hash.count  = 0;
        output_info     = NULL;
        i_handle        = NULL;
        c_ID_list       = NULL;
        c_ID_pull       = NULL;
        scratch         = NULL;

        return DXEndField ( out );

    error:
        ERROR_SECTION;

        DXDestroyHash     ( edge_hash.table );
        DXDestroyHash     ( vtx_hash.table );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        edge_hash.table = NULL;
        edge_hash.count = 0;
        vtx_hash.table  = NULL;
        vtx_hash.count  = 0;
        output_info     = NULL;
        i_handle        = NULL;
        c_ID_list       = NULL;
        c_ID_pull       = NULL;
        scratch         = NULL;

        return ERROR;
}



#ifdef INCLUDES_BAND
static
Field create_band_surface
          ( field_info     input_info, 
            float          *value_ptr,
            int            number,
            band_remap_types remap,
            int            is_grown,
            float          *min_max,
            Error (*convert_connection)
                  (void *,float *,float *,int,hash_table_ptr,SegList *,int),
            Field out )

{
    array_info      pi_info, ci_info, di_info, ni_info = NULL;
    array_info      po_info, co_info, do_info, no_info = NULL;
    hash_table_rec  vtx_hash;
    SegList         *tri_list = NULL;
    SegList         *cval_list = NULL;
    Quadrilateral   quad;
    Point           point[4];
    float           data[4];
    double          t;
    band_edge_hash_ptr   element_ptr = NULL;
    int             D;
    int             i, j;
    field_info      output_info = NULL;
    int             position_dim;
    int             connection_shape;
    int             connection_count;
    float           value_low, value_high;
    float           old_min, old_max;
    float           *value2_ptr = NULL;
    int             have_normals = 0;

    SegList         *c_ID_list = NULL;
    int             *c_ID_pull = NULL;
    int             n_c_dep, n_p_dep;
    array_info      a_info;
    component_info  comp;
    Array           array    = NULL;
    Pointer         scratch  = NULL;
    Pointer         out_data = NULL;
    int             itemsize;

    InvalidComponentHandle  i_handle = NULL;

    DXASSERTGOTO ( ERROR != input_info );
    DXASSERTGOTO ( ERROR != value_ptr );
    DXASSERTGOTO ( ERROR != convert_connection );
    DXASSERTGOTO ( ERROR != out );

    vtx_hash.count        = 0;
    vtx_hash.table        = NULL;
    vtx_hash.set_vertices = NULL;

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    if ( ERROR == ( value2_ptr = band_value_list
                                     ( input_info->field,
                                       value_ptr, &number, min_max,
                                       &old_min, &old_max ) ) )
        goto error;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);
    ni_info = (input_info->std_comps[(int)NORMALS]==NULL)?
              NULL: (array_info) &(input_info->std_comps[(int)NORMALS]->array);

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    if ( ( pi_info->shape[0] != 2 ) &&
         ( pi_info->shape[0] != 3 ) )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#11400", /* %s has invalid dimensionality; does not match %s */
              "\"positions\" component", "\"connections\"" );

    position_dim     = pi_info->shape[0];
    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( ( connection_shape == 3 ) || ( connection_shape == 4 ) );

    DXASSERTGOTO ( ci_info->items >= ci_info->original_items );

    TST_HASH_INTS;

    if ( ( ERROR == ( vtx_hash.table
             = DXCreateHash
                 ( sizeof(band_edge_hash_rec),
                   band_edge_hash_func,
                   band_edge_compare_func ) ) )
         ||
         ( ERROR == ( tri_list
                          = DXNewSegList ( sizeof(Triangle), 1000, 1000 ) ) ) )
        goto error;

    if ( remap != ORIGINAL_DATA )
       if ( ERROR == ( cval_list = DXNewSegList ( sizeof(float), 1000, 1000 )))
            goto error;

    if ( !tally_dependers ( input_info, &n_c_dep, &n_p_dep ) )
        goto error;

    if ( ( n_c_dep > 1 )
         &&
         ( ERROR == ( c_ID_list = DXNewSegList ( sizeof(int), 1000, 1000 ) ) ) )
        goto error;

    if ( ci_info->get_item == _dxf_get_conn_grid_QUADS )
    {
        array_info  t0, t1;
        int         II, JJ;
        int         I0, J0;

        t0 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[0]);

        t1 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[1]);

        DXASSERTGOTO ( ((grid_connections_info)ci_info)->counts != NULL );
        DXASSERTGOTO
           ( t0->items == ( ((grid_connections_info)ci_info)->counts[0] - 1 ) );
        DXASSERTGOTO
           ( t1->items == ( ((grid_connections_info)ci_info)->counts[1] - 1 ) );

        if ( ((mesh_array_info)ci_info)->grow_offsets != NULL )
        {
            DXASSERTGOTO ( is_grown );

            I0 = ((mesh_array_info)ci_info)->grow_offsets[0];
            J0 = ((mesh_array_info)ci_info)->grow_offsets[1];
        }
        else
            I0 = J0 = 0;

        for ( II=I0;
              II<( I0 + t0->original_items );
              II++ )
        {
            if ( !ci_info->get_item 
                      ( ( ( II * t1->items ) + J0 ),
                        ci_info, &quad )
                 ||
                 !di_info->get_item ( quad.p, di_info, &data[0] ) ||
                 !di_info->get_item ( quad.r, di_info, &data[2] )   )
                goto error;

            for ( JJ=J0;
                  JJ<( J0 + t1->original_items );
                  JJ++,
                        quad.p++, quad.q++, quad.r++, quad.s++,
                        data[0] = data[1],  data[2] = data[3] )
            {
                if ( !di_info->get_item ( quad.q, di_info, &data[1] ) ||
                     !di_info->get_item ( quad.s, di_info, &data[3] )   )
                    goto error;

                if ( ( NULL != i_handle )
                     &&
                     DXIsElementInvalid ( i_handle, ((II*t1->items)+JJ) ) )
                    continue;
                else
                    for ( D=0; D<number-1; D++ )
                    {
                        if ( !convert_connection
                                 ( &quad, data, value2_ptr, D,
                                   &vtx_hash, tri_list, 1 ) )
                            goto error;
                        else if ( ( NULL != c_ID_list )
                                  &&
                                  !set_c_ID
                                       ( c_ID_list,
                                         DXGetSegListItemCount ( tri_list ),
                                         II ) )
                            goto error;

                        switch ( remap )
                        {
                            case ORIGINAL_DATA: break;

                            case DATA_LOW:
                                if ( !set_cval
                                         ( cval_list,
                                           DXGetSegListItemCount ( tri_list ),
                                           ( ( D == 0 ) ?
                                               old_min : value2_ptr[D] ) ) )
                                    goto error;
                                break;

                            case DATA_HIGH:
                                if ( !set_cval
                                         ( cval_list,
                                           DXGetSegListItemCount ( tri_list ),
                                           ( ( (D+1) == number ) ?
                                               old_max : value2_ptr[D+1] ) ) )
                                    goto error;
                                break;
                        }
                    }
            }
        }
    }
    else
    {
        for ( i=0;
              i<ci_info->original_items;
              i++ )
            if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
                continue;
            else if ( !ci_info->get_item ( i, ci_info, &quad ) )
                goto error;
            else
            {
                for ( j=0; j<connection_shape; j++ )
                    if ( !di_info->get_item
                              ( ((int *)&quad)[j], di_info, &data[j] ) )
                        goto error;

                for ( D=0; D<number-1; D++ )
                {
                    if ( !convert_connection
                             ( &quad, data, value2_ptr, D,
                               &vtx_hash, tri_list, 1 ) )
                        goto error;
                    else if ( ( NULL != c_ID_list )
                              &&
                              !set_c_ID
                                   ( c_ID_list,
                                     DXGetSegListItemCount ( tri_list ), i ) )
                        goto error;

                    switch ( remap )
                    {
                        case ORIGINAL_DATA: break;

                        case DATA_LOW:
                            if ( !set_cval
                                     ( cval_list,
                                       DXGetSegListItemCount ( tri_list ),
                                       ( ( D == 0 ) ?
                                           old_min : value2_ptr[D] ) ) )
                                goto error;
                            break;

                        case DATA_HIGH:
                            if ( !set_cval
                                     ( cval_list,
                                       DXGetSegListItemCount ( tri_list ),
                                       ( ( (D+1) == number ) ?
                                           old_max : value2_ptr[D+1] ) ) )
                                goto error;
                            break;
                    }
                }
            }
    }

    connection_count = DXGetSegListItemCount ( tri_list );

    if ( connection_count == 0 )
        if ( !_dxf_MakeFieldEmpty ( out ) )
            goto error;
        else
            goto done;

    if ( !create_output_arrays
              ( out,
                vtx_hash.count,   position_dim,
                connection_count, 2,
                ( (have_normals==1)? vtx_hash.count : 0 ),
                DEFAULT_SURFACE_COLOR, 
                0.0, ( (remap==ORIGINAL_DATA)? DEP_POSITIONS: DEP_CONNECTIONS ),
                BAND ) )
        goto error;

    if ( vtx_hash.count == 0 ) 
        goto done;

    if ( ( connection_count > 0 ) && ( c_ID_list != NULL ) )
    {
        if ( ( ERROR == ( c_ID_pull = DXAllocateLocal
                                          ( connection_count * sizeof(int) ) ) )
             ||
             !copy_seglist_to_memory
                  ( c_ID_list, c_ID_pull, sizeof(int), connection_count ) )
            goto error;
    
        DXDeleteSegList ( c_ID_list );  c_ID_list = NULL;
    }

    if ( ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
         ||
         !DXInitGetNextHashElement ( vtx_hash.table ) )
        goto error;

    DXASSERTGOTO ( output_info->std_comps[(int)POSITIONS] != NULL );

    po_info = (array_info) &(output_info->std_comps[(int)POSITIONS]->array);
    co_info = (array_info) &(output_info->std_comps[(int)CONNECTIONS]->array);
    do_info = (array_info) &(output_info->std_comps[(int)DATA]->array);
    no_info = (output_info->std_comps[(int)NORMALS]==NULL)?
             NULL: (array_info) &(output_info->std_comps[(int)NORMALS]->array);

    if ( !copy_seglist_to_array ( tri_list, co_info ) )
        goto error;

    if ( remap != ORIGINAL_DATA )
        if ( !copy_seglist_to_array ( cval_list, do_info ) )
            goto error;

    DXDeleteSegList ( tri_list  );  tri_list = NULL;
    DXDeleteSegList ( cval_list );  cval_list = NULL;

    DXASSERTGOTO ( ERROR != do_info->data );


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {
        if ( ( 0 != strcmp ( comp->name, "positions" ) ) &&
             ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
               ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
               ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            continue;

        if ( ( ( 0 == strcmp ( comp->name, "data" ) )
               &&
               ( remap != ORIGINAL_DATA ) )
             ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

    if ( CLASS_CONSTANTARRAY == a_info->class )
    {
        if ( ( ERROR == ( array = (Array)DXNewConstantArrayV
                                     ( vtx_hash.count,
                                       DXGetConstantArrayData ( a_info->array ),
                                       a_info->type,
                                       a_info->category,
                                       a_info->rank,
                                       a_info->shape ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) ) )

            goto error;

        continue;
    }

        if ( ( ERROR == ( array
                   = DXNewArrayV
                         ( TYPE_FLOAT, a_info->category,
                           a_info->rank, &a_info->shape[0] ) ) )
             ||
             !DXAllocateArray     ( array, vtx_hash.count )           ||
             !DXAddArrayData      ( array, 0, vtx_hash.count, NULL )  ||
             !DXTrim              ( array )                           ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) )
             ||
             !_dxf_SetIterator    ( comp )
             ||
             ( ERROR == ( out_data = DXGetArrayData  ( array )))
	     ||
	     !(itemsize = DXGetItemSize(array))
	     ||
             ( ERROR == ( scratch  = DXAllocateLocal ( 2 * itemsize)))
             ||
             !DXInitGetNextHashElement ( vtx_hash.table ) )
            goto error;

        array = NULL;

        for ( i = 0;
              ( NULL != ( element_ptr
                              = (band_edge_hash_ptr) DXGetNextHashElement
                                                     ( vtx_hash.table ) ) );
              i++ )
        {
            DXASSERTGOTO ( element_ptr->seq < po_info->items );

            if ( element_ptr->edge.p == element_ptr->edge.q )
                t = 0.0;
            else
            {
                if ( !di_info->get_item
                         ( element_ptr->edge.p, di_info, &data[0] )||
                     !di_info->get_item
                         ( element_ptr->edge.q, di_info, &data[1] ) )
                    goto error;

                /* DXASSERTGOTO ( element_ptr->select != NEITHER_VALUE ); */

                value_low = value2_ptr [ element_ptr->select ];

                if      ( data[0] == value_low ) t = 0.0;
                else if ( data[1] == value_low ) t = 1.0;
                else
                    t = ( (double)data[0] - (double)value_low ) / ( (double)data[0] - (double)data[1] );

                DXASSERTGOTO ( ( t >= 0.0 ) && ( t <= 1.0 ) );
            }

            if ( !interp ( a_info, scratch,
                           element_ptr->edge.p, element_ptr->edge.q, t, 
                           &(((char*)out_data)
                               [ itemsize * element_ptr->seq ]),
			       itemsize/sizeof(float),
			       itemsize) )
                goto error;
        }

        DXFree ( scratch );  scratch = NULL;
    }


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
             ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
             ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                             "connections" ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "connections" ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
	     ( 0 == strcmp  ( comp->name, "invalid connections" ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

        if ( ( ERROR == ( array = _dxf_resample_array
                                      ( comp, 1,
                                        ((array_info) &(comp->array))->items,
                                        connection_count, NULL, c_ID_pull ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object)array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "connections" ) ) )
            goto error;
    }

    if ( !remove_degenerates ( out ) )
        goto error;

    done:
        /* This is create_band_surface */
        if ( DXGetAttribute ( (Object)input_info->field, "shade" ) )
            if ( !DXSetAttribute
                      ( (Object) out,
                        "shade",
                        DXGetAttribute
                            ( (Object)input_info->field, "shade" ) ) )
                goto error;


        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( (Pointer) value2_ptr );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        value2_ptr     = NULL;
        output_info    = NULL;
        i_handle       = NULL;
        c_ID_list      = NULL;
        c_ID_pull      = NULL;
        scratch        = NULL;

        return DXEndField ( out );

    error:
        ERROR_SECTION;

        DXDestroyHash     ( vtx_hash.table );
        DXFree            ( (Pointer) value2_ptr );
        _dxf_FreeInMemory ( output_info );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        DXFreeInvalidComponentHandle ( i_handle );

        vtx_hash.table = NULL;
        vtx_hash.count = 0;
        value2_ptr     = NULL;
        output_info    = NULL;
        i_handle       = NULL;
        c_ID_list      = NULL;
        c_ID_pull      = NULL;
        scratch        = NULL;

        return ERROR;
}
#endif



#if 1
#define IsFlippedTet_METHOD _dxf_IsFlippedTet_4x4_method 
#else
#define IsFlippedTet_METHOD _dxf_IsFlippedTet_3x3_method 
#endif



#if ( IsFlippedTet_METHOD == _dxf_IsFlippedTet_4x4_method )
static
 /* required here and in showboundary.m */
int _dxf_IsFlippedTet_4x4_method ( Tetrahedron tet, Point *geom )
{
   /*
    * Calculate the geometric determinate of a tetrahedron, use it's sign.
    * The expression below is the matrix we are 'determining', with 'g'
    * representing geometry, 't' representing tetrahedral connection indices.
    *
    * +-----------------------+----------------------------------------------+
    * |   a11  a12  a13  a14  |   1.0        1.0        1.0        1.0       |
    * |   a21  a22  a23  a24  |   g[t.p].x   g[t.q].x   g[t.r].x   g[t.s].x  |
    * |   a31  a32  a33  a34  |   g[t.p].y   g[t.q].y   g[t.r].y   g[t.s].y  |
    * |   a41  a42  a43  a44  |   g[t.p].z   g[t.q].z   g[t.r].z   g[t.s].z  |
    * +-----------------------+----------------------------------------------+
    */
    int ret;

    double  a11, a12, a13, a14,   a21, a22, a23, a24, 
            a31, a32, a33, a34,   a41, a42, a43, a44;

    double  d;

    DXASSERTGOTO ( geom != NULL );

    a11=1.0;           a12=1.0;           a13=1.0;           a14=1.0;
    a21=geom[tet.p].x; a22=geom[tet.q].x; a23=geom[tet.r].x; a24=geom[tet.s].x;
    a31=geom[tet.p].y; a32=geom[tet.q].y; a33=geom[tet.r].y; a34=geom[tet.s].y;
    a41=geom[tet.p].z; a42=geom[tet.q].z; a43=geom[tet.r].z; a44=geom[tet.s].z;

    d = a11*(a22*(a33*a44-a43*a34)-a23*(a32*a44-a42*a34)+a24*(a32*a43-a42*a33))
       -a12*(a21*(a33*a44-a43*a34)-a23*(a31*a44-a41*a34)+a24*(a31*a43-a41*a33))
       +a13*(a21*(a32*a44-a42*a34)-a22*(a31*a44-a41*a34)+a24*(a31*a42-a41*a32))
      -a14*(a21*(a32*a43-a42*a33)-a22*(a31*a43-a41*a33)+a23*(a31*a42-a41*a32));

    ret = ( ABS(d) < DXD_MIN_FLOAT ) ? 0 : ( d < 0.0 )? 1 : -1;

    DXDebug ( "I", "_dxf_IsFlippedTet_4x4_method() = %g = %d", d, ret );

    return ret;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



#if ( IsFlippedTet_METHOD == _dxf_IsFlippedTet_3x3_method )
static
 /* required here and in showboundary.m */
int _dxf_IsFlippedTet_3x3_method ( Tetrahedron tet, Point *geom )
{
   /*
    * Calculate the geometric determinate of a tetrahedron, use it's sign.
    * Three delta vectors method.
    *
    *+-------------+-----------------------------------------------------------+
    *| a11 a12 a13 | (g[t.p].x-g[t.q].x) (g[t.p].x-g[t.r].x) (g[t.p].x-g[t.s].x)
    *| a21 a22 a23 | (g[t.p].y-g[t.q].y) (g[t.p].y-g[t.r].y) (g[t.p].y-g[t.s].y)
    *| a31 a32 a33 | (g[t.p].z-g[t.q].z) (g[t.p].z-g[t.r].z) (g[t.p].z-g[t.s].z)
    *+-------------+-----------------------------------------------------------+
    */
    int    ret;

    double  a11, a12, a13,   a21, a22, a23,   a31, a32, a33;

    double  d;

    DXASSERTGOTO ( geom != NULL );

    a11 = geom[tet.p].x - geom[tet.q].x;
    a21 = geom[tet.p].y - geom[tet.q].y;
    a31 = geom[tet.p].z - geom[tet.q].z;

    a12 = geom[tet.p].x - geom[tet.r].x;
    a22 = geom[tet.p].y - geom[tet.r].y;
    a32 = geom[tet.p].z - geom[tet.r].z;

    a13 = geom[tet.p].x - geom[tet.s].x;
    a23 = geom[tet.p].y - geom[tet.s].y;
    a33 = geom[tet.p].z - geom[tet.s].z;

    d = a11*(a22*a33-a32*a23) - a12*(a21*a33-a31*a23) + a13*(a21*a32-a31*a22);

    ret = ( ABS(d) < DXD_MIN_FLOAT ) ? 0 : ( d < 0.0 )? 1 : -1;

    DXDebug ( "I", "_dxf_IsFlippedTet_3x3_method() = %g = %d", d, ret );

    return ret;

    error:
        ERROR_SECTION;
        return ERROR;
}
#endif



extern
int _dxf_get_flip
        ( field_info input_info,
          int        *have_connections,
          int        *have_nondegeneracy )
{
    array_info  p_info, c_info = NULL;
    Array       array;
    Point       pseudo_pt[4];

    static Tetrahedron pseudo_tet = { 0, 1, 2, 3 };

    int flipq;
    int i;

    /* XXX - DXEmptyField call belongs here */

    if ( NULL == input_info->std_comps[(int)CONNECTIONS] )
        DXErrorGoto2
            ( ERROR_INVALID_DATA,
              "#10240", /* missing %s component */ "connections" );

    if ( (input_info->std_comps[(int)CONNECTIONS]->element_type != TETRAHEDRA)
         &&
         (input_info->std_comps[(int)CONNECTIONS]->element_type != CUBES ) )
        DXErrorGoto3 ( ERROR_INVALID_DATA, "#10203", /* %s must be one of: %s */
                       "connections element type", "tetrahedra, cubes" );

    p_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    c_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);

    if ( p_info->get_item == NULL )
        if ( !_dxf_SetIterator ( input_info->std_comps[(int)POSITIONS] ) )
            goto error;

    if ( c_info->get_item == NULL )
        if ( !_dxf_SetIterator ( input_info->std_comps[(int)CONNECTIONS] ) )
            goto error;

    if ( c_info->items != 0 )
        *have_connections = 1;

    for ( i=0, flipq=0; ( ( flipq == 0 ) && ( i < c_info->items ) ); i++ )
    {
        switch ( input_info->std_comps[(int)CONNECTIONS]->element_type )
        {
            case TETRAHEDRA:
                {
                    Tetrahedron tet;
                    if ( !c_info->get_item ( i, c_info, &tet ) ||
                         !p_info->get_item ( tet.p, p_info, &pseudo_pt[0] ) ||
                         !p_info->get_item ( tet.q, p_info, &pseudo_pt[1] ) ||
                         !p_info->get_item ( tet.r, p_info, &pseudo_pt[2] ) ||
                         !p_info->get_item ( tet.s, p_info, &pseudo_pt[3] ) )
                        goto error;
                }
                break;
    
            case CUBES:
                {
                    Cube cube;
                    /* XXX - 0132 */
                    if ( !c_info->get_item ( i, c_info, &cube ) ||
                         !p_info->get_item ( cube.p, p_info, &pseudo_pt[0] ) ||
                         !p_info->get_item ( cube.s, p_info, &pseudo_pt[1] ) ||
                         !p_info->get_item ( cube.u, p_info, &pseudo_pt[3] ) ||
                         !p_info->get_item ( cube.v, p_info, &pseudo_pt[2] ) )
                        goto error;
                }
                break;

            default:
                goto error;
        }
        flipq = IsFlippedTet_METHOD ( pseudo_tet, pseudo_pt );
    }


    if ( ( flipq == 1 ) || ( flipq == -1 ) )
        *have_nondegeneracy = 1;
    else
        /* arbitrary */
        flipq = 1;
        
    return flipq;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
Vector *tetra_grad
           ( Point *p0,  Point *p1,  Point *p2,  Point *p3, 
             float  d0,  float  d1,  float  d2,  float  d3, 
             Vector *gradient )
{
    float  A, B, C, D;
    float  invD;

    float  px, py, pz, pw;
    float  a0, a1, a2, a3;
    float  b0, b1, b2, b3;
    float  c0, c1, c2, c3;
    float  ab01, ac01, bc01, ab23, ac23, bc23;

    /* 
     * (Somewhat mixed symbology, EG: a0,a1,a2 represents x,y,z coordinates).
     *
     * Algorithm as from _gradient.c/worker_3D_tetrahedra_manhattan
     *   which is this:
     *
     * We now form 4-vectors whose first 3 coordinates are the x, y, and z
     * coordinates of a vertex of the tetrahedron, and whose 4th coordinate
     * w is the scalar data value associated with that vertex.  There are
     * 4 such 4-vectors.  We then find the equation of the 3D hyperplane
     * in 4D space which is determined by these 4 4-vectors.  We seek
     * an equation of the form: w = K1 * x + K2 * y + K3 * z + K4.
     * In this equation, K1 == df/dx, K2 == df/dy, and K3 == df/dz.
     *
     *
     * To find the equation, we represent the first point 
     * and its data value p3 as a 4-vector <px, py, pz, pw>.  We form 
     * difference 4-vectors a, b, and c whose first three coordinates
     * are the x, y, and z differences between the remaining three 
     * points of the tetrahedron and the first point, and whose last
     * coordinate is the difference between the data values of the
     * points of the tetrahedron and the data value of the first point.
     * The equation is given by the determinant:
     *
     *  | x - px   y - py   z - pz   w - pw |
     *  |   a0       a1       a2       a3   | == 0
     *  |   b0       b1       b2       b3   |
     *  |   c0       c1       c2       c3   |
     *
     * The trick here is to solve this determinant with as few
     * multiplications as possible.
     */

    px = p0->x;
    py = p0->y;
    pz = p0->z;
    pw = d0;

    a0 = p1->x - px;
    a1 = p1->y - py;
    a2 = p1->z - pz;
    a3 = d1    - pw;

    b0 = p2->x - px;
    b1 = p2->y - py;
    b2 = p2->z - pz;
    b3 = d2    - pw;

    c0 = p3->x - px;
    c1 = p3->y - py;
    c2 = p3->z - pz;
    c3 = d3    - pw;

    ab01 = ( a0 * b1 ) - ( b0 * a1 );
    ac01 = ( a0 * c1 ) - ( c0 * a1 );
    bc01 = ( b0 * c1 ) - ( c0 * b1 );
    ab23 = ( a2 * b3 ) - ( b2 * a3 );
    ac23 = ( a2 * c3 ) - ( c2 * a3 );
    bc23 = ( b2 * c3 ) - ( c2 * b3 );

    A = ( a1 * bc23 ) - ( b1 * ac23 ) + ( c1 * ab23 );
    B = ( a0 * bc23 ) - ( b0 * ac23 ) + ( c0 * ab23 );
    C = ( a3 * bc01 ) - ( b3 * ac01 ) + ( c3 * ab01 );
    D = ( a2 * bc01 ) - ( b2 * ac01 ) + ( c2 * ab01 );

    if ( ( D >= -DXD_MIN_FLOAT ) && ( D <= DXD_MIN_FLOAT ) )
    {
        /* Degenerate tetrahedron. */
        /* Note: still will get 0 magnitude gradient when all dvalues equal */

        DXDebug ( "I", "Degenerate tetrahedron." );
        gradient->x = gradient->y = gradient->z = 0.0;
    }
    else
    {
        /* Normal tetrahedron. */

        invD        = 1.0 / D;
        gradient->x =  A * invD;
        gradient->y = -B * invD;
        gradient->z =  C * invD;
    }

    return gradient;
}



static
Error flip_normals_and_triangles
          ( field_info  input_info,
            field_info  output_info,
            int         normal_direction )
{
    array_info  pi_info, ci_info = NULL;
    array_info  co_info, no_info = NULL;
    Point       pseudo_pt[4];

    static Tetrahedron pseudo_tet = { 0, 1, 2, 3 };

    int flipq;

    Vector   *np;
    Triangle *tp;
    float     length;
    int       i, save;

    int have_connects = 0;
    int have_nondegen = 0;

    DXASSERTGOTO ( ( normal_direction == 1 ) || ( normal_direction == -1 ) );

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    co_info = (array_info) &(output_info->std_comps[(int)CONNECTIONS]->array);
    no_info = (array_info) &(output_info->std_comps[(int)NORMALS]->array);

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );


    if ( ERROR == ( flipq = _dxf_get_flip
                                ( input_info,
                                  &have_connects, &have_nondegen ) ) )
        goto error;

    if ( ( have_connects == 1 ) && ( have_nondegen == 0 ) )
         DXErrorGoto
             ( ERROR_INVALID_DATA,
               "#11836" /* all connections are degenerate */ );


    switch ( input_info->std_comps[(int)CONNECTIONS]->element_type )
    {
        case CUBES: flipq *= -1;  break;
        default: ;
    }

#if 0
    DXWarning ( "normal_direction = %d, flipq = %d", normal_direction, flipq );
#endif

    if ( normal_direction == flipq )
        for ( i=0, tp=(Triangle*)co_info->data;
              i<co_info->items;
              i++, tp++ )
        {
            save  = tp->p;
            tp->p = tp->q;
            tp->q = save;
        }

    if ( normal_direction == 1 )
    {
        for ( i=0, np=(Vector*) no_info->data;
              i<no_info->items;
              i++, np++ )
            if ( ( length = DXLength ( *np ) ) != 0.0 )  /* CONSTANT NORMALS */
            {
                np->x /= length;
                np->y /= length;
                np->z /= length;
            }
    }
    else
        for ( i=0, np=(Vector*) no_info->data;
              i< no_info->items;
              i++, np++ )
            if ( ( length = -DXLength ( *np ) ) != 0.0 )  /* CONSTANT NORMALS */
            {
                np->x /= length;
                np->y /= length;
                np->z /= length;
            }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}



static
grad_table_ptr create_special_gradient
             ( field_info      input_info,
               hash_table_ptr  vtx_hash,
               grad_table_ptr  grad )
{
    array_info    pi_info, ci_info, di_info = NULL;
    Cube          connection;
    Point         centroid;
    Point         point[8];
    float         data[8];
    Vector        ccg;
    Point         d;
    Vector        *g[8];
    int           i, j, k;
    int           connection_shape;
    float         dist, invdist;
    float         _small_ = sqrt ( DXD_MIN_FLOAT );

    InvalidComponentHandle  i_handle = NULL;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( ( connection_shape == 4 ) || ( connection_shape == 8 ) );

    for ( i=0; i<ci_info->items; i++ )
        if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
            continue;
        else if ( !ci_info->get_item ( i, ci_info, &connection ) )
            goto error;
        else
            for ( j=0; j<connection_shape; j++ )
#if 1
                if ( GETBIT ( vtx_hash->set_vertices, ((int*)&connection)[j] ) )
#else
                if ( 1 )
#endif
                {
                    for ( k=0; k<connection_shape; k++ )
                    {
                        if ( !di_info->get_item 
                                 ( ((int*)&connection)[k], di_info, &data[k] )
                             ||
                             !pi_info->get_item
                                 ( ((int*)&connection)[k], pi_info, &point[k]))
                            goto error;

                        if ( k == 0 )
                            centroid = point[0];
                        else
                        {
                            centroid.x += point[k].x;
                            centroid.y += point[k].y;
                            centroid.z += point[k].z;
                        }

                        if ( NULL ==
                                 ( g[k] = grad->index[((int*)&connection)[k]]) )
                        {
                            if ( ERROR ==
                                    ( g[k] = grad->index[((int*)&connection)[k]]
                                        = (Vector*) DXNewSegListItem 
                                                        ( grad->list ) ) )
                                goto error;
                            else
                                g[k]->x = g[k]->y = g[k]->z = 0.0;
                        }
                    }

/* XXX - dist was not used in this calculation? */

#define WEIGHTED_CONTRIBUTION(N) \
    d.x = centroid.x-point[N].x; d.y = centroid.y-point[N].y;\
    d.z = centroid.z-point[N].z; \
    dist = ABS(d.x) + ABS(d.y) + ABS(d.z);    \
    if (dist > DXD_MIN_FLOAT) { invdist = 1.0 / dist;   \
        g[N]->x += ccg.x * invdist;   \
        g[N]->y += ccg.y * invdist;   \
        g[N]->z += ccg.z * invdist; }
#if 0
    if (dist > 0.0) { \
        g[N]->x += ccg.x;   \
        g[N]->y += ccg.y;   \
        g[N]->z += ccg.z; }
    if (dist > _small_) { invdist = 1.0 / ( dist * dist );   \
        g[N]->x += ccg.x * invdist;   \
        g[N]->y += ccg.y * invdist;   \
        g[N]->z += ccg.z * invdist; }
#endif

                    if ( connection_shape == 4 )
                    {
                        centroid.x *= 0.25;
                        centroid.y *= 0.25;
                        centroid.z *= 0.25;

                        if ( !tetra_grad ( &point[0], &point[1],
                                           &point[2], &point[3],
                                           data[0], data[1],
                                           data[2], data[3], &ccg ) )
                            goto error;

                        WEIGHTED_CONTRIBUTION ( 0 ); 
                        WEIGHTED_CONTRIBUTION ( 1 ); 
                        WEIGHTED_CONTRIBUTION ( 2 ); 
                        WEIGHTED_CONTRIBUTION ( 3 ); 
                    }
                    else if ( connection_shape == 8 )
                    {
                        centroid.x *= 0.125;
                        centroid.y *= 0.125;
                        centroid.z *= 0.125;

                        /* PSUV=0356 */

                        if ( !tetra_grad ( &point[0], &point[3],
                                           &point[5], &point[6],
                                           data[0], data[3],
                                           data[5], data[6], &ccg ) )
                            goto error;

                        WEIGHTED_CONTRIBUTION ( 0 ); 
                        WEIGHTED_CONTRIBUTION ( 1 ); 
                        WEIGHTED_CONTRIBUTION ( 2 ); 
                        WEIGHTED_CONTRIBUTION ( 3 ); 
                        WEIGHTED_CONTRIBUTION ( 4 ); 
                        WEIGHTED_CONTRIBUTION ( 5 ); 
                        WEIGHTED_CONTRIBUTION ( 6 ); 
                        WEIGHTED_CONTRIBUTION ( 7 ); 
                    }
                    break /*j*/;
                }
#undef  WEIGHTED_CONTRIBUTION

    DXFreeInvalidComponentHandle ( i_handle );

    i_handle = NULL;

    return grad;

    error:
        ERROR_SECTION;

        DXFreeInvalidComponentHandle ( i_handle );

        i_handle = NULL;

        return ERROR;
}



static
/*
 * Special scan to remove geometrically degenerate connectives.
 *   Zero area triangles.
 *   (Zero length lines we'll ignore until problems are revealed)
 * 
 *   Comment: since this field was constructed by isosurface,
 *     we skip some integrity checks, about connection dependency and so on.
 *   XXX:
 *     since edge degeneration is coalesced, point compare by ID should be OK.
 *     (below its by geometric value)
 */
Field remove_degenerates ( Field in )
{
#if 1

    /* XXX - should not remove follow-ons, EG "color map" ? */

    /*
     * Remove unused points.
     */

    if ( DXEmptyField ( in ) )
        return in;

    if ( !DXInvalidateUnreferencedPositions ( (Object)in ) ||
         !DXCull                            ( (Object)in )   )
        return ERROR;

    return in;

#else

    array_info p_info;
    array_info c_info;

    component_info comp;

    Array array;

    int      *new_pts        = NULL;
    int      *new_tris       = NULL;
    Triangle *tri_base, *t_a = NULL;
    Point    *pts_base, *p_a = NULL;
    int      i, j;
    int      nto, npo;

    DXDebug ( "I", "start:RemoveDegenerates()" );

    /*
     * Initialize bad_pts to 0, bad_tris to 0, new_pts[i] to i.
     * Foreach tri
     *    if any two of the three points coincide then
     *        set higher of 2 points' new_pts to the other matching
     *        mark bad_pts of higher
     *        mark bad_tri
     * Foreach pt
     *    if not a bad_pt then
     *        Copy the coordinates up in memory
     *        Assign a unique ID for the point
     *    else
     *        Assign the ID of the coincident point
     * Set the component array lengths based on the new point count.
     * Foreach tri
     *    if not a bad_tri then
     *        set the three ID's based on new_pts
     * Set the connections component lengths based on the new triangle count.
     * Clean up.
     */

    /* XXX - QueryConnectivity ( in, 0 ) == HAS_TRIANGLES */

    if ( !DXEmptyField ( in->field ) )
    {
        p_info = (array_info) &(in->std_comps[(int)POSITIONS]->array);
        c_info = (array_info) &(in->std_comps[(int)CONNECTIONS]->array);

        tri_base = (Triangle *) c_info->data;
        pts_base = (Point *)    p_info->data;


#define SETUP_AREAS \
{if ((ERROR==(new_pts=(int*)DXAllocateLocal(p_info->items*sizeof(int))))\
 ||(ERROR==(new_tris=(int*)DXAllocateLocal(c_info->items*sizeof(int)))))\
    goto error; \
    DXDebug ( "I", "RemoveDegenerates: Doin' it at i=%d.", i ); \
    for ( j=0; j<c_info->items; j++ ) new_tris [ j ] = j; \
    for ( j=0; j<p_info->items; j++ ) new_pts  [ j ] = j; }


        for ( i=0, nto=c_info->items, t_a=tri_base;
              i<c_info->items;
              i++, t_a++ )
        {
            if ( 0 == memcmp ( (char *) &pts_base [ t_a->p ],
                               (char *) &pts_base [ t_a->q ], 
                               sizeof ( Point ) ) )
            {
                if ( new_tris == NULL ) SETUP_AREAS;

                new_tris [ i ] = -1;   nto--;

                if ( t_a->p < t_a->q ) new_pts [ t_a->q ] = t_a->p;
                else                   new_pts [ t_a->p ] = t_a->q;
            }

            if ( 0 == memcmp ( (char *) &pts_base [ t_a->q ],
                               (char *) &pts_base [ t_a->r ], 
                               sizeof ( Point ) ) )
            {
                if ( new_tris == NULL ) SETUP_AREAS;

                new_tris [ i ] = -1;   nto--;

                if ( t_a->q < t_a->r ) new_pts [ t_a->r ] = t_a->q;
                else                   new_pts [ t_a->q ] = t_a->r;
            }

            if ( 0 == memcmp ( (char *) &pts_base [ t_a->r ],
                               (char *) &pts_base [ t_a->p ], 
                               sizeof ( Point ) ) )
            {
                if ( new_tris == NULL ) SETUP_AREAS;

                new_tris [ i ] = -1;   nto--;

                if ( t_a->r < t_a->p ) new_pts [ t_a->p ] = t_a->r;
                else                   new_pts [ t_a->r ] = t_a->p;
            }
        }
#undef SETUP_AREAS

        if ( new_tris == NULL )
            return in;

#if 0
        Warning ( "Areas = %s", ( new_tris == NULL )? "NULL" : "Stuffed" );

        for ( i=0; i<c_info->items; i++ )
            if ( new_tris[i] != i ) 
                DXWarning
                   ( "bad[%d] = [%d,%d,%d], {[%g,%g,%g],[%g,%g,%g],[%g,%g,%g]}",
                     i,
                           tri_base[i].p, tri_base[i].q, tri_base[i].r,
                           pts_base [ tri_base[i].p ].x,
                           pts_base [ tri_base[i].p ].y,
                           pts_base [ tri_base[i].p ].z,
                           pts_base [ tri_base[i].q ].x,
                           pts_base [ tri_base[i].q ].y,
                           pts_base [ tri_base[i].q ].z,
                           pts_base [ tri_base[i].r ].x,
                           pts_base [ tri_base[i].r ].y,
                           pts_base [ tri_base[i].r ].z );
#endif

        for ( i=0, npo=0; i<p_info->items; i++ )
            if ( new_pts [ i ] == i )
                new_pts [ i ] = npo++;
            else
                new_pts [ i ] = new_pts [ new_pts [ i ] ];

        for ( i=0, nto=0, t_a=tri_base; i<c_info->items; i++, t_a++ )
            if ( new_tris [ i ] != -1 )
            {
                new_tris [ i ] = nto++;

                t_a->p = new_pts [ t_a->p ];
                t_a->q = new_pts [ t_a->q ];
                t_a->r = new_pts [ t_a->r ];
            }

        for ( j=0, comp=in->comp_list;   j<in->comp_count;   j++, comp++ )
        {
            if ( ( 0 == strcmp ( comp->name, "positions" ) ) ||
                 ( ( NULL  != comp->std_attribs[(int)DEP]        ) &&
                   ( ERROR != comp->std_attribs[(int)DEP]->value ) &&
                   ( 0 == strcmp ( comp->std_attribs[(int)DEP]->value,
                                   "positions" ) ) ) )
            {
                if ( ( ERROR == ( array = _dxf_resample_array
                                            ( comp, 1,
                                              p_info->items,
                                              npo, new_pts, NULL ) ) )
                     ||
                     !DXSetComponentValue
                         ( in->field, comp->name, (Object)array ) )
                    goto error;
            }
            else
            if ( ( 0 == strcmp ( comp->name, "connections" ) ) ||
                 ( ( NULL  != comp->std_attribs[(int)DEP]        ) &&
                   ( ERROR != comp->std_attribs[(int)DEP]->value ) &&
                   ( 0 == strcmp ( comp->std_attribs[(int)DEP]->value,
                                   "connections" ) ) ) )
                if ( ( ERROR == ( array = _dxf_resample_array
                                            ( comp, 1,
                                              c_info->items,
                                              nto, new_tris, NULL ) ) )
                     ||
                     !DXSetComponentValue
                         ( in->field, comp->name, (Object)array ) )
                    goto error;
        }

        DXFree ( (Pointer) new_tris );  new_tris = NULL;
        DXFree ( (Pointer) new_pts  );  new_pts  = NULL;
    }

    return in;

    error:
        ERROR_SECTION;

        DXFree ( (Pointer) new_tris );  new_tris = NULL;
        DXFree ( (Pointer) new_pts  );  new_pts  = NULL;

        return ERROR;
#endif
}



static
Field create_iso_surface
          ( field_info     input_info, 
            float          *value_ptr,
            int            is_grown,
            iso_norm_types normal_type,
            int            normal_direction,
            Interpolator   gradient_interpolator,
            Error (*convert_connection)
                         (void *, float *, hash_table_ptr, hash_table_ptr, int),
                      /* (void *, float *, hash_table_ptr, SegList *,    int),*/
            Field out )
{
    float           value = value_ptr [ 0 ];
    array_info      pi_info, ci_info, di_info  = NULL;
    array_info      po_info, co_info, no_info  = NULL;
    hash_table_rec  vtx_hash;
    grad_table_rec  grad_table;
    SegList         *tri_list = NULL;
    Cube            cube;
    Point           point[8];
    float           data[8];
    double          t;
    iso_edge_hash_ptr   element_ptr = NULL;
    int             i, j;
    field_info      output_info = NULL;
    int             position_dim;
    int             connection_count;
    int             connection_shape;
    Object          copy_g  = NULL;

    SegList         *c_ID_list = NULL;
    int             *c_ID_pull = NULL;
    int             n_c_dep, n_p_dep;
    array_info      a_info;
    component_info  comp;
    Array           array    = NULL;
    Pointer         scratch  = NULL;
    Pointer         out_data = NULL;

    InvalidComponentHandle  i_handle = NULL;

    DXASSERTGOTO ( ERROR != input_info );
    DXASSERTGOTO ( ERROR != value_ptr );
    DXASSERTGOTO ( ERROR != convert_connection );
    DXASSERTGOTO ( ERROR != out );

    vtx_hash.count        = 0;
    vtx_hash.table        = NULL;
    vtx_hash.set_vertices = NULL;
    grad_table.index      = NULL;
    grad_table.list       = NULL;

    pi_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    ci_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    di_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    if ( ( NULL  != input_info->std_comps[(int)INVALID_CONNECTIONS] ) &&
         ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                     ( (Object)input_info->field,
                                       NULL, "connections" ) ) ) )
        goto error;

    DXASSERTGOTO ( pi_info->get_item != ERROR );
    DXASSERTGOTO ( ci_info->get_item != ERROR );
    DXASSERTGOTO ( di_info->get_item != ERROR );

    if ( ( pi_info->shape[0] != 3 ) )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#11400", /* %s has invalid dimensionality; does not match %s */
              "\"positions\" component", "\"connections\"" );

    position_dim     = pi_info->shape[0];
    connection_shape = ci_info->shape[0];

    DXASSERTGOTO ( ( connection_shape == 4 ) || ( connection_shape == 8 ) );

    TST_HASH_INTS;

    if ( ( ERROR == ( vtx_hash.table
                          = DXCreateHash
                                ( sizeof(iso_edge_hash_rec),
                                  iso_edge_hash_func, iso_edge_compare_func )) )
         ||
         ( ERROR == ( tri_list
                          = DXNewSegList ( sizeof(Triangle), 1000, 1000 ) ) ) )
        goto error;

    if ( !tally_dependers ( input_info, &n_c_dep, &n_p_dep ) )
        goto error;

    if ( ( n_c_dep > 1 )
         &&
         ( ERROR == ( c_ID_list = DXNewSegList ( sizeof(int), 1000, 1000 ) ) ) )
        goto error;

    if ( normal_type == NORMALS_COMPUTED )
        if ( ( ERROR == ( grad_table.list
                              = DXNewSegList ( sizeof(Vector), 1000, 1000 ) ) ) 
             ||
             ( ERROR == ( grad_table.index = (Vector**)
                              DXAllocateLocalZero
                                  ( pi_info->items * sizeof(Vector*) ) ) )
             ||
             ( ERROR == ( vtx_hash.set_vertices
                              = NEWBIT ( pi_info->items ) ) ) )
            goto error;

    DXASSERTGOTO ( ci_info->items >= ci_info->original_items );

    if ( ci_info->get_item == _dxf_get_conn_grid_CUBES )
    {
        array_info  t0, t1, t2;
        int         II, JJ, KK;
        int         I0, J0, K0;

        t0 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[0]);

        t1 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[1]);

        t2 = (array_info)
         &(((struct _array_allocator *)((mesh_array_info)ci_info)->terms)[2]);

        DXASSERTGOTO ( ((mesh_array_info)ci_info)->grid == TTRUE );
        DXASSERTGOTO ( ((grid_connections_info)ci_info)->counts != NULL );
        DXASSERTGOTO
           ( t0->items == ( ((grid_connections_info)ci_info)->counts[0] - 1 ) );
        DXASSERTGOTO
           ( t1->items == ( ((grid_connections_info)ci_info)->counts[1] - 1 ) );
        DXASSERTGOTO
           ( t2->items == ( ((grid_connections_info)ci_info)->counts[2] - 1 ) );

        if ( ((mesh_array_info)ci_info)->grow_offsets != NULL )
        {
            DXASSERTGOTO ( is_grown );

            I0 = ((mesh_array_info)ci_info)->grow_offsets[0];
            J0 = ((mesh_array_info)ci_info)->grow_offsets[1];
            K0 = ((mesh_array_info)ci_info)->grow_offsets[2];
        }
        else
            I0 = J0 = K0 = 0;

        for ( II=I0;
              II<( I0 + t0->original_items );
              II++ )
            for ( JJ=J0;
                  JJ<( J0 + t1->original_items );
                  JJ++ )
            {
                if ( !ci_info->get_item 
                          ( (((( II * t1->items ) + JJ ) * t2->items ) + K0 ),
                            ci_info, &cube )
                     ||
                     !di_info->get_item ( cube.p, di_info, &data[0] ) ||
                     !di_info->get_item ( cube.r, di_info, &data[2] ) ||
                     !di_info->get_item ( cube.t, di_info, &data[4] ) ||
                     !di_info->get_item ( cube.v, di_info, &data[6] )   )
                    goto error;

                data[0] -= value;
                data[2] -= value;
                data[4] -= value;
                data[6] -= value;

                for ( KK=K0;
                      KK<( K0 + t2->original_items );
                      KK++,
                            cube.p++, cube.q++, cube.r++, cube.s++,
                            cube.t++, cube.u++, cube.v++, cube.w++,
                            data[0] = data[1],  data[2] = data[3],
                            data[4] = data[5],  data[6] = data[7] )
                {
                    if ( !di_info->get_item ( cube.q, di_info, &data[1] ) ||
                         !di_info->get_item ( cube.s, di_info, &data[3] ) ||
                         !di_info->get_item ( cube.u, di_info, &data[5] ) ||
                         !di_info->get_item ( cube.w, di_info, &data[7] )   )
                        goto error;

                    data[1] -= value;
                    data[3] -= value;
                    data[5] -= value;
                    data[7] -= value;

                    if ( ( NULL != i_handle )
                         &&
                         DXIsElementInvalid
                            ( i_handle, ((((II*t1->items)+JJ)*t2->items)+KK) ) )
                        continue;
                    else if ( !convert_connection
                                  ( &cube, data, &vtx_hash, 
                                    (hash_table_ptr) tri_list,
                                    /* XXX parameter 4 */
                                    /* equivalence */ 1 ) )
                        goto error;
                    else if ( ( NULL != c_ID_list )
                              &&
                              !set_c_ID
                                   ( c_ID_list,
                                     DXGetSegListItemCount ( tri_list ),
                                     ((((II*t1->items)+JJ)*t2->items)+KK) ) )
                        goto error;
                }
            }
    }
    else
    {
        for ( i=0; i<ci_info->original_items; i++ )
            if ( ( NULL != i_handle ) && DXIsElementInvalid ( i_handle, i ) )
                continue;
            else if ( !ci_info->get_item ( i, ci_info, &cube ) )
                goto error;
            else
            {
                for ( j=0; j<connection_shape; j++ )
                {
                    if ( !di_info->get_item
                              ( ((int *)&cube)[j], di_info, &data[j] ) )
                        goto error;
                    data[j] -= value;
                }

                if ( !convert_connection
                          ( &cube, data, &vtx_hash,
                            (hash_table_ptr) tri_list, /* XXX parameter 4 */
                            /* equivalence */ 1 ) )
                    goto error;
                else if ( ( NULL != c_ID_list )
                          &&
                          !set_c_ID
                               ( c_ID_list,
                                 DXGetSegListItemCount ( tri_list ), i ) )
                    goto error;
            }
    }

    connection_count = DXGetSegListItemCount ( tri_list );

    if ( connection_count == 0 )
        if ( !_dxf_MakeFieldEmpty ( out ) )
            goto error;
        else
            goto done;

    if ( normal_type == NORMALS_COMPUTED )
    {
        if ( !create_output_arrays
                  ( out,
                    vtx_hash.count,        position_dim,
                    connection_count,      2,
                    vtx_hash.count,
                    DEFAULT_SURFACE_COLOR,
                    value,                 DEP_POSITIONS,
                    ISOSURFACE ) )
            goto error;

        if ( vtx_hash.count == 0 ) 
            goto done;

        if ( !create_special_gradient ( input_info, &vtx_hash, &grad_table ) )
            goto error;
    }
    else
    {
        if ( !create_output_arrays
                  ( out,
                    vtx_hash.count,        position_dim,
                    connection_count,      2,
                    0,
                    DEFAULT_SURFACE_COLOR,
                    value,                 DEP_POSITIONS,
                    ISOSURFACE ) )
            goto error;

        if ( vtx_hash.count == 0 ) 
            goto done;
    }

    if ( ( connection_count > 0 ) && ( c_ID_list != NULL ) )
    {
        if ( ( ERROR == ( c_ID_pull = DXAllocateLocal
                                          ( connection_count * sizeof(int) ) ) )
             ||
             !copy_seglist_to_memory
                  ( c_ID_list, c_ID_pull, sizeof(int), connection_count ) )
            goto error;
    
        DXDeleteSegList ( c_ID_list );  c_ID_list = NULL;
    }

    if ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
        goto error;

    DXASSERTGOTO ( output_info->std_comps[(int)POSITIONS]   != NULL );
    DXASSERTGOTO ( output_info->std_comps[(int)CONNECTIONS] != NULL );

    po_info = (array_info) &(output_info->std_comps[(int)POSITIONS]->array);
    co_info = (array_info) &(output_info->std_comps[(int)CONNECTIONS]->array);
    no_info = ( normal_type == NORMALS_COMPUTED )?
              (array_info) &(output_info->std_comps[(int)NORMALS]->array)
              : NULL;

    if ( !copy_seglist_to_array ( tri_list, co_info ) )
        goto error;

    DXDeleteSegList ( tri_list ); tri_list = NULL;

    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {

        if ( ( 0 != strcmp ( comp->name, "positions" ) ) &&
             ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
               ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
               ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                               "positions" ) ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "data"        ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        if ( ( 0 == strcmp ( comp->name, "normals" ) ) &&
             ( normal_type != NO_NORMALS )               )
            continue;

        a_info = (array_info) &(comp->array);

    if ( CLASS_CONSTANTARRAY == a_info->class )
    {
        if ( ( ERROR == ( array = (Array)DXNewConstantArrayV
                                     ( vtx_hash.count,
                                       DXGetConstantArrayData ( a_info->array ),
                                       a_info->type,
                                       a_info->category,
                                       a_info->rank,
                                       a_info->shape ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) ) )

            goto error;

        continue;
    }

        if ( ( ERROR == ( array
                   = DXNewArrayV
                         ( a_info->type, a_info->category,
                           a_info->rank, &a_info->shape[0] ) ) )
             ||
             !DXAllocateArray     ( array, vtx_hash.count )           ||
             !DXAddArrayData      ( array, 0, vtx_hash.count, NULL )  ||
             !DXTrim              ( array )                           ||
             !DXSetComponentValue ( out, comp->name, (Object) array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "positions" ) )
             ||
             !_dxf_SetIterator    ( comp )
             ||
             ( ERROR == ( out_data = DXGetArrayData  ( array )))             ||
             ( ERROR == ( scratch  = DXAllocateLocal ( 2 * a_info->itemsize )))
             ||
             !DXInitGetNextHashElement ( vtx_hash.table ) )
            goto error;

        array = NULL;

        for ( i = 0;
              ( NULL != ( element_ptr
                              = (iso_edge_hash_ptr) DXGetNextHashElement
                                                    ( vtx_hash.table ) ) );
              i++ )
        {
            DXASSERTGOTO ( element_ptr->seq < po_info->items );

            if ( element_ptr->edge.p == element_ptr->edge.q )
                t = 0.0;
            else
            {
                if ( !di_info->get_item
                         ( element_ptr->edge.p, di_info, &data[0] )||
                     !di_info->get_item
                         ( element_ptr->edge.q, di_info, &data[1] ) )
                    goto error;

                data[0] -= value;
                data[1] -= value;

                if      ( data[0] == 0.0 ) t = 0.0;
                else if ( data[1] == 0.0 ) t = 1.0;
                else {
                    t = (double)data[0] / ((double)data[0] - (double)data[1]);
		}

                DXASSERTGOTO ( ( t >= 0.0 ) && ( t <= 1.0 ) );
            }

            if ( !interp ( a_info, scratch,
                           element_ptr->edge.p, element_ptr->edge.q, t, 
                           &(((char*)out_data)
                               [ a_info->itemsize * element_ptr->seq ]),
			       a_info->itemsize/sizeof(float),
			       a_info->itemsize) )
                goto error;

            if ( ( a_info == pi_info ) && ( normal_type == NORMALS_COMPUTED ) )
            {
                DXASSERTGOTO ( grad_table.index [ element_ptr->edge.p ] );
                DXASSERTGOTO ( grad_table.index [ element_ptr->edge.q ] );

                point[0] = *grad_table.index [ element_ptr->edge.p ];
                point[1] = *grad_table.index [ element_ptr->edge.q ];

                point[0].x += ( point[1].x - point[0].x ) * t;
                point[0].y += ( point[1].y - point[0].y ) * t;
                point[0].z += ( point[1].z - point[0].z ) * t;

                ((Vector *)no_info->data) [ element_ptr->seq ] = point[0];
            }
        }

        DXFree ( scratch );  scratch = NULL;
    }


    for ( j=0, comp=input_info->comp_list;
          j<input_info->comp_count;
          j++, comp++ )
    {
        if ( ( NULL  == comp->std_attribs[(int)DEP]        ) ||
             ( ERROR == comp->std_attribs[(int)DEP]->value ) ||
             ( 0 != strcmp ( comp->std_attribs[(int)DEP]->value,
                             "connections" ) ) )
            continue;

        if ( ( 0 == strcmp  ( comp->name, "connections" ) ) ||
             ( 0 == strncmp ( comp->name, "original", 8 ) ) ||
             ( NULL != comp->std_attribs[(int)REF]        )  )
            continue;

        a_info = (array_info) &(comp->array);

        if ( ( ERROR == ( array = _dxf_resample_array
                                      ( comp, 1,
                                        ((array_info) &(comp->array))->items,
                                        connection_count, NULL, c_ID_pull ) ) )
             ||
             !DXSetComponentValue ( out, comp->name, (Object)array )
             ||
             !DXSetComponentAttribute
                  ( out, comp->name, "dep",
                    (Object) DXNewString ( "connections" ) ) )
            goto error;
    }

    _dxf_FreeInMemory ( output_info );

    if ( ERROR == ( output_info = _dxf_InMemory ( out ) ) )
        goto error;

    switch ( normal_type )
    {
        case GRADIENT_NORMALS:
        {
            Object inv_bef = DXExists ( (Object) out, "invalid positions" );

            if ( ( ERROR == ( copy_g
                       = DXCopy ( (Object) gradient_interpolator,
                                  COPY_STRUCTURE ) ) )
                 ||
                 !DXMap ( (Object) out, copy_g, "positions", "normals" ) )
                goto error;

            if ( !inv_bef && DXExists ( (Object) out, "invalid positions" ) )
            {
            DXWarning
            ( "Isosurface: Mapping external gradient reveals invalid points." );
            DXWarning
               ( "Isosurface: Check that external gradient encompasses data." );
            }

            _dxf_FreeInMemory ( output_info );
            DXDelete          ( copy_g );
            copy_g            = NULL;

            if ( ERROR == ( output_info = ( _dxf_InMemory ( out ) ) ) )
                goto error;
        }

        case NORMALS_COMPUTED:
            if ( !flip_normals_and_triangles
                      ( input_info,
                        output_info,
                        normal_direction ) )
                goto error;
            break;

        case NO_NORMALS:
            break;
    }

    /* Do this last since it invalidates (( output_info ))  */
    if ( !remove_degenerates ( out ) )
        goto error;

    done:
        /* This is create_iso_surface */
        if ( DXGetAttribute ( (Object)input_info->field, "shade" ) )
            if ( !DXSetAttribute
                      ( (Object) out,
                        "shade",
                        DXGetAttribute
                            ( (Object)input_info->field, "shade" ) ) )
                goto error;

        DXDestroyHash     ( vtx_hash.table        );
        DXFree            ( vtx_hash.set_vertices );
        DXFree            ( (Pointer) grad_table.index );
        DXDeleteSegList   ( grad_table.list       ); 
        DXDeleteSegList   ( tri_list              );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        _dxf_FreeInMemory ( output_info );

        DXFreeInvalidComponentHandle ( i_handle );

        i_handle              = NULL;
        vtx_hash.table        = NULL;
        vtx_hash.set_vertices = NULL;
        grad_table.index      = NULL;
        grad_table.list       = NULL;
        output_info           = NULL;
        tri_list              = NULL;
        c_ID_list             = NULL;
        c_ID_pull             = NULL;
        scratch               = NULL;

        return DXEndField ( out );

    error:
        ERROR_SECTION;

        DXDestroyHash     ( vtx_hash.table        );
        DXFree            ( vtx_hash.set_vertices );
        DXFree            ( (Pointer) grad_table.index );
        DXDeleteSegList   ( grad_table.list       ); 
        DXDeleteSegList   ( tri_list              );
        DXDeleteSegList   ( c_ID_list );
        DXFree            ( c_ID_pull );
        DXFree            ( scratch );

        _dxf_FreeInMemory ( output_info );

        DXFreeInvalidComponentHandle ( i_handle );

        i_handle              = NULL;
        vtx_hash.table        = NULL;
        vtx_hash.set_vertices = NULL;
        grad_table.index      = NULL;
        grad_table.list       = NULL;
        output_info           = NULL;
        tri_list              = NULL;
        c_ID_list             = NULL;
        c_ID_pull             = NULL;
        scratch               = NULL;

        return ERROR;
}



static
Error isosurface_field
          ( Field          input,
            int            number,
            float          *values,
            float          *min_max,
            iso_module_types module,
            band_remap_types remap,
            Field          *out_h,
            int            *skipvec,
            iso_norm_types normal_type,
            Interpolator   gradient_interpolator,
            int            is_grown,
            int            normal_direction )
{
    Array         data_array;
    int           i;
    Interpolator  copy_g     = NULL;
    field_info    input_info = NULL;
    array_info    p_info, c_info, d_info = NULL;

    Field (*create_output)      () = NULL;
    Error (*convert_connection) () = NULL;

#ifdef WHITEBOX_TEST
    for ( i=0; i<8;   i++ ) histograms.polygonalizations[i] = 0;
    for ( i=0; i<256; i++ ) histograms.case_signatures  [i] = 0;
#endif

    DXASSERTGOTO ( input != ERROR );
    DXASSERTGOTO ( !DXEmptyField ( input ) );
    DXASSERTGOTO ( DXGetObjectClass ( (Object)input ) == CLASS_FIELD );

    if ( !DXInvalidateConnections ( (Object) input )
         ||
         ( ERROR == ( input_info = _dxf_InMemory ( input ) ) ) )
        goto error;

    if ( input_info->std_comps[(int)POSITIONS] == NULL )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#10250", /*%s is missing %s component*/
              "'data' parameter",
              "\"positions\"" );

    if ( input_info->std_comps[(int)CONNECTIONS] == NULL )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#10250", /*%s is missing %s component*/
              "'data' parameter",
              "\"connections\"" );

    if ( input_info->std_comps[(int)DATA] == NULL )
        DXErrorGoto3
            ( ERROR_INVALID_DATA,
              "#10250", /*%s is missing %s component*/
              "'data' parameter",
              "\"data\"" );

    if ( ( input_info->std_comps[(int)DATA]->std_attribs[(int)DEP] == NULL )
         ||
         ( strcmp 
              ( input_info->std_comps[(int)DATA]->std_attribs[(int)DEP]->value,
                "positions" ) != 0 ) )
        DXErrorGoto2
            ( ERROR_INVALID_DATA,
              "#11251", /* %s must be dependent on positions */
              "the \"data\" component" );

    p_info = (array_info) &(input_info->std_comps[(int)POSITIONS]->array);
    c_info = (array_info) &(input_info->std_comps[(int)CONNECTIONS]->array);
    d_info = (array_info) &(input_info->std_comps[(int)DATA]->array);

    if ( ( d_info->rank != 0 )
         &&
         ( ( d_info->rank != 1 ) || ( d_info->shape[0] != 1 ) ) )
        DXErrorGoto2
            ( ERROR_INVALID_DATA,
              "#10081", /* %s must be scalar or 1-vector */
              "\"data\" component" );

    if ( !_dxf_SetIterator   ( input_info->std_comps[(int)POSITIONS]   ) ||
         !_dxf_SetIterator   ( input_info->std_comps[(int)CONNECTIONS] ) ||
         !_dxf_SetIterator_i ( input_info->std_comps[(int)DATA]        )   )
        goto error;

    switch ( module )
    {
        case ISOSURFACE:
            switch ( input_info->std_comps[(int)CONNECTIONS]->element_type )
            {
                case LINES:
                    create_output      = create_iso_points;
                    convert_connection = NULL;
                    break;

                case QUADRILATERALS:
                    create_output      = create_iso_contours;
                    convert_connection = iso_convert_quadrilateral;
                    break;

                case TRIANGLES:
                    create_output      = create_iso_contours;
                    convert_connection = iso_convert_triangle;
                    break;

                case CUBES:
                    create_output      = create_iso_surface;
                    convert_connection = iso_convert_cube;
                    break;

                case TETRAHEDRA:
                    create_output      = create_iso_surface;
                    convert_connection = iso_convert_tetrahedron;
                    break;

                case PRISMS:
                    create_output      = create_iso_surface;
                    convert_connection = iso_convert_prism;
                    break;

                case CUBES4D:
                case NONE:
                case ET_UNKNOWN:
                default:
                    DXErrorGoto2
                        ( ERROR_INVALID_DATA,
                          "#11380", /* %s is an invalid connections type */
                          input_info->std_comps[(int)CONNECTIONS]->name );
            }
            break;

#ifdef INCLUDES_BAND
        case BAND:
            switch ( input_info->std_comps[(int)CONNECTIONS]->element_type )
            {
                case LINES:
                    create_output      = create_band_lines;
                    convert_connection = band_convert_line;
                    break;

                case QUADRILATERALS:
                    create_output      = create_band_surface;
                    convert_connection = band_convert_quadrilateral;
                    break;

                case TRIANGLES:
                    create_output      = create_band_surface;
                    convert_connection = band_convert_triangle;
                    break;

                case CUBES:
                case TETRAHEDRA:
                case PRISMS:
                case CUBES4D:
                case NONE:
                case ET_UNKNOWN:
                default:
                    DXErrorGoto2
                        ( ERROR_INVALID_DATA,
                          "#11380", /* %s is an invalid connections type */
                          input_info->std_comps[(int)CONNECTIONS]->
                              std_attribs[ELEMENT_TYPE]->value );
            }
#endif
    }

    if ( ( create_output == create_iso_points   ) ||
         ( create_output == create_iso_contours )   )
    {
        for ( i=0; i<number; i++ )
            if ( !skipvec[i] )
            {
                out_h[i] = create_output
                               ( input_info,
                                 &values[i],
                                 is_grown,
                                 convert_connection,
                                 out_h[i] );

                if ( DXGetError() != ERROR_NONE )
                    goto error;
            }
    }
    else if ( create_output == create_iso_surface )
    {
        /* XXX - create and preserve gradient between accesses */

        for ( i=0; i<number; i++ )
            if ( !skipvec[i] )
            {
                out_h[i] = create_iso_surface
                               ( input_info,
                                 &values[i],
                                 is_grown,
                                 normal_type,
                                 normal_direction,
                                 gradient_interpolator,
                                 convert_connection,
                                 out_h[i] );

                if ( DXGetError() != ERROR_NONE )
                    goto error;
            }
    }
    else if ( ( create_output == create_band_lines   ) ||
              ( create_output == create_band_surface )  )
#if 0
              ( create_output == create_band_volume  ) 
#endif
    {
        out_h[0] = create_output
                       ( input_info,
                         values, number,
                         remap,
                         is_grown,
                         min_max,
                         convert_connection,
                         out_h[0] );

        if ( DXGetError() != ERROR_NONE )
            goto error;
    }
    else
        goto error;

#ifdef WHITEBOX_TEST
    if ( DXQueryDebug ( "I" ) )
    { 
        int hit;

    DXDebug ( "I",
         "Poly frequency: [2]:%6d, [3]:%6d, [4]:%6d, [5]:%6d, [6]:%6d, [7]:%6d",
              histograms.polygonalizations[2],
              histograms.polygonalizations[3],
              histograms.polygonalizations[4],
              histograms.polygonalizations[5],
              histograms.polygonalizations[6],
              histograms.polygonalizations[7] );

    for ( i=0, hit=0;
          i<256;
          hit += ( histograms.case_signatures [ i++ ] == 0 )? 0: 1 );

    DXDebug ( "I", "Case hits: %d, misses %d", hit, (256-hit) );

#if 0
    DXDebug ( "I", "Case frequencies:" );

    for ( i=0; i<256; i+=8 )
        DXDebug ( "i",
                  "   [%03d]: %7d %7d %7d %7d %7d %7d %7d %7d",
                  i,
                  histograms.case_signatures[i],
                  histograms.case_signatures[i+1],
                  histograms.case_signatures[i+2],
                  histograms.case_signatures[i+3],
                  histograms.case_signatures[i+4],
                  histograms.case_signatures[i+5],
                  histograms.case_signatures[i+6],
                  histograms.case_signatures[i+7] );
#endif

    } 
#endif

    if ( !_dxf_FreeInMemory ( input_info ) ) goto error;   input_info = NULL;

    return OK;

    error:
        ERROR_SECTION;

        _dxf_FreeInMemory ( input_info );   input_info = NULL;

        return ERROR;
}


static
int is_volume_topology ( CompositeField cf )
{
    Field child;
    char  *type;
    int   i;
    enum { dont_know, is_volume, isnt_volume } topo = dont_know;

    DXASSERTGOTO ( DXGetObjectClass ( (Object) cf ) == CLASS_GROUP          );
    DXASSERTGOTO ( DXGetGroupClass  ( (Group)  cf ) == CLASS_COMPOSITEFIELD );

    i = 0;
    while ( topo == dont_know )
    {
        if ( ERROR == ( child = (Field)DXGetEnumeratedMember
                                           ( (Group)cf, i++, NULL ) ) )
            DXErrorGoto
                ( ERROR_INVALID_DATA,
                  "CompositeField has no members containing \"connections\"" );

        if ( DXGetObjectClass ( (Object) child ) != CLASS_FIELD )
            DXErrorGoto
                ( ERROR_INVALID_DATA,
                  "CompositeField contains a member that is not a Field" );

        if ( NULL != DXGetComponentValue ( child, "connections" ) )
        {
            type = DXGetString
                       ( (String) DXGetComponentAttribute
                                    ( child, "connections", "element type" ) );

            if ( ( ERROR == type ) || ( DXGetError() != ERROR_NONE ) )
                goto error;

            if ( ( 0 == strcmp ( type, "cubes"      ) ) ||
                 ( 0 == strcmp ( type, "pyramids"   ) ) ||
                 ( 0 == strcmp ( type, "tetrahedra" ) )   )

                topo = is_volume;
            else
                topo = isnt_volume;
        }
    }

    switch ( topo )
    {
        case is_volume:    return 1;
        case isnt_volume:  return 0;
        case dont_know:
            DXSetError
                ( ERROR_INVALID_DATA,
                  "#11380", /* %s is an invalid connections type */
                  "composite field member" );
            goto error;
    }

    error:
        ERROR_SECTION;
        return ERROR;
}


/*
 * If any ERROR, all ERROR
 * else If any OK, all OK
 * else EMPTY
 */
typedef enum { OBJ_ERROR, OBJ_OK, OBJ_EMPTY } type_check;

static
type_check iso_object_type_check ( Object o )
{
    Type     type;
    Category cat;
    int      rank;
    int      shape;
    Object   child;
    int      i;

    DXASSERT ( o != NULL );

    if ( ( DXGetObjectClass ( o ) != CLASS_XFORM   ) &&
         ( DXGetObjectClass ( o ) != CLASS_SCREEN  ) &&
         ( DXGetObjectClass ( o ) != CLASS_CLIPPED )
         &&
         DXGetType ( o, &type, &cat, &rank, NULL  ) )
    {
        if ( ( cat == CATEGORY_REAL )
             &&
             ( ( rank == 0 )
               ||
               ( ( rank == 1 )
                 &&
                 DXGetType ( o, NULL, NULL, NULL, &shape )
                 &&
                 ( shape == 1 ) ) ) )

            return OBJ_OK;
        else
            DXErrorGoto2
                ( ERROR_INVALID_DATA, 
                  "#10081", /* %s must be scalar or 1-vector */
                  "\"data\" component" );
    }
    else if ( DXGetError() != ERROR_NONE )
        goto error;

    else
        switch ( DXGetObjectClass ( o ) )
        {
            case CLASS_FIELD:
                if ( DXEmptyField ( (Field) o ) )
                    return OBJ_EMPTY;
                else
                    DXErrorGoto3
                        ( ERROR_INVALID_DATA,
                          "#10250", /* %s is missing %s component */
                          "Field (non-empty)", "\"data\"" );

            case CLASS_XFORM:
                if ( !DXGetXformInfo ( (Xform)o, &child, NULL ) )
                    goto error;

                switch ( iso_object_type_check ( child ) )
                {
                    case OBJ_OK:    return OBJ_OK;
                    case OBJ_EMPTY: return OBJ_EMPTY;
                    case OBJ_ERROR: goto error;
                }
                break;

            case CLASS_SCREEN:
                if ( !DXGetScreenInfo ( (Screen)o, &child, NULL, NULL ) )
                    goto error;

                switch ( iso_object_type_check ( child ) )
                {
                    case OBJ_OK:    return OBJ_OK;
                    case OBJ_EMPTY: return OBJ_EMPTY;
                    case OBJ_ERROR: goto error;
                }
                break;

            case CLASS_CLIPPED:
                if ( !DXGetClippedInfo ( (Clipped)o, &child, NULL ) )
                    goto error;

                switch ( iso_object_type_check ( child ) )
                {
                    case OBJ_OK:    return OBJ_OK;
                    case OBJ_EMPTY: return OBJ_EMPTY;
                    case OBJ_ERROR: goto error;
                }
                break;

            case CLASS_GROUP:
            {
                int Nok = 0;

                for ( i=0;
                      NULL != ( child = DXGetEnumeratedMember
                                            ( (Group) o, i, NULL ) );
                      i++ )
                    switch ( iso_object_type_check ( (Object) child ) )
                    {
                        case OBJ_OK:    Nok++; break;
                        case OBJ_EMPTY:        break;
                        case OBJ_ERROR:   goto error;
                    }

                return ( Nok > 0 ) ? OBJ_OK : OBJ_EMPTY;
            }
                break;

            default:
                DXErrorGoto2
                    ( ERROR_INVALID_DATA,
                      "#10190", /* %s must be a field or a group */
                      "'data' parameter" );
        }

    error:
        ERROR_SECTION;
        return OBJ_ERROR;
}


static
/*
 * Special return codes:  Pointer and NULL are both acceptable as
 * non-error conditions.  when DXGetError() signifies that there is error,
 * then there is one.
 * 
 * XXX - Important:
 *    Statistics will give an error if called with an DXEmptyField
 *    or a group containing no fields with "data" components
 *      (as with EmptyFields)
 */
iso_arg_type * stats ( iso_arg_type *iso_arg, float **deletable )
{
    float  min, mean, max;
    int    i;

    DXASSERTGOTO ( deletable != NULL );

    *deletable = NULL;

    if ( ( !iso_arg->have_minmax )
         &&
         ( DXGetObjectClass ( iso_arg->self ) == CLASS_GROUP )
         &&
         ( DXGetGroupClass ( (Group) iso_arg->self ) == CLASS_COMPOSITEFIELD ) )
    {
        switch ( iso_object_type_check ( iso_arg->self ) )
        {
            case OBJ_EMPTY: return iso_arg; /* XXX - This is a hack */
            case OBJ_OK:    break;
            case OBJ_ERROR: goto error;
        }

        if ( !DXStatistics
                  ( iso_arg->self,
                    "data",
                    &iso_arg->band_min_cf,
                    &iso_arg->band_max_cf,
                    NULL, NULL ) )
            goto error;

        iso_arg->have_minmax = 1;
    }

    if ( iso_arg->isovals )
        return iso_arg;

    if ( _dxd_user_def_values )
    {
        iso_arg->isovals = _dxd_user_def_values;
        return iso_arg;
    }

    if ( ( DXGetObjectClass ( iso_arg->self ) == CLASS_GROUP )
         &&
         ( DXGetGroupClass ( (Group) iso_arg->self ) == CLASS_GROUP ) )
        return iso_arg;
        /* No error:  Isovalues are not evaluated over generic groups */

    switch ( iso_object_type_check ( iso_arg->self ) )
    {
        case OBJ_EMPTY: return iso_arg; /* XXX - This is a hack */
        case OBJ_OK:    break;
        case OBJ_ERROR: goto error;
    }

    if ( !DXStatistics ( iso_arg->self, "data", &min, &max, &mean, NULL )
         ||
         ( ERROR == ( iso_arg->isovals 
                = (float*) DXAllocate
                              ( sizeof(float) * VALUE_COUNT ( iso_arg ) ) ) ) )
        goto error;

    *deletable = iso_arg->isovals;

    if ( iso_arg->use_mean )
    {
        iso_arg->isovals[0] = mean;

        DXMessage ( "%s: 'value' defaults to mean: %g .",
                    MOD_NAME ( iso_arg ), iso_arg->isovals[0] );
    }
    else if ( VALUE_COUNT(iso_arg) > 0 )
    {
        for ( i=0; i<VALUE_COUNT(iso_arg); i++ )
            iso_arg->isovals[i] = min + ( ( max - min )
                               * ( (float) ( i + 1 ) ) /
                                 ( (float) ( VALUE_COUNT(iso_arg) + 1 ) ) );

        if ( VALUE_COUNT ( iso_arg ) == 1 )
            DXMessage
                ( "%s: 'value' is %g .",
                  MOD_NAME ( iso_arg ),
                  iso_arg->isovals[0] );
        else
            DXMessage
                ( "%s: 'value' is %d values ranging from %g to %g .",
                  MOD_NAME ( iso_arg ),
                  VALUE_COUNT ( iso_arg ),
                  iso_arg->isovals [ 0 ],
                  iso_arg->isovals [ VALUE_COUNT ( iso_arg ) - 1 ] );
    }

    return iso_arg;

    error:
        ERROR_SECTION;

        DXFree ( (Pointer) *deletable );

        *deletable = NULL;

        return ERROR;
}


#define SavedCopiesMaxDEF 1024

static struct
{
    CompositeField cf [ SavedCopiesMaxDEF ];
    int            used;
}
SavedCopies;

#define init_saved_copies_DEF \
 { SavedCopies.used = -1; }

#define free_saved_copies_DEF \
    {while(SavedCopies.used>-1) \
         DXDelete((Object)SavedCopies.cf[SavedCopies.used--]);}


static
CompositeField copy_grow_save ( CompositeField input )
{
    char    *growl[100];
    char    *dep;
    int     ll;
    int     i, j;
    Field   f;
    Object  a;

    CompositeField  copy  = NULL;

    for ( i=0;
          ( NULL != ( f = (Field)DXGetEnumeratedMember 
                                     ( (Group)input, i, NULL ) ) );
          i++ )

        if ( !DXEmptyField ( f ) )
        {
            for ( j=0, ll=0;
                  ( NULL != ( a = DXGetEnumeratedComponentValue
                                      ( f, j, &growl[ll] ) ) );
                  j++ )
            {
                if ( ll >= 100 )
                    DXErrorGoto
                        ( ERROR_UNEXPECTED,
                         "more than 100 components to grow in copy_grow_save" );

                if ( NULL != growl[ll] )

                    if ( ( 0 == strcmp ( growl[ll], "positions"   ) ) ||
                         ( 0 == strcmp ( growl[ll], "connections" ) ) ||
                         ( 0 == strcmp ( growl[ll], "data"        ) )  )

                        ll++;

                    else if ( ( NULL != ( a = DXGetAttribute ( a, "dep" ) ) )
                              &&
                              ( CLASS_STRING == DXGetObjectClass ( a ) )
                              &&
                              ( NULL != ( dep = DXGetString ( (String)a ) ) )
                              &&
                              ( ( 0 == strcmp ( dep, "positions"   ) ) ||
                                ( 0 == strcmp ( dep, "connections" ) )  ) )

                        ll++;
            }

            break;
        }

    growl [ ll ] = NULL;

    if ( DXQueryDebug ( "I" ) )
    {
        DXDebug ( "I", "Grow component list length = %d", ll );

        for ( i=0; i<ll; i+=4 )
            DXDebug ( "I", (ll>=(i+4)) ? "    = `%s',`%s',`%s',`%s'" :
                           (ll==(i+3)) ? "    = `%s',`%s',`%s'" :
                           (ll==(i+2)) ? "    = `%s',`%s'" :
                                         "    = `%s'",
                      growl[i], growl[i+1], growl[i+2], growl[i+3] );
    }

    DXASSERTGOTO ( DXGetGroupClass ( (Group) input ) == CLASS_COMPOSITEFIELD );

    if ( SavedCopies.used >= ( SavedCopiesMaxDEF - 1 ) )
        DXErrorGoto
            ( ERROR_UNEXPECTED,
              "more compositefields to grow in copy_grow_save" );

    /* XXX - COPY_STRUCTURE vs COPY_ATTRIBUTES */

    if ( ERROR == ( copy = (CompositeField) DXCopy ( (Object) input,
                                                  COPY_STRUCTURE ) ) )
         if ( DXGetError() != ERROR_NONE ) 
             /* proper error */
             goto error;
         else
             DXErrorGoto
                 ( ERROR_INTERNAL,
                   "DXCopy: returned error but didn't set the ErrorCode" )
     else
         if ( DXGetError() != ERROR_NONE ) 
             DXMessageGoto
                 ( "DXCopy: set the ErrorCode but didn't return error" )
         /* else: properly succeeded */

    if ( !DXGrowV ( (Object) copy, 1, GROW_NONE, growl ) )
         if ( DXGetError() != ERROR_NONE ) 
             /* proper error */
             goto error;
         else
             DXErrorGoto
                 ( ERROR_INTERNAL,
                   "DXGrow: returned error but didn't set the ErrorCode" )
     else
         if ( DXGetError() != ERROR_NONE ) 
             DXMessageGoto
                 ( "DXGrow: set the ErrorCode but didn't return error" )
         /* else: properly succeeded */

    SavedCopies.cf [ ++SavedCopies.used ] = copy;

    return copy;

    error:
        ERROR_SECTION;

        DXDelete ( (Object) copy );
        copy = NULL;

        return ERROR;
}



/*
 * Traversal section:
 *   See parts.c/DXProcessParts for desired bahavior
 *      for a traverser such as this.
 *   Who calls who:
 *       m_Isosurface
 *           _dxf_IsosurfaceObject
 *               stats
 *               iso_part
 *                   stats
 *                   iso_part{recursive}
 *                   DXAddTask with iso_added_task 
 *               - or -
 *               iso_added_task 
 *                   isosurface_field
 */

/*-----------------------------------------*\
\*-----------------------------------------*/
static
Error add_attribute ( Object object, iso_arg_type *iso_arg, int i )
{
    Array   array = NULL;
    Pointer valp  = NULL;
    int     valc;

    DXASSERTGOTO ( ERROR != iso_arg->isovals );

    /* if i<0 and module==ISOSURFACE, add entire array of isovals */

    if (iso_arg->module==BAND || (iso_arg->module==ISOSURFACE && i<0))
    {
	  valp = iso_arg->isovals;
	  valc = iso_arg->number;
    }
    else
    {
	  valp = &iso_arg->isovals[i];
	  valc = 1;
    }

    if ( object != NULL )
    {
        if ( ( ERROR == ( array
                   = DXNewArray ( TYPE_FLOAT, CATEGORY_REAL, 0, 0 ) ) )
             ||
             !DXAllocateArray ( array, valc )          ||
             !DXAddArrayData  ( array, 0, valc, valp ) ||
             !DXTrim          ( array )
             ||
             !DXSetAttribute ( object, ATTR_NAME(iso_arg), (Object)array ) )
            goto error;

        if ( ( iso_arg->module == ISOSURFACE && i > -1 )
             &&
             ( CLASS_GROUP == DXGetObjectClass ( iso_arg->parent[0] ) )
             &&
             ( CLASS_SERIES == DXGetGroupClass  ( (Group) iso_arg->parent[0] ) )
             &&
             ( NULL == DXGetAttribute ( (Object)object, "series position" ) ) )
            if ( !DXSetAttribute ( object, "series position", (Object)array ) )
                goto error;
    }

    return OK;

    error:
        ERROR_SECTION;
        return ERROR;
}


/*-----------------------------------------*\
   set by DXAddTask
   called by DXExecuteTaskGroup
\*-----------------------------------------*/
static
Error iso_added_task ( Pointer args )
{
    iso_arg_type *argcast = (iso_arg_type *) args;
    Field        *outputs = NULL;
    int          *skipvec = NULL;
    float        min, max;
    int          i;
    int          have_hit;
    int          can_cull_fields;
    int          can_setmember;

    if ( !args )
        DXErrorGoto ( ERROR_MISSING_DATA, "during DXAdd(ed)Task" );

    if ( OBJ_ERROR == iso_object_type_check ( argcast->self ) )
        goto error;

    if ( !DXStatistics ( argcast->self, "data", &min, &max, NULL, NULL ) )
        DXMessageGoto
            ( "Statistics on field \"data\" component" )

    switch ( DXGetObjectClass ( argcast->parent[0] ) )
    {
        case CLASS_XFORM: case CLASS_SCREEN: case CLASS_CLIPPED:
            can_cull_fields = 0; break;

        case CLASS_GROUP:
            can_cull_fields
                = ( DXGetGroupClass ( (Group) argcast->parent[0] )
                    == CLASS_COMPOSITEFIELD );
            break;

        default: 
            DXErrorGoto ( ERROR_UNEXPECTED, "parent not a group" );
            break;
    }

    have_hit = 0;
    if ( argcast->module == BAND )
        have_hit = 1;
    else
        for ( i=0; ( ( i < VALUE_COUNT ( argcast ) ) && !have_hit ); i++ )
            if ( ( argcast->isovals[i] >= min ) &&
                 ( argcast->isovals[i] <= max )   )
                have_hit = 1;

    /* this trivial case shadda been done call -1 */
    if ( !have_hit && can_cull_fields )
        /* XXX */
        goto ok;

    if ( ( ERROR == ( outputs
               = (Field *) DXAllocateZero
                             ( sizeof(Field) * STRUCT_COUNT ( argcast ) ) ) )
         ||
         ( ERROR == ( skipvec
               = (int *) DXAllocate
                             ( sizeof(int) * STRUCT_COUNT ( argcast ) ) ) ) )
        goto error;

    for ( i=0; i<STRUCT_COUNT ( argcast ); i++ )
        if ( ( argcast->isovals[i] >= min ) &&
             ( argcast->isovals[i] <= max )   )

            skipvec[i] = 0;
        else
            /* skip test not valid for a 2-valued system */
            skipvec[i] = ( argcast->module == ISOSURFACE ) ? 1 : 0;

    if ( can_cull_fields )
    {
        for ( i=0; i<STRUCT_COUNT(argcast); i++ )
            if ( skipvec[i] )
                outputs[i] = NULL;
            else if ( ERROR == ( outputs[i] = DXNewField () ) )
                goto error;
    }
    else
        for ( i=0; i<STRUCT_COUNT(argcast); i++ )
            if ( ERROR == ( outputs[i] = DXNewField () ) )
                goto error;

    if ( !isosurface_field ( (Field) argcast->self,
                             argcast->number,
                             argcast->isovals,
                             (argcast->have_minmax)?
                                 &argcast->band_min_cf : NULL,
                             argcast->module,
                             argcast->remap,
                             outputs,
                             skipvec,
                             argcast->normal_type,
                             argcast->gradient_interpolator,
                             argcast->is_grown,
                             argcast->normal_direction )
         ||
         ( DXGetError() != ERROR_NONE ) )
        goto error;

    /* if(argcast->is_grown)DXShrink(); not required: grown area is Deleted */

    for ( i=0; i<STRUCT_COUNT(argcast); i++ )
    { 
        can_setmember = 0;

        if ( !outputs[i] )
        { 
            if ( !can_cull_fields )
            { 
                if ( ERROR == ( outputs[i] = DXEndField ( DXNewField() ) ) )
                    goto error;

                can_setmember = 1;
            } 
        } 
        else
        { 
            if ( ERROR == ( outputs[i] = DXEndField ( outputs[i] ) ) )
                goto error;

            if ( !DXEmptyField ( outputs[i] ) )
            { 
                can_setmember = 1;
            } 
            else
            { 
                if ( !can_cull_fields )
                    can_setmember = 1;
                else
                { 
                    if ( !DXDelete ( (Object) outputs[i] ) )
                        goto error;
                    else
                        outputs[i] = NULL;
                } 
            } 
        } 

        if ( !add_attribute ( (Object)outputs[i], argcast, i ) )
            goto error;

        /* XXX - DXEndField ? */
        if ( can_setmember )
            switch ( DXGetObjectClass ( argcast->parent[i] ) )
            {
                case CLASS_GROUP:
                    if ( ( CLASS_SERIES ==
                              DXGetGroupClass ( (Group) argcast->parent[i] ) )
                         &&
                         ( -1 != argcast->series_ordinal ) )
                    {
                        if ( !DXSetSeriesMember
                                  ( (Series) argcast->parent[i],
                                    argcast->series_ordinal,
                                    (double) argcast->series_FP_val,
                                    (Object) outputs[i] ) )
                             goto error;
                    }
                    else

                    if ( !DXSetMember ( (Group) argcast->parent[i],
                                        argcast->member_name,
                                        (Object) outputs[i] ) )
                        goto error;


                    break;

                case CLASS_XFORM:
                    if ( !DXSetXformObject
                              ( (Xform) argcast->parent[i],
                                (Object) outputs[i] ) )
                        goto error;
                    break;

                case CLASS_SCREEN:
                    if ( !DXSetScreenObject
                              ( (Screen) argcast->parent[i],
                                (Object) outputs[i] ) )
                        goto error;
                    break;

                case CLASS_CLIPPED:
                    if ( !DXSetClippedObjects
                              ( (Clipped) argcast->parent[i],
                                (Object) outputs[i], NULL ) )
                        goto error;
                    break;
            }
    }

    if (argcast && argcast->parent  && argcast->module == ISOSURFACE && argcast->task_counter == 0)
    {
	add_attribute((Object)argcast->parent[0], argcast, -1);
    }

    ok:

    DXFree ( (Pointer) outputs );
    DXFree ( (Pointer) skipvec );
    DXFree ( (Pointer) argcast->isovals );
    DXFree ( (Pointer) argcast->parent  );

    outputs          = NULL;
    skipvec          = NULL;
    argcast->isovals = NULL;
    argcast->parent  = NULL;

    return OK;

    error:
        ERROR_SECTION;

        if ( outputs )
        {
            for ( i=0; i<STRUCT_COUNT(argcast); i++ )
                if ( outputs[i] )
                    DXDelete ( (Object) outputs[i] );

            DXFree ( (Pointer) outputs );
            outputs = NULL;
        }

        DXFree ( (Pointer) skipvec );
        DXFree ( (Pointer) argcast->isovals );
        DXFree ( (Pointer) argcast->parent  );

        skipvec          = NULL;
        argcast->isovals = NULL;
        argcast->parent  = NULL;

        return ERROR;
}


/*--------------------------------*\
  recursive
\*--------------------------------*/
static
Error iso_part ( iso_arg_type iso_arg )
{
    Class          class;
    int            i;
    iso_arg_type   call_arg;
    float          *deletable_isovals = NULL;
    CompositeField grown;

    call_arg         = iso_arg;
    call_arg.isovals = NULL;
    call_arg.parent  = NULL;
        /* Significantly: copy over the invariant attributes */

    /*
     * Traverse down from:
     *   iso_arg.parent
     *   iso_arg.self  to  call_arg.parent
     *                     call_arg.self
     *
     * Or act on iso_arg.self if a leaf.
     */

    switch ( class = DXGetObjectClass ( iso_arg.self ) )
    {
        case CLASS_FIELD:

            /* XXX - output culling is 'kosher'? */

            /* 
             * DXCopy Arrayed data (make unique) and submit task.
             */

            if ( !DXEmptyField ( (Field) iso_arg.self ) )
            {
                /*
                 * The stats>DXStatistics call will fail on EmptyFields.
                 *   hence it had to be moved here.
                 */

                if ( !stats ( &iso_arg, &deletable_isovals ) )
                     goto error;

                DXASSERTGOTO ( iso_arg.isovals != NULL );
                /*
                 * arguments are copied so that they can be deleted 
                 * without contention by each added task 
                 */
                if ( ( ERROR == ( call_arg.parent
                           = (Object *) DXAllocate
                                  ( STRUCT_COUNT(&iso_arg) * sizeof (Object) )))
                     ||
                     ( ERROR == ( call_arg.isovals
                           = (float *) DXAllocate
                                  ( VALUE_COUNT(&iso_arg) * sizeof (float) ) ) )
                     ||
                     ! memcpy ( (char *)call_arg.parent,
                                (char *)iso_arg.parent,
                                ( STRUCT_COUNT(&iso_arg) * sizeof (Object) ) )
                     ||
                     ! memcpy ( (char *)call_arg.isovals,
                                (char *)iso_arg.isovals,
                                ( VALUE_COUNT(&iso_arg) * sizeof (float) ) ) )
                    goto error;
                /*
                 * Recall that DXAddTask will copy its input when
                 * it is passed in as non-zero length.
                 */

		call_arg.task_counter = _dxd_isosurface_task_counter++;
                if ( !DXAddTask ( iso_added_task,
                                  (Pointer)&call_arg,
                                  sizeof( iso_arg_type ),
                                  (double)1.0 ) )
                    goto error;
            }
            break;

        case CLASS_XFORM:
        case CLASS_SCREEN:
        case CLASS_CLIPPED:

            call_arg.isovals = iso_arg.isovals;

            if ( ERROR == ( call_arg.parent
                     = (Object *) DXAllocate
                                      ( STRUCT_COUNT(&iso_arg) *
                                        sizeof (Object) ) ) )
                goto error;

            for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                if ( ERROR == ( call_arg.parent[i]
                         = DXCopy ( iso_arg.self, COPY_ATTRIBUTES ) ) )
                    goto error;

            switch ( DXGetObjectClass ( iso_arg.parent[0] ) )
            {
                case CLASS_GROUP:
                    if ( ( CLASS_SERIES ==
                               DXGetGroupClass ( (Group) iso_arg.parent[0] ) )
                         &&
                         ( -1 != iso_arg.series_ordinal ) )
                        for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        {
                            if ( !DXSetSeriesMember
                                     ( (Series) iso_arg.parent[i],
                                       iso_arg.series_ordinal,
                                       (double) iso_arg.series_FP_val,
                                       (Object) call_arg.parent[i] ) )
                                goto error;
                        }
                    else

                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetMember ( (Group) iso_arg.parent[i],
                                            iso_arg.member_name,
                                            call_arg.parent[i] ) )
                            goto error;

                    break;

                case CLASS_XFORM:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetXformObject
                                  ( (Xform)iso_arg.parent[i],
                                    call_arg.parent[i] ) )
                            goto error;
                    break;

                case CLASS_SCREEN:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetScreenObject
                                  ( (Screen)iso_arg.parent[i],
                                    call_arg.parent[i] ) )
                            goto error;
                    break;

                case CLASS_CLIPPED:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetClippedObjects
                                  ( (Clipped)iso_arg.parent[i],
                                    call_arg.parent[i],
                                    NULL ) )
                            goto error;
                    break;
            }

            switch ( class )
            {
                case CLASS_XFORM:
                    if ( !DXGetXformInfo
                              ( (Xform)iso_arg.self, &call_arg.self, NULL )
                         ||
                         !iso_part ( call_arg ) )
                        goto error;

                    break;

                case CLASS_SCREEN:
                    if ( !DXGetScreenInfo
                              ( (Screen)iso_arg.self, &call_arg.self,
                                NULL, NULL )
                         ||
                         !iso_part ( call_arg ) )
                        goto error;

                    break;

                case CLASS_CLIPPED:
                    if ( !DXGetClippedInfo
                              ( (Clipped)iso_arg.self, &call_arg.self, NULL )
                         ||
                         !iso_part ( call_arg ) )
                        goto error;

                    break;
            }

            DXFree ( (Pointer) call_arg.parent );
            call_arg.parent = NULL;

            break;

        case CLASS_GROUP:

             /* Two checks before calling is_volume_topology */
            switch ( iso_object_type_check ( iso_arg.self ) )
            {
                /* is_volume_topology doesn't like empty CF's */
                case OBJ_ERROR: goto error;

                /* see CLASS_FIELD above for example of early exit */
                case OBJ_EMPTY: return OK;
            }

            /*
             * The stats>DXStatistics call would fail on EmptyFields.
             *   hence it had to be moved away from the top of this routine.
             */

            if ( !stats ( &iso_arg, &deletable_isovals ) )
                 goto error;

            /* 
             * DXCopy and DXGrow if a composite field and forming local gradient
             * Form contents of variable call_arg with each group member.
             *  - miscellaneous settings are copied.
             *  - isovalues are referenced
             *  - parents are created from attribute copy of the group
             *  - parents are setmembered up one level within 'grandparent'
             * Call IsoPart with call_arg across the membership of the group.
             */

            if ( ( DXGetGroupClass ( (Group)iso_arg.self )
                       == CLASS_COMPOSITEFIELD )
                 && 
                 /* Valid complaint: only useful for volume connections! */
                 is_volume_topology ( (CompositeField)iso_arg.self )
                 && 
                 ( iso_arg.normal_type == NORMALS_COMPUTED ) )
            {
                 if ( ERROR == ( grown = copy_grow_save
                                            ( (CompositeField) iso_arg.self )) )
                     goto error;

                 iso_arg.self      = (Object)grown;
                 call_arg.is_grown = 1;
            }

            if ( DXGetError() != ERROR_NONE ) /* is_volume_topology */
                 goto error;

            call_arg.isovals = iso_arg.isovals;

            if ( ERROR == ( call_arg.parent
                     = (Object *) DXAllocate
                                      ( STRUCT_COUNT(&iso_arg) *
                                        sizeof (Object) ) ) )
                goto error;

            for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                if ( ERROR == ( call_arg.parent[i]
                                  = DXCopy ( iso_arg.self, COPY_ATTRIBUTES ) ) )
                    goto error;

            if ( DXGetGroupClass((Group)iso_arg.self) == CLASS_COMPOSITEFIELD )
                for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                    if ( !add_attribute ( call_arg.parent[i], &iso_arg, i ) )
                        goto error;

            switch ( DXGetObjectClass ( iso_arg.parent[0] ) )
            {
                case CLASS_GROUP:
                    if ( ( CLASS_SERIES ==
                                DXGetGroupClass ( (Group) iso_arg.parent[0] ) )
                         &&
                         ( -1 != iso_arg.series_ordinal ) )
                        for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        {
                            if ( !DXSetSeriesMember
                                      ( (Series) iso_arg.parent[i],
                                        iso_arg.series_ordinal,
                                        (double) iso_arg.series_FP_val,
                                        call_arg.parent[i] ) )
                                goto error;
                        }
                    else
                        for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                            if ( !DXSetMember ( (Group) iso_arg.parent[i],
                                                iso_arg.member_name,
                                                call_arg.parent[i] ) )
                                goto error;

                    break;

                case CLASS_XFORM:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetXformObject
                                  ( (Xform)iso_arg.parent[i],
                                    call_arg.parent[i] ) )
                            goto error;
                    break;

                case CLASS_SCREEN:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetScreenObject
                                  ( (Screen)iso_arg.parent[i],
                                    call_arg.parent[i] ) )
                            goto error;
                    break;

                case CLASS_CLIPPED:
                    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
                        if ( !DXSetClippedObjects
                                  ( (Clipped)iso_arg.parent[i],
                                    call_arg.parent[i],
                                    NULL ) )
                            goto error;
                    break;
            }

            /* XXX - errorcheck DXGetEnumeratedMember */
            for ( i             = 0;
                  call_arg.self = DXGetEnumeratedMember
                                      ( (Group)iso_arg.self,
                                        i,
                                        &call_arg.member_name );
                  i++ )
            {
                call_arg.series_ordinal = -1;
                call_arg.series_FP_val  = -1.0;

                if ( CLASS_SERIES == DXGetGroupClass ( (Group)iso_arg.self ) )
                {
                    Object check = NULL;

                    call_arg.series_ordinal = i;

                    if ( ERROR == ( check = DXGetSeriesMember
                                              ( (Series)iso_arg.self,
                                                call_arg.series_ordinal,
                                                &call_arg.series_FP_val ) ) )
                        goto error;

                    else if ( call_arg.self != check )
                        DXWarning
                            ( "DXGetSeriesMember != DXGetEnumeratedMember" );
                }

                if ( !iso_part ( call_arg ) )
                    goto error;
            }

            /* XXX: Also: error recovery deletion */
            DXFree ( (Pointer) call_arg.parent );
            call_arg.parent = NULL;

            break;

        default:
            /* Luckily we know that this is the 'data' parameter" */
            DXSetError
                ( ERROR_INVALID_DATA,
                  "#10190", /* %s must be a field or a group */
                  "'data' parameter" );
            DXDebug
                ( "I", "Unknown Object type (%s)", _dxf_ClassName ( class ) );
            goto error;
            break;
    }

    DXFree ( (Pointer) deletable_isovals );
    deletable_isovals = NULL;

    return OK;

    error:
        ERROR_SECTION;

        if ( call_arg.isovals != _dxd_user_def_values )
        {
            DXFree ( (Pointer) call_arg.isovals );
            call_arg.isovals = NULL;
        }

        DXFree ( (Pointer) call_arg.parent  );

        call_arg.parent  = NULL;

        return ERROR;
}



/*----------------------------------------------------------*\
\*----------------------------------------------------------*/
extern
Object _dxf_IsosurfaceObject ( iso_arg_type iso_arg )
{
    Series  base   = NULL;
    Object  member = NULL;
    Field   tempty_f = NULL;
    Class   class;
    int     i;
    float   *copy_isovals      = NULL;
    float   *deletable_isovals = NULL;

    init_saved_copies_DEF;
    _dxd_isosurface_task_counter = 0;

    if ( DXGetError() != ERROR_NONE ) goto error;

    if ( ( ERROR == ( base = DXNewSeries() ) )
         ||
         ( ERROR == ( iso_arg.parent
               = (Object *) DXAllocate ( STRUCT_COUNT(&iso_arg) *
                                         sizeof (Object) ) ) ) )
        goto error;

    for ( i=0; i<STRUCT_COUNT(&iso_arg); i++ )
        iso_arg.parent[i] = (Object) base;

    if ( _dxd_user_def_values )
    {
        float bubble;
        int   j;

        for ( i=0; i<(VALUE_COUNT(&iso_arg)-1); i++ )
            for ( j=(i+1); j<VALUE_COUNT(&iso_arg); j++ )
                if ( _dxd_user_def_values[j] < _dxd_user_def_values[i] )
                {
                    bubble                  = _dxd_user_def_values[j];
                    _dxd_user_def_values[j] = _dxd_user_def_values[i];
                    _dxd_user_def_values[i] = bubble;
                }
    }

    switch ( class = DXGetObjectClass ( iso_arg.self ) )
    {
        case CLASS_FIELD:
            if ( DXEmptyField ( (Field) iso_arg.self ) )
            {
                if ( ( ERROR == ( tempty_f = DXNewField() ) )
                     ||
                     !DXSetMember ( (Group) base, NULL, (Object) tempty_f ) )
                    goto error;

                tempty_f = NULL;
            }
            else
            {
                if ( !stats ( &iso_arg, &deletable_isovals ) )
                    goto error;

                /* most noticeable when isovals = _dxd_user_def_values */
                if ( ( ERROR == ( copy_isovals
                            = (float *) DXAllocate
                                  ( VALUE_COUNT(&iso_arg) * sizeof (float) ) ) )
                     ||
                     ! memcpy ( (char *)copy_isovals,
                                (char *)iso_arg.isovals,
                                ( VALUE_COUNT(&iso_arg) * sizeof (float) ) ) )
                    goto error;

                iso_arg.isovals = copy_isovals;

                /* recall that this function deallo's arrays passed in to it */
                if ( !iso_added_task ( (Pointer) &iso_arg ) )
                    goto error;
            }
            break;

        case CLASS_XFORM: case CLASS_SCREEN: case CLASS_CLIPPED:
        case CLASS_GROUP:

            if ( !stats ( &iso_arg, &deletable_isovals ) )
                goto error;

            if ( !DXCreateTaskGroup  ()          ||
                 !iso_part           ( iso_arg ) ||
                 !DXExecuteTaskGroup ()            )
                goto error;
            break;

        default:
            /* Luckily we know that this is the 'data' parameter" */
            DXSetError
                ( ERROR_INVALID_DATA,
                  "#10190", /* %s must be a field or a group */
                  "'data' parameter" );
            DXDebug
                ( "I", "Unknown Object type (%s)", _dxf_ClassName ( class ) );
            goto error;
    }

    if ( DXGetError() != ERROR_NONE )
        DXMessageGoto
            ( "'process_part' set the error code, but didn't return error" )

#if 0
    if ( !DXProcessParts ( (Object) base,
                         iso_normals_field,
                         (Pointer)&iso_arg.normal_direction,
                         sizeof(int), 0, 1 ) )
        goto error;
#endif

    DXFree ( (Pointer) iso_arg.parent    );
    DXFree ( (Pointer) deletable_isovals );
    free_saved_copies_DEF;

    iso_arg.parent    = NULL;
    deletable_isovals = NULL;

    if ( DXGetEnumeratedMember ( (Group) base, 1, NULL ) )
        return ( (Object) base );

    else
    {
        if ( NULL != ( member = DXGetEnumeratedMember
                                    ( (Group) base, 0, NULL ) ) )
        {
            if ( ( ERROR == ( member = DXCopy ( member, COPY_STRUCTURE ) ) )
                 ||
                 !DXDelete ( (Object) base ) )
                goto error;

            base = NULL;
        }
        else
            if ( ERROR == ( member = (Object) DXNewField () ) )
                goto error;

        return ( member );
    }

    error:
        ERROR_SECTION;

        DXDelete ( (Object) base );
        DXDelete ( (Object) tempty_f );
        DXFree   ( (Pointer) iso_arg.parent    );
        DXFree   ( (Pointer) deletable_isovals );

        base              = NULL;
        tempty_f          = NULL;
        iso_arg.parent    = NULL;
        deletable_isovals = NULL;

        free_saved_copies_DEF;

        return ERROR;
}

#endif /* ( _ISOSURFACE_PASS_NUMBER == 1 ) */
