/*****************************************************************************

   udo_occ_sample_udo.c

=========================================================================
        Copyright (c) 2004  UGS PLM Solutions and EDS Company
                   Unpublished - All rights reserved

=========================================================================
File Description

      Contains Unigraphics entry points for registering the UDO Occ 
      Sample Class and the UDO Non Occ Sample Class used by 
      udo_occ_sample_application.c

      Please see the uf_udobj chapter of the Open API Reference Guide for a 
      detailed description of this example.

      *** PLEASE NOTE ***
      The shared library created from this program should be placed in the
      udo directory specified by the UGII_SITE_DIR or the UGII_USER_DIR
      environment variable.

*****************************************************************************/

/* Include files */
#include <stdio.h>
#include <uf.h>
#include <uf_ui.h>
#include <uf_udobj.h>
#include <uf_modl.h>
#include <uf_vec.h>
#include <uf_mtx.h>
#include <uf_obj.h>
#include <math.h>
#include <uf_view.h>
#include <string.h>


/* The class id's for associating UDO's with two perpendicular/adjacent edges   */
static unsigned int     UDO_occ_sample_class;
static unsigned int     UDO_non_occ_sample_class;

/* structure used to hold the data that defines the placement of the circle */
typedef struct edge_data_s{
    tag_t horiz_edge; /* linear edge selected by the user */
    double horiz_point1[3]; /* end point of the horiz_edge,
                               this should be the same point as vert_point1*/
    double horiz_point2[3]; /* other end point of the horiz_edge */
    tag_t vert_edge; /* linear edge selected by the user,
                        must be adjacent and perpendicular to the horiz_edge */
    double vert_point1[3]; /* end point of the vert_edge,
                              this should be the same point as horiz_point1 */
    double vert_point2[3]; /* other end point of the vert_edge */
}edge_data_t,*edge_data_p_t;


/* UF_CALL is the standard User Function error handling mechanism
   that is recommended to anyone calling a user function. */
#define UF_CALL(X)(report_error( __FILE__, __LINE__, #X, (X)))

/* report_error works in conjunction with the UF_CALL macro */
static int report_error( char *file, int line, char *call, int irc )
{
    if(irc)
    {
        char err[133],
             msg[133];
        sprintf( msg, "*** ERROR code %d at line %d in %s:\n+++",
            irc, line, file );
        UF_get_fail_message( irc, err );

        UF_print_syslog( msg, FALSE );
        UF_print_syslog( err, FALSE );
        UF_print_syslog( "\n", FALSE );
        UF_print_syslog( call, FALSE );
        UF_print_syslog( ";\n", FALSE );

        if( !UF_UI_open_listing_window() )
        {
            UF_UI_write_listing_window( msg );
            UF_UI_write_listing_window( err );
            UF_UI_write_listing_window( "\n" );
            UF_UI_write_listing_window( call );
            UF_UI_write_listing_window( ";\n" );
        }
    }
    return( irc );
}


/****************************************************************************
 *  create_sample_classes
 *
 *  DESCRIPTION
 *      Creates the occurenceable and nonoccurrenceable sample classes, 
 *      and registers the callback functions for display, selection, update, 
 *      info, delete, and is_occurenceable.
 *
 ****************************************************************************/
static void create_sample_classes( void );

/********************************************************************
*  is_occurrenceable_func
*
*  DESCRIPTION
*      Given the tag of a UDO determine if it should be occurrenceable.
*
*  INPUT
*      udo_tag
*          Tag of the UDO to determine occurrenceablity of.
*
*  OUPUT
*      is_occurrenceable
*          TRUE - The UDO will be displayed in an assembly.
*          FALSE - The UDO will only be displayed in an assembly if
*                  the UDO was created in the current displayed part.
********************************************************************/
static void is_occurrenceable_func( tag_t udo_tag, logical *is_occurrenceable );

/********************************************************************
*  update_sample_cb
*
*  DESCRIPTION
*      Given the tag of a UDO and the link that caused the UDO to
*      go through update, define what it means to update this UDO.
*
*      This function is called when one of the edges linked to
*      a UDO go through update.
*
*  INPUT
*      udo_tag
*          Tag of the UDO to update
*      link
*          The link containing the edge that has just gone through update.
*
*  OUPUT
*      The UDO is updated if necessary.
********************************************************************/
static void update_sample_cb( tag_t udo_tag, UF_UDOBJ_link_p_t link );

