/*
	Copyright (c) 2024 PTC Inc. and/or Its Subsidiary Companies. All Rights Reserved.
*/


/*--------------------------------------------------------------------*\
Pro/Toolkit includes
\*--------------------------------------------------------------------*/
#include <ProToolkit.h>
#include <ProMdl.h>
#include <ProMenuBar.h>
#include <ProArray.h>
#include <ProObjects.h>
#include <ProSurfacedata.h>
#include <ProSurface.h>
#include <ProAxis.h>
#include <ProDtmAxis.h>
#include <ProElemId.h>
#include <ProValue.h>
#include <ProElement.h>
#include <ProFeature.h>
#include <ProFeatType.h>
#include <ProFeatType.h>
#include <ProSelection.h>
#include <ProElempath.h>
#include <ProAsmcomp.h>
#include <ProGroup.h>
#include <math.h>
#include <ProTKRunTime.h>
#include <ProSolidBody.h>

typedef struct auto_srf_data
{
  ProSurface rev_srf;
  int axis_id;
  double diameter;
} Auto_srf_data; 

typedef struct auto_axis_pnts
{
  Pro3dPnt p1;
  Pro3dPnt p2;
} Axis_p12;

typedef struct auto_srfs_data
{
  int total_srfs; 
  int num_rev_srfs; 
  Auto_srf_data *srfs_array;
  int num_axes; 
  Axis_p12 *axes_pnts;
  int axes_created; 
  ProMdl owner;
} Auto_srfs_data; 

/* Signals that a representative axis was already found for this "hole" */
#define AXIS_ALT_FOUND -5 


/*--------------------------------------------------------------------*\
  PROTOTYPES OF LOCAL FUNCTIONS
\*--------------------------------------------------------------------*/
ProError AutoaxisMain( ProMdl imported_model );

ProError AutoaxisCollectAxes ( ProAxis  axis,
                                 ProError status,
                                 ProAppData app_data );

ProError AutoaxisCollectRevSurfaces ( ProSurface p_surface,
                                        ProError status,
                                        ProAppData app_data );

ProError AutoaxisSurfaceFilter( ProSurface p_surface, 
                                  ProAppData app_data );

ProError AutoaxisElementAdd( ProElemId id, ProElement *parent,
                      ProValueData *elem_data, ProElement *elem );

ProError AutoaxisSurfaceAxisCreate( Auto_srfs_data *srfs,
                                       ProSurface surface, int* axis_id);

ProError AutoaxisSurfaceCheckAxis( ProSurface p_surface,
                                            Axis_p12 *p12 );

ProError AutoaxisGeomVisitAction ( ProGeomitem *p_geomitem,
                                   ProError  err,
                                   ProAppData app_data) ; 

ProError AutoaxisCheckAxis( ProSurface surface , 
                                 Auto_srfs_data *srfs );

/*--------------------------------------------------------------------*\
  USER INTRFACE
\*--------------------------------------------------------------------*/
static uiCmdAccessState TestAccessDefault(uiCmdAccessMode access_mode)
{
    return (ACCESS_AVAILABLE);
}

/*====================================================================*\
FUNCTION : user_initialize()
PURPOSE  : 
\*====================================================================*/
int user_initialize(
    int argc,			
    char *argv[],
    char *version, 
    char *build,
    wchar_t errbuf[80])
{
    uiCmdCmdId	cmd_id;
    ProError status;
    int RunAutoAxis( );

    status = ProCmdActionAdd("AutoAxis", (uiCmdCmdActFn)RunAutoAxis,
	uiProe2ndImmediate, TestAccessDefault,
	PRO_B_TRUE, PRO_B_TRUE, &cmd_id);

    status = ProMenubarmenuPushbuttonAdd(
	"Applications", "AutoAxis", "AutoAxis", "Creates axes in the model",
	NULL, PRO_B_FALSE, cmd_id, L"usrmsg.txt");

    return (0);
}

/*====================================================================*\
FUNCTION : user_terminate()
PURPOSE  : To handle any termination actions
\*====================================================================*/
void user_terminate()
{
}

int RunAutoAxis( )
{
  ProMdl cur_mdl;
  ProError status; 

  status = ProMdlCurrentGet( &cur_mdl);
  if ( status == PRO_TK_NO_ERROR )
  {
    status = AutoaxisMain(cur_mdl);
  }
  return 0;
}

