/* Next available MSG number is   9 */

/*    

   ARBMAT.CPP

   Copyright (C) 1996 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 "rxdefs.h"
#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];

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

extern "C" AcRx::AppRetCode 
acrxEntryPoint(AcRx::AppMsgCode msg, void* ptr)
{
    switch(msg) {
	case AcRx::kInitAppMsg:
        acrxUnlockApplication(ptr);
        break;
    case AcRx::kInvkSubrMsg:
        dofun();
        break;
    case AcRx::kLoadDwgMsg:
        funcload();
        break;
    case AcRx::kUnloadDwgMsg:
        funcunload();
        ads_printf(/*MSG2*/"Unloading.\n");
    }
    return AcRx::kRetOK;
}
/*-----------------------------------------------------------------------*/
/* FUNCLOAD  --  Define this application's external functions  */

int funcload()
{
    short i;
    for (i = 0; i < ExtFuncCount; i++) {
        if (!ads_defun(exfun[i], i))
            return RTERROR;
    }
    return RTNORM;
}
/*-----------------------------------------------------------------------*/
/* FUNCUNLOAD  --  Undefine this application's external functions  */

int
/*FCN*/funcunload()
{
    short i;

    /* Undefine each function we defined */

    for (i = 0; i < ExtFuncCount; i++) {
        ads_undef(exfun[i],i);
    }

    return RTNORM;
}
/*-------------------------------------------------------------------------*/
/* DOFUN -- Execute external function (called upon a kInvkSubrMsg 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 arguments.", 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();
    return 1;
}

int
arbmat(matrix m, vec 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(vec a, vec 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(real a)
{
    return (a > 0.0 ? a : -a);
}


void
cross(vec a, vec b, vec 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(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]);
    }
}