/********************************************************************
*  display_sample_cb
*
*  DESCRIPTION
*      The display callback makes calls to the display primitives
*      which are used for attention point, fit, display, and selection.
*      Therefore this same callback can be used in place of all
*      4 callbacks.
*
*      This UDO is displayed as a circle centered on the rectangle
*      defined by two linear, adjacent, and parpendicular edges.
*      The center point of the circle is also displayed.
*
*      Assume you have a face that looks like the following:
*
*        V  E
*        e  d
*        r  g  +-----------------------.
*        t  e  |                      /
*        i  |  |                     /
*        c  '->|                    /
*        a     +-------------------+
*        l                       ^
*                               /|\
*               Horizontal Edge--'
*
*      To construct the center of the circle, construct a line from
*      the midpoint of the horizontal edge that runs parallel to the
*      vertical edge.  Next construct a line through the midpoint of
*      the vertical edge that runs parallel to the horizontal edge.
*      The center of the circle is defined by the intersection of
*      the two lines.  The diameter of the circle is the length
*      of the shorter edge.  In this example the diameter is the
*      length of the vertical edge.
*
*                    midpoint line 1
*        V  E            :
*        e  d            :
*        r  g  +--------.-.------------.
*        t  e  |      /  :  \         /
*        i  |  +.....|...+...|......./....... midpoint line 2
*        c  '->|      \  :  /       /
*        a     +--------'-'--------+
*        l               :       ^
*                               /|\
*               Horizontal Edge--'
*
*
*      The finished UDO should look like:
*
*        V  E
*        e  d
*        r  g  +--------.-.------------.
*        t  e  |      /     \         /
*        i  |  |     |   +   |       /
*        c  '->|      \     /       /
*        a     +--------'-'--------+
*        l                       ^
*                               /|\
*               Horizontal Edge--'
*
*  INPUT
*      udo_tag
*          Tag of the udo to display.
*      context
*          Used as input for display primitive functions.
*
********************************************************************/
static void display_sample_cb( tag_t udo_tag, void *context );

/********************************************************************
*  info_sample_cb
*
*  DESCRIPTION
*      This function is called interactively when selecting
*      Information->Object from the UG Menubar and then selecting a
*      UDO.
*
*      This function displays the following information to the
*      UI Listing window (Text shown in []'s are replaced by real values
*      in the actual listing window):
*
*      UDO Sample [Tag of UDO] is defined by edge [Tag of H.Edge] and edge [Tag of V.Edge]
*      The radius is [Radisu] and the origin in absolute coordinates is ([X-Coord], [Y-Coord], [Z-Coord])
*      The circle transform matrix is:
*      ([X1], [Y1], [Z1])
*      ([X2], [Y2], [Z2])
*      ([X3], [Y3], [Z3])
*      The circle origin in circle space coordinates is ([X-Coord], [Y-Coord], [Z-Coord])
*
*  INPUT
*      udo_tag
*          Tag of UDO you wish to inquire.  This tag is used to get
*          the linked edges, which in turn is used to generate the
*          rest of the data.
********************************************************************/
static void info_sample_cb( tag_t udo_tag );

/********************************************************************
*  find_arc_csys_and_origin
*
*  DESCRIPTION
*      Find the csys matrix, origin, and radius that define a circle
*      centered on the rectangle defined by two
*      linear/adjacent/perpendicular edges.
*
*  INPUT
*      edge_data
*          horiz_edge
*              The tag of the horizontal linear edge
*          horiz_point1
*              The common endpoint between the horizontal
*              and vertical edges.
*          horiz_point2
*              The other horizontal endpoint.
*          vert_edge
*              The tag of the vertical linear edge
*          vert_point1
*              The common endpoint between the horizontal
*              and vertical edges.
*          vert_point2
*              The other vertical endpoint.
*
*  OUTPUT
*      matrix
*          Matrix that defines the coordinate system of the circle.
*      origin
*          The center of the circle in absolute coordinates.
*      radius
*          The radius of the circle.
********************************************************************/
static void find_arc_csys_and_origin( edge_data_p_t edge_data, double matrix[9], double origin[3], double *radius );