/*--------------------------------------------------------------------*\
  --------------------------------------------------------------------
  FUNCTION DEFINITIONS
  --------------------------------------------------------------------
\*--------------------------------------------------------------------*/

/*====================================================*\
  Function : AutoaxisSurfaceCheckAxis()
  Purpose  : To check if any axis exists which can be
             related to given surface.
\*====================================================*/
ProError AutoaxisSurfaceCheckAxis( ProSurface p_surface, Axis_p12 *p12)
{
  ProError status;
  ProGeomitemdata *gitem_data;
  ProSrftype srftype;
  ProUvParam uv_min, uv_max;
  ProSurfaceOrient s_orient;
  ProSurfaceshapedata s_shapedata;
  ProVector e1, e2, e3;
  Pro3dPnt origin;
  ProCurvedata cur1;
  double rad1, alpha;
  int s_id;

  double epsilan;
  double a,b,c;
  double x01,y01,z01;
  double det1,det2,det3;
  double cp,dist,abc;
  ProVector a_v;
  ProError axis_stat;

  if ( p_surface == NULL || p12 == NULL )
     return PRO_TK_BAD_INPUTS;

  gitem_data = NULL;
  epsilan = 0.00000001; /* Value is enough ? */
  axis_stat = PRO_TK_E_NOT_FOUND;

  status = ProSurfaceDataGet(p_surface, &gitem_data);
  if ( status == PRO_TK_NO_ERROR )
  {
    status = ProSurfacedataGet( gitem_data->data.p_surface_data,
                                &srftype, uv_min, uv_max, &s_orient, 
                                &s_shapedata, &s_id);

    if ( status == PRO_TK_NO_ERROR )
    {
      switch(srftype)
      {
        case PRO_SRF_CYL:
          status = ProCylinderdataGet( &s_shapedata, e1, e2, e3, 
				       origin, &rad1);
          break;

        case PRO_SRF_CONE:
          status = ProConedataGet( &s_shapedata, e1, e2, e3, origin, 
				   &alpha);
          break;

        case PRO_SRF_REV:
          status = ProSrfrevdataGet( &s_shapedata, e1, e2, e3, origin, 
				     &cur1);
          break;

        default :
          ProTKPrintf("Not a Rev. Type  surface..\n");
      }
    }
  }

  if ( status == PRO_TK_NO_ERROR )
  {
  /* To find collinearity between exsting axis with
     axis to be created using the given surface.
     1. Data available from axis ( p1,p2 )
     2. Data available from surface Origin(x0,y0,z0) and Vectors(X,Y,Z)

     Assumption is made that the axis will be always
     coincident/parallel with Z vector of the surface.
     In this scenario, the condition for being parallel
     and coincident vectors,
     a) Cross product must be zero, ( Axis x Z = 0 )
     b) The distance between the Axis and O(origin) of the surface
        must be zero
     If above two conditions are true, no axis will be created because 
     one already exists.
   */

    /* Axis from p1,p2 */
    /* In vector form (a,b,c) */
    a = p12->p1[0] - p12->p2[0] ;
    b = p12->p1[1] - p12->p2[1] ;
    c = p12->p1[2] - p12->p2[2] ;

    abc = sqrt ( a*a + b*b + c*c );

    /* Unit vector along axis */
    a_v[0] = a/abc ; 
    a_v[1] = b/abc ; 
    a_v[2] = c/abc ; 

    /* Cross Product  ( Axis x Z_vec ) */

    cp = a_v[1]*e3[2] - a_v[2]*e3[1] ;
    cp += a_v[2]*e3[0] - a_v[0]*e3[2] ;
    cp += a_v[0]*e3[1] - a_v[1]*e3[0] ;

    if (  cp <= epsilan )
    {
      /* Distance between origin an Axis */
      x01 = origin[0] - p12->p1[0] ;
      y01 = origin[1] - p12->p1[1] ;
      z01 = origin[2] - p12->p1[2] ;

      det1 = c*y01 - b*z01 ;
      det2 = a*z01 - c*x01 ;
      det3 = b*x01 - a*y01 ;

      /*
      Actual formula for Shortest Distace to a line( Localized vector)
      from a point. 
      dist =  sqrt (( det1*det1 + det2*det2 + det3*det3 ) 
                        / ( a*a + b*b + c*c ) ) ;
      But denominator can be ignored .
      */ 

      dist = det1*det1 + det2*det2 + det3*det3 ;

      if (  dist  <= epsilan )
      { 
        axis_stat = PRO_TK_E_FOUND;
      } 
      else
      {
        axis_stat = PRO_TK_E_NOT_FOUND;
      }
    }
  }

  if( gitem_data != NULL )
  {
     ProGeomitemdataFree( &gitem_data );
  }

  return axis_stat;
}

