home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 13 / 13.iso / p / p024 / 12.img / ADS2.LIB / TADC.C < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-12  |  24.5 KB  |  792 lines

  1. /* Next available MSG number is  31 */
  2.  
  3. /*    TADC.C
  4.       ¬⌐┼v (C) 1991-1992  Autodesk ñ╜Ñq
  5.  
  6.       Ñ╗│n┼ΘºK╢O¿╤▒z╢iªµÑ⌠ª≤Ñ╬│~╗▌¿D¬║½■¿⌐íB¡╫º∩ñ╬╡oªµ, ª²¼O░╚╜╨┐φ┤`ñU¡z
  7.       ¡∞½h :
  8.  
  9.       1)  ñWªC¬║¬⌐┼v│qºi░╚╗▌ÑX▓{ªb¿Cñ@Ñ≈½■¿⌐∙╪íC
  10.       2)  ¼█├÷¬║╗í⌐·ñσÑ≤ñ]Ñ▓╢╖⌐·╕ⁿ¬⌐┼v│qºiñ╬Ñ╗╢╡│\Ñi│qºiíC
  11.  
  12.       Ñ╗│n┼Θ╢╚┤ú¿╤º@¼░└│Ñ╬ñW¬║░╤ª╥, ª╙Ñ╝┴n⌐·⌐╬┴⌠ºtÑ⌠ª≤½O├╥; ╣∩⌐≤Ñ⌠ª≤»S«φ
  13.       Ñ╬│~ñº╛A║┘⌐╩, ÑHñ╬░╙╖~╛P░Γ⌐╥┴⌠ºtÑX¿π¬║½O├╥, ªbª╣ñ@╖ºñ⌐ÑHº_╗{íC
  14.  
  15.  
  16.  
  17.     Some example ADS code demonstrating the use of ads_tablet() and
  18.     tablet mode in general.
  19.  
  20.     DIGPASS shows how to establish a "pass-through" tablet
  21.     transformation in any UCS (doing it in world coordinates is
  22.     trivial).  It is used by BILINCAL.
  23.  
  24.     SAMPLE, BILINCAL, and BILINMPT together demonstrate a possible
  25.     implementation of a different tablet transformation than any
  26.     supported directly by AutoCAD.
  27.  
  28.     SAVCAL and RSTCAL constitute a trivial matched pair of functions
  29.     to save the current tablet calibration and subsequently to restore
  30.     it.
  31.  
  32.         Greg Lutz
  33.         May, 1991
  34.  
  35.     The matrix arithmetic in this file holds strictly to the ADS/LISP
  36.     conventions:
  37.  
  38.         1.  m[i][j] is the element in row i, column j of matrix m.
  39.         2.  Transforming vector v by matrix m is done by treating v
  40.             as a column vector and computing m x v.
  41. */
  42.  
  43. #include  <stdio.h>
  44. #include  <math.h>
  45. #include  "adslib.h"
  46.  
  47. /* Special assertion handler for ADS applications. */
  48.  
  49. #ifndef NDEBUG
  50. #define assert(ex) {if (!(ex)){ads_printf( \
  51.                     /*MSG1*/"TADC: ┴n⌐· (%s) Ñó▒╤: └╔«╫íu%sív, \
  52.                     ▓─ %d ªC\n", /*MSG0*/" ex ",__FILE__,__LINE__); \
  53.                     ads_abort(/*MSG2*/"íu┴n⌐·ívÑó▒╤íC");}}
  54. #else
  55. #define assert(ex)
  56. #endif
  57.  
  58. #ifndef GOOD
  59. #define GOOD    0
  60. #define BAD     (-1)
  61. #endif
  62.  
  63.  
  64. /*  Some macros which we need more generally available somewhere, sometime: */
  65.  
  66. #define TYPSTRUC(size)  typedef struct {char dummy[size];}
  67. #define movecnt(c,t,f)  if (1) {TYPSTRUC(c)*_p_; *(_p_)(t) = *(_p_)(f);} else
  68. #define cpy(s,t,f) movecnt(sizeof(s), t, f)
  69.  
  70. #define cpyname(to, from)   cpy(ads_name, to, from)  /* Copy an ads_name */
  71. #define cpypoint(to, from)  cpy(ads_point, to, from) /* Copy an ads_point */
  72.  
  73.  
  74. /*  The arbaxis crossover point: */
  75. #define ARBBOUND  0.015625            /* aka "1/64" */
  76.  
  77. /*  Saved tablet calibration from savcal() */
  78. static struct resbuf *tcal = NULL;
  79.  
  80.  
  81. /*  Local function declarations */
  82.  
  83. int     digpass     _((void));
  84. int     idigpass    _((void));
  85. int     bilincal    _((void));
  86. int     ibilinmpt   _((ads_real *in, ads_real *out));
  87. int     bilinmpt    _((void));
  88. void    cross       _((ads_real *ap, ads_real *bp, ads_real *cp));
  89. void    matxmat     _((ads_matrix cp, ads_matrix ap, ads_matrix bp));
  90. int     normalize   _((ads_real *ap, ads_real *bp));
  91. void    pswap       _((ads_real *p1, ads_real *p2));
  92. int     sample      _((void));
  93. void    dline       _((char *p1str, char *p2str));
  94. void    dvert       _((char *p1str));
  95. int     savcal      _((void));
  96. int     rstcal      _((void));
  97. void    aperror     _((char *routine));
  98.  
  99.  
  100. /*  Table of externally defined functions */
  101. static struct {
  102.     char *fcn_name;
  103.     int (*fcn) _((void));
  104. } fcn_table[] = {
  105.     {/*MSG0*/"digpass", digpass},
  106.     {/*MSG0*/"c:bilincal", bilincal},
  107.     {/*MSG0*/"bilinmpt", bilinmpt},
  108.     {/*MSG0*/"c:sample", sample},
  109.     {/*MSG0*/"savcal", savcal},
  110.     {/*MSG0*/"rstcal", rstcal},
  111. };
  112. #define NFCN    (sizeof fcn_table/sizeof fcn_table[0])
  113.  
  114.  
  115. /*  Calibration points for bilincal() and bilinmpt(): */
  116. static ads_point t[4], p[4];
  117. static int cbrated = FALSE;           /* Not calibrated yet */
  118. static int bilinsgn;                  /* Sign to use in quadratic */
  119.  
  120.  
  121. /* MAIN - the main routine */
  122.  
  123.  
  124. void
  125. /*FCN*/main(argc, argv)
  126.   int argc;
  127.   char *argv[];
  128. {
  129.     int rqcode, rscode, i;
  130.  
  131.     ads_init(argc, argv);             /* Initialize the interface */
  132.     rscode = RSRSLT;                  /* Initial request code */
  133.     for ( ;; ) {
  134.         /*  Send link partner a result code, get back a request: */
  135.         if ((rqcode = ads_link(rscode)) < 0) {
  136.  
  137.             printf(/*MSG1*/"TADC: Ñ╤ ads_link() ╢╟ª^¬║ñú¿╬íu╗▌¿D╜Xív= %d\n", rqcode);
  138.             /*  Note the printf, not ADS_printf */
  139.             fflush(stdout);
  140.             exit(1);
  141.         }
  142.  
  143.         /*  Handle the request code  */
  144.         switch (rqcode) {
  145.         case RQXLOAD:
  146.             /*  (Re-)define functions  */
  147.             rscode = RSRSLT;
  148.             for (i = 0; i < NFCN && rscode == RSRSLT; i++)
  149.                 if (ads_defun(fcn_table[i].fcn_name, i) != RTNORM
  150.                     || ads_regfunc(fcn_table[i].fcn, i) != RTNORM
  151.                    )
  152.                 rscode = RSERR;       /* Result code = error */
  153.             if (rscode == RSRSLT)
  154.                 /*  NOTE!!!  The following line will have unknown
  155.                     consequences if this application is loaded via
  156.                     acad.ads!!!!*/
  157.                 ads_printf(/*MSG2*/"\n\n┴ΣñJ SAMPLE ¿╙░⌡ªµíu└│Ñ╬╡{ªíív┤·╕╒\n");
  158.             break;
  159.         case RQSUBR:
  160.             i = ads_getfuncode();
  161.             rscode = (*fcn_table[i].fcn)();
  162.             break;
  163.         }
  164.     }
  165. }
  166.  
  167.  
  168. /*  DIGPASS  --  External function to establish a tablet transformation
  169.                  such that a tablet point with coordinates (x,y) maps
  170.                  into the point (x,y) in the current UCS.  Returns
  171.                  RSRSLT or RSERR.  */
  172.  
  173. static int
  174. /*FCN*/digpass()
  175. {
  176.     register int stat = idigpass();
  177.     ads_retvoid();
  178.     return stat;
  179. }
  180.  
  181.  
  182.  
  183. /*  IDIGPASS  --  Internal function which does the actual work of
  184.                   DIGPASS.  Returns RSRSLT or RSERR.  */
  185. static int
  186. /*FCN*/idigpass()
  187. {
  188.     ads_matrix uwxform, wlxform, ulxform;
  189.     static ads_point ey = {0.0, 1.0, 0.0},
  190.                      ez = {0.0, 0.0, 1.0};
  191.     ads_point ucszdir;
  192.     struct resbuf uo, ux, uy, *ttl;
  193.     int i;
  194.  
  195.     /*  Fetch the origin, x-axis direction, and y-axis direction of the
  196.         current UCS, and compute the z-axis direction  */
  197.  
  198.     ads_getvar(/*MSG0*/"UCSORG", &uo);
  199.     ads_getvar(/*MSG0*/"UCSXDIR", &ux);
  200.     ads_getvar(/*MSG0*/"UCSYDIR", &uy);
  201.     cross(ucszdir, ux.resval.rpoint, uy.resval.rpoint);
  202.  
  203.     /*  Use these to build the transformation from UCS to world coords
  204.         (the above axis vectors, and the origin, become the COLUMNS of
  205.         this matrix).  */
  206.  
  207.     for (i = X; i <= Z; i++) {
  208.         uwxform[i][X] = ux.resval.rpoint[i];
  209.         uwxform[i][Y] = uy.resval.rpoint[i];
  210.         uwxform[i][Z] = ucszdir[i];
  211.         uwxform[i][T] = uo.resval.rpoint[i];
  212.     }
  213.  
  214.     /*  Use the AutoCAD "arbaxis" algorithm to construct the transforma-
  215.         tion from world coordinates to the current LCS (determined
  216.         solely by the UCS Z-axis direction) */
  217.  
  218.     if (fabs(ucszdir[X]) < ARBBOUND && fabs(ucszdir[Y]) < ARBBOUND)
  219.         cross(wlxform[X], ey, ucszdir);
  220.     else
  221.         cross(wlxform[X], ez, ucszdir);
  222.     normalize(wlxform[X], wlxform[X]);
  223.     cross(wlxform[Y], ucszdir, wlxform[X]);
  224.     normalize(wlxform[Y], wlxform[Y]);
  225.     cpypoint(wlxform[Z], ucszdir);
  226.     for (i = X; i <= Z; i++)
  227.         wlxform[i][T] = 0.0;          /* No translation */
  228.  
  229.     /*  Compose those two matrices to get the UCS-to-LCS transformation  */
  230.     matxmat(ulxform, wlxform, uwxform);
  231.  
  232.     /*  Transformation's Z-column should be (0,0,1): */
  233.  
  234. #if 0  /* These asserts cause compilation errors on the Sparc... */
  235.     assert(fabs(ulxform[X][Z]) < 1.0E-10);
  236.     assert(fabs(ulxform[Y][Z]) < 1.0E-10);
  237.     assert(fabs(ulxform[Z][Z] - 1.0) < 1.0E-10);
  238. #endif
  239.  
  240.     /*  That's it: if the tablet sends in point (x,y), this matrix will
  241.         act as if x and y were in UCS coords and transform them into the
  242.         corresponding point (u,v) in LCS coords.  AutoCAD needs to turn
  243.         that back into UCS coordinates, and (x,y) is the pair it comes
  244.         up with.  We just need to throw away the Z-column (which we just
  245.         assured is zero anyway) and pass the rows to ads_tablet().  */
  246.  
  247.     ulxform[X][Z] = ulxform[X][T];
  248.     ulxform[Y][Z] = ulxform[Y][T];
  249.     ttl = ads_buildlist(RTSHORT, 1,
  250.                         RT3DPOINT, ulxform[X],
  251.                         RT3DPOINT, ulxform[Y],
  252.                         RT3DPOINT, ez,
  253.                         RT3DPOINT, ucszdir,
  254.                         RTNONE);
  255.     i = ads_tablet(ttl, NULL);        /* Send it to AutoCAD */
  256.     ads_relrb(ttl);
  257.     if (i != RTNORM) {
  258.         aperror(/*MSG0*/"ads_tablet");
  259.         return RSERR;
  260.     } else
  261.         return RSRSLT;
  262. }
  263.  
  264.  
  265.  
  266. /*  Some geometric utility functions  */
  267.  
  268.  
  269. /* CROSS - cross product of two vectors, a = b X c */
  270.  
  271. void
  272. /*FCN*/cross(ap, bp, cp)
  273.   ads_real *ap;
  274.   ads_real *bp, *cp;
  275. {
  276.     ap[X] = bp[Y] * cp[Z] - bp[Z] * cp[Y];
  277.     ap[Y] = bp[Z] * cp[X] - bp[X] * cp[Z];
  278.     ap[Z] = bp[X] * cp[Y] - bp[Y] * cp[X];
  279. }
  280.  
  281.  
  282. /*  MATXMAT -- Multiply two matrices.  The matrix type, ads_matrix,
  283.                may be any array type with at least three rows and four
  284.                columns.  */
  285.  
  286. void
  287. /*FCN*/matxmat(matrixc, matrixa, matrixb)
  288.   ads_matrix matrixc, matrixa, matrixb;
  289. {
  290.     int i, k;
  291.  
  292.     for (i = X; i <= Z; i++)
  293.         for (k = X; k <= Z; k++)
  294.             matrixc[i][k] = matrixa[i][X] * matrixb[X][k] +
  295.                             matrixa[i][Y] * matrixb[Y][k] +
  296.                             matrixa[i][Z] * matrixb[Z][k];
  297.     for (i = X; i <= Z; i++)
  298.         matrixc[i][T] = matrixa[i][X] * matrixb[X][T] +
  299.                         matrixa[i][Y] * matrixb[Y][T] +
  300.                         matrixa[i][Z] * matrixb[Z][T] +
  301.                         matrixa[i][T];
  302. }
  303.  
  304.  
  305. /*  NORMALIZE  --  Normalize a vector: ap = unit vector in direction of bp.
  306.                    Returns BAD if bp is null.  */
  307.  
  308. int
  309. /*FCN*/normalize(ap, bp)
  310.   register ads_real *ap;
  311.   ads_real *bp;
  312. {
  313.     ads_real d;
  314.  
  315.     if ((d = (bp[X] * bp[X] + bp[Y] * bp[Y] + bp[Z] * bp[Z]))
  316.         <= 1.0E-12)
  317.         return BAD;
  318.     d = 1.0 / sqrt(d);
  319.     ap[X] = bp[X] * d;
  320.     ap[Y] = bp[Y] * d;
  321.     ap[Z] = bp[Z] * d;
  322.     return GOOD;
  323. }
  324.  
  325.  
  326.  
  327. /*  BILINCAL  --  Calibrate the digitizer for a bilinear mapping, i.e.,
  328.                   a transformation which maps a quadrilateral on the
  329.                   tablet into a quadrilateral in model space, with edges
  330.                   mapping linearly into edges and interior points
  331.                   mapping as follows: given point P inside the
  332.                   quadrilateral on the tablet, draw a line which passes
  333.                   through P and divides the two quadrilateral edges it
  334.                   intersects in equal ratios.  Draw a line between the
  335.                   two corresponding edges of the target quadrilateral
  336.                   (in model space) which divides those edges in the same
  337.                   ratio as that determined above.  Repeat this
  338.                   operation, constructing a line between the other pair
  339.                   of edges of the tablet quadrilateral, and similarly
  340.                   draw a line between the corresponding edges of the
  341.                   target quadrilateral dividing them in the same ratio.
  342.                   P maps into the point at the intersection of the two
  343.                   lines thus drawn in the target quadrilateral.  This is
  344.                   essentially the 2-space analogue of a Coon's patch.
  345.  
  346.                   BILINCAL just determines the mapping by soliciting the
  347.                   calibration points.  bilinmpt(), below, does the hard
  348.                   work.  Note that, though there is one case in which we
  349.                   complain about degenerate calibration points, there
  350.                   are many other cases we miss entirely.  This code does
  351.                   not purport to be very robust with respect to bad
  352.                   input points.  */
  353.  
  354. static int
  355. /*FCN*/bilincal()
  356. {
  357.     struct resbuf tm;
  358.     int i;
  359.     char sb[100];
  360.     ads_point p11, p12, p0;
  361.  
  362.     /*  Make AutoCAD pass us raw digitizer coordinates  */
  363.  
  364.     if (digpass() != RSRSLT) {
  365.         ads_fail(/*MSG3*/"╡L¬k½╪Ñ▀íu╝╞ñ╞╗÷ívñº pass-through ºδ¼M (mapping)");
  366.         return RSERR;
  367.     }
  368.  
  369.     /*  Turn Tablet mode on  */
  370.  
  371.     tm.restype = RTSHORT;
  372.     tm.resval.rint = 1;
  373.     ads_setvar(/*MSG0*/"TABMODE", &tm);
  374.     ads_getvar(/*MSG0*/"TABMODE", &tm);
  375.     if (tm.resval.rint == 0) {
  376.         ads_fail(/*MSG4*/"╡L¬k╢}▒╥íu╝╞ª∞¬Oív╝╥ªí");
  377.         return RSERR;
  378.     }
  379.  
  380.     /*  Get the calibration points */
  381.  
  382.     ads_printf(/*MSG5*/"\n╢iªµíu4-┬I  «╒Ñ┐íví╨ bilinear ºδ¼M (mapping) ...");
  383.     for (i = 0; i < 4; i++) {
  384.         tm.resval.rint = 1;
  385.         ads_setvar(/*MSG0*/"TABMODE", &tm);
  386.         sprintf(sb, /*MSG6*/"\n╝╞ñ╞ í╨ ┬I#%d: ", i + 1);
  387.         if (ads_getpoint(NULL, sb, t[i]) != RTNORM)
  388.             return RSERR;
  389.         tm.resval.rint = 0;
  390.         ads_setvar(/*MSG0*/"TABMODE", &tm);   /* Turn Tablet mode off */
  391.         sprintf(sb, /*MSG7*/"\n┐ΘñJ«y╝╨ñ⌐íu┬I #%dív: ", i + 1);
  392.         if (ads_getpoint(NULL, sb, p[i]) != RTNORM)
  393.             return RSERR;
  394.     }
  395.  
  396.     /*  Reorder the points to assure that the last two tablet points
  397.         have different Y-coordinates (to avoid some divisions by zero
  398.         in bilinmpt)  */
  399.  
  400.     if (t[2][Y] == t[3][Y]) {
  401.         if (t[1][Y] != t[3][Y]) {
  402.             pswap(t[1],t[2]);
  403.             pswap(p[1],p[2]);
  404.         } else if (t[0][Y] != t[3][Y]) {
  405.             pswap(t[0],t[2]);
  406.             pswap(p[0],p[2]);
  407.         } else {
  408.             ads_fail(/*MSG8*/"íu⌐Φ¿ε▓úÑ═ív¬║«╒Ñ┐┬I");
  409.             return RSERR;
  410.         }
  411.     }
  412.  
  413.     /*  Figure out which sign to use in the solution of the quadratic
  414.         equation inside bilinmpt, by determining which choice most
  415.         nearly maps one of the calibration points correctly: */
  416.  
  417.     bilinsgn = 1;
  418.     ibilinmpt(t[0], p11);
  419.     bilinsgn = -1;
  420.     ibilinmpt(t[0], p12);
  421.     cpypoint(p0, p[0]);
  422.     p0[Z] = p11[Z] = p12[Z] = 0.0;
  423.     if (ads_distance(p11, p0) < ads_distance(p12, p0))
  424.         bilinsgn = 1;
  425.  
  426.     tm.resval.rint = 1;               /* Exit with Tablet mode ON */
  427.     ads_setvar(/*MSG0*/"TABMODE", &tm);
  428.  
  429.     cbrated = TRUE;
  430.     ads_retvoid();
  431.     return RSRSLT;
  432. }
  433.  
  434.  
  435. /*  BILINMPT  --  External function to perform the bilinear mapping on
  436.                   a point.  Expects a single ads_point as argument,
  437.                   returns an ads_point as value (if successful).  */
  438.  
  439. static int
  440. /*FCN*/bilinmpt()
  441. {
  442.     struct resbuf *args;
  443.     ads_point pprime;
  444.  
  445.     if (!cbrated) {
  446.         ads_fail(/*MSG9*/"Bilinear ºδ¼M (mapping) Ñ╝«╒Ñ┐");
  447.         return RSERR;
  448.     }
  449.     args = ads_getargs();
  450.     if (args->rbnext != NULL ||
  451.         (args->restype != RTPOINT && args->restype != RT3DPOINT)) {
  452.         ads_fail(/*MSG10*/"ñ⌐ bilinmpt ¬║íuñ▐╝╞ívñúÑ┐╜T");
  453.         return RSERR;
  454.     }
  455.  
  456.     /*  Call function to do the actual mathematics */
  457.  
  458.     if (ibilinmpt(args->resval.rpoint, pprime) != RSRSLT) {
  459.         ads_fail(/*MSG11*/"bilinmpt ╡L¬k┬α╕m┬Iª∞");
  460.         return RSERR;
  461.     } else {
  462.         pprime[Z] = 0.0;
  463.         ads_retpoint(pprime);
  464.         return RSRSLT;
  465.     }
  466. }
  467.  
  468.  
  469. /*  IBILINMPT  --  Perform the bilinear transformation on point "inpt",
  470.                    leaving the result in "pprime"  */
  471.  
  472. static int
  473. /*FCN*/ibilinmpt(inpt, pprime)
  474.   ads_real *inpt, *pprime;
  475. {
  476.     ads_real x, y, xb, yb, xt, yt, a, b, c, r1, r2;
  477.     ads_real x1, x2, x3, x4, y1, y2, y3, y4;
  478.  
  479.     x = inpt[X];
  480.     y = inpt[Y];
  481.  
  482.     /*  Ok, Mathematica time.  Pardon the dearth of details, and the
  483.         utter lack of simplification.  */
  484.  
  485.     /*  Put the four corners of the tablet quadrilateral in individual
  486.         variables (this was helpful during debugging)  */
  487.  
  488.     x1 = t[0][X];
  489.     x2 = t[1][X];
  490.     x3 = t[2][X];
  491.     x4 = t[3][X];
  492.     y1 = t[0][Y];
  493.     y2 = t[1][Y];
  494.     y3 = t[2][Y];
  495.     y4 = t[3][Y];
  496.  
  497.     /*  Compute the three coefficients in the quadratic equation whose
  498.         solution is yt */
  499.  
  500.     c = x2*y*y3*y3 - x4*y*y3*y3 - x*y2*y3*y3 + x4*y2*y3*y3 - x1*y*y3*y4 -
  501.         x2*y*y3*y4 + x3*y*y3*y4 + x4*y*y3*y4 + x*y1*y3*y4 - x4*y1*y3*y4 +
  502.         x*y2*y3*y4 - x3*y2*y3*y4 + x1*y*y4*y4 - x3*y*y4*y4 - x*y1*y4*y4 +
  503.         x3*y1*y4*y4;
  504.  
  505.     b = x1*y*y3 - x2*y*y3 - x3*y*y3 + x4*y*y3 - x*y1*y3 + x4*y1*y3 +
  506.         x*y2*y3 + x3*y2*y3 - 2*x4*y2*y3 + x*y3*y3 - x2*y3*y3 - x1*y*y4 +
  507.         x2*y*y4 + x3*y*y4 - x4*y*y4 + x*y1*y4 - 2*x3*y1*y4 + x4*y1*y4 -
  508.         x*y2*y4 + x3*y2*y4 - 2*x*y3*y4 + x1*y3*y4 + x2*y3*y4 + x*y4*y4 -
  509.         x1*y4*y4;
  510.  
  511.     a = x3*y1 - x4*y1 - x3*y2 + x4*y2 - x1*y3 + x2*y3 + x1*y4 -
  512.         x2*y4;
  513.  
  514.     /*  The quadratic equation for yt has two solutions; the choice of
  515.         which to use is made externally by an appropriate setting of
  516.         bilinsgn.  */
  517.  
  518.     yt = (-b + bilinsgn * sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
  519.  
  520.     /*  Fill in xb, yb, and xt.  When finished, (xb,yb) will be a point
  521.         on the lower edge of the quadrilateral, and (xt,yt) will be a
  522.         point on its upper edge.  They will divide their respective
  523.         edges in the same ratio, and inpt(x,y) will lie on the line
  524.         joining them.  */
  525.  
  526.     xb = (x2*y3 - x1*y4 + x1*yt - x2*yt) / (y3 - y4);
  527.     yb = (y2*y3 - y1*y4 + y1*yt - y2*yt) / (y3 - y4);
  528.     xt = (x4*y3 - x3*y4 + x3*yt - x4*yt) / (y3 - y4);
  529.     if (x2 == x1)
  530.         r1 = (y2 - yb) / (y2 - y1);
  531.     else
  532.         r1 = (x2 - xb) / (x2 - x1);
  533.     if (xt == xb)
  534.         r2 = (yt - y) / (yt - yb);
  535.     else
  536.         r2 = (xt - x) / (xt - xb);
  537.  
  538.     /*  The r1 and r2 are the "Coon's patch" coordinates of the input
  539.         point; transfer them to the output.  */
  540.  
  541.     xb = r1 * p[0][X] + (1 - r1) * p[1][X];
  542.     yb = r1 * p[0][Y] + (1 - r1) * p[1][Y];
  543.     xt = r1 * p[2][X] + (1 - r1) * p[3][X];
  544.     yt = r1 * p[2][Y] + (1 - r1) * p[3][Y];
  545.     pprime[X] = r2 * xb + (1 - r2) * xt;
  546.     pprime[Y] = r2 * yb + (1 - r2) * yt;
  547.     return RSRSLT;
  548. }
  549.  
  550.  
  551.  
  552. /*  PSWAP  --  Swap two 2D points */
  553.  
  554. static void
  555. /*FCN*/pswap(p1, p2)
  556.   ads_real *p1, *p2;
  557. {
  558.     ads_real t;
  559.  
  560.     t = p1[X];
  561.     p1[X] = p2[X];
  562.     p2[X] = t;
  563.     t = p1[Y];
  564.     p1[Y] = p2[Y];
  565.     p2[Y] = t;
  566. }
  567.  
  568.  
  569.  
  570. /*  SAMPLE  --  Draw a sample figure for exercising the bilinear
  571.                 mapping.  This function is not religious about
  572.                 restoring system variables to previous settings.  */
  573.  
  574. static int
  575. /*FCN*/sample()
  576. {
  577.     char junk[20];
  578.     struct resbuf svb;
  579.     int i;
  580.     static char *msg[] = {
  581.         "",
  582.         /*MSG12*/"We will now draw a sample figure which you can use to test",
  583.         /*MSG13*/"bilinear tablet transformations.  When complete, plot this",
  584.         /*MSG14*/"figure and attach it to your tablet surface.  Replace one of",
  585.         /*MSG15*/"the items in your BUTTONS menu with the following:",
  586.         "",
  587.         /*MSG0*/"^P(bilinmpt (getpoint)) \\^P",
  588.         "",
  589.         /*MSG16*/"Then enter BILINCAL, and when you are prompted for points,",
  590.         /*MSG17*/"digitize (with the normal pick button) the corners of the",
  591.         /*MSG18*/"quadrilateral in this order:",
  592.         "",
  593.         /*MSG19*/"   lower left, lower right, upper left, upper right",
  594.         "",
  595.         /*MSG20*/"Give them coordinates of (0,0), (16,0), (0,16), and (16,16),",
  596.         /*MSG21*/"respectively.  Then, using the button to which you attached",
  597.         /*MSG22*/"the above menu item, digitize lines connecting the four",
  598.         /*MSG23*/"corners of the quadrilateral.  To see what this transformation",
  599.         /*MSG24*/"does to straight lines, enter a polyline following the curved",
  600.         /*MSG25*/"path in the figure and another polyline along the straight",
  601.         /*MSG26*/"diagonal path.",
  602.         "",
  603.         NULL
  604.     };
  605.  
  606.     /*  Print the message */
  607.  
  608.     ads_textscr();
  609.     for (i = 0; msg[i] != NULL; i++)
  610.         ads_printf("%s\n", msg[i]);
  611.     if (ads_getstring(0, /*MSG27*/"½÷ <Return> ┴Σ─~─≥: ", junk) != RTNORM)
  612.         return RSERR;
  613.  
  614.     /*  Draw the figure */
  615.  
  616.     ads_graphscr();
  617.     ads_getvar(/*MSG0*/"CMDECHO", &svb);
  618.     svb.resval.rint = 0;
  619.     ads_setvar(/*MSG0*/"CMDECHO", &svb);
  620.  
  621.     ads_command(RTSTR, /*MSG0*/"ZOOM", RTSTR, /*MSG0*/"W", RTSTR, "-10,0",
  622.                 RTSTR, "1.5,12", RTNONE);
  623.  
  624.     dline("1.0,1.0", "-7.0,1.0");
  625.     dline("-7.0,1.0", "-9.0,8.0");
  626.     dline("-9.0,8.0", "-6.0,11.0");
  627.     dline("-6.0,11.0", "1.0,1.0");
  628.     dline("0.5,1.0", "-6.1875,10.8125");
  629.     dline("-6.375,10.625", "0.0,1.0");
  630.     dline("-0.5,1.0", "-6.5625,10.4375");
  631.     dline("-6.75,10.25", "-1.0,1.0");
  632.     dline("-1.5,1.0", "-6.9375,10.0625");
  633.     dline("-7.125,9.875", "-2.0,1.0");
  634.     dline("-2.5,1.0", "-7.3125,9.6875");
  635.     dline("-7.5,9.5", "-3.0,1.0");
  636.     dline("-3.5,1.0", "-7.6875,9.3125");
  637.     dline("-7.875,9.125", "-4.0,1.0");
  638.     dline("-4.5,1.0", "-8.0625,8.9375");
  639.     dline("-8.25,8.75", "-5.0,1.0");
  640.     dline("-5.5,1.0", "-8.4375,8.5625");
  641.     dline("-8.625,8.375", "-6.0,1.0");
  642.     dline("-6.5,1.0", "-8.8125,8.1875");
  643.     dline("0.5625,1.625", "-7.125,1.4375");
  644.     dline("-7.25,1.875", "0.125,2.25");
  645.     dline("-0.3125,2.875", "-7.375,2.3125");
  646.     dline("-7.5,2.75", "-0.75,3.5");
  647.     dline("-1.1875,4.125", "-7.625,3.1875");
  648.     dline("-7.75,3.625", "-1.625,4.75");
  649.     dline("-2.0625,5.375", "-7.875,4.0625");
  650.     dline("-8.0,4.5", "-2.5,6.0");
  651.     dline("-2.9375,6.625", "-8.125,4.9375");
  652.     dline("-8.25,5.375", "-3.375,7.25");
  653.     dline("-3.8125,7.875", "-8.375,5.8125");
  654.     dline("-8.5,6.25", "-4.25,8.5");
  655.     dline("-4.6875,9.125", "-8.625,6.6875");
  656.     dline("-8.75,7.125", "-5.125,9.75");
  657.     dline("-5.5625,10.375", "-8.875,7.5625");
  658.  
  659.     ads_command(RTSTR,/*MSG0*/"PLINE", RTNONE);
  660.     dvert("-7.0,1.0");
  661.     dvert("-6.7636,1.2868");
  662.     dvert("-6.6445,1.4492");
  663.     dvert("-6.3673,1.8608");
  664.     dvert("-6.3281,1.9219");
  665.     dvert("-6.1843,2.1674");
  666.     dvert("-6.0508,2.418");
  667.     dvert("-5.926,2.6764");
  668.     dvert("-5.8125,2.9375");
  669.     dvert("-5.7069,3.2089");
  670.     dvert("-5.6133,3.4805");
  671.     dvert("-5.5271,3.7649");
  672.     dvert("-5.4531,4.0469");
  673.     dvert("-5.3866,4.3442");
  674.     dvert("-5.332,4.6367");
  675.     dvert("-5.2853,4.947");
  676.     dvert("-5.25,5.25");
  677.     dvert("-5.2231,5.573");
  678.     dvert("-5.207,5.8867");
  679.     dvert("-5.2001,6.2224");
  680.     dvert("-5.2031,6.5469");
  681.     dvert("-5.2163,6.8951");
  682.     dvert("-5.2383,7.2305");
  683.     dvert("-5.2716,7.5911");
  684.     dvert("-5.3125,7.9375");
  685.     dvert("-5.3659,8.3105");
  686.     dvert("-5.4258,8.668");
  687.     dvert("-5.4993,9.0532");
  688.     dvert("-5.5781,9.4219");
  689.     dvert("-5.5911,9.477");
  690.     dvert("-5.7695,10.1992");
  691.     dvert("-5.8442,10.4783");
  692.     dvert("-6.0,11.0");
  693.     ads_command(RTSTR, "", RTNONE);
  694.  
  695.     dline("-7.0,1.0", "-6.0,11.0");   /* Draw the "diagonal" */
  696.  
  697.     svb.resval.rint = 1;
  698.     ads_setvar(/*MSG0*/"CMDECHO", &svb);      /* Turn command echo back on. */
  699.  
  700.     ads_retvoid();
  701.     return RSRSLT;
  702. }
  703.  
  704.  
  705. /*  DLINE  --  Draw a line.  */
  706.  
  707. static void
  708. /*FCN*/dline(p1str, p2str)
  709.   char *p1str, *p2str;
  710. {
  711.     ads_command(RTSTR, /*MSG0*/"LINE", RTSTR, p1str, RTSTR, p2str, RTSTR, "",
  712.                 RTNONE);
  713. }
  714.  
  715. /*  DVERT  --  Draw one vertex of a Polyline */
  716.  
  717. static void
  718. /*FCN*/dvert(p1str)
  719.   char *p1str;
  720. {
  721.     ads_command(RTSTR, p1str, RTNONE);
  722. }
  723.  
  724.  
  725. /*  SAVCAL  --  External function to save the current tablet calibration
  726.                 locally.  */
  727.  
  728. static int
  729. /*FCN*/savcal()
  730. {
  731.     int stat;
  732.     struct resbuf *rb;
  733.  
  734.     /*  Free any previously saved calibration  */
  735.     if (tcal != NULL) {
  736.         ads_relrb(tcal);
  737.         tcal = NULL;
  738.     }
  739.  
  740.     /*  Build argument list to request current calibration from
  741.         ads_tablet()  */
  742.     rb = ads_buildlist(RTSHORT, 0, RTNONE);
  743.     stat = ads_tablet(rb, &tcal);
  744.     ads_relrb(rb);
  745.  
  746.     if (stat == RTNORM)
  747.         stat = RSRSLT;
  748.     else {
  749.         aperror(/*MSG0*/"ads_tablet");
  750.         stat = RSERR;
  751.     }
  752.     ads_retvoid();
  753.     return stat;
  754. }
  755.  
  756.  
  757. /*  RSTCAL  --  External function to restore a tablet calibration
  758.                 previously saved by savcal().  */
  759.  
  760. static int
  761. /*FCN*/rstcal()
  762. {
  763.     int stat = RSERR;
  764.  
  765.     if (tcal == NULL)
  766.         ads_fail(/*MSG28*/"rstcal: savcal Ñ╝└xªsÑ⌠ª≤íu«╒Ñ┐ív\n");
  767.     else if (ads_tablet(tcal, NULL) != RTNORM)
  768.         aperror(/*MSG0*/"ads_tablet");
  769.     else
  770.         stat = RSRSLT;
  771.     ads_retvoid();
  772.     return stat;
  773. }
  774.  
  775.  
  776. /*  APERROR  --  Poor brother to ads_perr:  display the value of
  777.                  errno associated with a given ADS routine.  */
  778. static void
  779. /*FCN*/aperror(routine)
  780.   char *routine;
  781. {
  782.     struct resbuf gvb;
  783.     char msgbuf[60];
  784.  
  785.     ads_getvar(/*MSG0*/"ERRNO", &gvb);
  786.     if (routine == NULL)
  787.         sprintf(msgbuf, /*MSG29*/"┐∙╗~ %d", gvb.resval.rint);
  788.     else
  789.         sprintf(msgbuf, /*MSG30*/"┐∙╗~ %d í╨ ╖╜ª█ %s", gvb.resval.rint, routine);
  790.     ads_fail(msgbuf);
  791. }
  792.