/********************************************************************
*  find_common_point
*
*  DESCRIPTION
*      Given the edge data, check to see if the two edges have a
*      common endpoint.  If the two edges have a common
*      endpoint, return TRUE and set horiz_point1 and vert_point1
*      to the common endpoint.  Set horiz_point2 to the other
*      endpoint of the horizontal edge, and vert_point2 to the
*      other endpoint of the vertical edge.
*
*  INPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              One of the endpoints of the horizontal edge.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The potential adjacent linear edge to the horizontal edge.
*          vert_point1
*              One of the endpoints of the vertical edge.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  OUTPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              The common endpoint, if one exists, of the horizontal
*              and vertical edges, otherwise this is unchanged.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The potential adjacent linear edge to the horizontal edge.
*          vert_point1
*              The common endpoint, if one exists, of the horizontal
*              and vertical edges, otherwise this is unchanged.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  RETURNS
*      TRUE - There was a common endpoint.
*      FALSE - The edges do not have a common endpoint.
********************************************************************/
static logical find_common_point( edge_data_p_t data );

/********************************************************************
*  swap_edge_data_points
*
*  DESCRIPTION
*      Given the edge data, if swap_horiz_pts is true, swap
*      the two horizontal points, and if swap_vert_pts is true,
*      swap the two vertical points.
*
*  INPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              One of the endpoints of the horizontal edge.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The vertical linear edge.
*          vert_point1
*              One of the endpoints of the vertical edge.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  OUTPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              If swap_horiz_pts was true, then (input)horiz_point2
*                  otherwise this variable is unchanged.
*          horiz_point2
*              If swap_horiz_pts was true, then (input)horiz_point1
*                  otherwise this variable is unchanged.
*          vert_edge
*              The vertical linear edge.
*          vert_point1
*              If swap_vert_pts was true, then (input)horiz_point2
*                  otherwise this variable is unchanged.
*          vert_point2
*              If swap_vert_pts was true, then (input)horiz_point1
*                  otherwise this variable is unchanged.
*
********************************************************************/
static void swap_edge_data_points( edge_data_p_t data,
                                       logical swap_horiz_pts,
                                       logical swap_vert_pts );



/*****************************************************************************
**  Activation Methods
*****************************************************************************/
/*  Unigraphics Startup
**      This entry point activates the application at Unigraphics startup */
extern DllExport void ufsta( char *param, int *returnCode, int rlen )
{
    /* Initialize the API environment */
    UF_CALL( UF_initialize() );

    UDO_occ_sample_class = 0;
    UDO_non_occ_sample_class = 0;

    create_sample_classes();

    UF_CALL( UF_terminate() );
}


/****************************************************************************
 *  create_sample_class
 *
 *  DESCRIPTION
 *      Creates the sample class, and registers the callback functions for
 *      display, selection, update, info and delete.
 *
 ****************************************************************************/
static void create_sample_classes( void )
{
    /*  create a UDO class */
    if( !UF_CALL( UF_UDOBJ_create_class( "udo_occ_sample",
	            		                 "UDO Occ Sample",
                                         &UDO_occ_sample_class) ));
    {
        /* Register the display callbacks */
        UF_CALL( UF_UDOBJ_register_attn_pt_cb( UDO_occ_sample_class, display_sample_cb ) );
        UF_CALL( UF_UDOBJ_register_fit_cb( UDO_occ_sample_class, display_sample_cb ) );
        UF_CALL( UF_UDOBJ_register_display_cb( UDO_occ_sample_class, display_sample_cb ) );

        /* Make the class selectable */
        UF_CALL( UF_UI_add_to_class_sel( UDO_occ_sample_class  ) );
        UF_CALL( UF_UDOBJ_set_query_class_id( UDO_occ_sample_class, UF_UDOBJ_ALLOW_QUERY_CLASS_ID ) );

        /* Register the selection callback */
        UF_CALL( UF_UDOBJ_register_select_cb( UDO_occ_sample_class, display_sample_cb ) );
        /* Register the update callback */
        UF_CALL( UF_UDOBJ_register_update_cb( UDO_occ_sample_class, update_sample_cb) );
        /* Register the info callback */
        UF_CALL( UF_UDOBJ_register_info_obj_cb( UDO_occ_sample_class, info_sample_cb) );
        UF_CALL( UF_UDOBJ_register_is_occurrenceable_cb ( UDO_occ_sample_class, is_occurrenceable_func ));
    }
    if( !UF_CALL( UF_UDOBJ_create_class( "udo_non_occ_sample",
	            		                 "UDO Non-Occ Sample",
                                         &UDO_non_occ_sample_class) ));
    {
        /* Register the display callbacks */
        UF_CALL( UF_UDOBJ_register_attn_pt_cb( UDO_non_occ_sample_class, display_sample_cb ) );
        UF_CALL( UF_UDOBJ_register_fit_cb( UDO_non_occ_sample_class, display_sample_cb ) );
        UF_CALL( UF_UDOBJ_register_display_cb( UDO_non_occ_sample_class, display_sample_cb ) );

        /* Make the class selectable */
        UF_CALL( UF_UI_add_to_class_sel( UDO_non_occ_sample_class  ) );
        UF_CALL( UF_UDOBJ_set_query_class_id( UDO_non_occ_sample_class, UF_UDOBJ_ALLOW_QUERY_CLASS_ID ) );

        /* Register the selection callback */
        UF_CALL( UF_UDOBJ_register_select_cb( UDO_non_occ_sample_class, display_sample_cb ) );
        /* Register the update callback */
        UF_CALL( UF_UDOBJ_register_update_cb( UDO_non_occ_sample_class, update_sample_cb) );
        /* Register the info callback */
        UF_CALL( UF_UDOBJ_register_info_obj_cb( UDO_non_occ_sample_class, info_sample_cb) );
        
        /* to make a class non-occurrenceable, you can register an is_occurrenceable_cb
           that always returns false, or you can register a NULL function as the 
           is_occurrenceable_cb, or you can avoid registering the is_occurrenceable_cb 
           altogether */
        UF_CALL( UF_UDOBJ_register_is_occurrenceable_cb ( UDO_non_occ_sample_class, NULL ));
    }
}