/*====================================================*\
  Function : AutoaxisSurfaceIsHole()
  Purpose  : Determine if the surface is likely a part of a "hole" that needs
             an axis.
\*====================================================*/
ProBoolean AutoaxisSurfaceIsHole (ProSolid solid, ProSurface surface)
{
  ProError status;
  ProSurface* same_surfs;
  int n_same_surfs;

/*-----------------------------------------------------*\
  Determine if the surface has a matching surface 
  (typically  the other half of a split cylinder or cone).  
\*-----------------------------------------------------*/
  ProArrayAlloc (0, sizeof (ProSurface), 1, (ProArray*)&same_surfs);

  status = ProSurfaceSameSrfsFind (solid,
				   surface,
				   &same_surfs,
				   &n_same_surfs);

  ProArrayFree ((ProArray*)&same_surfs);

  if (status == PRO_TK_NO_ERROR && n_same_surfs > 1)
      return PRO_B_TRUE;
  else
    return PRO_B_FALSE;		 
}

/*====================================================*\
  Function : AutoaxisSurfaceFilter()
  Purpose  : Filter function for ProSolidSurfaceVisit
\*====================================================*/
ProError AutoaxisSurfaceFilter( ProSurface p_surface, ProAppData app_data )
{
  ProSrftype surface_type;
  ProError status;
  Auto_srfs_data *data;
  ProFeattype ftype;
  ProFeature feature;
  ProGeomitem geom;
  int s_id;

  status = PRO_TK_NO_ERROR;
  data = (Auto_srfs_data *)app_data;
  data->total_srfs++;

  status = ProSurfaceIdGet ( p_surface, &s_id );
  if(status == PRO_TK_NO_ERROR)
  {
    geom.id = s_id;
    geom.owner = data->owner;
    geom.type = PRO_SURFACE;
  }

  if(status == PRO_TK_NO_ERROR)
    status = ProSurfaceTypeGet ( p_surface, &surface_type); 

  if ( status == PRO_TK_NO_ERROR &&
       ( surface_type == PRO_SRF_CYL ||
         surface_type == PRO_SRF_CONE ||
         surface_type == PRO_SRF_REV ) &&
      AutoaxisSurfaceIsHole (data->owner, p_surface) )
    {
    return PRO_TK_NO_ERROR ;
  }

  return PRO_TK_CONTINUE; 
}

/*====================================================*\
  Function : AutoaxisAddSortedSurface()
  Purpose :  Add the given surface to the surface array, 
             in a position sorted
	     by diameter (largest to smallest).  Non-cylindrical
	     surfaces are added to the end of the array.
\*====================================================*/
ProError AutoaxisAddSortedSurface (Auto_srfs_data* data, 
				   Auto_srf_data* srf)
{
  int num_srfs = data->num_rev_srfs;
  int i;
  ProSurface current_srf;
  ProSrftype surface_type;
  double current_diam = 0.0;
  ProError status;
  ProUvParam param = {0.0, 0.0};
  
  status = ProSurfaceTypeGet ( srf->rev_srf, &surface_type); 
  if (surface_type == PRO_SRF_CYL)
    {
      ProSurfaceDiameterEval (srf->rev_srf, param, &srf->diameter);
    }
  else
    srf->diameter = -1.0;
  
  if (num_srfs == 0)
    {
      ProArrayObjectAdd ((ProArray*)&data->srfs_array,
			 PRO_VALUE_UNUSED, 1, srf);
      data->num_rev_srfs++;
      
    }
  else
    {
      for (i = 0; i < num_srfs; i++)
	{
	  current_srf = data->srfs_array[i].rev_srf;
	  current_diam = data->srfs_array[i].diameter;
	  
	  /* If its bigger than this surface, add it to the array just
	     before */
	  if (srf->diameter > current_diam)
	    {
	      ProArrayObjectAdd ((ProArray*)&data->srfs_array,
				 i, 1, srf);
	      data->num_rev_srfs++;
	      break;
	    }				
	  
	  /* If its smaller than all other surfaces, add it to the end 
	     of the array */
	  if (i == num_srfs - 1)
	    {
	      ProArrayObjectAdd ((ProArray*)&data->srfs_array,
				 PRO_VALUE_UNUSED, 1, srf);
	      data->num_rev_srfs++;
	    }
	}
    }
  
  return PRO_TK_NO_ERROR; 
}


