/***********************************************************************/
/* 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"          */
/***********************************************************************/
/*
 * $Header: /home/gda/dxcvs/dx/src/exec/dxmods/showconnect.c,v 1.3 1999/05/10 15:45:30 gda Exp $
 */

#include <dxconfig.h>


/***
MODULE:
    ShowConnections
SHORTDESCRIPTION:
    shows the outline of connective elements
CATEGORY:
    Realization
INPUTS:
    input; field; NULL; field to show connections of
OUTPUTS: 
    output; color field; NULL; original field with connections outlined
BUGS:
AUTHOR:
    John E. Allain
END:
***/

#include <string.h>
#include <stdio.h>
#include <dx/dx.h>
#include <_helper_jea.h>

#define DEFAULT_CONNECT_COLOR    DXRGB ( 0.7, 0.7, 0.0 )


typedef struct { HashTable table;  int count; }  hash_table_rec;
typedef struct { Line line; char invalid; }      hash_element_rec;
typedef hash_element_rec                         *hash_element_ptr;


static Error process_triangles
         ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle );
static Error process_quads
         ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle );
static Error process_cubes
         ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle );
static Error process_tetrahedra 
         ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle );
static Error process_faces_loops_edges
         ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle );

static Field field_show_connections ( Field field, char *dummy, int dummysize );
 /* Application dependant */



static
int compare_func ( Key i, Element j )
{
    return ( ( ( ((Line *)i)->p == ((Line *)j)->p ) &&
               ( ((Line *)i)->q == ((Line *)j)->q )   ) ? 0 : 1 );
}


static
PseudoKey hash_func ( Key i )
{
#define  k1       ((int)((Line *)i)->p)
#define  k2       ((int)((Line *)i)->q)
#define  PRIME_1  10607 
#define  PRIME_2  18913   

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

    return address;

#undef  k1
#undef  k2
}