/********************************************************************
*  is_occurrenceable_func
*
*  DESCRIPTION
*      Given the tag of a UDO determine if it should be occurrenceable.
*
*  INPUT
*      udo_tag
*          Tag of the UDO to determine occurrenceablity of.
*
*  OUPUT
*      is_occurrenceable
*          TRUE - The UDO will be displayed in an assembly.
*          FALSE - The UDO will only be displayed in an assembly if
*                  the UDO was created in the current displayed part.
********************************************************************/
static void is_occurrenceable_func( tag_t udo_tag, logical *is_occurrenceable )
{
    *is_occurrenceable = TRUE;
}
/********************************************************************
*  update_sample_cb
*
*  DESCRIPTION
*      Given the tag of a UDO and the link that caused the UDO to
*      go through update, define what it means to update this UDO.
*
*      This function is called when one of the edges linked to
*      a UDO go through update.
*
*  INPUT
*      udo_tag
*          Tag of the UDO to update
*      link
*          The link containing the edge that has just gone through update.
*
*  OUPUT
*      The UDO is updated if necessary.
********************************************************************/
static void update_sample_cb( tag_t udo_tag, UF_UDOBJ_link_p_t link )
{
    tag_t assoc_tag = NULL_TAG;

    /* If the link is null, there is nothing to do */
    if( link == NULL)
    {
        return;
    }

    UF_CALL( UF_initialize() );
    assoc_tag = link->assoc_ug_tag;
    /* Make sure that the associated tag still exists */
    if( assoc_tag == NULL_TAG )
    {
        UF_CALL( UF_terminate() );
        return;
    }
    /* Make sure that the associated tag is still alive */
    if( UF_OBJ_ask_status( assoc_tag ) != UF_OBJ_ALIVE )
    {
        /* The object is no longer alive, this can happen when
           an edge is blended away, the edge still exists, but
           is not an active part of the model */
        UF_CALL( UF_terminate() );
        return;
    }

    /* Redisplay the item with the updated edge */
    UF_CALL( UF_DISP_add_item_to_display( udo_tag ));
    UF_CALL( UF_terminate() );
}
/********************************************************************
*  display_sample_cb
*
*  DESCRIPTION
*      The display callback makes calls to the display primitives
*      which are used for attention point, fit, display, and selection.
*      Therefore this same callback can be used in place of all
*      4 callbacks.
*
*      This UDO is displayed as a circle centered on the rectangle
*      defined by two linear, adjacent, and parpendicular edges.
*      The center point of the circle is also displayed.
*
*      Assume you have a face that looks like the following:
*
*        V  E
*        e  d
*        r  g  +-----------------------.
*        t  e  |                      /
*        i  |  |                     /
*        c  '->|                    /
*        a     +-------------------+
*        l                       ^
*                               /|\
*               Horizontal Edge--'
*
*      To construct the center of the circle, construct a line from
*      the midpoint of the horizontal edge that runs parallel to the
*      vertical edge.  Next construct a line through the midpoint of
*      the vertical edge that runs parallel to the horizontal edge.
*      The center of the circle is defined by the intersection of
*      the two lines.  The diameter of the circle is the length
*      of the shorter edge.  In this example the diameter is the
*      length of the vertical edge.
*
*                    midpoint line 1
*        V  E            :
*        e  d            :
*        r  g  +--------.-.------------.
*        t  e  |      /  :  \         /
*        i  |  +.....|...+...|......./....... midpoint line 2
*        c  '->|      \  :  /       /
*        a     +--------'-'--------+
*        l               :       ^
*                               /|\
*               Horizontal Edge--'
*
*
*      The finished UDO should look like:
*
*        V  E
*        e  d
*        r  g  +--------.-.------------.
*        t  e  |      /     \         /
*        i  |  |     |   +   |       /
*        c  '->|      \     /       /
*        a     +--------'-'--------+
*        l                       ^
*                               /|\
*               Horizontal Edge--'
*
*  INPUT
*      udo_tag
*          Tag of the udo to display.
*      context
*          Used as input for display primitive functions.
*
********************************************************************/
static void display_sample_cb( tag_t udo_tag, void *context )
{
    UF_UDOBJ_all_data_t all_data;
    edge_data_t edge_data;
    tag_t edge_1 = NULL_TAG;
    tag_t edge_2 = NULL_TAG;
    int vertex_count = 0;
    double origin[3] = {0.0, 0.0, 0.0};
    double matrix[9] = {0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0};
    double radius = 0.0;
    char *class_name = NULL;
    
    UF_CALL( UF_initialize() );

    /* find the data that defines this udo */
    UF_CALL( UF_UDOBJ_ask_udo_data ( udo_tag, &all_data ) );

    /* note that the links are returned in random order, but for this
       example, the order of the two edges is irrelevant. */
    edge_data.horiz_edge = all_data.link_defs[0].assoc_ug_tag;
    edge_data.vert_edge = all_data.link_defs[1].assoc_ug_tag;

    /* If either edge is not alive, do not display this udo */
    if( (UF_OBJ_ask_status( edge_data.horiz_edge ) != UF_OBJ_ALIVE) ||
        (UF_OBJ_ask_status( edge_data.vert_edge ) != UF_OBJ_ALIVE))
    {
        UF_CALL( UF_terminate() );
        return;
    }

    /* fill in the edge_data structure */
    UF_CALL( UF_MODL_ask_edge_verts( edge_data.horiz_edge, edge_data.horiz_point1,
                                     edge_data.horiz_point2, &vertex_count ) );
    UF_CALL( UF_MODL_ask_edge_verts( edge_data.vert_edge, edge_data.vert_point1,
        edge_data.vert_point2, &vertex_count ) );
    find_common_point( &edge_data );

    /* find the csys matrix, origin and radius that define the circle */
    find_arc_csys_and_origin( &edge_data, matrix, origin, &radius);

    /* display a point at the origin of the circle in absolute coordinates */
    UF_CALL( UF_DISP_display_points( origin, 1, UF_DISP_POINT, context ));

    /* display text next to the point that tells the user the class name of this udo */
    UF_CALL( UF_UDOBJ_ask_udo_class_name( udo_tag, &class_name ) );
    UF_CALL( UF_DISP_display_text( class_name, origin, UF_DISP_TOPLEFT, context ) );
    UF_free( class_name );


    /* find the origin of the circle in terms of the arc space defined by matrix */
    UF_MTX3_vec_multiply( origin, matrix, origin );

    /* display a circle centered with respect to the two edges */
    UF_CALL( UF_DISP_display_circle( matrix, origin, radius, FALSE, context ));
    
    /* free the udo data */
    UF_CALL( UF_UDOBJ_free_udo_data( &all_data ) );
    UF_CALL( UF_terminate() );
}
/********************************************************************
*  info_sample_cb
*
*  DESCRIPTION
*      This function is called interactively when selecting
*      Information->Object from the UG Menubar and then selecting a
*      UDO.
*
*      This function displays the following information to the
*      UI Listing window (Text shown in []'s are replaced by real values
*      in the actual listing window):
*
*      UDO Sample [Tag of UDO] is defined by edge [Tag of H.Edge] and edge [Tag of V.Edge]
*      The radius is [Radisu] and the origin in absolute coordinates is ([X-Coord], [Y-Coord], [Z-Coord])
*      The circle transform matrix is:
*      ([X1], [Y1], [Z1])
*      ([X2], [Y2], [Z2])
*      ([X3], [Y3], [Z3])
*      The circle origin in circle space coordinates is ([X-Coord], [Y-Coord], [Z-Coord])
*
*  INPUT
*      udo_tag
*          Tag of UDO you wish to inquire.  This tag is used to get
*          the linked edges, which in turn is used to generate the
*          rest of the data.
********************************************************************/
static void info_sample_cb( tag_t udo_tag )
{
    UF_UDOBJ_all_data_t all_data;
    edge_data_t edge_data;
    logical alive = TRUE;
    tag_t edge_1 = NULL_TAG;
    tag_t edge_2 = NULL_TAG;
    int vertex_count = 0;
    double origin[3] = {0.0, 0.0, 0.0};
    double matrix[9] = {0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0,
                        0.0, 0.0, 0.0};
    double radius = 0.0;
    char info_msg[133];

    UF_CALL( UF_initialize() );

    if( !UF_UI_open_listing_window() )
    {
        UF_CALL( UF_UDOBJ_ask_udo_data ( udo_tag, &all_data ) );

        /* note that the links are returned in random order, but for this
           example, the two edges are interchangeable. */
        edge_data.horiz_edge = all_data.link_defs[0].assoc_ug_tag;
        edge_data.vert_edge = all_data.link_defs[1].assoc_ug_tag;

        /* output the tags of the edges */
        sprintf( info_msg, "UDO Sample %u is defined by edge %u and edge %u\n",
                 udo_tag, edge_data.horiz_edge, edge_data.vert_edge );
        UF_UI_write_listing_window( info_msg );

        /* If either, or both, edges are not alive, inform the user */
        if( UF_OBJ_ask_status( edge_data.horiz_edge ) != UF_OBJ_ALIVE )
        {
            alive = FALSE;
            sprintf( info_msg, "The edge %u is not alive, therefore this UDO is not displayed.\n",
                 edge_data.horiz_edge );
            UF_UI_write_listing_window( info_msg );
        }
        if( UF_OBJ_ask_status( edge_data.vert_edge ) != UF_OBJ_ALIVE )
        {
            alive = FALSE;
            sprintf( info_msg, "The edge %u is not alive, therefore this UDO is not displayed.\n",
                 edge_data.vert_edge );
            UF_UI_write_listing_window( info_msg );
        }
        /* if an edge is not alive, you can't ask for
           its endpoints, so we should return */
        if( alive == FALSE )
        {
            UF_CALL( UF_terminate() );
            return;
        }

        /* fill in the edge data structure */
        UF_CALL( UF_MODL_ask_edge_verts( edge_data.horiz_edge, edge_data.horiz_point1,
                                         edge_data.horiz_point2, &vertex_count ) );
        UF_CALL( UF_MODL_ask_edge_verts( edge_data.vert_edge, edge_data.vert_point1,
            edge_data.vert_point2, &vertex_count ) );
        find_common_point( &edge_data );

        /* find the csys matrix, origin and radius that define the circle */
        find_arc_csys_and_origin( &edge_data, matrix, origin, &radius);

        /* output the radius and origin of the circle */
        sprintf( info_msg, "The radius is %lf and the origin in absolute coordinates is (%lf, %lf, %lf)\n",
                 radius, origin[0], origin[1], origin[2] );
        UF_UI_write_listing_window( info_msg );

        /* output the matrix that defined the csys of the circle */
        sprintf( info_msg, "The circle transform matrix is:\n" );
        UF_UI_write_listing_window( info_msg );

        sprintf( info_msg, "(%lf, %lf, %lf)\n", matrix[0], matrix[3], matrix[6] );
        UF_UI_write_listing_window( info_msg );
        sprintf( info_msg, "(%lf, %lf, %lf)\n", matrix[1], matrix[4], matrix[7] );
        UF_UI_write_listing_window( info_msg );
        sprintf( info_msg, "(%lf, %lf, %lf)\n", matrix[2], matrix[5], matrix[8] );
        UF_UI_write_listing_window( info_msg );

        /* find the origin of the circle in terms of the arc space defined by matrix */
        UF_MTX3_vec_multiply( origin, matrix, origin );

        /* output the origin of the circle in circle space coordinates */
        sprintf( info_msg, "The circle origin in circle space cooridnates is(%lf, %lf, %lf)\n",
            origin[0], origin[1], origin[2] );
        UF_UI_write_listing_window( info_msg );

        /* free the UDO data */
        UF_CALL( UF_UDOBJ_free_udo_data( &all_data ) );
    }
    UF_CALL( UF_terminate() );
}
/********************************************************************
*  find_arc_csys_and_origin
*
*  DESCRIPTION
*      Find the csys matrix, origin, and radius that define a circle
*      centered on the rectangle defined by two
*      linear/adjacent/perpendicular edges.
*
*  INPUT
*      edge_data
*          horiz_edge
*              The tag of the horizontal linear edge
*          horiz_point1
*              The common endpoint between the horizontal
*              and vertical edges.
*          horiz_point2
*              The other horizontal endpoint.
*          vert_edge
*              The tag of the vertical linear edge
*          vert_point1
*              The common endpoint between the horizontal
*              and vertical edges.
*          vert_point2
*              The other vertical endpoint.
*
*  OUTPUT
*      matrix
*          Matrix that defines the coordinate system of the circle.
*      origin
*          The center of the circle in absolute coordinates.
*      radius
*          The radius of the circle.
********************************************************************/
static void find_arc_csys_and_origin( edge_data_p_t edge_data,
                                      double matrix[9],
                                      double origin[3],
                                      double *radius )
{
    double diagonal_vec[3] = {0.0, 0.0, 0.0};
    double x_vec[3] = {0.0, 0.0, 0.0};
    double y_vec[3] = {0.0, 0.0, 0.0};
    double x_length = 0.0;
    double y_length = 0.0;

/*  Assume you have a face that looks like the following:

       vert_point2
               \
                *-----------------------.
                |                      /
                |                     /
                |                    /
                *-------------------+
              / |                    \
             /  |                   horiz_point2
 horiz_point1 & vert_point1


    Find the diagonal vector from vert_point2 to horiz_point2, then
    find the midpoint(M) of the line by scaling the vector by 50% and
    adding the scaled vector to vert_point2.  The midpoint defines
    the origin of the circle

       vert_point2
               \
                *-----------------------.
                | `  .                 /
                |       ` M.          /
                |              `  .  /
                *-------------------+
              / |                    \
             /  |                   horiz_point2
 horiz_point1 & vert_point1
*/
    UF_VEC3_sub( edge_data->horiz_point2, edge_data->vert_point2, diagonal_vec );
    UF_VEC3_scale( .5, diagonal_vec, diagonal_vec );
    UF_VEC3_add( edge_data->vert_point2, diagonal_vec, origin );


/* Find the x and y vectors for the circle csys, and then get the csys matrix

                ^ Y-Vector
               /|\
                |
                *-----------------------.
                | `  .                 /
                |       ` M.          /
                |              `  .  /
                *-------------------+-----> X-Vector
*/

    UF_VEC3_sub( edge_data->horiz_point2, edge_data->horiz_point1, x_vec );
    UF_VEC3_sub( edge_data->vert_point2, edge_data->vert_point1, y_vec );
    UF_CALL( UF_MTX3_initialize( x_vec, y_vec, matrix ) );

    /* set the raidus to half the length of the shortest edge */
    UF_VEC3_mag( x_vec, &x_length );
    UF_VEC3_mag( y_vec, &y_length );
    if( x_length < y_length )
    {
        *radius = x_length/2;
    }
    else
    {
        *radius = y_length/2;
    }
}