/*====================================================*\
  Function : AutoaxisCollectRevSurfaces()
  Purpose  : Action function for ProSolidSurfaceVisit
             Collects revolved surfaces
\*====================================================*/
ProError AutoaxisCollectRevSurfaces ( ProSurface p_surface,
                                        ProError status,
                                        ProAppData app_data )
{
  ProError err;
  int  srf_id;
  Auto_srf_data srf;
  Auto_srfs_data *data;

  data = (Auto_srfs_data *)app_data;

  if ( status == PRO_TK_NO_ERROR )
  {
    err = PRO_TK_NO_ERROR ;
    srf_id = 0;
    err = ProSurfaceIdGet ( p_surface, &srf_id);
    if ( err == PRO_TK_NO_ERROR )
    {
      srf.axis_id = PRO_VALUE_UNUSED ;
      err = ProSurfaceInit ( data->owner, srf_id, &srf.rev_srf);
      if ( err == PRO_TK_NO_ERROR )
      {		  
	err = AutoaxisAddSortedSurface (data, &srf);
      }
    }
  }
  return status; 
}

/*====================================================*\
  Function : AutoaxisCollectBodyRevSurfaces()
  Purpose  : Action function for ProSolidBodySurfaceVisit
             Collects revolved surfaces on body
\*====================================================*/
ProError AutoaxisCollectBodyRevSurfaces ( ProSurface p_surface,
                                        ProError status,
                                        ProAppData app_data )
{  
 
  if ( PRO_TK_NO_ERROR == AutoaxisSurfaceFilter(p_surface, app_data) )
  	status = AutoaxisCollectRevSurfaces (p_surface, status, app_data);  
  
  return status; 
}


/*====================================================*\
  Function : AutoaxisCollectQuiltSurfaces()
  Purpose  : Action function for ProSolidQuiltVisit
             Collects the rev surfaces in the model.
\*====================================================*/
ProError AutoaxisCollectQuiltSurfaces ( ProQuilt p_quilt,
                                        ProError status,
                                        ProAppData app_data )
{
  ProError err;

  if ( status == PRO_TK_NO_ERROR )
  {
    err = ProQuiltSurfaceVisit( p_quilt,AutoaxisCollectRevSurfaces,
                                AutoaxisSurfaceFilter,app_data );
  }
  return err;
}
/*====================================================*\
  Function : AutoaxisCollectAxes()
  Purpose  : Action function for ProSolidAxisVisit
             Collects the axes in the model.
\*====================================================*/

ProError AutoaxisCollectAxes ( ProAxis  axis,
                               ProError status,
                               ProAppData app_data )
{
  ProError err;
  Auto_srfs_data *data;
  Axis_p12 pnts;
  ProGeomitemdata *a_gitem_data;

  a_gitem_data = NULL;
  data = ( Auto_srfs_data * ) app_data;

  err = ProAxisDataGet(axis, &a_gitem_data);
  if ( err == PRO_TK_NO_ERROR && a_gitem_data != NULL )
  {
    err = ProLinedataGet( a_gitem_data->data.p_curve_data,
			  pnts.p1, pnts.p2);
  }

  if ( err == PRO_TK_NO_ERROR  )
  {
    err = ProArrayObjectAdd ((ProArray*)&data->axes_pnts,
                           PRO_VALUE_UNUSED, 1, &pnts);
    if ( err == PRO_TK_NO_ERROR )
    {
      data->num_axes++;
    }
  }

  if( a_gitem_data != NULL )
  {
     ProGeomitemdataFree( &a_gitem_data );
  }
  return status;
}

