home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 13 / 13.iso / p / p024 / 16.img / AME3.LIB / ASM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-17  |  51.8 KB  |  1,730 lines

  1. /* Next available MSG number is 42 */
  2.  
  3. /************************************************************************
  4. Name: asm.c.  AME2_APLIB_SAMP_asm.c
  5.  
  6. Description: AME/API sample program for objects transformation.
  7.  
  8. Author: AME Group
  9.         Autodesk, Inc.
  10.  
  11.  
  12.  
  13.  
  14. Copyright (C) 1992 by Autodesk, Inc.
  15. **************************************************************************
  16. *                                                                        *
  17. *    Permission to use, copy, modify, and distribute this software       *
  18. *    for any purpose and without fee is hereby granted, provided         *
  19. *    that the above copyright notice appears in all copies and that      *
  20. *    both that copyright notice and this permission notice appear in     *
  21. *    all supporting documentation.                                       *
  22. *                                                                        *
  23. *    THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED        *
  24. *    WARRANTY.  ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR     *
  25. *    PURPOSE AND OF MERCHANTABILITY ARE HEREBY DISCLAIMED.               *
  26. *                                                                        *
  27. *                                                                        *
  28. *    This program allows the user to move and assemble objects           * 
  29. *    by specifying mating features such as alignment or contact.         *
  30. *    Motion coordinate icon is used as the visualization aid for         *
  31. *    the transformation.  Two commands "SOLALIGN" and "SOLCONTACT"       *
  32. *    are available in this implementation.                               *
  33. *                                                                        *
  34. *    "SOLCONTACT" allows the users to move a set of objects (component,  *
  35. *    subassembly or assembly) such that one face of the selected object  *
  36. *    is in contact but against with another face of the specified        *
  37. *    object.  The base point on the first (moving) selected face will    *
  38. *    move and match to that on the second (destination) selected face.   * 
  39. *    The reference point on the first selected face defines the feature  *
  40. *    axis (from base point to reference point) which will align to       *
  41. *    that on the second selected face.  In the case that the             *
  42. *    selected objects (cylinder for example) are axial symmetric, the    *
  43. *    reference point is determined automatically by the program using    *
  44. *    arbitrary axis algorithm.  Only AME objects are eligible for move   *
  45. *    and both moving and destination faces must be plane.                *
  46. *    The command syntax for "SOLCONTACT" is shown as follows:            *
  47. *                                                                        *
  48. *          Command: solcontact                                           *
  49. *          Select objects:  Pick objects                                 *
  50. *                                                                        *
  51. *          Select a face:  Pick a face                                   *
  52. *          Old base point <0,0,0>:  Pick a point                         *
  53. *          Old reference point <Axial Symmetry>: Pick a point or RETURN  *
  54. *                                                                        *
  55. *          Select a face:  Pick a face                                   *
  56. *          New base point <0,0,0>:  Pick a point                         *
  57. *          New reference point <Axial Symmetry>: Pick a point or RETURN  *
  58. *                                                                        *
  59. *          Ok to move the objects, No/<Yes>:  y                          * 
  60. *                                                                        * 
  61. *                                                                        * 
  62. *    "SOLALIGN" allows the users to move a set of objects (component,    *
  63. *    subassembly or assembly) such that old axis of the selected object  *
  64. *    is in alignment with the specified new axis.  The first (old)       *
  65. *    selected base and end points define the feature axis which will     *
  66. *    align to that of the second (destination) selected set.             * 
  67. *    The reference point together with feature axis define a feature     *
  68. *    plane which will lock the selected objects from free orientation    *
  69. *    about the selected axis.  In the case that the selected objects     *
  70. *    (cylinder for example) are axial symmetric, the reference point is  *
  71. *    determined automatically by the program using arbitrary axis        *
  72. *    algorithm.  Only AME objects are eligible for move.                 *
  73. *    The command syntax for "SOLALIGN" is shown as follows:              *
  74. *                                                                        *
  75. *          Command: solalign                                             *
  76. *          Select objects:  Pick objects                                 *
  77. *                                                                        *
  78. *          Old base point <0,0,0>:  Pick a point                         *
  79. *          Old end point <0,0,1>: Pick a point                           *
  80. *          Old reference point <Axial Symmetry>: Pick a point or RETURN  * 
  81. *                                                                        *  
  82. *          New base point <0,0,0>:  Pick a point                         *
  83. *          New end point <0,0,1>: Pick a point                           *
  84. *          New reference point <Axial Symmetry>: Pick a point or RETURN  * 
  85. *                                                                        *
  86. *          Ok to move the objects, No/<Yes>:  y                          * 
  87. *                                                                        *
  88. **************************************************************************
  89.  
  90.  
  91. Modification history:
  92.     Refer to the RCS section at the end of this file.
  93.  
  94. Bugs and restrictions on use:
  95.  
  96. Notes:
  97.  
  98. **************************************************************************/
  99.  
  100.  
  101. /*************************************************************************/
  102. /* Includes */
  103. /*************************************************************************/
  104.  
  105. #include <stdio.h>
  106. #include <string.h>
  107. #include <math.h>
  108. #include <adslib.h>
  109. #include <aplib.h>
  110.  
  111. /*************************************************************************/
  112. /* Defines */
  113. /*************************************************************************/
  114.  
  115. #define  PI         3.14159265358927
  116. #define  EPSILON    1.0e-8
  117. #define  PMAX       20
  118. #define  OFF        0
  119. #define  ON         1
  120. #define  ORIGIN     2
  121. #define  NO         0
  122. #define  YES        1
  123. #define  CANCEL    -1
  124. #define  YELLOW     2
  125. #define  X_AXIS     1
  126. #define  Y_AXIS     2
  127. #define  Z_AXIS     3
  128. #define  GC_STRING  0
  129. #define  GC_LAYER   8
  130. #define  GC_LTYPE   6
  131. #define  GC_COLOR   62
  132. #define  GC_THICK   39
  133. #define  GC_RREAL   40
  134. #define  GC_XCOORD  10
  135. #define  GC_XPOS    11
  136. #define  GC_NORMAL  210
  137.  
  138. #ifndef ELEMENTS
  139. #define ELEMENTS(array) (sizeof(array)/sizeof((array)[0]))
  140. #endif
  141.  
  142. /*************************************************************************/
  143. /* Typedefs */
  144. /*************************************************************************/
  145.  
  146.  
  147. /*************************************************************************/
  148. /* Global variables */
  149. /*************************************************************************/
  150.  
  151. /* Function type declaration */
  152.  
  153. int      funcload();
  154. void     print_new_command_set();
  155. void     command_echo_off();
  156. void     command_echo_on();
  157. void     align_objects_func();
  158. ap_Bool  select_objects();
  159. ap_Bool  get_align_data();
  160. ap_Bool  get_reference_data();
  161. void     contact_objects_func();
  162. ap_Bool  get_contact_layer();
  163. ap_Bool  get_norm_from_face();
  164. void     turn_ucsicon();
  165. void     copy_point();
  166. ap_Bool  verify_2pts_equal();
  167. ap_Bool  get_current_layer();
  168. ap_Bool  get_current_ucs();
  169. ap_Bool  set_current_ucs();
  170. void     trans_wc2uc();
  171. void     trans_uc2wc();
  172. void     trans_wc2ec();
  173. void     add_vector();
  174. void     sub_vector();
  175. void     cross_vector();
  176. ads_real dot_vector();
  177. ap_Bool  normalize_vector();
  178. int      get_yesno_from_user();
  179. ap_Bool  verify_and_reset_ucs();
  180. ap_Bool  get_xdir_from_zdir();
  181. ap_Bool  create_axis();
  182. ap_Bool  create_arrowhead();
  183. ap_Bool  create_line();
  184. ap_Bool  create_circle();
  185.  
  186.  
  187. struct resbuf  Echo1, Echo2;
  188.  
  189. /* Command definition and dispatch table.  */
  190.  
  191. struct ads_comm {
  192.     char   *cmdname;
  193.     void   (*cmdfunc) ();
  194. };
  195.  
  196. struct ads_comm cmdtab[] = {
  197.  
  198.      {/*MSG1*/"C:SOLALIGN", align_objects_func},
  199.      {/*MSG2*/"C:SOLCONTACT", contact_objects_func},
  200.     };
  201.  
  202.  
  203. /*************************************************************************/
  204. /* .doc main() */
  205. /*+
  206. -*/
  207. /*************************************************************************/
  208.  
  209. void
  210. /*FCN*/main(argc, argv)
  211.   int             argc;
  212.   char           *argv[];
  213. {
  214.     int             stat, cindex;
  215.     short           scode = 1;
  216.  
  217.     ads_init(argc, argv);
  218.  
  219.     while (TRUE) {
  220.         if ((stat = ads_link(scode)) < 0) {
  221.             printf(/*MSG3*/"ASM: bad status from ads_link() = %d\n",
  222.                    stat);
  223.             fflush(stdout);
  224.             ads_exit(1);
  225.         }
  226.         scode = -RSRSLT;
  227.  
  228.         switch (stat) {
  229.  
  230.         case RQXLOAD:                 /* Load & register functions */
  231.             scode = -(funcload() ? RSRSLT : RSERR);
  232.             scode = -RSRSLT;
  233.             print_new_command_set();
  234.             break;
  235.  
  236.         case RQSUBR:                  /* Evaluate external lisp function */
  237.             cindex = ads_getfuncode();
  238.             (*cmdtab[cindex].cmdfunc) ();
  239.             break;
  240.  
  241.         case RQXUNLD:                 /* Unloading */
  242.             ads_printf (/*MSG4*/"Unloading: ");
  243.             break;
  244.  
  245.         case RQSAVE:                  /* AutoCAD SAVE notification */
  246.             break;
  247.  
  248.         case RQQUIT:                  /* AutoCAD QUIT notification */
  249.             break;
  250.  
  251.         case RQEND:                   /* AutoCAD END notification */
  252.             break;
  253.  
  254.         default:
  255.             break;
  256.  
  257.         }
  258.  
  259.     }
  260. }
  261.  
  262. /*************************************************************************/
  263. /* .doc funcload() */
  264. /*+
  265.    Load external functions into AutoCAD system
  266. -*/
  267. /*************************************************************************/
  268.  
  269. static int 
  270. /*FCN*/funcload()
  271.  
  272. {
  273.     int             i;
  274.  
  275.     for (i = 0; i < ELEMENTS(cmdtab); i++) {
  276.         if(! ads_defun (cmdtab[i].cmdname , i))
  277.             return  RTERROR;
  278.     }
  279.     return RTNORM;
  280. }
  281.  
  282. /*************************************************************************/
  283. /* .doc print_new_command_set() */
  284. /*+
  285. -*/
  286. /*************************************************************************/
  287.  
  288. static  void
  289. /*FCN*/print_new_command_set()
  290. {
  291.     ads_printf(/*MSG5*/"\nNew loaded commands: SOLALIGN, SOLCONTACT\n");
  292. }
  293.  
  294. /*************************************************************************/
  295. /* .doc command_echo_off() */
  296. /*+
  297.    Suppress command echo
  298. -*/
  299. /*************************************************************************/
  300.  
  301. static void
  302. /*FCN*/command_echo_off()
  303. {
  304.     ads_getvar (/*MSG0*/"CMDECHO", &Echo1);
  305.     Echo2.restype = RTSHORT;
  306.     Echo2.resval.rint = FALSE;
  307.     ads_setvar (/*MSG0*/"CMDECHO", &Echo2);
  308. }
  309.  
  310. /*************************************************************************/
  311. /* .doc command_echo_on() */
  312. /*+
  313.    Resume command echo
  314. -*/
  315. /*************************************************************************/
  316.  
  317. static void
  318. /*FCN*/command_echo_on()
  319. {
  320.     ads_setvar (/*MSG0*/"CMDECHO", &Echo1);
  321. }
  322.  
  323. /*************************************************************************/
  324. /* .doc align_objects_func() */
  325. /*+
  326.    This routine performs the following tasks:
  327.  
  328.    1.  Prompt the user to select the objects to be aligned.
  329.    2.  Highlight the selected solids.
  330.    3.  Prompt the user to select two points for old alignment axis.
  331.    4.  Display X_AXIS icon for visual aid.  
  332.    5.  Prompt the user to select reference point.
  333.    6.  Display Y_AXIS icon for visual aid.  
  334.    7.  Compute the transformation matrix m1.
  335.    8.  Repeat step 3 to step 7 for new base, end & reference points.
  336.    9.  Compute the transformation matrix m2.
  337.    10. Compose matrix m1 with m2.
  338.    11. Move the selected objects to the desired location by matrix m.
  339. -*/
  340. /*************************************************************************/
  341.  
  342. static void
  343. /*FCN*/align_objects_func()
  344. {
  345.     ap_Objid  *solset;
  346.     ap_Trans3d m, m1, m2, temp;
  347.     ads_point  px, py, py1, py2, pts[3];
  348.     ads_point  poorg, pb1, ps1, pe1, pb2, ps2, pe2;
  349.     ads_point  xodir, yodir, zodir;
  350.     ads_point  xdir1, ydir1, zdir1, xdir2, ydir2, zdir2;
  351.     ads_name   sset, sset_axes,  chosen;
  352.     long  number_obj, i;
  353.     int   stat, choice;
  354.     int   reset_ucs1 = FALSE, reset_ucs2 = FALSE;
  355.     char  layer[32], ltype[32];
  356.     long   nent;
  357.  
  358.     /* Initialization */
  359.  
  360.     stat = ap_init();
  361.     if (stat != AP_NORMAL)
  362.         goto Error;
  363.  
  364.     get_current_layer(layer);
  365.     strcpy(ltype, /*MSG6*/"CONTINUOUS");
  366.     ads_ssadd(NULL, NULL, sset_axes);
  367.     get_current_ucs (poorg, xodir, yodir);
  368.     cross_vector(xodir, yodir, zodir);
  369.  
  370.     /* Prompt the user to select the objects to be moved */
  371.  
  372.     stat = select_objects(&solset, sset, &number_obj);
  373.     if(stat == FALSE)
  374.         goto Error1;
  375.  
  376.     /* Highlight all selected solids */
  377.  
  378.     for (i = 0; i < number_obj; i++) {
  379.         ads_ssname (sset, i, chosen);
  380.         ads_redraw(chosen, 3);
  381.     }
  382.  
  383.     /* Prompt user to select two points for old axis */
  384.  
  385.     stat = get_align_data(1, pb1, pe1);
  386.     if(stat == FALSE)
  387.         goto Error2;
  388.  
  389.     /* Display X_AXIS icon (with one arrowhead) */
  390.  
  391.     create_axis(X_AXIS, pb1, pe1, layer, ltype, YELLOW, sset_axes);
  392.  
  393.     /* Set new ucs plane normal to coincide with alignment
  394.            axis and through the old base point pb1 */
  395.  
  396.     sub_vector(pe1, pb1, zdir1);
  397.     reset_ucs1 = verify_and_reset_ucs(poorg, zodir, pb1, zdir1);
  398.  
  399.     /* Prompt the user to select a reference point on current 
  400.            ucs plane to lock the selected objects from  
  401.            free rotation about the alignment axis */
  402.  
  403.     stat = get_reference_data(1, pb1, ps1);
  404.     if(stat == FALSE)
  405.         goto Error2;
  406.  
  407.     /* Display reference axis icon (with two arrowheads) */
  408.  
  409.     create_axis(Y_AXIS, pb1, ps1, layer, ltype, YELLOW, sset_axes);
  410.  
  411.     /* Compute the transformation matrix m1 which transforms old
  412.            base point to the origin, old alignment axis to coincide
  413.            with Z-axis, and old reference axis to X-axis in WCS */
  414.  
  415.     sub_vector(ps1, pb1, xdir1);
  416.     cross_vector(zdir1, xdir1, ydir1);
  417.     add_vector(pb1, ydir1, py1);
  418.     copy_point(pb1, pts[0]);
  419.     copy_point(ps1, pts[1]);
  420.     copy_point(py1, pts[2]);
  421.     ap_pts2xfm(pts, 3, temp);
  422.     ap_invert(temp, m1);
  423.  
  424.     /* Prompt user to select two points for new axis */
  425.  
  426.     stat = get_align_data(2, pb2, pe2);
  427.     if(stat == FALSE)
  428.         goto Error2;
  429.  
  430.     /* Display alignment axis icon (with one arrowhead) */
  431.  
  432.     create_axis(X_AXIS, pb2, pe2, layer, ltype, YELLOW, sset_axes);
  433.  
  434.     /* Set new ucs plane normal to coincide with alignment
  435.            axis and through the new base point pb2 */
  436.  
  437.     sub_vector(pe2, pb2, zdir2);
  438.     reset_ucs2 = verify_and_reset_ucs(pb1, zdir1, pb2, zdir2);
  439.  
  440.     /* Prompt the user to select a reference point on current 
  441.            ucs plane to lock the selected objects from  
  442.            free rotation about the alignment axis */
  443.  
  444.     stat = get_reference_data(2, pb2, ps2);
  445.     if(stat == FALSE)
  446.         goto Error2;
  447.  
  448.     /* Display reference axis icon (with two arrowheads) */
  449.  
  450.     create_axis(Y_AXIS, pb2, ps2, layer, ltype, YELLOW, sset_axes);
  451.  
  452.     /* Compute the second transformation matrix m2 
  453.            which transforms the origin to new base point,
  454.            Z-axis to coincide with new alignment axis, and
  455.            X-axis to coincide with new reference axis */
  456.  
  457.     sub_vector(ps2, pb2, xdir2);
  458.     cross_vector(zdir2, xdir2, ydir2);
  459.     add_vector(pb2, ydir2, py2);
  460.     copy_point(pb2, pts[0]);
  461.     copy_point(ps2, pts[1]);
  462.     copy_point(py2, pts[2]);
  463.     ap_pts2xfm(pts, 3, m2);
  464.  
  465.     /* Combine matries m1 with m2 */
  466.  
  467.     ap_compose(m2, m1, m);
  468.  
  469.     /* Prompt the user to confirm the move */
  470.  
  471.     ads_printf(/*MSG7*/"\nOk to move the objects, No/<Yes>: ");
  472.     choice = get_yesno_from_user(YES);
  473.     if((choice == NO) || (choice == CANCEL))
  474.         goto Error2;
  475.  
  476.     /* Move the objects to new location and update the display */
  477.  
  478.     for (i=0; i<number_obj; i++) {
  479.         ap_move_obj(solset[i], m, FALSE);
  480.     }
  481.  
  482. Error2:                               /* Restore the original ucs */
  483.     if((reset_ucs1 == TRUE) || (reset_ucs2 == TRUE)) {
  484.         add_vector(poorg, xodir, px);
  485.         add_vector(poorg, yodir, py);
  486.         set_current_ucs (poorg, px, py);
  487.         turn_ucsicon(ON);
  488.     }
  489.  
  490. Error1:                               /* Clean the memory */
  491.     ads_sslength(sset_axes, &nent);
  492.     for(i=0;i<nent;i++) {
  493.         ads_ssname(sset_axes, i, chosen);
  494.         ads_entdel (chosen);
  495.     }
  496.     ads_ssfree (sset);
  497.     ads_ssfree (sset_axes);
  498.     if (solset != (ap_Objid *)NULL)
  499.         free(solset);
  500.     ads_redraw(NULL, 1);
  501. Error: 
  502.     ads_retvoid ();
  503.     return;
  504. }
  505.  
  506. /*************************************************************************/
  507. /* .doc select_objects() */
  508. /*+
  509.    This routine prompts user to select a set of objects.
  510. -*/
  511. /*************************************************************************/
  512. static ap_Bool
  513. /*FCN*/select_objects (sols, sset, ns)
  514.   ap_Objid  **sols;
  515.   ads_name  sset;
  516.   long *ns;
  517. {
  518.     long ne, i, count = 0L;
  519.     ads_name  chosen;
  520.     ap_Objid  object;
  521.     int  stat;
  522.  
  523.     /* Initialization */
  524.  
  525.     *sols = (ap_Objid)NULL;
  526.     *ns = 0L;
  527.  
  528.     if(ads_ssget (NULL, NULL, NULL, NULL, sset) != RTNORM) 
  529.         return FALSE;
  530.     if (ads_sslength (sset, &ne) != RTNORM) 
  531.         return FALSE;
  532.  
  533.     /* Verify whether the selected objects are solid.
  534.        Delete non-solids from the selection set.
  535.        Count the total number of solids in the selection set. */
  536.  
  537.     for (i=0; i<ne; i++) {
  538.         ads_ssname (sset, i, chosen);
  539.         stat = ap_name2obj(chosen, &object);
  540.         if (stat == AP_NORMAL) {
  541.             count = count + 1;
  542.         } else
  543.             ads_ssdel(chosen, sset);
  544.     }
  545.     if (count <= 0) {
  546.         ads_printf (/*MSG8*/"\nNo solids found.\n");
  547.         return FALSE;
  548.     }
  549.     if((*sols = (ap_Objid *)malloc(count * sizeof(ap_Objid)))
  550.        == (ap_Objid *)NULL) {
  551.         ads_printf (/*MSG9*/"\nCan't allocate memory for solids array.\n");
  552.         return FALSE;
  553.     }
  554.  
  555.     for (i=0; i<count; i++) {
  556.         ads_ssname (sset, i, chosen);
  557.         stat = ap_name2obj(chosen, &object);
  558.         (*sols)[i] = object;
  559.     }
  560.  
  561.     *ns = count;
  562.     return TRUE;
  563. }
  564.  
  565. /*************************************************************************/
  566. /* .doc get_align_data() */
  567. /*+
  568.    This routine prompts the user to enter two points that
  569.    define the axis to be aligned.
  570. -*/
  571. /*************************************************************************/
  572. static ap_Bool
  573. /*FCN*/get_align_data (choice, p1, p2)
  574.   int choice;
  575.   ads_point  p1, p2;
  576. {
  577.     int  stat;
  578.     ads_point  pt1, pt2;
  579.  
  580.     /* Initialization of data for default input */
  581.  
  582.     p1[X] = pt1[X] = 0.0;
  583.     p1[Y] = pt1[Y] = 0.0;
  584.     p1[Z] = pt1[Z] = 0.0;
  585.     p2[X] = pt2[X] = 0.0;
  586.     p2[Y] = pt2[Y] = 0.0;
  587.     p2[Z] = pt2[Z] = 1.0;
  588.  
  589.     /* Prompt the user to select the base point of the axis */
  590.  
  591.     if (choice == 1)
  592.         stat = ads_getpoint (NULL, 
  593.         /*MSG10*/"\nBase point of old axis <0,0,0>: ", pt1);
  594.     else if (choice == 2)
  595.         stat = ads_getpoint (NULL, 
  596.         /*MSG11*/"\nBase point of new axis <0,0,0>: ", pt1);
  597.     else
  598.         return FALSE;
  599.  
  600.     if ((stat == RTCAN) || (stat == RTERROR))
  601.         return FALSE;
  602.  
  603.     /* Prompt the user to select the end point of the axis */
  604.  
  605.     if (choice == 1)
  606.         stat = ads_getpoint (pt1, /*MSG12*/"\nEnd point of old axis <0,0,1>: ", pt2);
  607.     else if (choice == 2)
  608.         stat = ads_getpoint (pt1, /*MSG13*/"\nEnd point of new axis <0,0,1>: ", pt2);
  609.     else
  610.         return FALSE;
  611.  
  612.     if ((stat == RTCAN) || (stat == RTERROR))
  613.         return FALSE;
  614.  
  615.     /* Make sure that base and end points are not coincident */
  616.  
  617.     stat = verify_2pts_equal(pt1, pt2);
  618.     if(stat == TRUE) {
  619.         ads_printf (/*MSG14*/"\nTwo points are coincident.\n");
  620.         return FALSE;
  621.     }
  622.  
  623.     /* Transform base and end points from ucs to wcs */
  624.  
  625.     trans_uc2wc(pt1, FALSE, p1);
  626.     trans_uc2wc(pt2, FALSE, p2);
  627.     return TRUE;
  628. }
  629.  
  630. /*************************************************************************/
  631. /* .doc get_reference_data() */
  632. /*+
  633.    This routine prompts the user to enter a point that
  634.    define the reference axis.
  635. -*/
  636. /*************************************************************************/
  637. static ap_Bool
  638. /*FCN*/get_reference_data (choice, p1, p2)
  639.   int choice;
  640.   ads_point  p1, p2;
  641. {
  642.     int  stat;
  643.     ads_point  pt1, pt2;
  644.  
  645.     /* Initialization of data for default input */
  646.  
  647.     p2[X] = pt2[X] = 0.0;
  648.     p2[Y] = pt2[Y] = 1.0;
  649.     p2[Z] = pt2[Z] = 0.0;
  650.  
  651.     /* Transform base point from wcs to ucs */
  652.  
  653.     trans_wc2uc(p1, FALSE, pt1);
  654.  
  655.     /* Prompt the user to select the reference point */
  656.  
  657.     if (choice == 1)
  658.         stat = ads_getpoint(pt1,
  659.                     /*MSG15*/"\nOld reference point <Axial Symmetry>:", pt2);
  660.     else if (choice == 2)
  661.         stat = ads_getpoint(pt1,
  662.                     /*MSG16*/"\nNew reference point <Axial Symmetry>:", pt2);
  663.     else
  664.         return FALSE;
  665.  
  666.     if ((stat == RTCAN) || (stat == RTERROR))
  667.         return FALSE;
  668.     else if(stat == RTNONE) {
  669.         pt2[X] = pt1[X] + 1.0;
  670.         pt2[Y] = pt1[Y];
  671.     }
  672.     pt2[Z] = 0.0;
  673.  
  674.     /* Make sure that base and reference points are not coincident */
  675.  
  676.     stat = verify_2pts_equal(pt1, pt2);
  677.     if(stat == TRUE) {
  678.         ads_printf (/*MSG17*/"\nInvalid reference point.\n");
  679.         return FALSE;
  680.     }
  681.  
  682.     /* Transform reference point from ucs to wcs */
  683.  
  684.     trans_uc2wc(pt2, FALSE, p2);
  685.     return TRUE;
  686. }
  687.  
  688. /*************************************************************************/
  689. /* .doc contact_objects_func() */
  690. /*+
  691.    This routine performs the following tasks:
  692.  
  693.    1.  Prompt the user to select the objects.
  694.    2.  Highlight the selected solids.
  695.    3.  Prompt the user to select a face, a base and a reference point.
  696.    4.  Display motion coordinate icon for visual aid. 
  697.    5.  Compute the transformation matrix m1.
  698.    6.  Repeat step 3 to 5 for destination face, base and reference point.
  699.    7.  Compute the transformation matrix m2.
  700.    8.  Compose matrix m1 with m2.
  701.    9.  Move the objects to new location by matrix m.
  702. -*/
  703. /*************************************************************************/
  704.  
  705. static void
  706. /*FCN*/contact_objects_func()
  707. {
  708.     ap_Objid object, *solset;
  709.     ap_Featid face;
  710.     ap_Trans3d m, m1, m2, temp;
  711.     ads_point  pc1, pc2, pf1, ps1, py1, pf2, ps2,  py2;
  712.     ads_point  xdir1, ydir1, zdir1, xdir2, ydir2, zdir2;
  713.     ads_point  poorg, xodir, yodir, zodir, px, py, pts[3];
  714.     ads_name   sset, chosen, sset_axes;
  715.     int choice, stat, reset_ucs1 = FALSE, reset_ucs2 = FALSE;
  716.     long  i, number_obj, nent;
  717.     char  layer[32], ltype[32];
  718.  
  719.     /* Initialization */
  720.  
  721.     stat = ap_init();
  722.     if (stat != AP_NORMAL)
  723.         goto Error;
  724.  
  725.     get_current_layer(layer);
  726.     strcpy(ltype, /*MSG18*/"CONTINUOUS");
  727.     ads_ssadd(NULL, NULL, sset_axes);
  728.     get_current_ucs (poorg, xodir, yodir);
  729.     cross_vector(xodir, yodir, zodir);
  730.  
  731.     /* Prompt user to select the objects to be moved */
  732.  
  733.     stat = select_objects(&solset, sset, &number_obj);
  734.     if(stat == FALSE)
  735.         goto Error1;
  736.  
  737.     /* Prompt user to select the desired contact face */
  738.  
  739.     stat = ap_sel_face (/*MSG19*/"\nSelect old contact face:\n", &object, &face);
  740.     if (stat != AP_NORMAL)
  741.         goto Error1;
  742.  
  743.     /* Highlight selected solids */
  744.  
  745.     for (i = 0; i < number_obj; i++) {
  746.         ads_ssname (sset, i, chosen);
  747.         ads_redraw(chosen, 3);
  748.     }
  749.  
  750.     /* Retrieve a point on and a normal to the face */
  751.  
  752.     stat = get_norm_from_face(object, face, pc1, zdir1);
  753.     if (stat == FALSE)
  754.         goto Error2;
  755.  
  756.     /* Verify whether the selected face lies in current ucs plane.
  757.        If not, set new ucs to lie in the selected face
  758.        and move the ucsicon to new origin */
  759.  
  760.     reset_ucs1 = verify_and_reset_ucs(poorg, zodir, pc1, zdir1);
  761.  
  762.     /* Prompt user to select old base and reference points */
  763.  
  764.     stat = get_contact_data(1, pf1, ps1);
  765.     if(stat == FALSE)
  766.         goto Error2;
  767.  
  768.     /* Display motion coordinate icon on the selected face.
  769.            The first axis goes from base to reference point.
  770.            The second axis starts from base point and goes to 
  771.            a direction perpendicular to the first axis */
  772.  
  773.     sub_vector(ps1, pf1, xdir1);
  774.     cross_vector(zdir1, xdir1, ydir1);
  775.     add_vector(pf1, ydir1, py1);
  776.     create_axis(X_AXIS, pf1, ps1, layer, ltype, YELLOW, sset_axes);
  777.     create_axis(Y_AXIS, pf1, py1, layer, ltype, YELLOW, sset_axes);
  778.  
  779.     /* Compute the matrix m1 which transforms face normal direction
  780.            to Z-axis, reference direction to X-axis in wcs */
  781.  
  782.     copy_point(pf1, pts[0]);
  783.     copy_point(ps1, pts[1]);
  784.     copy_point(py1, pts[2]);
  785.     ap_pts2xfm(pts, 3, temp);
  786.     ap_invert(temp, m1);
  787.  
  788.     /* Prompt user to select destination face of an object */
  789.  
  790.     stat = ap_sel_face (/*MSG20*/"\nSelect new contact face:\n", &object, &face);
  791.     if (stat != AP_NORMAL) {
  792.         goto Error2;
  793.     }
  794.  
  795.     /* retrieve a point on the face and inward normal to the face */
  796.  
  797.     stat = get_norm_from_face(object, face, pc2, zdir2);
  798.     if (stat != TRUE)
  799.         goto Error2;
  800.  
  801.     /* Verify whether the second selected face lies in 
  802.        current ucs plane */
  803.  
  804.     reset_ucs2 = verify_and_reset_ucs(pc1, zdir1, pc2, zdir2);
  805.  
  806.     /* Prompt user to select new base and reference points */
  807.  
  808.     stat = get_contact_data(2, pf2, ps2);
  809.     if(stat == FALSE)
  810.         goto Error2;
  811.  
  812.     /* Display motion coordinate icon on the selected face */
  813.  
  814.     zdir2[X] = -zdir2[X];
  815.     zdir2[Y] = -zdir2[Y];
  816.     zdir2[Z] = -zdir2[Z];
  817.     sub_vector(ps2, pf2, xdir2);
  818.     cross_vector(zdir2, xdir2, ydir2);
  819.     add_vector(pf2, ydir2, py2);
  820.     create_axis(X_AXIS, pf2, ps2, layer, ltype, YELLOW, sset_axes);
  821.     create_axis(Y_AXIS, pf2, py2, layer, ltype, YELLOW, sset_axes);
  822.  
  823.     /* Compute the matrix m2 */
  824.  
  825.     copy_point(pf2, pts[0]);
  826.     copy_point(ps2, pts[1]);
  827.     copy_point(py2, pts[2]);
  828.     ap_pts2xfm(pts, 3, m2);
  829.     ap_compose(m2, m1, m);
  830.  
  831.     /* Prompt the user to confirm the move of objects */
  832.  
  833.     ads_printf(/*MSG21*/"\nOk to move the objects, No/<Yes>: ");
  834.     choice = get_yesno_from_user(YES);
  835.     if((choice == NO) || (choice == CANCEL)) {
  836.         goto Error2;
  837.     }
  838.  
  839.     /* Move the objects and update the display */
  840.  
  841.     for (i=0; i<number_obj; i++)
  842.         ap_move_obj(solset[i], m, FALSE);
  843.  
  844. Error2:                               /* Restore the original ucs */
  845.     if((reset_ucs1 == TRUE) || (reset_ucs2 == TRUE)) {
  846.         add_vector(poorg, xodir, px);
  847.         add_vector(poorg, yodir, py);
  848.         set_current_ucs (poorg, px, py);
  849.         turn_ucsicon(ON);
  850.     }
  851.  
  852. Error1:                               /* Clean the memory */
  853.     ads_sslength(sset_axes, &nent);
  854.     for(i=0;i<nent;i++) {
  855.         ads_ssname(sset_axes, i, chosen);
  856.         ads_entdel (chosen);
  857.     }
  858.     ads_ssfree (sset);
  859.     ads_ssfree (sset_axes);
  860.     if (solset != (ap_Objid *)NULL)
  861.         free(solset);
  862.     ads_redraw(NULL, 1);
  863. Error:
  864.     ads_retvoid ();
  865.     return;
  866. }
  867.  
  868. /*************************************************************************/
  869. /* .doc get_contact_data() */
  870. /*+
  871.    This routine prompts the user to select base and
  872.    reference points.
  873. -*/
  874. /*************************************************************************/
  875. static ap_Bool
  876. /*FCN*/get_contact_data (choice, p1, p2)
  877.   int  choice;
  878.   ads_point  p1, p2;
  879. {
  880.     int  stat;
  881.     ads_point  pt1, pt2;
  882.  
  883.     /* Initialization */
  884.  
  885.     p1[X] = pt1[X] = 0.0;
  886.     p1[Y] = pt1[Y] = 0.0;
  887.     p1[Z] = pt1[Z] = 0.0;
  888.  
  889.     /* Select the contact base point */
  890.  
  891.     if (choice == 1)
  892.         stat = ads_getpoint (NULL, /*MSG22*/"\nOld base point <0,0,0>: ", pt1);
  893.     else if (choice == 2)
  894.         stat = ads_getpoint (NULL, /*MSG23*/"\nNew base point <0,0,0>: ", pt1);
  895.     else
  896.         return FALSE;
  897.  
  898.     if ((stat == RTCAN) || (stat == RTERROR))
  899.         return FALSE;
  900.  
  901.     /* Project base point onto the selected face */
  902.  
  903.     pt1[Z] = 0.0;
  904.  
  905.     if (choice == 1)
  906.         stat = ads_getpoint (pt1,
  907.                      /*MSG24*/"\nOld reference point <Axial Symmetry>: ", pt2);
  908.     else if (choice == 2)
  909.         stat = ads_getpoint (pt1,
  910.                      /*MSG25*/"\nNew reference point <Axial Symmetry>: ", pt2);
  911.     else
  912.         return FALSE;
  913.  
  914.     if ((stat == RTCAN) || (stat == RTERROR))
  915.         return FALSE;
  916.     else if (stat == RTNONE) {
  917.         pt2[X] = pt1[X] + 1.0;
  918.         pt2[Y] = pt1[Y];
  919.     }
  920.  
  921.     pt2[Z] = 0.0;
  922.  
  923.     /* Make sure that base and reference points are not coincident */
  924.  
  925.     stat = verify_2pts_equal(pt1, pt2);
  926.     if(stat == TRUE) {
  927.         ads_printf (/*MSG26*/"\nInvalid reference point.\n");
  928.         return FALSE;
  929.     }
  930.  
  931.     trans_uc2wc(pt1, FALSE, p1);
  932.     trans_uc2wc(pt2, FALSE, p2);
  933.     return TRUE;
  934. }
  935.  
  936. /*************************************************************************/
  937. /* .doc get_norm_from_face() */
  938. /*+
  939.    This routine performs the following tasks:
  940.  
  941.    1.  Verify that the selected face is a plane face.
  942.    2.  Retrieve a point pc on the face. 
  943.    3.  Retrieve the inward face normal direction.
  944. -*/
  945. /*************************************************************************/
  946.  
  947. static ap_Bool
  948. /*FCN*/get_norm_from_face(object, face, pc, norm)
  949.   ap_Objid  object;
  950.   ap_Featid  face;
  951.   ads_point  pc, norm;
  952. {
  953.     ap_Faceinfo  *finfo;
  954.     int  stat;
  955.  
  956.     /* Retrieve face type and verify whether it is plane */
  957.  
  958.     stat = ap_get_faceinfo(object, face, &finfo);
  959.     if (stat != AP_NORMAL) {
  960.         ads_printf (/*MSG27*/"\nUnable to retrieve face information");
  961.         ads_retvoid();
  962.         return FALSE;
  963.     }
  964.     if (finfo->stype != AP_PLANAR) {
  965.         ads_printf (/*MSG28*/"\nThe selected face must be plane.");
  966.         ads_retvoid();
  967.         return FALSE;
  968.     }
  969.  
  970.     /* Retrieve inward face normal direction,
  971.            and a point pc on the face */
  972.  
  973.     pc[X] = finfo->face_rm[0][3];
  974.     pc[Y] = finfo->face_rm[1][3];
  975.     pc[Z] = finfo->face_rm[2][3];
  976.     copy_point(finfo->surf.pla.norm, norm);
  977.     norm[X] = - norm[X];
  978.     norm[Y] = - norm[Y];
  979.     norm[Z] = - norm[Z];
  980.     ap_free_faceinfo(finfo);
  981.     return TRUE;
  982. }
  983.  
  984.  
  985. /*************************************************************************/
  986. /* .doc turn_ucsicon() */
  987. /*+
  988.    This routine will turn ucsicon on, off or move it to new origin
  989. -*/
  990. /*************************************************************************/
  991.  
  992. static void
  993. /* FCN */turn_ucsicon(action)
  994.   int action;
  995. {
  996.     command_echo_off ();
  997.     if (action == ON)
  998.         ads_command (RTSTR, /*MSG29*/"_.UCSICON", RTSTR, /*MSG30*/"_ON", NULL);
  999.     else if(action == ORIGIN)
  1000.         ads_command (RTSTR, /*MSG31*/"_.UCSICON", RTSTR, /*MSG32*/"_ORIGIN", NULL);
  1001.     else
  1002.         ads_command (RTSTR, /*MSG33*/"_.UCSICON", RTSTR, /*MSG34*/"_OFF", NULL);
  1003.  
  1004.     command_echo_on ();
  1005. }
  1006.  
  1007. /*************************************************************************/
  1008. /* .doc copy_point() */
  1009. /*+
  1010.     Duplicate a point, that is pt2 = pt1
  1011. -*/
  1012. /*************************************************************************/
  1013.  
  1014. static void
  1015. /*FCN*/copy_point (pt1, pt2)
  1016.   ads_point  pt1, pt2;
  1017. {
  1018.     pt2[X] = pt1[X];
  1019.     pt2[Y] = pt1[Y];
  1020.     pt2[Z] = pt1[Z];
  1021. }
  1022.  
  1023. /*************************************************************************/
  1024. /* .doc verify_2pts_equal() */
  1025. /*+
  1026.    Verify whether two points p1 & p2 are equal
  1027.    that is, within tolerance EPSILON
  1028. -*/
  1029. /*************************************************************************/
  1030.  
  1031. static ap_Bool
  1032. /* FCN */verify_2pts_equal (p1, p2)
  1033.   ads_point  p1, p2;
  1034. {
  1035.     ads_real   dx, dy, dz;
  1036.  
  1037.     dx = fabs (p2[X] - p1[X]);
  1038.     dy = fabs (p2[Y] - p1[Y]);
  1039.     dz = fabs (p2[Z] - p1[Z]);
  1040.  
  1041.  
  1042.     if ((dx < EPSILON) && (dy < EPSILON) && (dz < EPSILON))
  1043.         return TRUE;
  1044.     else
  1045.         return FALSE;
  1046. }
  1047.  
  1048. /************************************************************************/
  1049. /*.doc get_current_layer() */
  1050. /*+
  1051.    This routine retrieves the name of current layer from 
  1052.    AutoCAD database.
  1053. -*/
  1054. /************************************************************************/
  1055.  
  1056. static ap_Bool
  1057. /*FCN*/get_current_layer (layer)
  1058.   char  layer[32];
  1059. {
  1060.     int stat;
  1061.     struct resbuf   Cla;
  1062.  
  1063.     stat = ads_getvar (/*MSG0*/"CLAYER", &Cla);
  1064.     if (stat == RTNORM) {
  1065.         strcpy(layer,  Cla.resval.rstring);
  1066.         return TRUE;
  1067.     } else
  1068.         return FALSE;
  1069. }
  1070.  
  1071. /*************************************************************************/
  1072. /* .doc get_current_ucs() */
  1073. /*+
  1074.     Retrieve current ucs's origin, x-direction & y-direction.
  1075.         Return them through arguments po, px & py respectively.
  1076. -*/
  1077. /*************************************************************************/
  1078.  
  1079. static ap_Bool
  1080. /*FCN*/get_current_ucs(po, px, py)
  1081.   ads_point po, px, py;
  1082.  
  1083. {
  1084.     struct resbuf   ucso, ucsx, ucsy;
  1085.     int stat;
  1086.  
  1087.     stat = ads_getvar (/*MSG0*/"UCSORG", &ucso);
  1088.     if (stat != RTNORM)
  1089.         return FALSE;
  1090.     stat = ads_getvar (/*MSG0*/"UCSXDIR", &ucsx);
  1091.     if (stat != RTNORM)
  1092.         return FALSE;
  1093.     stat = ads_getvar (/*MSG0*/"UCSYDIR", &ucsy);
  1094.     if (stat != RTNORM)
  1095.         return FALSE;
  1096.  
  1097.     copy_point(ucso.resval.rpoint, po);
  1098.     copy_point(ucsx.resval.rpoint, px);
  1099.     copy_point(ucsy.resval.rpoint, py);
  1100.     return TRUE;
  1101. }
  1102. /*************************************************************************/
  1103. /* .doc set_current_ucs() */
  1104. /*+
  1105.    Reset current ucs by using three points p1, p2 & p3, where
  1106.    p1 = new origin
  1107.    p1p2 = new x-direction
  1108.    p1p2 x p1p3 = new z-direction 
  1109. -*/
  1110. /*************************************************************************/
  1111.  
  1112. static ap_Bool
  1113. /*FCN*/set_current_ucs(p1, p2, p3)
  1114.   ads_point p1, p2, p3;
  1115. {
  1116.     ads_point pt1, pt2, pt3;
  1117.     int stat1, stat2, stat3, stat;
  1118.  
  1119.     stat1 = verify_2pts_equal (p1, p2);
  1120.     stat2 = verify_2pts_equal (p2, p3);
  1121.     stat3 = verify_2pts_equal (p3, p1);
  1122.     if ((stat1 == TRUE) || (stat2 == TRUE) || (stat3 == TRUE))
  1123.         return FALSE;
  1124.  
  1125.     trans_wc2uc(p1, FALSE, pt1);
  1126.     trans_wc2uc(p2, FALSE, pt2);
  1127.     trans_wc2uc(p3, FALSE, pt3);
  1128.  
  1129.     command_echo_off ();
  1130.     stat = ads_command(RTSTR, /*MSG35*/"_.ucs", RTSTR, /*MSG36*/"_3", RT3DPOINT,
  1131.                        pt1, RT3DPOINT, pt2, RT3DPOINT, pt3, NULL);
  1132.  
  1133.     if (stat != RTNORM)
  1134.     {
  1135.         command_echo_on ();
  1136.         return FALSE;
  1137.     }
  1138.     command_echo_on ();
  1139.     return TRUE;
  1140. }
  1141.  
  1142. /*************************************************************************/
  1143. /* .doc trans_wc2uc() */
  1144. /*+
  1145.    Transform a point p in wcs into point q in ucs
  1146.    If vec = TRUE, then p is a vector
  1147.    If vec = FALSE, then p is a point
  1148. -*/
  1149. /*************************************************************************/
  1150.  
  1151. static void
  1152. /*FCN*/trans_wc2uc(p,vec,q)
  1153.   ads_point p, q;
  1154.   ap_Bool vec;
  1155. {
  1156.     struct resbuf rbfrom, rbto;
  1157.  
  1158.     rbfrom.restype = RTSHORT;
  1159.     rbto.restype = RTSHORT;
  1160.     rbfrom.resval.rint = 0;           /* from world */
  1161.     rbto.resval.rint = 1;             /* to ucs */
  1162.     ads_trans(p, &rbfrom, &rbto, vec, q);
  1163. }
  1164.  
  1165. /*************************************************************************/
  1166. /* .doc trans_uc2wc() */
  1167. /*+
  1168.    Transform a point p in ucs into point q in wcs
  1169.    If vec = TRUE, then p is a vector
  1170.    If vec = FALSE, then p is a point
  1171. -*/
  1172. /*************************************************************************/
  1173.  
  1174. static void
  1175. /*FCN*/trans_uc2wc(p,vec,q)
  1176.   ads_point p, q;
  1177.   ap_Bool vec;
  1178. {
  1179.     struct resbuf rbfrom, rbto;
  1180.  
  1181.     rbfrom.restype = RTSHORT;
  1182.     rbto.restype = RTSHORT;
  1183.     rbfrom.resval.rint = 1;           /* from ucs */
  1184.     rbto.resval.rint = 0;             /* to world */
  1185.     ads_trans(p, &rbfrom, &rbto, vec, q);
  1186. }
  1187.  
  1188. /*************************************************************************/
  1189. /*.doc trans_wc2ec(external) */
  1190. /*+
  1191.    Transform a point p in wcs into point q in ecs
  1192.    If vec = TRUE, then p is a vector
  1193.    If vec = FALSE, then p is a point
  1194. -*/
  1195. /*************************************************************************/
  1196.  
  1197. static void
  1198. /*FCN*/trans_wc2ec(p, norm, vec, q)
  1199.   ads_point p, norm, q;
  1200.   ap_Bool vec;
  1201. {
  1202.     struct resbuf rbfrom, rbto;
  1203.  
  1204.     normalize_vector(norm, norm);
  1205.     rbfrom.restype = RTSHORT;
  1206.     rbfrom.resval.rint = 0;           /* from world */
  1207.     rbto.restype = RT3DPOINT;
  1208.     rbto.resval.rpoint[X] = norm[X];  /* to ecs */
  1209.     rbto.resval.rpoint[Y] = norm[Y];
  1210.     rbto.resval.rpoint[Z] = norm[Z];
  1211.     ads_trans(p, &rbfrom, &rbto, vec, q);
  1212. }
  1213.  
  1214. /*************************************************************************/
  1215. /* .doc add_vector() */
  1216. /*+
  1217.     Add two vectors cp = ap + bp 
  1218. -*/
  1219. /*************************************************************************/
  1220.  
  1221. static void
  1222. /*FCN*/add_vector( ap, bp, cp)
  1223.   ads_point ap, bp, cp;
  1224. {
  1225.     cp[X] = ap[X] + bp[X];
  1226.     cp[Y] = ap[Y] + bp[Y];
  1227.     cp[Z] = ap[Z] + bp[Z];
  1228. }
  1229.  
  1230. /*************************************************************************/
  1231. /* .doc sub_vector() */
  1232. /*+
  1233.     Subtract vector bp from vector ap with result cp = ap - bp 
  1234. -*/
  1235. /*************************************************************************/
  1236.  
  1237. static void
  1238. /*FCN*/sub_vector(ap, bp, cp)
  1239.   ads_point ap, bp, cp;
  1240. {
  1241.     cp[X] = ap[X] - bp[X];
  1242.     cp[Y] = ap[Y] - bp[Y];
  1243.     cp[Z] = ap[Z] - bp[Z];
  1244. }
  1245.  
  1246. /*************************************************************************/
  1247. /* .doc cross_vector() */
  1248. /*+
  1249.     Cross product of two vectors, cp = ap X bp.  
  1250. -*/
  1251. /*************************************************************************/
  1252.  
  1253. static void
  1254. /*FCN*/cross_vector(ap, bp, cp)
  1255.   ads_point ap, bp, cp;
  1256. {
  1257.     ads_point result;
  1258.  
  1259.     result[X] = ap[Y] * bp[Z] - ap[Z] * bp[Y];
  1260.     result[Y] = ap[Z] * bp[X] - ap[X] * bp[Z];
  1261.     result[Z] = ap[X] * bp[Y] - ap[Y] * bp[X];
  1262.     copy_point (result, cp);
  1263. }
  1264.  
  1265. /*************************************************************************/
  1266. /* .doc dot_vector() */
  1267. /*+
  1268.     Dot product of two vectors, The result = ap.bp 
  1269. -*/
  1270. /*************************************************************************/
  1271.  
  1272. static ads_real
  1273. /*FCN*/dot_vector(ap, bp)
  1274.   ads_point ap, bp;
  1275. {
  1276.     ads_real result;
  1277.  
  1278.     result =  ap[X] * bp[X] + ap[Y] * bp[Y] + ap[Z] * bp[Z];
  1279.     return result;
  1280. }
  1281.  
  1282. /*************************************************************************/
  1283. /* .doc normalize_vector() */
  1284. /*+
  1285.    Normalize a vector, that is q = p/||p||
  1286. -*/
  1287. /*************************************************************************/
  1288.  
  1289. static ap_Bool
  1290. /*FCN*/normalize_vector (p, q)
  1291.   ads_point  p, q;
  1292. {
  1293.     ap_Real temp, dis;
  1294.  
  1295.     temp = p[X]*p[X] + p[Y]*p[Y] + p[Z]*p[Z];
  1296.     if (temp < EPSILON * EPSILON)
  1297.         return FALSE;
  1298.  
  1299.     dis = sqrt (temp);
  1300.     q[X] = p[X] / dis;
  1301.     q[Y] = p[Y] / dis;
  1302.     q[Z] = p[Z] / dis;
  1303.     return TRUE;
  1304. }
  1305.  
  1306. /*************************************************************************/
  1307. /* .doc get_yesno_from_user() */
  1308. /*+
  1309.    Prompt user to choice "Yes" or "No".
  1310.    The function return "YES", "NO" or "CANCEL" depending on
  1311.    user's action and the setup for default_choice.
  1312. -*/
  1313. /*************************************************************************/
  1314. static int
  1315. /*FCN*/get_yesno_from_user (default_choice)
  1316.   ap_Bool default_choice;
  1317. {
  1318.     char  inbuf[132];
  1319.     int   status;
  1320.  
  1321.     ads_initget(0, /*MSG37*/"Yes No");
  1322.     strcpy(inbuf, "");
  1323.  
  1324.     status = ads_getkword (NULL, inbuf);
  1325.  
  1326.     if (status == RTCAN) {
  1327.         ads_retvoid ();
  1328.         return CANCEL;
  1329.     } else if ((default_choice == YES) && (status == RTNONE)) {
  1330.         ads_retvoid ();
  1331.         return YES;
  1332.     } else if ((default_choice == NO) && (status == RTNONE)) {
  1333.         ads_retvoid ();
  1334.         return NO;
  1335.     } else if (status == RTNORM) {
  1336.         if (strcmp (/*MSG38*/"Yes", inbuf) == 0) {
  1337.             ads_retvoid ();
  1338.             return YES;
  1339.         } else if (strcmp (/*MSG39*/"No", inbuf) == 0) {
  1340.             ads_retvoid ();
  1341.             return NO;
  1342.         } else {
  1343.             ads_retvoid ();
  1344.             return CANCEL;
  1345.         }
  1346.     } else
  1347.         return CANCEL;
  1348. }
  1349.  
  1350. /*************************************************************************/
  1351. /* .doc verify_and_reset_ucs() */
  1352. /*+
  1353.    This routine verify whether two planes are coincident.
  1354.    The plane is defined by its normal and a through point.
  1355.    If they are not coincident, set new ucs plane to coincide
  1356.    with second plane defined by origin org2 and normal norm2.
  1357. -*/
  1358. /*************************************************************************/
  1359. static ap_Bool
  1360. /*FCN*/verify_and_reset_ucs (org1, norm1, org2, norm2)
  1361.   ads_point  org1, norm1, org2, norm2;
  1362. {
  1363.     ads_point  dir, pd1, pd2, xdir, ydir, px, py;
  1364.     ads_real   dx, dy, dz, du, dv;
  1365.  
  1366.     if (((fabs (norm1[X]) < EPSILON) &&
  1367.          (fabs (norm1[Y]) < EPSILON) &&
  1368.          (fabs (norm1[Z]) < EPSILON)) ||
  1369.         ((fabs (norm2[X]) < EPSILON) &&
  1370.          (fabs (norm2[Y]) < EPSILON) &&
  1371.          (fabs (norm2[Z]) < EPSILON)))
  1372.         return FALSE;
  1373.  
  1374.     normalize_vector(norm1, pd1);
  1375.     normalize_vector(norm2, pd2);
  1376.     dx = pd1[Y]*pd2[Z] - pd1[Z]*pd2[Y];
  1377.     dy = pd1[Z]*pd2[X] - pd1[X]*pd2[Z];
  1378.     dz = pd1[X]*pd2[Y] - pd1[Y]*pd2[X];
  1379.  
  1380.     sub_vector(org2, org1, dir);
  1381.     du = dot_vector (dir, pd1);
  1382.     dv = dot_vector (dir, pd2);
  1383.  
  1384.     if ((fabs (dx) < EPSILON) && (fabs (dy) < EPSILON) &&
  1385.         (fabs (dz) < EPSILON) && (fabs (du) < EPSILON) &&
  1386.         (fabs (dv) < EPSILON))
  1387.         return FALSE;
  1388.  
  1389.     /* Set ucs plane to new location */
  1390.  
  1391.     get_xdir_from_zdir(pd2, xdir);
  1392.     cross_vector(pd2, xdir, ydir);
  1393.     add_vector(org2, xdir, px);
  1394.     add_vector(org2, ydir, py);
  1395.     turn_ucsicon(OFF);
  1396.     set_current_ucs (org2, px, py);
  1397.     return TRUE;
  1398. }
  1399.  
  1400. /*************************************************************************/
  1401. /* .doc get_xdir_from_zdir() */
  1402. /*+
  1403.    This routine compute the x-axis direction from given 
  1404.    z-axis direction based on arbitrary axis algorithm.
  1405. -*/
  1406. /*************************************************************************/
  1407. static ap_Bool
  1408. /*FCN*/get_xdir_from_zdir (zdir, xdir)
  1409.   ads_point  zdir, xdir;
  1410. {
  1411.     ads_point  u, v, wydir, wzdir;
  1412.  
  1413.     wydir[X] = 0.0;
  1414.     wydir[Y] = 1.0;
  1415.     wydir[Z] = 0.0;
  1416.  
  1417.     wzdir[X] = 0.0;
  1418.     wzdir[Y] = 0.0;
  1419.     wzdir[Z] = 1.0;
  1420.  
  1421.     if ((fabs (zdir[X]) < EPSILON) &&
  1422.         (fabs (zdir[Y]) < EPSILON) &&
  1423.         (fabs (zdir[Z]) < EPSILON))
  1424.         return FALSE;
  1425.  
  1426.     else
  1427.     {
  1428.         normalize_vector(zdir, u);
  1429.  
  1430.         if ((fabs(u[X]) < 1.0/64.0) && (fabs (u[Y]) < 1.0/64.0))
  1431.             cross_vector (wydir, u, v);
  1432.  
  1433.         else
  1434.             cross_vector (wzdir, u, v);
  1435.  
  1436.         normalize_vector(v, xdir);
  1437.         return (TRUE);
  1438.     }
  1439. }
  1440.  
  1441. /*************************************************************************/
  1442. /* .doc create_axis() */
  1443. /*+
  1444.    This routine creates axis icon consisting of center line
  1445.    and arrowheads.  The center line starts from pt1 and goes
  1446.    to pt2 direction.  The length of the axis is scaled 
  1447.    automatically to be proportional to (1/5) viewport size. 
  1448.    The number of arrowheads is determined by argument axis_type.
  1449.    If axis_type = X_AXIS  -  One arrowhead.
  1450.    If axis_type = Y_AXIS  -  Two arrowheads.
  1451.    If axis_type = Z_AXIS  -  Three arrowheads.
  1452.  
  1453.    If the creation is successful, sset contains the collection 
  1454.    of entity names.
  1455. -*/
  1456. /*************************************************************************/
  1457. static ap_Bool
  1458. /*FCN*/create_axis(axis_type, pt1, pt2, lay, ltp, color, sset)
  1459.   ads_point  pt1, pt2;
  1460.   int  axis_type, color;
  1461.   char *lay, *ltp;
  1462.   ads_name  sset;
  1463. {
  1464.     ads_point  pc1, pc2, pc3, pd, norm;
  1465.     ads_real  length, len, rad, vsize;
  1466.     struct resbuf  vrb;
  1467.     ads_name  ent;
  1468.     int  stat;
  1469.  
  1470.     length = ads_distance(pt1, pt2);
  1471.     if (length < EPSILON)
  1472.         return FALSE;
  1473.     stat = ads_getvar(/*MSG0*/"VIEWSIZE", &vrb);
  1474.     if (stat != RTNORM)
  1475.         return FALSE;
  1476.     vsize = vrb.resval.rreal;
  1477.     len = 0.2 * vsize;
  1478.     rad = 0.05 * len;
  1479.     sub_vector(pt2, pt1, pd);
  1480.     normalize_vector(pd, norm);
  1481.     pt2[X] = pt1[X] + len * norm[X];
  1482.     pt2[Y] = pt1[Y] + len * norm[Y];
  1483.     pt2[Z] = pt1[Z] + len * norm[Z];
  1484.  
  1485.     pc1[X] = pt1[X] + 0.9 * len * norm[X];
  1486.     pc1[Y] = pt1[Y] + 0.9 * len * norm[Y];
  1487.     pc1[Z] = pt1[Z] + 0.9 * len * norm[Z];
  1488.  
  1489.     pc2[X] = pt1[X] + 0.8 * len * norm[X];
  1490.     pc2[Y] = pt1[Y] + 0.8 * len * norm[Y];
  1491.     pc2[Z] = pt1[Z] + 0.8 * len * norm[Z];
  1492.  
  1493.     pc3[X] = pt1[X] + 0.7 * len * norm[X];
  1494.     pc3[Y] = pt1[Y] + 0.7 * len * norm[Y];
  1495.     pc3[Z] = pt1[Z] + 0.7 * len * norm[Z];
  1496.  
  1497.     switch(axis_type) {
  1498.     case 3:
  1499.         stat = create_line(pt1, pt2, lay, ltp, color, ent);
  1500.         if (stat == TRUE)
  1501.             ads_ssadd(ent, sset, sset);
  1502.         create_arrowhead(pc1, pt2, rad, lay, ltp, color, sset);
  1503.         create_arrowhead(pc2, pc1, rad, lay, ltp, color, sset);
  1504.         create_arrowhead(pc3, pc2, rad, lay, ltp, color, sset);
  1505.         break;
  1506.  
  1507.     case 2:
  1508.  
  1509.         stat = create_line(pt1, pt2, lay, ltp, color, ent);
  1510.         if (stat == TRUE)
  1511.             ads_ssadd(ent, sset, sset);
  1512.         create_arrowhead(pc1, pt2, rad, lay, ltp, color, sset);
  1513.         create_arrowhead(pc2, pc1, rad, lay, ltp, color, sset);
  1514.         break;
  1515.  
  1516.     case 1:
  1517.     default:
  1518.  
  1519.         stat = create_line(pt1, pt2, lay, ltp, color, ent);
  1520.         if (stat == TRUE)
  1521.             ads_ssadd(ent, sset, sset);
  1522.         create_arrowhead(pc1, pt2, rad, lay, ltp, color, sset);
  1523.         break;
  1524.     }
  1525.     return TRUE;
  1526. }
  1527.  
  1528. /*************************************************************************/
  1529. /* .doc create_arrowhead() */
  1530. /*+
  1531.    This routine creates an arrowhead icon consisting of
  1532.    a circle and 4 lines segments. The circle of specified 
  1533.    radius is centered at point pt1 and is normal to the 
  1534.    direction from pt1 to pt2.  
  1535.    The four line segments are created by connecting
  1536.    pt2 to four quadrant points of the circle respectively.
  1537. -*/
  1538. /*************************************************************************/
  1539. static ap_Bool
  1540. /*FCN*/create_arrowhead(pt1, pt2, radius, layer, ltype, color, sset)
  1541.   ads_point  pt1, pt2;
  1542.   ads_real   radius;
  1543.   char  *layer, *ltype;
  1544.   int   color;
  1545.   ads_name  sset;
  1546. {
  1547.     ads_point  pd, pe, pw, ps, pn, xdir, ydir, norm;
  1548.     ads_name   ent;
  1549.     int  stat;
  1550.  
  1551.     sub_vector(pt2, pt1, pd);
  1552.     normalize_vector(pd, norm);
  1553.     get_xdir_from_zdir(norm, pd);
  1554.     normalize_vector(pd, xdir);
  1555.     cross_vector(norm, xdir, pd);
  1556.     normalize_vector(pd, ydir);
  1557.  
  1558.     pe[X] = pt1[X] + radius * xdir[X];
  1559.     pe[Y] = pt1[Y] + radius * xdir[Y];
  1560.     pe[Z] = pt1[Z] + radius * xdir[Z];
  1561.  
  1562.     pw[X] = pt1[X] - radius * xdir[X];
  1563.     pw[Y] = pt1[Y] - radius * xdir[Y];
  1564.     pw[Z] = pt1[Z] - radius * xdir[Z];
  1565.  
  1566.     pn[X] = pt1[X] + radius * ydir[X];
  1567.     pn[Y] = pt1[Y] + radius * ydir[Y];
  1568.     pn[Z] = pt1[Z] + radius * ydir[Z];
  1569.  
  1570.     ps[X] = pt1[X] - radius * ydir[X];
  1571.     ps[Y] = pt1[Y] - radius * ydir[Y];
  1572.     ps[Z] = pt1[Z] - radius * ydir[Z];
  1573.  
  1574.     stat = create_circle(pt1, radius, norm, layer, ltype, color, ent);
  1575.     if (stat == TRUE)
  1576.         ads_ssadd(ent, sset, sset);
  1577.     stat = create_line(pt2, pe, layer, ltype, color, ent);
  1578.     if (stat == TRUE)
  1579.         ads_ssadd(ent, sset, sset);
  1580.     stat = create_line(pt2, pn, layer, ltype, color, ent);
  1581.     if (stat == TRUE)
  1582.         ads_ssadd(ent, sset, sset);
  1583.     stat = create_line(pt2, ps, layer, ltype, color, ent);
  1584.     if (stat == TRUE)
  1585.         ads_ssadd(ent, sset, sset);
  1586.     stat = create_line(pt2, pw, layer, ltype, color, ent);
  1587.     if (stat == TRUE)
  1588.         ads_ssadd(ent, sset, sset);
  1589.     return TRUE;
  1590. }
  1591.  
  1592. /*************************************************************************/
  1593. /* .doc create_line() */
  1594. /*+
  1595.    This routine creates a line from point p1 to p2 in wcs
  1596.    with specified layer, ltype and color attribute.
  1597.    The line is inserted into AutoCAD database by ads_entmake().
  1598. -*/
  1599. /*************************************************************************/
  1600. static ap_Bool
  1601. /*FCN*/create_line( p1, p2, layer, ltype, color, ename)
  1602.   ads_point p1, p2;
  1603.   char *layer, *ltype;
  1604.   int  color;
  1605.   ads_name  ename;
  1606. {
  1607.     int  stat;
  1608.     struct resbuf pth,
  1609.                   layh,               /* layer */
  1610.                   ltph,               /* line type */
  1611.                   pcol,               /* color */
  1612.                   ptk,                /* thickness */
  1613.                   p1h,
  1614.                   p2h;
  1615.  
  1616.     pth.rbnext = &layh;
  1617.     layh.rbnext = <ph;
  1618.     ltph.rbnext = &pcol;
  1619.     pcol.rbnext = &ptk;
  1620.     ptk.rbnext = &p1h;
  1621.     p1h.rbnext = &p2h;
  1622.     p2h.rbnext = NULL;
  1623.  
  1624.     pth.restype = GC_STRING;
  1625.     pth.resval.rstring = /*MSG0*/"LINE";
  1626.     layh.restype = GC_LAYER;
  1627.     layh.resval.rstring = layer;
  1628.     ltph.restype = GC_LTYPE;
  1629.     ltph.resval.rstring = ltype;
  1630.     pcol.restype = GC_COLOR;
  1631.     pcol.resval.rint = color;
  1632.     ptk.restype = GC_THICK;
  1633.     ptk.resval.rreal = 0.0;
  1634.     pth.restype = GC_STRING;
  1635.     pth.resval.rstring = /*MSG0*/"LINE";
  1636.  
  1637.     p1h.restype = GC_XCOORD;
  1638.     p1h.resval.rpoint[X] = p1[X];
  1639.     p1h.resval.rpoint[Y] = p1[Y];
  1640.     p1h.resval.rpoint[Z] = p1[Z];
  1641.  
  1642.     p2h.restype = GC_XPOS;
  1643.     p2h.resval.rpoint[X] = p2[X];
  1644.     p2h.resval.rpoint[Y] = p2[Y];
  1645.     p2h.resval.rpoint[Z] = p2[Z];
  1646.  
  1647.     stat = ads_entmake(&pth);
  1648.     if (stat != RTNORM) {
  1649.         ads_printf (/*MSG40*/"\nCan't make a line.\n");
  1650.         return FALSE;
  1651.     }
  1652.     ads_entlast(ename);
  1653.     return TRUE;
  1654. }
  1655.  
  1656. /*************************************************************************/
  1657. /* .doc create_circle() */
  1658. /*+
  1659.    This routine creates a circle of a given radius at  
  1660.    specified center and normal direction. The circle
  1661.    has specified layer, ltype and color attribute.
  1662.    The circle is inserted into AutoCAD database by ads_entmake().
  1663. -*/
  1664. /*************************************************************************/
  1665. static ap_Bool
  1666. /*FCN*/create_circle(cen, r, norm, layer, ltype, color, ename)
  1667.   ads_point cen;
  1668.   ads_real r;
  1669.   ads_point  norm;
  1670.   char  *layer, *ltype;
  1671.   int  color;
  1672.   ads_name  ename;
  1673. {
  1674.     int  stat;
  1675.     ads_point ecs_cen;
  1676.     struct resbuf circh,
  1677.                   layh,               /* layer */
  1678.                   ltph,               /* line type */
  1679.                   pcol,               /* color */
  1680.                   ptk,                /* thickness */
  1681.                   cenh,
  1682.                   radh,
  1683.                   normh;
  1684.  
  1685.     circh.rbnext = &layh;
  1686.     layh.rbnext = <ph;
  1687.     ltph.rbnext = &pcol;
  1688.     pcol.rbnext = &ptk;
  1689.     ptk.rbnext = &cenh;
  1690.     cenh.rbnext = &radh;
  1691.     radh.rbnext = &normh;
  1692.     normh.rbnext = NULL;
  1693.  
  1694.     circh.restype = GC_STRING;
  1695.     circh.resval.rstring = /*MSG0*/"CIRCLE";
  1696.     layh.restype = GC_LAYER;
  1697.     layh.resval.rstring = layer;
  1698.     ltph.restype = GC_LTYPE;
  1699.     ltph.resval.rstring = ltype;
  1700.     pcol.restype = GC_COLOR;
  1701.     pcol.resval.rint = color;
  1702.     ptk.restype = GC_THICK;
  1703.     ptk.resval.rreal = 0.0;
  1704.  
  1705.     cenh.restype = GC_XCOORD;
  1706.     trans_wc2ec(cen, norm, FALSE, ecs_cen);
  1707.     cenh.resval.rpoint[X] = ecs_cen[X];
  1708.     cenh.resval.rpoint[Y] = ecs_cen[Y];
  1709.     cenh.resval.rpoint[Z] = ecs_cen[Z];
  1710.  
  1711.     radh.restype = GC_RREAL;
  1712.     radh.resval.rreal = r;
  1713.  
  1714.     normh.restype = GC_NORMAL;
  1715.     normh.resval.rpoint[X] = norm[X];
  1716.     normh.resval.rpoint[Y] = norm[Y];
  1717.     normh.resval.rpoint[Z] = norm[Z];
  1718.  
  1719.     stat = ads_entmake(&circh);
  1720.     if (stat != RTNORM) {
  1721.         ads_printf (/*MSG41*/"\nCan't make a circle.\n");
  1722.         return FALSE;
  1723.     }
  1724.     ads_entlast(ename);
  1725.     return TRUE;
  1726. }
  1727.  
  1728.  
  1729. /* EOF */
  1730.