/********************************************************************
*  find_common_point
*
*  DESCRIPTION
*      Given the edge data, check to see if the two edges have a
*      common endpoint.  If the two edges have a common
*      endpoint, return TRUE and set horiz_point1 and vert_point1
*      to the common endpoint.  Set horiz_point2 to the other
*      endpoint of the horizontal edge, and vert_point2 to the
*      other endpoint of the vertical edge.
*
*  INPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              One of the endpoints of the horizontal edge.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The potential adjacent linear edge to the horizontal edge.
*          vert_point1
*              One of the endpoints of the vertical edge.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  OUTPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              The common endpoint, if one exists, of the horizontal
*              and vertical edges, otherwise this is unchanged.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The potential adjacent linear edge to the horizontal edge.
*          vert_point1
*              The common endpoint, if one exists, of the horizontal
*              and vertical edges, otherwise this is unchanged.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  RETURNS
*      TRUE - There was a common endpoint.
*      FALSE - The edges do not have a common endpoint.
********************************************************************/
static logical find_common_point( edge_data_p_t data )
{
    double tol = 0.00001;
    double distance = 0.0;

    /* check the distance between the the first horizontal endpoint
       and the first vertical endpoint. */
    UF_VEC3_distance( data->horiz_point1, data->vert_point1, &distance );

    if( distance > tol )
    {
        /* now check the distance between the first horizontal endpoint
           and the second vertical endpoint */
        UF_VEC3_distance( data->horiz_point1, data->vert_point2, &distance );
        if( distance <= tol )
        {
            /* If the first horizontal point is equal to the second
               vertical point, swap the two vertical endpoints. */
            swap_edge_data_points( data, FALSE, TRUE );
        }
    }
    if( distance > tol )
    {
        /* now check the distance between the second horizontal endpoint
           and the first vertical endpoint */
        UF_VEC3_distance( data->horiz_point2, data->vert_point1, &distance );
        if( distance <= tol )
        {
            /* If the first horizontal point is equal to the second
               vertical point, swap the two horizontal endpoints. */
            swap_edge_data_points( data, TRUE, FALSE );
        }
    }
    if( distance > tol )
    {
        /* now check the distance between the second horizontal endpoint
           and the second vertical endpoint */
        UF_VEC3_distance( data->horiz_point2, data->vert_point2, &distance );
        if( distance <= tol )
        {
            /* If the second horizontal point is equal to the second
               vertical point, swap the two horizontal endpoints and
               swap the two vertical endpoints. */
            swap_edge_data_points( data, TRUE, TRUE );
        }
    }
    if( distance <= tol )
    {
        /* a common endpoint was found */
        return( TRUE );
    }

    /* there were no common endpoints */
    return( FALSE );
}