/*====================================================*\
  Function : AutoaxisElementAdd()
  Purpose  : Adds elem to parent
\*====================================================*/
ProError AutoaxisElementAdd( ProElemId id, ProElement *parent,
      ProValueData *elem_data, ProElement *elem )
{
   ProValue value ;
   ProError status = PRO_TK_NOT_VALID ;

   if ( elem_data == NULL || elem == NULL || parent == NULL )
      return PRO_TK_BAD_INPUTS ;

   status =  ProElementAlloc(id, elem) ;
   if ( status != PRO_TK_NO_ERROR )
       return status;

   status = ProValueAlloc (&value) ;
   if ( status == PRO_TK_NO_ERROR )
       status = ProValueDataSet (value, elem_data) ;
       if ( status == PRO_TK_NO_ERROR )
          status = ProElementValueSet (*elem, value) ;

   if ( elem_data != NULL && status != PRO_TK_NO_ERROR )
      return status;

   status = ProElemtreeElementAdd( *parent, NULL, *elem ) ;
   return status;
}

/*====================================================*\
  Function : AutoaxisGeomVisitAction()
  Purpose  : Collects end points of an axis
\*====================================================*/
ProError AutoaxisGeomVisitAction ( ProGeomitem *p_geomitem,
                                   ProError  err,
                                   ProAppData app_data)   
{
  ProError status;
  Auto_srfs_data *data;
  Axis_p12 pnts;
  ProGeomitemdata *a_gitem_data;
  ProAxis axis;
   
  if ( app_data == NULL )
     return PRO_TK_BAD_INPUTS;

  status = PRO_TK_NO_ERROR;
  a_gitem_data = NULL;
  data = (Auto_srfs_data*) app_data;

  if ( status == PRO_TK_NO_ERROR )
  status = ProGeomitemToAxis(p_geomitem, &axis);

  if ( status == PRO_TK_NO_ERROR )
  status = ProAxisDataGet(axis, &a_gitem_data);
  if ( status == PRO_TK_NO_ERROR && a_gitem_data != NULL )
  {
    status = ProLinedataGet( a_gitem_data->data.p_curve_data, 
			     pnts.p1, pnts.p2);
  }

  if ( status == PRO_TK_NO_ERROR  )
  {
    status = ProArrayObjectAdd ((ProArray*)&data->axes_pnts,
                           PRO_VALUE_UNUSED, 1, &pnts);
    if ( status == PRO_TK_NO_ERROR )
    {
      data->axes_created++ ;
    }
  }

  if( a_gitem_data != NULL )
  {
     ProGeomitemdataFree( &a_gitem_data );
  }
  return err;
}
/*====================================================*\
  Function : AutoaxisSurfaceAxisCreate()
  Purpose  : Creates axis for given revolve surface
\*====================================================*/
ProError AutoaxisSurfaceAxisCreate( Auto_srfs_data *srfs, 
				    ProSurface surface, int* axis_id)
{
  ProValueData value_data;
  ProElement root_tree, feat_type;
  ProElement cnstrnts, cnstrnt;
  ProElement cnstrnt_type, cnstrnt_ref;
  ProElemId  elemid;
  
  ProError status;
  ProFeature feature;
  ProFeatureCreateOptions opts[1];
  ProModelitem modelitem;
  ProSelection  mdl_sel;

  ProErrorlist        errors;
  ProGeomitem	      rev_srf;
  ProSelection	      rev_srf_sel;
  int	              srf_id;
  ProMdl              model;

  if ( srfs == NULL || surface == NULL )
     return PRO_TK_BAD_INPUTS;

  status = PRO_TK_NO_ERROR;
  srf_id = -1;
  mdl_sel = NULL;
  rev_srf_sel = NULL;
  root_tree = NULL;
  model = srfs->owner ;

  status = ProSurfaceIdGet (surface, &srf_id);
  if ( status == PRO_TK_NO_ERROR && srf_id )
  {
    rev_srf.owner = model ;
    rev_srf.id = srf_id ;
    rev_srf.type = PRO_SURFACE ;
    status = ProSelectionAlloc(NULL, &rev_srf, &rev_srf_sel);
    if ( status != PRO_TK_NO_ERROR )
       return status;
  }

/*---------------------------------------------------------------------------*\
  Prepare element tree for Axis Feature 
\*---------------------------------------------------------------------------*/
  status = ProElementAlloc(PRO_E_FEATURE_TREE, &root_tree);

  elemid = PRO_E_FEATURE_TYPE ;
  value_data.type = PRO_VALUE_TYPE_INT;
  value_data.v.i = PRO_FEAT_DATUM_AXIS ;
  status = AutoaxisElementAdd(elemid, &root_tree,&value_data, 
			      &feat_type);

  elemid = PRO_E_DTMAXIS_CONSTRAINTS ;
  status = ProElementAlloc(elemid, &cnstrnts );
  status = ProElemtreeElementAdd(root_tree, NULL, cnstrnts );

  elemid = PRO_E_DTMAXIS_CONSTRAINT ;
  status = ProElementAlloc(elemid, &cnstrnt );
  status = ProElemtreeElementAdd(cnstrnts, NULL, cnstrnt );

  elemid = PRO_E_DTMAXIS_CONSTR_TYPE ;
  value_data.type = PRO_VALUE_TYPE_INT;
  value_data.v.i =  PRO_DTMAXIS_CONSTR_TYPE_THRU ;
  status = AutoaxisElementAdd(elemid, &cnstrnt,&value_data, 
			      &cnstrnt_type);

  if ( rev_srf_sel != NULL )
  {
     elemid = PRO_E_DTMAXIS_CONSTR_REF ;
     value_data.type = PRO_VALUE_TYPE_SELECTION;
     value_data.v.r =  rev_srf_sel ;
     status = AutoaxisElementAdd(elemid, &cnstrnt,&value_data, 
				 &cnstrnt_ref);
  }

  status = ProMdlToModelitem(model, &modelitem);
  if ( status == PRO_TK_NO_ERROR )
  status = ProSelectionAlloc(NULL, &modelitem, &mdl_sel);
  opts[0] = PRO_FEAT_CR_NO_OPTS;

  if ( status == PRO_TK_NO_ERROR )
  {
    status = ProFeatureCreate(mdl_sel, root_tree, opts, 1, &feature, 
			      &errors);
  }
  
  if ( status == PRO_TK_NO_ERROR )
  { 
	*axis_id = feature.id;
    /* Collect the axis end points. 
       This is to avoid visiting it
       again while creating another axis.
    */
    ProFeatureGeomitemVisit( &feature, PRO_AXIS,
                             AutoaxisGeomVisitAction,
                              NULL,(ProAppData)srfs);
  } 
  
  if( rev_srf_sel != NULL )
    ProSelectionFree(&rev_srf_sel);

  if( rev_srf_sel != NULL )
    ProSelectionFree(&mdl_sel);

  if( root_tree != NULL )
    ProElementFree( &root_tree);

  return status;
}

