/* Next available MSG number is   9 */

/*    

   ARBMAT.C

   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994 by Autodesk, Inc.
 
   Permission to use, copy, modify, and distribute this software in 
   object code form for any purpose and without fee is hereby granted, 
   provided that the above copyright notice appears in all copies and 
   that both that copyright notice and the limited warranty and 
   restricted rights notice below appear in all supporting 
   documentation.
 
   AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.  
   AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF 
   MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
   DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE 
   UNINTERRUPTED OR ERROR FREE.
 
   Use, duplication, or disclosure by the U.S. Government is subject to 
   restrictions set forth in FAR 52.227-19 (Commercial Computer 
   Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) 
   (Rights in Technical Data and Computer Software), as applicable.
    
   .
      DESCRIPTION:

      Prints out the axis of the entity coordinate system (ECS)
      specified by the entity's X,Y and Z DXF fields.

      Usage: (arbmat X Y Z)

      where X,Y and Z are real numbers.

      For more information on ECS and the arbitrary axis  
      algorithm see the AutoCAD Reference manual.
*/

#include <stdio.h>                    /* printf, sprintf, sscanf etc... */
#include <math.h>                     /* for sqrt() */
#include "adslib.h"

#define ExtFuncCount    1             /* Must increase this count if new 
                                         external functions are added */

char *exfun[] = {/*MSG0*/"arbmat"};   /* No "C:" -- to be called as an AutoLISP
                                         function, not as an AutoCAD command */

#define GOOD       0
#define BAD        (-1)

#define ARBBOUND   0.015625           /*  1/64th  */
#define EPS        (1E-10)

typedef double real;
typedef real vec[3];
typedef vec matrix[3];

void    main            _((int, char **));
int     funcload        _((void));
int     dofun           _((void));
int     arbmat          _((matrix, vec));
int     univec          _((vec, vec));
real    rabs            _((real));
void    cross           _((vec, vec, vec));
void    prtaxis         _((char *, vec));


void
main(argc, argv)
  int argc;
  char *argv[];

{
    int stat;
    short scode = RSRSLT;             /* This is the default result code */
    char errmsg[80];

    ads_init(argc, argv);             /* Initialize the interface */

    for ( ;; ) {                      /* Note loop conditions */

        if ((stat = ads_link(scode)) < 0) {
            sprintf(errmsg,
                    /*MSG1*/"ARBMAT: bad status from ads_link() = %d\n",
                    stat);

            /* Can't use ads_printf to display 
               this message, because the link failed */
#ifdef Macintosh
            macalert(errmsg);
#else
            puts(errmsg);
            fflush(stdout);
#endif /* Macintosh */
            exit(1);
        }

        scode = RSRSLT;               /* Default return value */


        /* Check for AT LEAST the following cases here */

        switch (stat) {

        case RQXLOAD:                 /* Load & define functions */
            scode = funcload() ? RSRSLT : -RSERR;
            break;

        case RQSUBR:                  /* Handle external function request */
            scode = dofun() ? RSRSLT: RSERR;
            break;

        default:
            break;
        }
    }
}
/*-----------------------------------------------------------------------*/
/* FUNCLOAD  --  Define this application's external functions  */

int funcload()
{
    int i;
    for (i = 0; i < ExtFuncCount; i++) {
        if (!ads_defun(exfun[i], i))
            return RTERROR;
    }
    return RTNORM;
}
/*-----------------------------------------------------------------------*/
/* DOFUN -- Execute external function (called upon an RQSUBR request) */

int
/*FCN*/dofun()
{
    struct resbuf *getargs = NULL, *rb = NULL;
    int val;
    matrix m;
    vec zaxis;
    int count = 0;                    /* counter for # of arguments */

    /* Get the required arguments */

    if ((getargs = ads_getargs()) == NULL)
        return 0;

    /* Check the type and number of args */

    rb = getargs;
    while (rb != NULL) {
        if (rb->restype != RTREAL) {
            ads_printf(/*MSG2*/"\nARBMAT called with type %d. ", rb->restype);
            ads_printf(/*MSG3*/"\nERROR:  Arg. %d --  Real expected. ", count);
            ads_relrb(getargs);
            return 0;
        }
        zaxis[count] = rb->resval.rreal;
        rb = rb->rbnext;
        count++;
    }

    /* Release the argument list */
    ads_relrb(getargs);

    /* Make sure we have enough arguments after leaving while loop */
    if (count < 2) {
        ads_printf(/*MSG4*/"\n\
ERROR: Arg. %d -- Insufficient number of argumenets.", count);
        ads_printf(/*MSG5*/"\nUsage: (arbmat X Y Z)\n");
        return 0;
    }

    if ((val = ads_getfuncode()) < 0)
        return 0;

    switch (val) {

    case 0:
        prtaxis(/*MSG6*/"Input", zaxis);
        ads_printf("\n");

        if (arbmat(m, zaxis) == BAD) {
            ads_printf(/*MSG7*/"X, Y, or Z must be non zero.\n");
        } else {
            prtaxis(/*MSG0*/"X", m[X]);
            prtaxis(/*MSG0*/"Y", m[Y]);
            prtaxis(/*MSG0*/"Z", m[Z]);
            ads_printf("\n");
        }
        break;
    default:
        break;
    }

    ads_retvoid();

}


int
arbmat(m, zaxis)
  matrix m;                           /* output matrix */
  vec zaxis;                          /* X, Y, Z */
{
    static matrix ref = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};

    if (univec(m[Z], zaxis) == BAD)
        return (BAD);

    if (rabs(m[Z][X]) < ARBBOUND && rabs(m[Z][Y]) < ARBBOUND)
        cross(m[X], ref[Y], m[Z]);
    else
        cross(m[X], ref[Z], m[Z]);

    univec(m[X], m[X]);

    cross(m[Y], m[Z], m[X]);
    univec(m[Y], m[Y]);

    return (GOOD);
}


int
univec(a, b)
  vec a, b;
{
    real d;

    if ((d = (b[X] * b[X] + b[Y] * b[Y] + b[Z] * b[Z])) < EPS)
        return (BAD);

    d = 1.0 / sqrt(d);

    a[X] = b[X] * d;
    a[Y] = b[Y] * d;
    a[Z] = b[Z] * d;
    return (GOOD);
}


real
rabs(a)
  real a;
{
    return (a > 0.0 ? a : -a);
}


void
cross(a, b, c)
  vec a, b, c;
{
    a[X] = b[Y] * c[Z] - b[Z] * c[Y];
    a[Y] = b[Z] * c[X] - b[X] * c[Z];
    a[Z] = b[X] * c[Y] - b[Y] * c[X];
}


void
prtaxis(s, a)
  char *s;
  vec a;
{
    int i;
    char temp[50];

    ads_printf(/*MSG8*/"\n%s axis: ", s);
    for (i = X; i <= Z; i++) {
        sprintf(temp, "%f", a[i]);    /* printf can disagree with < 0.0 ! */
        ads_printf("%s%f ", (temp[0] == '-') ? "" : "+", a[i]);
    }
}