/********************************************************************
*  swap_edge_data_points
*
*  DESCRIPTION
*      Given the edge data, if swap_horiz_pts is true, swap
*      the two horizontal points, and if swap_vert_pts is true,
*      swap the two vertical points.
*
*  INPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              One of the endpoints of the horizontal edge.
*          horiz_point2
*              The other endpoint of the horizontal edge.
*          vert_edge
*              The vertical linear edge.
*          vert_point1
*              One of the endpoints of the vertical edge.
*          vert_point2
*              The other endpoint of the vertical edge.
*
*  OUTPUT
*      data
*          horiz_edge
*              The horizontal linear edge.
*          horiz_point1
*              If swap_horiz_pts was true, then (input)horiz_point2
*                  otherwise this variable is unchanged.
*          horiz_point2
*              If swap_horiz_pts was true, then (input)horiz_point1
*                  otherwise this variable is unchanged.
*          vert_edge
*              The vertical linear edge.
*          vert_point1
*              If swap_vert_pts was true, then (input)horiz_point2
*                  otherwise this variable is unchanged.
*          vert_point2
*              If swap_vert_pts was true, then (input)horiz_point1
*                  otherwise this variable is unchanged.
*
********************************************************************/
static void swap_edge_data_points( edge_data_p_t data,
                                       logical swap_horiz_pts,
                                       logical swap_vert_pts )
{
    double temp_point[3] = {0.0, 0.0, 0.0};
    if( swap_horiz_pts )
    {
        temp_point[0] = data->horiz_point1[0];
        temp_point[1] = data->horiz_point1[1];
        temp_point[2] = data->horiz_point1[2];

        data->horiz_point1[0] = data->horiz_point2[0];
        data->horiz_point1[1] = data->horiz_point2[1];
        data->horiz_point1[2] = data->horiz_point2[2];

        data->horiz_point2[0] = temp_point[0];
        data->horiz_point2[1] = temp_point[1];
        data->horiz_point2[2] = temp_point[2];
    }
    if( swap_vert_pts )
    {
        temp_point[0] = data->vert_point1[0];
        temp_point[1] = data->vert_point1[1];
        temp_point[2] = data->vert_point1[2];

        data->vert_point1[0] = data->vert_point2[0];
        data->vert_point1[1] = data->vert_point2[1];
        data->vert_point1[2] = data->vert_point2[2];

        data->vert_point2[0] = temp_point[0];
        data->vert_point2[1] = temp_point[1];
        data->vert_point2[2] = temp_point[2];
    }
}


/*****************************************************************************
**  Utilities
*****************************************************************************/

/* Unload Handler
**     This function specifies when to unload your application from Unigraphics.
**     If your application registers a callback (from a MenuScript item or a
**     User Defined Object for example), this function MUST return
**     "UF_UNLOAD_UG_TERMINATE". */
extern int ufusr_ask_unload( void )
{
    return( UF_UNLOAD_UG_TERMINATE );
}