/*====================================================*\
  Function : AutoaxisCheckAxis()
  Purpose  : Checks existance of any axis related to
             given surface
\*====================================================*/
ProError AutoaxisCheckAxis( ProSurface surface, Auto_srfs_data *srfs )
{
  ProError status;
  int i,total_axes;
  
  if ( surface == NULL || srfs == NULL )
     return PRO_TK_BAD_INPUTS;

  status = PRO_TK_E_NOT_FOUND ;
  total_axes = srfs->num_axes + srfs->axes_created ;

  for ( i = 0 ; i < total_axes ; i++ )
  {
    status = AutoaxisSurfaceCheckAxis ( surface, &srfs->axes_pnts[i] );
    if ( status == PRO_TK_E_FOUND )
       return status;
  }

  return PRO_TK_E_NOT_FOUND;
}
/*====================================================*\
  Function : AutoaxisAxesCreate()
  Purpose  : Creates axes for revolve surfaces
\*====================================================*/
ProError AutoaxisAxesCreate( Auto_srfs_data *surfaces)
{
  ProError status;
  int j, axis_id = PRO_VALUE_UNUSED;
 
  for ( j = 0; j <  surfaces->num_rev_srfs ; j++ )
  {
    if( surfaces->srfs_array[j].axis_id != PRO_VALUE_UNUSED && 
			surfaces->srfs_array[j].axis_id != AXIS_ALT_FOUND)
       continue;

    /* Any axis exists ? */
    status = AutoaxisCheckAxis ( surfaces->srfs_array[j].rev_srf,
                                 surfaces );
    if ( status == PRO_TK_E_FOUND )
    { 
      surfaces->srfs_array[j].axis_id = AXIS_ALT_FOUND;
      continue;
    }
       
    status = AutoaxisSurfaceAxisCreate( surfaces, 
                             surfaces->srfs_array[j].rev_srf, &axis_id);
    if ( status == PRO_TK_NO_ERROR )
    {
      surfaces->srfs_array[j].axis_id = axis_id;
    }
  }
  return status;
}