static
Error store_hash ( hash_table_rec *hash,  PointId p,  PointId q, char invalid )
{
    hash_element_rec element;
    hash_element_ptr query;

    if ( p <= q ) { element.line.p = p; element.line.q = q; }
    else          { element.line.p = q; element.line.q = p; }

    if ( NULL == ( query = (hash_element_ptr) DXQueryHashElement
                                                ( hash->table, (Key)&element )))
    {
        element.invalid = invalid;

        if ( !DXInsertHashElement ( hash->table, (Element)&element ) )
            goto error;
        else
            hash->count++;
    }
    else
        if ( !invalid ) query->invalid = invalid;

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



int m_ShowConnections ( Object *in, Object *out )
{
    if ( ERROR == ( out[0] = DXProcessParts
                             ( in[0], field_show_connections, NULL, 0, 1, 1 ) )
         &&
         ( DXGetError() != ERROR_NONE ) )
        goto error;

    return OK;

    error:
        if ( out[0] ) DXDelete ( out[0] );

        out[0] = NULL;

        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static 
Field field_show_connections ( Field in_field, char *dummy, int dummysize )
{
    Class     class;
    Line      *lines_ptr;
    int       n_lines;
    Array     larray  = NULL;
    hash_table_rec htab;
    hash_element_ptr elem_ptr;
    Array     carray  = NULL;
    int       np;
    Object    o_ptr;
    Object    conn_element;
    char      *conn_elem_name;
    Field     out_field  = NULL;
    int       i;
    Array     posi;

    InvalidComponentHandle i_handle = NULL;

    /* we keep connections, they are removed upon set component time */
    static char  *removables[] = { "faces", "loops", "edges", NULL };

    htab.table = NULL;

    /* 
     * Ensure that input field is Valid
     */

    DXASSERTGOTO ( in_field  != NULL );
    DXASSERTGOTO ( dummy     == NULL );
    DXASSERTGOTO ( dummysize == 0 );
    DXASSERTGOTO ( CLASS_FIELD == DXGetObjectClass ( (Object)in_field ) );

    if ( DXEmptyField ( in_field ) ) 
        return NULL;

    /* Find number of points for either regular or irregular data */

    if ( ERROR == ( posi = (Array) DXGetComponentValue
                                       ( in_field, "positions" ) ) )
        DXErrorGoto2 ( ERROR_INVALID_DATA, 
                       "#10240", /* missing %s component */ "\"positions\"" )


    else if ( DXGetObjectClass ( (Object) posi ) != CLASS_ARRAY )
        DXErrorGoto2 ( ERROR_BAD_CLASS,
                       "#4414", /* `%s' component is not an array */
                       "\"positions\"" )

    else if ( !DXGetArrayInfo ( posi, &np, NULL, NULL, NULL, NULL ) )
        goto error;


    if ( ERROR == ( out_field
                      = (Field) DXCopy ( (Object) in_field, COPY_STRUCTURE ) ) )
        goto error;

    if (NULL != (o_ptr = DXGetComponentValue(in_field, "polylines")))
    {
        /*
         * Fast exit, we already have results.
         */
	if ( !_dxf_SetDefaultColor ( out_field, DEFAULT_CONNECT_COLOR )
             ||
             !_dxf_SetFuzzAttribute ( out_field, 1 ) )
            goto error;
        return DXEndField ( out_field );
    }

   /*
    * Remove "connections", etc. from output field if present.
    */

    for ( i=0; removables[i] != NULL; i++ )

        if ( DXDeleteComponent ( out_field, removables[i] ) )
        {
            if ( !DXChangedComponentStructure ( out_field, removables[i] ) )
                goto error;
        }
        else if ( DXGetError() != ERROR_NONE )
            goto error;

   /*
    * New considerations with regards to "normals".
    */
   if ( !DXSetIntegerAttribute ( (Object)out_field, "shade", 0 ) )
       goto error;

    /*
     * Determine which type of input connectivity we have,
     *   perform the appropriate action.
     */

    if ( NULL != ( o_ptr = DXGetComponentValue ( in_field, "faces" ) ) )
        conn_elem_name = "faces";

    else if ( NULL != ( o_ptr = DXGetComponentValue
                                    ( in_field, "connections" ) ))
    {
        if ( ERROR == ( conn_element = DXGetComponentAttribute
                                           ( in_field,
                                             "connections", "element type" ) ) )
            DXErrorGoto3
                ( ERROR_MISSING_DATA,
                  "#10255", /* %s component is missing the `%s' attribute */
                  "\"connections\"",
                  "element type" )

        else if ( ( class = DXGetObjectClass ( conn_element ) ) != CLASS_STRING)
            DXErrorGoto2 ( ERROR_BAD_CLASS,
                          "#11080", /* name for object %d must be a string */
                          "connections component element type" )

        else if ( ERROR == ( conn_elem_name
                                 = DXGetString ( (String) conn_element ) ) )
            DXErrorGoto ( ERROR_INTERNAL,
                        "connections element type string not retrievable" )

        /*
         * Will invalidity be a factor?
         */

        /* XXX cost.  Can this be done while hashing? */
        if ( !DXInvalidateConnections           ( (Object) out_field ) ||
             !DXInvalidateUnreferencedPositions ( (Object) out_field )
             ||
             ( ERROR == ( i_handle = DXCreateInvalidComponentHandle
                                         ( (Object) out_field,
                                           NULL, "connections" ) ) ) )
             goto error;

        if ( 0 == DXGetInvalidCount ( i_handle ) )
        {
            DXFreeInvalidComponentHandle ( i_handle );
            i_handle = NULL;
        }

    }
    else
        DXErrorGoto2
            ( ERROR_MISSING_DATA,
              "#10240", /* missing %s component */
              "\"connections\" or (\"face\",\"loop\",\"edge\")" )

    htab.count = 0;
    if ( ERROR == ( htab.table = DXCreateHash
                                     ( sizeof(hash_element_rec),
                                       hash_func, compare_func ) ) )
        goto error;

    if ( strcmp ( conn_elem_name, "faces" ) == 0 )
    {
        if ( !process_faces_loops_edges ( in_field, &htab, i_handle ) )
            goto error;
    }
    else if ( strcmp ( conn_elem_name, "triangles" ) == 0 )
    {
        if ( !process_triangles ( in_field, &htab, i_handle ) )
            goto error;
    }
    else if ( strcmp ( conn_elem_name, "quads" ) == 0 )
    {
        if ( !process_quads ( in_field, &htab, i_handle ) )
            goto error;
    }
    else if ( strcmp ( conn_elem_name, "cubes" ) == 0 )
    {
        if ( !process_cubes ( in_field, &htab, i_handle ) )
            goto error;
    }
    else if ( strcmp ( conn_elem_name, "tetrahedra" ) == 0 )
    {
        if ( !process_tetrahedra ( in_field, &htab, i_handle ) )
            goto error;
    }
    else if ( strcmp ( conn_elem_name, "lines" ) == 0 )
    {
        /*
         * Fast exit, we already have results.
         *   Lines connectivity component is what ShowConnections constructs.
         *   Make sure that colors exist and leave.
         */

        DXDestroyHash ( htab.table );  htab.table = NULL;

        if ( !DXSetComponentValue
                 ( out_field, "connections", o_ptr )
             ||
             !DXSetComponentAttribute
                 ( out_field, "connections", "element type",
                   (Object) DXNewString ( "lines" ) )
             ||
             !_dxf_SetDefaultColor
                 ( out_field, DEFAULT_CONNECT_COLOR )
             ||
             !_dxf_SetFuzzAttribute ( out_field, 1 ) )
            goto error;

        return DXEndField ( out_field );
    }
    else
        DXErrorGoto2
            ( ERROR_INTERNAL,
              "#11380", /* %s is an invalid connections type */
              conn_elem_name )

    /*
     * Destroy connections dependent color, data.
     */
    if ( !DXChangedComponentStructure ( out_field, "connections" ) )
        goto error;

    if ( !DXExists ( (Object)out_field, "colors" ) )
        if ( !DXDeleteComponent ( out_field, "color map" )
             && ( DXGetError() != ERROR_NONE ) )
            goto error;


    /*
     * Convert edges in storage within hash table
     *   to component lines.
     *     Construct "lines" component of appropriate length.
     *     Extract from 'htab', place in component's array.
     *     Add colors if not already present.
     */

    if ( ( ERROR == ( larray = _dxf_NewComponentArray
                          ( LINE_CONNECTIONS_COMP, &htab.count, NULL, NULL ) ) )
         ||
         ( ERROR == ( lines_ptr = (Line *) DXGetArrayData ( larray ) ) )
         ||
         !DXSetComponentValue ( out_field, "connections", (Object)larray ) )
        goto error;

    larray = NULL;

    if ( !DXSetComponentAttribute
              ( out_field,
                "connections",
                "element type",
                (Object) DXNewString ( "lines" ) )
         ||
         !DXSetComponentValue ( out_field, "invalid connections", NULL )
         ||
         !DXInitGetNextHashElement ( htab.table ) )
        goto error;

    if ( i_handle != NULL )
    {
        if ( !DXFreeInvalidComponentHandle ( i_handle )
             ||
             ( ERROR == ( i_handle
                             = DXCreateInvalidComponentHandle
                                   ( (Object)out_field, NULL, "connections" ))))
             goto error;

        for ( i = 0;
              ( NULL != ( elem_ptr = (hash_element_ptr) DXGetNextHashElement
                                                           ( htab.table ) ) );
              i++, lines_ptr++ )
        {
            *lines_ptr = elem_ptr->line;

            if ( elem_ptr->invalid && !DXSetElementInvalid ( i_handle, i ) )
                goto error;
        }

        if ( !DXSaveInvalidComponent       ( out_field, i_handle ) ||
             !DXFreeInvalidComponentHandle ( i_handle ) )
            goto error;

        i_handle = NULL;
    }
    else
        for ( i = 0;
              ( NULL != ( elem_ptr = (hash_element_ptr) DXGetNextHashElement
                                                           ( htab.table ) ) );
              i++, lines_ptr++ )
            *lines_ptr = elem_ptr->line;

    if ( i != htab.count )
        DXErrorGoto
            ( ERROR_INTERNAL,
              "Unexpected end of table in DXGetNextHashElement call" );

    if ( ( DXGetError() != ERROR_NONE )
         ||
         !_dxf_SetDefaultColor  ( out_field, DEFAULT_CONNECT_COLOR )
         ||
         !_dxf_SetFuzzAttribute ( out_field, 1 )
         ||
         !DXDestroyHash ( htab.table ) )
        goto error;

    htab.table = NULL;

    return DXEndField ( out_field );

    error:
        if ( htab.table ) DXDestroyHash( htab.table );
        if ( larray     ) DXDelete     ( (Object) larray );
        if ( out_field  ) DXDelete     ( (Object) out_field );

        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static
Error process_triangles
          ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle )
{
    Triangle  *tris_ptr;
    char      invalid = 0;
    int	      nt;
    int	      i;

    /* 3 edges per triangle, each of which may be shared by 2 triangles */

    if ( ERROR == ( tris_ptr = (Triangle *) _dxf_GetComponentData
                                                ( (Object)field,
                                                  TRIANGLE_CONNECTIONS_COMP,
                                                  &nt, NULL, NULL ) ) )
        goto error;

    for ( i=0; i<nt; i++, tris_ptr++ )
    {
        invalid = (i_handle==NULL)? 0 :
                  DXIsElementInvalidSequential ( i_handle, i );

        if ( !store_hash ( htab, tris_ptr->p, tris_ptr->q, invalid ) ||
             !store_hash ( htab, tris_ptr->q, tris_ptr->r, invalid ) ||
             !store_hash ( htab, tris_ptr->r, tris_ptr->p, invalid )   )
            goto error;
    }

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static
Error process_quads
          ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle )
{
    Quadrilateral *quads_ptr;
    char      invalid = 0;
    int	          nq;
    int	          i;
    /*----------------*/
    /* quad ordering: */
    /*    p  q        */
    /*    r  s        */
    /*----------------*/
    /* 4 edges per quad, each of which may be shared by 2 quads */

    if ( ERROR == ( quads_ptr = (Quadrilateral *) _dxf_GetComponentData
                                                      ( (Object)field,
                                                        QUAD_CONNECTIONS_COMP,
                                                        &nq, NULL, NULL ) ) )
        goto error;

    for ( i=0; i<nq; i++, quads_ptr++ )
    {
        invalid = (i_handle==NULL)? 0 :
                  DXIsElementInvalidSequential ( i_handle, i );

        if ( !store_hash ( htab, quads_ptr->p, quads_ptr->q, invalid ) ||
             !store_hash ( htab, quads_ptr->q, quads_ptr->s, invalid ) ||
             !store_hash ( htab, quads_ptr->r, quads_ptr->s, invalid ) ||
             !store_hash ( htab, quads_ptr->r, quads_ptr->p, invalid )   )
            goto error;
    }

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static
Error process_faces_loops_edges
          ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle )
{
    Array     array;
    char      invalid = 0;
    int       *face_ptr, *fp, *fp_end, *fp_last;  /* base, cur, to, last */
    int       *loop_ptr, *lp, *lp_end, *lp_last;
    int       *edge_ptr, *ep, *ep_end, *ep_last;
    int	      nf;
    int	      nl;
    int	      ne;

#define ERROR_GET_DAT(I_c,O_p,O_n,EE) \
    if((ERROR==(array=(Array)DXGetComponentValue(field,I_c)))|| \
       (!DXGetArrayInfo(array,&O_n,NULL,NULL,NULL,NULL))|| \
       (ERROR==(O_p=(int*)DXGetArrayData(array)))) \
        DXErrorGoto(ERROR_INVALID_DATA,EE)

    ERROR_GET_DAT ( "faces", face_ptr, nf, "#13180" /* bad faces */ );
    ERROR_GET_DAT ( "loops", loop_ptr, nl, "#13190" /* no loops */  );
    ERROR_GET_DAT ( "edges", edge_ptr, ne, "#13200" /* no edges */  );

#undef  ERROR_GET_DAT

    fp_last = &face_ptr [ nf - 1 ];
    lp_last = &loop_ptr [ nl - 1 ];
    ep_last = &edge_ptr [ ne - 1 ];

    fp_end = fp_last;
    fp     = face_ptr;

    while ( fp <= fp_end )
    {
        lp_end = &loop_ptr [ ( ( fp != fp_last ) ? fp[1] : nl ) - 1 ];
        lp     = &loop_ptr [ fp[0] ];

        while ( lp <= lp_end )
        {
            ep_end = &edge_ptr [ ( ( lp != lp_last ) ? lp[1] : ne ) - 1 ];
            ep     = &edge_ptr [ lp[0] ];

            if ( !store_hash ( htab, ep[0], ep_end[0], invalid ) )
                goto error;

            while ( ep < ep_end )  /* Different inequality from previous two */
            {
                if ( !store_hash ( htab, ep[0], ep[1], invalid ) )
                    goto error;

                ep++;
            }

            lp++;
        }

        fp++;
    }

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static
Error process_tetrahedra
          ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle )
{
    Tetrahedron *tetras_ptr;
    char      invalid = 0;
    int	      nt;
    int	      i;
    /* 6 edges per tetrahedra, each of which may be shared by 6 tetrahedras */

    if ( ERROR == ( tetras_ptr = (Tetrahedron *) _dxf_GetComponentData
                                                     ( (Object)field,
                                                       TETRA_CONNECTIONS_COMP,
                                                       &nt, NULL, NULL ) ) )
        goto error;

    for ( i=0; i<nt; i++, tetras_ptr++ )
    {
        invalid = (i_handle==NULL)? 0 :
                  DXIsElementInvalidSequential ( i_handle, i );

        if ( !store_hash ( htab, tetras_ptr->p, tetras_ptr->q, invalid ) ||
             !store_hash ( htab, tetras_ptr->p, tetras_ptr->r, invalid ) ||
             !store_hash ( htab, tetras_ptr->p, tetras_ptr->s, invalid ) ||
             !store_hash ( htab, tetras_ptr->q, tetras_ptr->r, invalid ) ||
             !store_hash ( htab, tetras_ptr->r, tetras_ptr->s, invalid ) ||
             !store_hash ( htab, tetras_ptr->s, tetras_ptr->q, invalid )   )
            goto error;
    }

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}



static
Error process_cubes
          ( Field field, hash_table_rec *htab, InvalidComponentHandle i_handle )
{
    /*----------------*/
    /* cube ordering: */
    /*     p   q      */
    /*    r   s       */
    /*     t   u      */
    /*    v   w       */
    /*----------------*/
    Cube      *cubes_ptr;
    char      invalid = 0;
    int	      nc;
    int	      i;
    /* 12 edges per cube, each of which may be shared by 4 cubes */

    if ( ERROR == ( cubes_ptr = (Cube *) _dxf_GetComponentData
                                             ( (Object)field,
                                                CUBE_CONNECTIONS_COMP,
                                                &nc, NULL, NULL ) ) )
        goto error;

    for ( i=0; i<nc; i++, cubes_ptr++ )
    {
        invalid = (i_handle==NULL)? 0 :
                  DXIsElementInvalidSequential ( i_handle, i );

        if ( !store_hash ( htab, cubes_ptr->p, cubes_ptr->q, invalid ) ||
             !store_hash ( htab, cubes_ptr->q, cubes_ptr->s, invalid ) ||
             !store_hash ( htab, cubes_ptr->s, cubes_ptr->r, invalid ) ||
             !store_hash ( htab, cubes_ptr->r, cubes_ptr->p, invalid ) ||
             !store_hash ( htab, cubes_ptr->p, cubes_ptr->t, invalid ) ||
             !store_hash ( htab, cubes_ptr->q, cubes_ptr->u, invalid ) ||
             !store_hash ( htab, cubes_ptr->s, cubes_ptr->w, invalid ) ||
             !store_hash ( htab, cubes_ptr->r, cubes_ptr->v, invalid ) ||
             !store_hash ( htab, cubes_ptr->t, cubes_ptr->u, invalid ) ||
             !store_hash ( htab, cubes_ptr->u, cubes_ptr->w, invalid ) ||
             !store_hash ( htab, cubes_ptr->v, cubes_ptr->w, invalid ) ||
             !store_hash ( htab, cubes_ptr->v, cubes_ptr->t, invalid )    )
            goto error;
    }

    return OK;

    error:
        DXASSERT ( DXGetError() != ERROR_NONE );
        return ERROR;
}