/*====================================================*\
  Function : AutoaxisAsmComponent()
  Purpose  : Action function for components
             This is to run AutoAxis on Asm Comps.
\*====================================================*/
ProError  AutoaxisAsmComponent ( ProFeature *p_feature, ProError status,
                                 ProAppData app_data )
{
  ProMdl p_mdl;

  status = ProAsmcompMdlGet ((ProAsmcomp *)p_feature, &p_mdl);
  if ( status == PRO_TK_NO_ERROR ) 
    status = AutoaxisMain(p_mdl);

  return status;
}

/*====================================================*\
  Function : AutoaxisAsmcompFilter()
  Purpose  : Filter function for components
\*====================================================*/
ProError AutoaxisAsmcompFilter ( ProFeature* p_feature,
				 ProAppData app_data )
{
  ProError    status;
  ProFeattype ftype;
  ProMdl p_mdl;
  ProMdlType p_type;

  status = ProFeatureTypeGet(p_feature, &ftype);
  if ( status == PRO_TK_NO_ERROR && ftype == PRO_FEAT_COMPONENT)
  {
    status = ProAsmcompMdlGet ((ProAsmcomp *)p_feature, &p_mdl);
    if ( status == PRO_TK_NO_ERROR ) 
    {
      status = ProMdlTypeGet (p_mdl, &p_type);
      if ( status == PRO_TK_NO_ERROR )
      {
        if (p_type == PRO_MDL_ASSEMBLY || p_type == PRO_MDL_PART)
        {
          return(PRO_TK_NO_ERROR);
        }
      }
    }
  }

  return PRO_TK_CONTINUE;
}

/*====================================================*\
  Function : AutoaxisGroupCreate
  Purpose  : Create group of axes that go to matching diameter holes.
\*====================================================*/
ProError AutoaxisGroupCreate (ProSolid owner, double target_diameter, 
			      int* feat_ids)
{
  int num_feats;
  ProCharName name_buff;
  ProName name;
  ProGroup group;
  
  ProArraySizeGet (feat_ids, &num_feats);

  if (target_diameter > 0.0)
    {
      ProTKSprintf (name_buff, "AXES_DIAM_%.1lf", target_diameter);
      ProStringToWstring (name, name_buff);
    }
  else
    {
      ProTKSprintf (name_buff, "AXES_NOT_CYLINDRICAL");
      ProStringToWstring (name, name_buff);
    }
  ProLocalGroupCreate (owner, feat_ids, num_feats, name, &group);
  
  return (PRO_TK_NO_ERROR);
}

#define D_EPS 0.1

/*====================================================*\
  Function : AutoaxisAxesGroup
  Purpose  : Group axes by diameter.
\*====================================================*/
ProError  AutoaxisAxesGroup ( Auto_srfs_data* data )
{
  int i, num_feats, current_id;
  int num_srfs = data->num_rev_srfs;
  int* feat_ids = NULL;
  double target_diameter = -1.0;
  double current_diameter;

  for (i = 0; i < num_srfs; i++)
    {
      current_diameter = data->srfs_array [i].diameter;
      current_id = data->srfs_array[i].axis_id;
	
      /* For the first surface found */
      if (feat_ids == NULL)
	{
	  target_diameter = current_diameter;
	  /* if (target_diameter > 0.0) */
	  if (current_id > 0)
	    {
	      ProArrayAlloc (1, sizeof (int), 1, (ProArray*)&feat_ids);
	      feat_ids [0] = current_id;
	    }
	}
      else
	{	
	  /* If there's a true axis assigned to this surface */
	  if (current_id > 0)
	    {
	      /* If the diameter matches the target, add it and 
		 continue */
	      if (fabs (current_diameter - target_diameter) < D_EPS)
		{
		  ProArrayObjectAdd ((ProArray*)&feat_ids, 
				     PRO_VALUE_UNUSED, 1, &current_id);
		}
	      /* Otherwise, process the set of axes in the current group,
		 and start collecting a new one */
	      else if (i != num_srfs -1)
		{	
		  AutoaxisGroupCreate (data->owner, target_diameter, 
				       feat_ids);
		  ProArrayFree ((ProArray*)&feat_ids);
		  target_diameter = current_diameter;
	
		  ProArrayAlloc (1, sizeof (int), 1, (ProArray*)&feat_ids);
		  feat_ids [0] = current_id;
		}
	    }
	  /* Finish the last group for the last surface */
	  if (i == num_srfs - 1)
	    {
	      AutoaxisGroupCreate (data->owner, target_diameter, feat_ids);
	      ProArrayFree ((ProArray*)&feat_ids);
	    }
	}
    }
  return PRO_TK_NO_ERROR;
}

#undef D_EPS

/*====================================================*\
  Function : AutoaxisMain()
  Purpose  : Top level function to
             create Axes for Revolved surfaces 
\*====================================================*/
ProError AutoaxisMain( ProMdl imported_model )
{
  ProError status;
  Auto_srfs_data surfaces;
  ProMdlType mdl_type;
  ProSolidBody* bodies = NULL;
  int i = 0, body_num = 0;

  status = PRO_TK_NO_ERROR;

  status = ProMdlTypeGet ( imported_model, &mdl_type);
  if ( status != PRO_TK_NO_ERROR ||
       (mdl_type != PRO_MDL_PART && 
       mdl_type != PRO_MDL_ASSEMBLY &&
       mdl_type != PRO_MDL_MFG) )
  return PRO_TK_BAD_INPUTS;

  if ( status == PRO_TK_NO_ERROR )
  {
    if ( mdl_type != PRO_MDL_PART )
    {
    /* In case of assemblies visit all components recursively */
      status = ProSolidFeatVisit (( ProSolid )imported_model, 
                                    AutoaxisAsmComponent,
                                    AutoaxisAsmcompFilter, NULL ) ;
    }
  }
  surfaces.total_srfs = 0 ;
  surfaces.num_rev_srfs = 0 ;
  surfaces.srfs_array = NULL ;
  surfaces.num_axes = 0 ;
  surfaces.axes_pnts = NULL ;
  surfaces.axes_created = 0 ;
  surfaces.owner = imported_model ;

/* Collect all Axes(End points) in the Model */
  status = ProArrayAlloc (0, sizeof (Axis_p12), 1, 
        (ProArray*)&surfaces.axes_pnts);
  if ( status == PRO_TK_NO_ERROR )
  {
    status = ProSolidAxisVisit( (ProSolid) imported_model,
                               AutoaxisCollectAxes,
			       NULL, ( ProAppData ) &surfaces );
  }

/* Collect all revolve type surfaces in the Model */
  status = ProArrayAlloc (0, sizeof (Auto_srf_data), 1, 
        (ProArray*)&surfaces.srfs_array);
  if ( status == PRO_TK_NO_ERROR )
  {
      status = ProSolidBodiesCollect((ProSolid)imported_model, &bodies);
	  
	  if (status == PRO_TK_NO_ERROR)
	  {
	     status = ProArraySizeGet(bodies, &body_num);
		 
		 for (i=0 ; i<body_num ; i++)
		 	 status = ProSolidBodySurfaceVisit((ProSolidBody*)&bodies[i], 
					AutoaxisCollectBodyRevSurfaces, 
					( ProAppData ) &surfaces );		    		 
	  }
  }

  status = ProSolidQuiltVisit( (ProSolid) imported_model,
                               AutoaxisCollectQuiltSurfaces,
                               NULL, ( ProAppData ) &surfaces );

  if ( surfaces.num_rev_srfs > 0 )
  {
    /* Create and group the axes */
    status = AutoaxisAxesCreate( &surfaces );
    status = AutoaxisAxesGroup ( &surfaces );
  }

  ProTKPrintf("\n");
  ProTKPrintf("\n**********************************************************");
  ProTKPrintf("\n******************* AutoAxis Summary *********************");
  ProTKPrintf("\n**********************************************************");
  ProTKPrintf("\nTotal Surfaces found = %d",surfaces.total_srfs);
  ProTKPrintf("\nRev Surfaces found = %d",surfaces.num_rev_srfs);
  ProTKPrintf("\nAxes found = %d",surfaces.num_axes);
  ProTKPrintf("\nAxes Created = %d",surfaces.axes_created);
  ProTKPrintf("\n**********************************************************");
  ProTKPrintf("\n**********************************************************");
  ProTKPrintf("\n");

  if ( surfaces.axes_pnts != NULL )
  {
    status = ProArrayFree( ( ProArray *)&surfaces.axes_pnts);
  }
  if ( surfaces.srfs_array != NULL )
  {
    status = ProArrayFree( ( ProArray *) &surfaces.srfs_array);
  }
  return PRO_TK_NO_ERROR;
}