g_ell.c

Go to the documentation of this file.
00001 /*                         G _ E L L . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1985-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup g_  */
00023 
00024 /*@{*/
00025 /** @file g_ell.c
00026  *      Intersect a ray with a Generalized Ellipsoid.
00027  *
00028  *  Authors -
00029  *      Edwin O. Davisson       (Analysis)
00030  *      Michael John Muuss      (Programming)
00031  *      Peter F. Stiller        (Curvature Analysis)
00032  *      Phillip Dykstra         (RPPs, Curvature)
00033  *      Dave Becker             (Vectorization)
00034  *
00035  *  Source -
00036  *      SECAD/VLD Computing Consortium, Bldg 394
00037  *      The U. S. Army Ballistic Research Laboratory
00038  *      Aberdeen Proving Ground, Maryland  21005
00039  *
00040  */
00041 
00042 #ifndef lint
00043 static const char RCSell[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_ell.c,v 14.13 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00044 #endif
00045 
00046 #include "common.h"
00047 
00048 #include <stddef.h>
00049 #include <stdio.h>
00050 #ifdef HAVE_STRING_H
00051 #  include <string.h>
00052 #else
00053 #  include <strings.h>
00054 #endif
00055 #include <math.h>
00056 
00057 #include "machine.h"
00058 #include "vmath.h"
00059 #include "db.h"
00060 #include "nmg.h"
00061 #include "raytrace.h"
00062 #include "nurb.h"
00063 #include "rtgeom.h"
00064 #include "./debug.h"
00065 
00066 BU_EXTERN(int rt_sph_prep, (struct soltab *stp, struct rt_db_internal *ip,
00067         struct rt_i *rtip));
00068 
00069 const struct bu_structparse rt_ell_parse[] = {
00070     { "%f", 3, "V", bu_offsetof(struct rt_ell_internal, v[X]), BU_STRUCTPARSE_FUNC_NULL },
00071     { "%f", 3, "A", bu_offsetof(struct rt_ell_internal, a[X]), BU_STRUCTPARSE_FUNC_NULL },
00072     { "%f", 3, "B", bu_offsetof(struct rt_ell_internal, b[X]), BU_STRUCTPARSE_FUNC_NULL },
00073     { "%f", 3, "C", bu_offsetof(struct rt_ell_internal, c[X]), BU_STRUCTPARSE_FUNC_NULL },
00074     { {'\0','\0','\0','\0'}, 0, (char *)NULL, 0, BU_STRUCTPARSE_FUNC_NULL }
00075  };
00076 
00077 static void  nmg_sphere_face_snurb(struct faceuse *fu, const matp_t m);
00078 
00079 /*
00080  *  Algorithm:
00081  *
00082  *  Given V, A, B, and C, there is a set of points on this ellipsoid
00083  *
00084  *  { (x,y,z) | (x,y,z) is on ellipsoid defined by V, A, B, C }
00085  *
00086  *  Through a series of Affine Transformations, this set will be
00087  *  transformed into a set of points on a unit sphere at the origin
00088  *
00089  *  { (x',y',z') | (x',y',z') is on Sphere at origin }
00090  *
00091  *  The transformation from X to X' is accomplished by:
00092  *
00093  *  X' = S(R( X - V ))
00094  *
00095  *  where R(X) =  ( A/(|A|) )
00096  *               (  B/(|B|)  ) . X
00097  *                ( C/(|C|) )
00098  *
00099  *  and S(X) =   (  1/|A|   0     0   )
00100  *              (    0    1/|B|   0    ) . X
00101  *               (   0      0   1/|C| )
00102  *
00103  *  To find the intersection of a line with the ellipsoid, consider
00104  *  the parametric line L:
00105  *
00106  *      L : { P(n) | P + t(n) . D }
00107  *
00108  *  Call W the actual point of intersection between L and the ellipsoid.
00109  *  Let W' be the point of intersection between L' and the unit sphere.
00110  *
00111  *      L' : { P'(n) | P' + t(n) . D' }
00112  *
00113  *  W = invR( invS( W' ) ) + V
00114  *
00115  *  Where W' = k D' + P'.
00116  *
00117  *  Let dp = D' dot P'
00118  *  Let dd = D' dot D'
00119  *  Let pp = P' dot P'
00120  *
00121  *  and k = [ -dp +/- sqrt( dp*dp - dd * (pp - 1) ) ] / dd
00122  *  which is constant.
00123  *
00124  *  Now, D' = S( R( D ) )
00125  *  and  P' = S( R( P - V ) )
00126  *
00127  *  Substituting,
00128  *
00129  *  W = V + invR( invS[ k *( S( R( D ) ) ) + S( R( P - V ) ) ] )
00130  *    = V + invR( ( k * R( D ) ) + R( P - V ) )
00131  *    = V + k * D + P - V
00132  *    = k * D + P
00133  *
00134  *  Note that ``k'' is constant, and is the same in the formulations
00135  *  for both W and W'.
00136  *
00137  *  NORMALS.  Given the point W on the ellipsoid, what is the vector
00138  *  normal to the tangent plane at that point?
00139  *
00140  *  Map W onto the unit sphere, ie:  W' = S( R( W - V ) ).
00141  *
00142  *  Plane on unit sphere at W' has a normal vector of the same value(!).
00143  *  N' = W'
00144  *
00145  *  The plane transforms back to the tangent plane at W, and this
00146  *  new plane (on the ellipsoid) has a normal vector of N, viz:
00147  *
00148  *  N = inverse[ transpose( inverse[ S o R ] ) ] ( N' )
00149  *
00150  *  because if H is perpendicular to plane Q, and matrix M maps from
00151  *  Q to Q', then inverse[ transpose(M) ] (H) is perpendicular to Q'.
00152  *  Here, H and Q are in "prime space" with the unit sphere.
00153  *  [Somehow, the notation here is backwards].
00154  *  So, the mapping matrix M = inverse( S o R ), because
00155  *  S o R maps from normal space to the unit sphere.
00156  *
00157  *  N = inverse[ transpose( inverse[ S o R ] ) ] ( N' )
00158  *    = inverse[ transpose(invR o invS) ] ( N' )
00159  *    = inverse[ transpose(invS) o transpose(invR) ] ( N' )
00160  *    = inverse[ inverse(S) o R ] ( N' )
00161  *    = invR o S ( N' )
00162  *
00163  *    = invR o S ( W' )
00164  *    = invR( S( S( R( W - V ) ) ) )
00165  *
00166  *  because inverse(R) = transpose(R), so R = transpose( invR ),
00167  *  and S = transpose( S ).
00168  *
00169  *  Note that the normal vector N produced above will not have unit length.
00170  */
00171 
00172 struct ell_specific {
00173         vect_t  ell_V;          /* Vector to center of ellipsoid */
00174         vect_t  ell_Au;         /* unit-length A vector */
00175         vect_t  ell_Bu;
00176         vect_t  ell_Cu;
00177         vect_t  ell_invsq;      /* [ 1/(|A|**2), 1/(|B|**2), 1/(|C|**2) ] */
00178         mat_t   ell_SoR;        /* Scale(Rot(vect)) */
00179         mat_t   ell_invRSSR;    /* invRot(Scale(Scale(Rot(vect)))) */
00180 };
00181 #define ELL_NULL        ((struct ell_specific *)0)
00182 
00183 /**
00184  *                      R T _ E L L _ P R E P
00185  *
00186  *  Given a pointer to a GED database record, and a transformation matrix,
00187  *  determine if this is a valid ellipsoid, and if so, precompute various
00188  *  terms of the formula.
00189  *
00190  *  Returns -
00191  *      0       ELL is OK
00192  *      !0      Error in description
00193  *
00194  *  Implicit return -
00195  *      A struct ell_specific is created, and it's address is stored in
00196  *      stp->st_specific for use by rt_ell_shot().
00197  */
00198 int
00199 rt_ell_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00200 {
00201         register struct ell_specific *ell;
00202         struct rt_ell_internal  *eip;
00203         LOCAL fastf_t   magsq_a, magsq_b, magsq_c;
00204         LOCAL mat_t     R;
00205         LOCAL mat_t     Rinv;
00206         LOCAL mat_t     SS;
00207         LOCAL mat_t     mtemp;
00208         LOCAL vect_t    Au, Bu, Cu;     /* A,B,C with unit length */
00209         LOCAL vect_t    w1, w2, P;      /* used for bounding RPP */
00210         LOCAL fastf_t   f;
00211 
00212         eip = (struct rt_ell_internal *)ip->idb_ptr;
00213         RT_ELL_CK_MAGIC(eip);
00214 
00215         /*
00216          *  For a fast way out, hand this solid off to the SPH routine.
00217          *  If it takes it, then there is nothing to do, otherwise
00218          *  the solid is an ELL.
00219          */
00220         if( rt_sph_prep( stp, ip, rtip ) == 0 ) {
00221           return(0);            /* OK */
00222         }
00223 
00224         /* Validate that |A| > 0, |B| > 0, |C| > 0 */
00225         magsq_a = MAGSQ( eip->a );
00226         magsq_b = MAGSQ( eip->b );
00227         magsq_c = MAGSQ( eip->c );
00228 
00229         if( magsq_a < rtip->rti_tol.dist || magsq_b < rtip->rti_tol.dist || magsq_c < rtip->rti_tol.dist ) {
00230                 bu_log("sph(%s):  zero length A(%g), B(%g), or C(%g) vector\n",
00231                         stp->st_name, magsq_a, magsq_b, magsq_c );
00232                 return(1);              /* BAD */
00233         }
00234 
00235         /* Create unit length versions of A,B,C */
00236         f = 1.0/sqrt(magsq_a);
00237         VSCALE( Au, eip->a, f );
00238         f = 1.0/sqrt(magsq_b);
00239         VSCALE( Bu, eip->b, f );
00240         f = 1.0/sqrt(magsq_c);
00241         VSCALE( Cu, eip->c, f );
00242 
00243         /* Validate that A.B == 0, B.C == 0, A.C == 0 (check dir only) */
00244         f = VDOT( Au, Bu );
00245         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00246                 bu_log("ell(%s):  A not perpendicular to B, f=%f\n",stp->st_name, f);
00247                 return(1);              /* BAD */
00248         }
00249         f = VDOT( Bu, Cu );
00250         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00251                 bu_log("ell(%s):  B not perpendicular to C, f=%f\n",stp->st_name, f);
00252                 return(1);              /* BAD */
00253         }
00254         f = VDOT( Au, Cu );
00255         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00256                 bu_log("ell(%s):  A not perpendicular to C, f=%f\n",stp->st_name, f);
00257                 return(1);              /* BAD */
00258         }
00259 
00260         /* Solid is OK, compute constant terms now */
00261         BU_GETSTRUCT( ell, ell_specific );
00262         stp->st_specific = (genptr_t)ell;
00263 
00264         VMOVE( ell->ell_V, eip->v );
00265 
00266         VSET( ell->ell_invsq, 1.0/magsq_a, 1.0/magsq_b, 1.0/magsq_c );
00267         VMOVE( ell->ell_Au, Au );
00268         VMOVE( ell->ell_Bu, Bu );
00269         VMOVE( ell->ell_Cu, Cu );
00270 
00271         MAT_IDN( ell->ell_SoR );
00272         MAT_IDN( R );
00273 
00274         /* Compute R and Rinv matrices */
00275         VMOVE( &R[0], Au );
00276         VMOVE( &R[4], Bu );
00277         VMOVE( &R[8], Cu );
00278         bn_mat_trn( Rinv, R );                  /* inv of rot mat is trn */
00279 
00280         /* Compute SoS (Affine transformation) */
00281         MAT_IDN( SS );
00282         SS[ 0] = ell->ell_invsq[0];
00283         SS[ 5] = ell->ell_invsq[1];
00284         SS[10] = ell->ell_invsq[2];
00285 
00286         /* Compute invRSSR */
00287         bn_mat_mul( mtemp, SS, R );
00288         bn_mat_mul( ell->ell_invRSSR, Rinv, mtemp );
00289 
00290         /* Compute SoR */
00291         VSCALE( &ell->ell_SoR[0], eip->a, ell->ell_invsq[0] );
00292         VSCALE( &ell->ell_SoR[4], eip->b, ell->ell_invsq[1] );
00293         VSCALE( &ell->ell_SoR[8], eip->c, ell->ell_invsq[2] );
00294 
00295         /* Compute bounding sphere */
00296         VMOVE( stp->st_center, eip->v );
00297         f = magsq_a;
00298         if( magsq_b > f )
00299                 f = magsq_b;
00300         if( magsq_c > f )
00301                 f = magsq_c;
00302         stp->st_aradius = stp->st_bradius = sqrt(f);
00303 
00304         /* Compute bounding RPP */
00305         VSET( w1, magsq_a, magsq_b, magsq_c );
00306 
00307         /* X */
00308         VSET( P, 1.0, 0, 0 );           /* bounding plane normal */
00309         MAT3X3VEC( w2, R, P );          /* map plane to local coord syst */
00310         VELMUL( w2, w2, w2 );           /* square each term */
00311         f = VDOT( w1, w2 );
00312         f = sqrt(f);
00313         stp->st_min[X] = ell->ell_V[X] - f;     /* V.P +/- f */
00314         stp->st_max[X] = ell->ell_V[X] + f;
00315 
00316         /* Y */
00317         VSET( P, 0, 1.0, 0 );           /* bounding plane normal */
00318         MAT3X3VEC( w2, R, P );          /* map plane to local coord syst */
00319         VELMUL( w2, w2, w2 );           /* square each term */
00320         f = VDOT( w1, w2 );
00321         f = sqrt(f);
00322         stp->st_min[Y] = ell->ell_V[Y] - f;     /* V.P +/- f */
00323         stp->st_max[Y] = ell->ell_V[Y] + f;
00324 
00325         /* Z */
00326         VSET( P, 0, 0, 1.0 );           /* bounding plane normal */
00327         MAT3X3VEC( w2, R, P );          /* map plane to local coord syst */
00328         VELMUL( w2, w2, w2 );           /* square each term */
00329         f = VDOT( w1, w2 );
00330         f = sqrt(f);
00331         stp->st_min[Z] = ell->ell_V[Z] - f;     /* V.P +/- f */
00332         stp->st_max[Z] = ell->ell_V[Z] + f;
00333 
00334         return(0);                      /* OK */
00335 }
00336 
00337 /**
00338  *                      R T _ E L L _ P R I N T
00339  */
00340 void
00341 rt_ell_print(register const struct soltab *stp)
00342 {
00343         register struct ell_specific *ell =
00344                 (struct ell_specific *)stp->st_specific;
00345 
00346         VPRINT("V", ell->ell_V);
00347         bn_mat_print("S o R", ell->ell_SoR );
00348         bn_mat_print("invRSSR", ell->ell_invRSSR );
00349 }
00350 
00351 /**
00352  *                      R T _ E L L _ S H O T
00353  *
00354  *  Intersect a ray with an ellipsoid, where all constant terms have
00355  *  been precomputed by rt_ell_prep().  If an intersection occurs,
00356  *  a struct seg will be acquired and filled in.
00357  *
00358  *  Returns -
00359  *      0       MISS
00360  *      >0      HIT
00361  */
00362 int
00363 rt_ell_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00364 {
00365         register struct ell_specific *ell =
00366                 (struct ell_specific *)stp->st_specific;
00367         register struct seg *segp;
00368         LOCAL vect_t    dprime;         /* D' */
00369         LOCAL vect_t    pprime;         /* P' */
00370         LOCAL vect_t    xlated;         /* translated vector */
00371         LOCAL fastf_t   dp, dd;         /* D' dot P', D' dot D' */
00372         LOCAL fastf_t   k1, k2;         /* distance constants of solution */
00373         FAST fastf_t    root;           /* root of radical */
00374 
00375         /* out, Mat, vect */
00376         MAT4X3VEC( dprime, ell->ell_SoR, rp->r_dir );
00377         VSUB2( xlated, rp->r_pt, ell->ell_V );
00378         MAT4X3VEC( pprime, ell->ell_SoR, xlated );
00379 
00380         dp = VDOT( dprime, pprime );
00381         dd = VDOT( dprime, dprime );
00382 
00383         if( (root = dp*dp - dd * (VDOT(pprime,pprime)-1.0)) < 0 )
00384                 return(0);              /* No hit */
00385         root = sqrt(root);
00386 
00387         RT_GET_SEG(segp, ap->a_resource);
00388         segp->seg_stp = stp;
00389         if( (k1=(-dp+root)/dd) <= (k2=(-dp-root)/dd) )  {
00390                 /* k1 is entry, k2 is exit */
00391                 segp->seg_in.hit_dist = k1;
00392                 segp->seg_out.hit_dist = k2;
00393         } else {
00394                 /* k2 is entry, k1 is exit */
00395                 segp->seg_in.hit_dist = k2;
00396                 segp->seg_out.hit_dist = k1;
00397         }
00398         BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00399         return(2);                      /* HIT */
00400 }
00401 
00402 #define RT_ELL_SEG_MISS(SEG)    (SEG).seg_stp=RT_SOLTAB_NULL
00403 
00404 /**
00405  *                      R T _ E L L _ V S H O T
00406  *
00407  *  This is the Becker vector version.
00408  */
00409 void
00410 rt_ell_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00411                                /* An array of solid pointers */
00412                                /* An array of ray pointers */
00413                                /* array of segs (results returned) */
00414                                /* Number of ray/object pairs */
00415 
00416 {
00417         register int    i;
00418         register struct ell_specific *ell;
00419         LOCAL vect_t    dprime;         /* D' */
00420         LOCAL vect_t    pprime;         /* P' */
00421         LOCAL vect_t    xlated;         /* translated vector */
00422         LOCAL fastf_t   dp, dd;         /* D' dot P', D' dot D' */
00423         LOCAL fastf_t   k1, k2;         /* distance constants of solution */
00424         FAST fastf_t    root;           /* root of radical */
00425 
00426         /* for each ray/ellipse pair */
00427 #       include "noalias.h"
00428         for(i = 0; i < n; i++){
00429 #if !CRAY /* XXX currently prevents vectorization on cray */
00430                 if (stp[i] == 0) continue; /* stp[i] == 0 signals skip ray */
00431 #endif
00432                 ell = (struct ell_specific *)stp[i]->st_specific;
00433 
00434                 MAT4X3VEC( dprime, ell->ell_SoR, rp[i]->r_dir );
00435                 VSUB2( xlated, rp[i]->r_pt, ell->ell_V );
00436                 MAT4X3VEC( pprime, ell->ell_SoR, xlated );
00437 
00438                 dp = VDOT( dprime, pprime );
00439                 dd = VDOT( dprime, dprime );
00440 
00441                 if( (root = dp*dp - dd * (VDOT(pprime,pprime)-1.0)) < 0 ) {
00442                         RT_ELL_SEG_MISS(segp[i]);               /* No hit */
00443                 }
00444                 else {
00445                         root = sqrt(root);
00446 
00447                         segp[i].seg_stp = stp[i];
00448 
00449                         if( (k1=(-dp+root)/dd) <= (k2=(-dp-root)/dd) )  {
00450                                 /* k1 is entry, k2 is exit */
00451                                 segp[i].seg_in.hit_dist = k1;
00452                                 segp[i].seg_out.hit_dist = k2;
00453                         } else {
00454                                 /* k2 is entry, k1 is exit */
00455                                 segp[i].seg_in.hit_dist = k2;
00456                                 segp[i].seg_out.hit_dist = k1;
00457                         }
00458                 }
00459         }
00460 }
00461 
00462 /**
00463  *                      R T _ E L L _ N O R M
00464  *
00465  *  Given ONE ray distance, return the normal and entry/exit point.
00466  */
00467 void
00468 rt_ell_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00469 {
00470         register struct ell_specific *ell =
00471                 (struct ell_specific *)stp->st_specific;
00472         LOCAL vect_t xlated;
00473         LOCAL fastf_t scale;
00474 
00475         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00476         VSUB2( xlated, hitp->hit_point, ell->ell_V );
00477         MAT4X3VEC( hitp->hit_normal, ell->ell_invRSSR, xlated );
00478         scale = 1.0 / MAGNITUDE( hitp->hit_normal );
00479         VSCALE( hitp->hit_normal, hitp->hit_normal, scale );
00480 
00481         /* tuck away this scale for the curvature routine */
00482         hitp->hit_vpriv[X] = scale;
00483 }
00484 
00485 /**
00486  *                      R T _ E L L _ C U R V E
00487  *
00488  *  Return the curvature of the ellipsoid.
00489  */
00490 void
00491 rt_ell_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00492 {
00493         register struct ell_specific *ell =
00494                 (struct ell_specific *)stp->st_specific;
00495         vect_t  u, v;                   /* basis vectors (with normal) */
00496         vect_t  vec1, vec2;             /* eigen vectors */
00497         vect_t  tmp;
00498         fastf_t a, b, c, scale;
00499 
00500         /*
00501          * choose a tangent plane coordinate system
00502          *  (u, v, normal) form a right-handed triple
00503          */
00504         bn_vec_ortho( u, hitp->hit_normal );
00505         VCROSS( v, hitp->hit_normal, u );
00506 
00507         /* get the saved away scale factor */
00508         scale = - hitp->hit_vpriv[X];
00509 
00510         /* find the second fundamental form */
00511         MAT4X3VEC( tmp, ell->ell_invRSSR, u );
00512         a = VDOT(u, tmp) * scale;
00513         b = VDOT(v, tmp) * scale;
00514         MAT4X3VEC( tmp, ell->ell_invRSSR, v );
00515         c = VDOT(v, tmp) * scale;
00516 
00517         bn_eigen2x2( &cvp->crv_c1, &cvp->crv_c2, vec1, vec2, a, b, c );
00518         VCOMB2( cvp->crv_pdir, vec1[X], u, vec1[Y], v );
00519         VUNITIZE( cvp->crv_pdir );
00520 }
00521 
00522 /**
00523  *                      R T _ E L L _ U V
00524  *
00525  *  For a hit on the surface of an ELL, return the (u,v) coordinates
00526  *  of the hit point, 0 <= u,v <= 1.
00527  *  u = azimuth
00528  *  v = elevation
00529  */
00530 void
00531 rt_ell_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00532 {
00533         register struct ell_specific *ell =
00534                 (struct ell_specific *)stp->st_specific;
00535         LOCAL vect_t work;
00536         LOCAL vect_t pprime;
00537         LOCAL fastf_t r;
00538 
00539         /* hit_point is on surface;  project back to unit sphere,
00540          * creating a vector from vertex to hit point which always
00541          * has length=1.0
00542          */
00543         VSUB2( work, hitp->hit_point, ell->ell_V );
00544         MAT4X3VEC( pprime, ell->ell_SoR, work );
00545         /* Assert that pprime has unit length */
00546 
00547         /* U is azimuth, atan() range: -pi to +pi */
00548         uvp->uv_u = bn_atan2( pprime[Y], pprime[X] ) * bn_inv2pi;
00549         if( uvp->uv_u < 0 )
00550                 uvp->uv_u += 1.0;
00551         /*
00552          *  V is elevation, atan() range: -pi/2 to +pi/2,
00553          *  because sqrt() ensures that X parameter is always >0
00554          */
00555         uvp->uv_v = bn_atan2( pprime[Z],
00556                 sqrt( pprime[X] * pprime[X] + pprime[Y] * pprime[Y]) ) *
00557                 bn_invpi + 0.5;
00558 
00559         /* approximation: r / (circumference, 2 * pi * aradius) */
00560         r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
00561         uvp->uv_du = uvp->uv_dv =
00562                 bn_inv2pi * r / stp->st_aradius;
00563 }
00564 
00565 /**
00566  *                      R T _ E L L _ F R E E
00567  */
00568 void
00569 rt_ell_free(register struct soltab *stp)
00570 {
00571         register struct ell_specific *ell =
00572                 (struct ell_specific *)stp->st_specific;
00573 
00574         bu_free( (char *)ell, "ell_specific" );
00575 }
00576 
00577 int
00578 rt_ell_class(void)
00579 {
00580         return(0);
00581 }
00582 
00583 /**
00584  *                      R T _ E L L _ 1 6 P T S
00585  *
00586  * Also used by the TGC code
00587  */
00588 #define ELLOUT(n)       ov+(n-1)*3
00589 void
00590 rt_ell_16pts(register fastf_t *ov,
00591              register fastf_t *V,
00592              fastf_t *A,
00593              fastf_t *B)
00594 {
00595         static fastf_t c, d, e, f,g,h;
00596 
00597         e = h = .92388;                 /* cos(22.5) */
00598         c = d = .707107;                /* cos(45) */
00599         g = f = .382683;                /* cos(67.5) */
00600 
00601         /*
00602          * For angle theta, compute surface point as
00603          *
00604          *      V  +  cos(theta) * A  + sin(theta) * B
00605          *
00606          * note that sin(theta) is cos(90-theta).
00607          */
00608 
00609         VADD2( ELLOUT(1), V, A );
00610         VJOIN2( ELLOUT(2), V, e, A, f, B );
00611         VJOIN2( ELLOUT(3), V, c, A, d, B );
00612         VJOIN2( ELLOUT(4), V, g, A, h, B );
00613         VADD2( ELLOUT(5), V, B );
00614         VJOIN2( ELLOUT(6), V, -g, A, h, B );
00615         VJOIN2( ELLOUT(7), V, -c, A, d, B );
00616         VJOIN2( ELLOUT(8), V, -e, A, f, B );
00617         VSUB2( ELLOUT(9), V, A );
00618         VJOIN2( ELLOUT(10), V, -e, A, -f, B );
00619         VJOIN2( ELLOUT(11), V, -c, A, -d, B );
00620         VJOIN2( ELLOUT(12), V, -g, A, -h, B );
00621         VSUB2( ELLOUT(13), V, B );
00622         VJOIN2( ELLOUT(14), V, g, A, -h, B );
00623         VJOIN2( ELLOUT(15), V, c, A, -d, B );
00624         VJOIN2( ELLOUT(16), V, e, A, -f, B );
00625 }
00626 
00627 /**
00628  *                      R T _ E L L _ P L O T
00629  */
00630 int
00631 rt_ell_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00632 {
00633         register int            i;
00634         struct rt_ell_internal  *eip;
00635         fastf_t top[16*3];
00636         fastf_t middle[16*3];
00637         fastf_t bottom[16*3];
00638 
00639         RT_CK_DB_INTERNAL(ip);
00640         eip = (struct rt_ell_internal *)ip->idb_ptr;
00641         RT_ELL_CK_MAGIC(eip);
00642 
00643         rt_ell_16pts( top, eip->v, eip->a, eip->b );
00644         rt_ell_16pts( bottom, eip->v, eip->b, eip->c );
00645         rt_ell_16pts( middle, eip->v, eip->a, eip->c );
00646 
00647         RT_ADD_VLIST( vhead, &top[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00648         for( i=0; i<16; i++ )  {
00649                 RT_ADD_VLIST( vhead, &top[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00650         }
00651 
00652         RT_ADD_VLIST( vhead, &bottom[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00653         for( i=0; i<16; i++ )  {
00654                 RT_ADD_VLIST( vhead, &bottom[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00655         }
00656 
00657         RT_ADD_VLIST( vhead, &middle[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00658         for( i=0; i<16; i++ )  {
00659                 RT_ADD_VLIST( vhead, &middle[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00660         }
00661         return(0);
00662 }
00663 
00664 #if 0
00665 static point_t  octa_verts[6] = {
00666         { 1, 0, 0 },    /* XPLUS */
00667         {-1, 0, 0 },    /* XMINUS */
00668         { 0, 1, 0 },    /* YPLUS */
00669         { 0,-1, 0 },    /* YMINUS */
00670         { 0, 0, 1 },    /* ZPLUS */
00671         { 0, 0,-1 }     /* ZMINUS */
00672 };
00673 
00674 #define XPLUS 0
00675 #define XMIN  1
00676 #define YPLUS 2
00677 #define YMIN  3
00678 #define ZPLUS 4
00679 #define ZMIN  5
00680 #endif
00681 
00682 /* Vertices of a unit octahedron */
00683 /* These need to be organized properly to give reasonable normals */
00684 /* static struct usvert {
00685  *      int     a;
00686  *      int     b;
00687  *      int     c;
00688  * } octahedron[8] = {
00689  *     { XPLUS, ZPLUS, YPLUS },
00690  *     { YPLUS, ZPLUS, XMIN  },
00691  *     { XMIN , ZPLUS, YMIN  },
00692  *     { YMIN , ZPLUS, XPLUS },
00693  *     { XPLUS, YPLUS, ZMIN  },
00694  *     { YPLUS, XMIN , ZMIN  },
00695  *     { XMIN , YMIN , ZMIN  },
00696  *     { YMIN , XPLUS, ZMIN  }
00697  * };
00698  */
00699 struct ell_state {
00700         struct shell    *s;
00701         struct rt_ell_internal  *eip;
00702         mat_t           invRinvS;
00703         mat_t           invRoS;
00704         fastf_t         theta_tol;
00705 };
00706 
00707 struct ell_vert_strip {
00708         int             nverts_per_strip;
00709         int             nverts;
00710         struct vertex   **vp;
00711         vect_t          *norms;
00712         int             nfaces;
00713         struct faceuse  **fu;
00714 };
00715 
00716 /**
00717  *                      R T _ E L L _ T E S S
00718  *
00719  *  Tessellate an ellipsoid.
00720  *
00721  *  The strategy is based upon the approach of Jon Leech 3/24/89,
00722  *  from program "sphere", which generates a polygon mesh
00723  *  approximating a sphere by
00724  *  recursive subdivision. First approximation is an octahedron;
00725  *  each level of refinement increases the number of polygons by
00726  *  a factor of 4.
00727  *  Level 3 (128 polygons) is a good tradeoff if gouraud
00728  *  shading is used to render the database.
00729  *
00730  *  At the start, points ABC lie on surface of the unit sphere.
00731  *  Pick DEF as the midpoints of the three edges of ABC.
00732  *  Normalize the new points to lie on surface of the unit sphere.
00733  *
00734  *        1
00735  *        B
00736  *       /\
00737  *    3 /  \ 4
00738  *    D/____\E
00739  *    /\    /\
00740  *   /  \  /  \
00741  *  /____\/____\
00742  * A      F     C
00743  * 0      5     2
00744  *
00745  *  Returns -
00746  *      -1      failure
00747  *       0      OK.  *r points to nmgregion that holds this tessellation.
00748  */
00749 int
00750 rt_ell_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00751 {
00752         LOCAL mat_t     R;
00753         LOCAL mat_t     S;
00754         LOCAL mat_t     invR;
00755         LOCAL mat_t     invS;
00756         LOCAL vect_t    Au, Bu, Cu;     /* A,B,C with unit length */
00757         LOCAL fastf_t   Alen, Blen, Clen;
00758         LOCAL fastf_t   invAlen, invBlen, invClen;
00759         LOCAL fastf_t   magsq_a, magsq_b, magsq_c;
00760         LOCAL fastf_t   f;
00761         struct ell_state        state;
00762         register int            i;
00763         fastf_t         radius;
00764         int             nsegs;
00765         int             nstrips;
00766         struct ell_vert_strip   *strips;
00767         int             j;
00768         struct vertex           **vertp[4];
00769         int     faceno;
00770         int     stripno;
00771         int     boff;           /* base offset */
00772         int     toff;           /* top offset */
00773         int     blim;           /* base subscript limit */
00774         int     tlim;           /* top subscrpit limit */
00775         fastf_t rel;            /* Absolutized relative tolerance */
00776 
00777         RT_CK_DB_INTERNAL(ip);
00778         state.eip = (struct rt_ell_internal *)ip->idb_ptr;
00779         RT_ELL_CK_MAGIC(state.eip);
00780 
00781         /* Validate that |A| > 0, |B| > 0, |C| > 0 */
00782         magsq_a = MAGSQ( state.eip->a );
00783         magsq_b = MAGSQ( state.eip->b );
00784         magsq_c = MAGSQ( state.eip->c );
00785         if( magsq_a < tol->dist || magsq_b < tol->dist || magsq_c < tol->dist ) {
00786                 bu_log("rt_ell_tess():  zero length A, B, or C vector\n");
00787                 return(-2);             /* BAD */
00788         }
00789 
00790         /* Create unit length versions of A,B,C */
00791         invAlen = 1.0/(Alen = sqrt(magsq_a));
00792         VSCALE( Au, state.eip->a, invAlen );
00793         invBlen = 1.0/(Blen = sqrt(magsq_b));
00794         VSCALE( Bu, state.eip->b, invBlen );
00795         invClen = 1.0/(Clen = sqrt(magsq_c));
00796         VSCALE( Cu, state.eip->c, invClen );
00797 
00798         /* Validate that A.B == 0, B.C == 0, A.C == 0 (check dir only) */
00799         f = VDOT( Au, Bu );
00800         if( ! NEAR_ZERO(f, tol->dist) )  {
00801                 bu_log("ell():  A not perpendicular to B, f=%f\n", f);
00802                 return(-3);             /* BAD */
00803         }
00804         f = VDOT( Bu, Cu );
00805         if( ! NEAR_ZERO(f, tol->dist) )  {
00806                 bu_log("ell():  B not perpendicular to C, f=%f\n", f);
00807                 return(-3);             /* BAD */
00808         }
00809         f = VDOT( Au, Cu );
00810         if( ! NEAR_ZERO(f, tol->dist) )  {
00811                 bu_log("ell():  A not perpendicular to C, f=%f\n", f);
00812                 return(-3);             /* BAD */
00813         }
00814 
00815         {
00816                 vect_t  axb;
00817                 VCROSS( axb, Au, Bu );
00818                 f = VDOT( axb, Cu );
00819                 if( f < 0 )  {
00820                         VREVERSE( Cu, Cu );
00821                         VREVERSE( state.eip->c, state.eip->c );
00822                 }
00823         }
00824 
00825         /* Compute R and Rinv matrices */
00826         MAT_IDN( R );
00827         VMOVE( &R[0], Au );
00828         VMOVE( &R[4], Bu );
00829         VMOVE( &R[8], Cu );
00830         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
00831 
00832         /* Compute S and invS matrices */
00833         /* invS is just 1/diagonal elements */
00834         MAT_IDN( S );
00835         S[ 0] = invAlen;
00836         S[ 5] = invBlen;
00837         S[10] = invClen;
00838         bn_mat_inv( invS, S );
00839 
00840         /* invRinvS, for converting points from unit sphere to model */
00841         bn_mat_mul( state.invRinvS, invR, invS );
00842 
00843         /* invRoS, for converting normals from unit sphere to model */
00844         bn_mat_mul( state.invRoS, invR, S );
00845 
00846         /* Compute radius of bounding sphere */
00847         radius = Alen;
00848         if( Blen > radius )
00849                 radius = Blen;
00850         if( Clen > radius )
00851                 radius = Clen;
00852 
00853         /*
00854          *  Establish tolerances
00855          */
00856         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )  {
00857                 rel = 0.0;              /* none */
00858         } else {
00859                 /* Convert rel to absolute by scaling by radius */
00860                 rel = ttol->rel * radius;
00861         }
00862         if( ttol->abs <= 0.0 )  {
00863                 if( rel <= 0.0 )  {
00864                         /* No tolerance given, use a default */
00865                         rel = 0.10 * radius;    /* 10% */
00866                 } else {
00867                         /* Use absolute-ized relative tolerance */
00868                 }
00869         } else {
00870                 /* Absolute tolerance was given, pick smaller */
00871                 if( ttol->rel <= 0.0 || rel > ttol->abs )
00872                 {
00873                         rel = ttol->abs;
00874                         if( rel > radius )
00875                                 rel = radius;
00876                 }
00877         }
00878 
00879         /*
00880          *  Converte distance tolerance into a maximum permissible
00881          *  angle tolerance.  'radius' is largest radius.
00882          */
00883         state.theta_tol = 2 * acos( 1.0 - rel / radius );
00884 
00885         /* To ensure normal tolerance, remain below this angle */
00886         if( ttol->norm > 0.0 && ttol->norm < state.theta_tol )  {
00887                 state.theta_tol = ttol->norm;
00888         }
00889 
00890         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
00891         state.s = BU_LIST_FIRST(shell, &(*r)->s_hd);
00892 
00893         /* Find the number of segments to divide 90 degrees worth into */
00894         nsegs = (int)(bn_halfpi / state.theta_tol + 0.999);
00895         if( nsegs < 2 )  nsegs = 2;
00896 
00897         /*  Find total number of strips of vertices that will be needed.
00898          *  nsegs for each hemisphere, plus the equator.
00899          *  Note that faces are listed in the the stripe ABOVE, ie, toward
00900          *  the poles.  Thus, strips[0] will have 4 faces.
00901          */
00902         nstrips = 2 * nsegs + 1;
00903         strips = (struct ell_vert_strip *)bu_calloc( nstrips,
00904                 sizeof(struct ell_vert_strip), "strips[]" );
00905 
00906         /* North pole */
00907         strips[0].nverts = 1;
00908         strips[0].nverts_per_strip = 0;
00909         strips[0].nfaces = 4;
00910         /* South pole */
00911         strips[nstrips-1].nverts = 1;
00912         strips[nstrips-1].nverts_per_strip = 0;
00913         strips[nstrips-1].nfaces = 4;
00914         /* equator */
00915         strips[nsegs].nverts = nsegs * 4;
00916         strips[nsegs].nverts_per_strip = nsegs;
00917         strips[nsegs].nfaces = 0;
00918 
00919         for( i=1; i<nsegs; i++ )  {
00920                 strips[i].nverts_per_strip =
00921                         strips[nstrips-1-i].nverts_per_strip = i;
00922                 strips[i].nverts =
00923                         strips[nstrips-1-i].nverts = i * 4;
00924                 strips[i].nfaces =
00925                         strips[nstrips-1-i].nfaces = (2 * i + 1)*4;
00926         }
00927         /* All strips have vertices and normals */
00928         for( i=0; i<nstrips; i++ )  {
00929                 strips[i].vp = (struct vertex **)bu_calloc( strips[i].nverts,
00930                         sizeof(struct vertex *), "strip vertex[]" );
00931                 strips[i].norms = (vect_t *)bu_calloc( strips[i].nverts,
00932                         sizeof( vect_t ), "strip normals[]" );
00933         }
00934         /* All strips have faces, except for the equator */
00935         for( i=0; i < nstrips; i++ )  {
00936                 if( strips[i].nfaces <= 0 )  continue;
00937                 strips[i].fu = (struct faceuse **)bu_calloc( strips[i].nfaces,
00938                         sizeof(struct faceuse *), "strip faceuse[]" );
00939         }
00940 
00941         /* First, build the triangular mesh topology */
00942         /* Do the top. "toff" in i-1 is UP, towards +B */
00943         for( i = 1; i <= nsegs; i++ )  {
00944                 faceno = 0;
00945                 tlim = strips[i-1].nverts;
00946                 blim = strips[i].nverts;
00947                 for( stripno=0; stripno<4; stripno++ )  {
00948                         toff = stripno * strips[i-1].nverts_per_strip;
00949                         boff = stripno * strips[i].nverts_per_strip;
00950 
00951                         /* Connect this quarter strip */
00952                         for( j = 0; j < strips[i].nverts_per_strip; j++ )  {
00953 
00954                                 /* "Right-side-up" triangle */
00955                                 vertp[0] = &(strips[i].vp[j+boff]);
00956                                 vertp[1] = &(strips[i-1].vp[(j+toff)%tlim]);
00957                                 vertp[2] = &(strips[i].vp[(j+1+boff)%blim]);
00958                                 if( (strips[i-1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
00959                                         bu_log("rt_ell_tess() nmg_cmface failure\n");
00960                                         goto fail;
00961                                 }
00962                                 if( j+1 >= strips[i].nverts_per_strip )  break;
00963 
00964                                 /* Follow with interior "Up-side-down" triangle */
00965                                 vertp[0] = &(strips[i].vp[(j+1+boff)%blim]);
00966                                 vertp[1] = &(strips[i-1].vp[(j+toff)%tlim]);
00967                                 vertp[2] = &(strips[i-1].vp[(j+1+toff)%tlim]);
00968                                 if( (strips[i-1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
00969                                         bu_log("rt_ell_tess() nmg_cmface failure\n");
00970                                         goto fail;
00971                                 }
00972                         }
00973                 }
00974         }
00975         /* Do the bottom.  Everything is upside down. "toff" in i+1 is DOWN */
00976         for( i = nsegs; i < nstrips; i++ )  {
00977                 faceno = 0;
00978                 tlim = strips[i+1].nverts;
00979                 blim = strips[i].nverts;
00980                 for( stripno=0; stripno<4; stripno++ )  {
00981                         toff = stripno * strips[i+1].nverts_per_strip;
00982                         boff = stripno * strips[i].nverts_per_strip;
00983 
00984                         /* Connect this quarter strip */
00985                         for( j = 0; j < strips[i].nverts_per_strip; j++ )  {
00986 
00987                                 /* "Right-side-up" triangle */
00988                                 vertp[0] = &(strips[i].vp[j+boff]);
00989                                 vertp[1] = &(strips[i].vp[(j+1+boff)%blim]);
00990                                 vertp[2] = &(strips[i+1].vp[(j+toff)%tlim]);
00991                                 if( (strips[i+1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
00992                                         bu_log("rt_ell_tess() nmg_cmface failure\n");
00993                                         goto fail;
00994                                 }
00995                                 if( j+1 >= strips[i].nverts_per_strip )  break;
00996 
00997                                 /* Follow with interior "Up-side-down" triangle */
00998                                 vertp[0] = &(strips[i].vp[(j+1+boff)%blim]);
00999                                 vertp[1] = &(strips[i+1].vp[(j+1+toff)%tlim]);
01000                                 vertp[2] = &(strips[i+1].vp[(j+toff)%tlim]);
01001                                 if( (strips[i+1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
01002                                         bu_log("rt_ell_tess() nmg_cmface failure\n");
01003                                         goto fail;
01004                                 }
01005                         }
01006                 }
01007         }
01008 
01009         /*  Compute the geometry of each vertex.
01010          *  Start with the location in the unit sphere, and project back.
01011          *  i=0 is "straight up" along +B.
01012          */
01013         for( i=0; i < nstrips; i++ )  {
01014                 double  alpha;          /* decline down from B to A */
01015                 double  beta;           /* angle around equator (azimuth) */
01016                 fastf_t         cos_alpha, sin_alpha;
01017                 fastf_t         cos_beta, sin_beta;
01018                 point_t         sphere_pt;
01019                 point_t         model_pt;
01020 
01021                 alpha = (((double)i) / (nstrips-1));
01022                 cos_alpha = cos(alpha*bn_pi);
01023                 sin_alpha = sin(alpha*bn_pi);
01024                 for( j=0; j < strips[i].nverts; j++ )  {
01025 
01026                         beta = ((double)j) / strips[i].nverts;
01027                         cos_beta = cos(beta*bn_twopi);
01028                         sin_beta = sin(beta*bn_twopi);
01029                         VSET( sphere_pt,
01030                                 cos_beta * sin_alpha,
01031                                 cos_alpha,
01032                                 sin_beta * sin_alpha );
01033                         /* Convert from ideal sphere coordinates */
01034                         MAT4X3PNT( model_pt, state.invRinvS, sphere_pt );
01035                         VADD2( model_pt, model_pt, state.eip->v );
01036                         /* Associate vertex geometry */
01037                         nmg_vertex_gv( strips[i].vp[j], model_pt );
01038 
01039                         /* Convert sphere normal to ellipsoid normal */
01040                         MAT4X3VEC( strips[i].norms[j], state.invRoS, sphere_pt );
01041                         /* May not be unit length anymore */
01042                         VUNITIZE( strips[i].norms[j] );
01043                 }
01044         }
01045 
01046         /* Associate face geometry.  Equator has no faces */
01047         for( i=0; i < nstrips; i++ )  {
01048                 for( j=0; j < strips[i].nfaces; j++ )  {
01049                         if( nmg_fu_planeeqn( strips[i].fu[j], tol ) < 0 )
01050                                 goto fail;
01051                 }
01052         }
01053 
01054         /* Associate normals with vertexuses */
01055         for( i=0; i < nstrips; i++ )
01056         {
01057                 for( j=0; j < strips[i].nverts; j++ )
01058                 {
01059                         struct faceuse *fu;
01060                         struct vertexuse *vu;
01061                         vect_t norm_opp;
01062 
01063                         NMG_CK_VERTEX( strips[i].vp[j] );
01064                         VREVERSE( norm_opp , strips[i].norms[j] )
01065 
01066                         for( BU_LIST_FOR( vu , vertexuse , &strips[i].vp[j]->vu_hd ) )
01067                         {
01068                                 fu = nmg_find_fu_of_vu( vu );
01069                                 NMG_CK_FACEUSE( fu );
01070                                 /* get correct direction of normals depending on
01071                                  * faceuse orientation
01072                                  */
01073                                 if( fu->orientation == OT_SAME )
01074                                         nmg_vertexuse_nv( vu , strips[i].norms[j] );
01075                                 else if( fu->orientation == OT_OPPOSITE )
01076                                         nmg_vertexuse_nv( vu , norm_opp );
01077                         }
01078                 }
01079         }
01080 
01081         /* Compute "geometry" for region and shell */
01082         nmg_region_a( *r, tol );
01083 
01084         /* Release memory */
01085         /* All strips have vertices and normals */
01086         for( i=0; i<nstrips; i++ )  {
01087                 bu_free( (char *)strips[i].vp, "strip vertex[]" );
01088                 bu_free( (char *)strips[i].norms, "strip norms[]" );
01089         }
01090         /* All strips have faces, except for equator */
01091         for( i=0; i < nstrips; i++ )  {
01092                 if( strips[i].fu == (struct faceuse **)0 )  continue;
01093                 bu_free( (char *)strips[i].fu, "strip faceuse[]" );
01094         }
01095         bu_free( (char *)strips, "strips[]" );
01096         return(0);
01097 fail:
01098         /* Release memory */
01099         /* All strips have vertices and normals */
01100         for( i=0; i<nstrips; i++ )  {
01101                 bu_free( (char *)strips[i].vp, "strip vertex[]" );
01102                 bu_free( (char *)strips[i].norms, "strip norms[]" );
01103         }
01104         /* All strips have faces, except for equator */
01105         for( i=0; i < nstrips; i++ )  {
01106                 if( strips[i].fu == (struct faceuse **)0 )  continue;
01107                 bu_free( (char *)strips[i].fu, "strip faceuse[]" );
01108         }
01109         bu_free( (char *)strips, "strips[]" );
01110         return(-1);
01111 }
01112 
01113 /**
01114  *                      R T _ E L L _ I M P O R T
01115  *
01116  *  Import an ellipsoid/sphere from the database format to
01117  *  the internal structure.
01118  *  Apply modeling transformations as well.
01119  */
01120 int
01121 rt_ell_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01122 {
01123         struct rt_ell_internal  *eip;
01124         union record            *rp;
01125         LOCAL fastf_t   vec[3*4];
01126 
01127         BU_CK_EXTERNAL( ep );
01128         rp = (union record *)ep->ext_buf;
01129         /* Check record type */
01130         if( rp->u_id != ID_SOLID )  {
01131                 bu_log("rt_ell_import: defective record\n");
01132                 return(-1);
01133         }
01134 
01135         RT_CK_DB_INTERNAL( ip );
01136         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01137         ip->idb_type = ID_ELL;
01138         ip->idb_meth = &rt_functab[ID_ELL];
01139         ip->idb_ptr = bu_malloc( sizeof(struct rt_ell_internal), "rt_ell_internal");
01140         eip = (struct rt_ell_internal *)ip->idb_ptr;
01141         eip->magic = RT_ELL_INTERNAL_MAGIC;
01142 
01143         /* Convert from database to internal format */
01144         rt_fastf_float( vec, rp->s.s_values, 4 );
01145 
01146         /* Apply modeling transformations */
01147         MAT4X3PNT( eip->v, mat, &vec[0*3] );
01148         MAT4X3VEC( eip->a, mat, &vec[1*3] );
01149         MAT4X3VEC( eip->b, mat, &vec[2*3] );
01150         MAT4X3VEC( eip->c, mat, &vec[3*3] );
01151 
01152         return(0);              /* OK */
01153 }
01154 
01155 /**
01156  *                      R T _ E L L _ E X P O R T
01157  */
01158 int
01159 rt_ell_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01160 {
01161         struct rt_ell_internal  *tip;
01162         union record            *rec;
01163 
01164         RT_CK_DB_INTERNAL(ip);
01165         if( ip->idb_type != ID_ELL && ip->idb_type != ID_SPH )  return(-1);
01166         tip = (struct rt_ell_internal *)ip->idb_ptr;
01167         RT_ELL_CK_MAGIC(tip);
01168 
01169         BU_CK_EXTERNAL(ep);
01170         ep->ext_nbytes = sizeof(union record);
01171         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "ell external");
01172         rec = (union record *)ep->ext_buf;
01173 
01174         rec->s.s_id = ID_SOLID;
01175         rec->s.s_type = GENELL;
01176 
01177         /* NOTE: This also converts to dbfloat_t */
01178         VSCALE( &rec->s.s_values[0], tip->v, local2mm );
01179         VSCALE( &rec->s.s_values[3], tip->a, local2mm );
01180         VSCALE( &rec->s.s_values[6], tip->b, local2mm );
01181         VSCALE( &rec->s.s_values[9], tip->c, local2mm );
01182 
01183         return(0);
01184 }
01185 
01186 /**
01187  *                      R T _ E L L _ I M P O R T 5
01188  *
01189  *  Import an ellipsoid/sphere from the database format to
01190  *  the internal structure.
01191  *  Apply modeling transformations as well.
01192  */
01193 int
01194 rt_ell_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01195 {
01196         struct rt_ell_internal  *eip;
01197         fastf_t                 vec[ELEMENTS_PER_VECT*4];
01198 
01199         RT_CK_DB_INTERNAL( ip );
01200         BU_CK_EXTERNAL( ep );
01201 
01202         BU_ASSERT_LONG( ep->ext_nbytes, ==, SIZEOF_NETWORK_DOUBLE * ELEMENTS_PER_VECT*4 );
01203 
01204         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01205         ip->idb_type = ID_ELL;
01206         ip->idb_meth = &rt_functab[ID_ELL];
01207         ip->idb_ptr = bu_malloc( sizeof(struct rt_ell_internal), "rt_ell_internal");
01208 
01209         eip = (struct rt_ell_internal *)ip->idb_ptr;
01210         eip->magic = RT_ELL_INTERNAL_MAGIC;
01211 
01212         /* Convert from database (network) to internal (host) format */
01213         ntohd( (unsigned char *)vec, ep->ext_buf, ELEMENTS_PER_VECT*4 );
01214 
01215         /* Apply modeling transformations */
01216         MAT4X3PNT( eip->v, mat, &vec[0*ELEMENTS_PER_VECT] );
01217         MAT4X3VEC( eip->a, mat, &vec[1*ELEMENTS_PER_VECT] );
01218         MAT4X3VEC( eip->b, mat, &vec[2*ELEMENTS_PER_VECT] );
01219         MAT4X3VEC( eip->c, mat, &vec[3*ELEMENTS_PER_VECT] );
01220 
01221         return 0;               /* OK */
01222 }
01223 
01224 /**
01225  *                      R T _ E L L _ E X P O R T 5
01226  *
01227  *  The external format is:
01228  *      V point
01229  *      A vector
01230  *      B vector
01231  *      C vector
01232  */
01233 int
01234 rt_ell_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01235 {
01236         struct rt_ell_internal  *eip;
01237         fastf_t                 vec[ELEMENTS_PER_VECT*4];
01238 
01239         RT_CK_DB_INTERNAL(ip);
01240         if( ip->idb_type != ID_ELL && ip->idb_type != ID_SPH )  return(-1);
01241         eip = (struct rt_ell_internal *)ip->idb_ptr;
01242         RT_ELL_CK_MAGIC(eip);
01243 
01244         BU_CK_EXTERNAL(ep);
01245         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE * ELEMENTS_PER_VECT*4;
01246         ep->ext_buf = (genptr_t)bu_malloc( ep->ext_nbytes, "ell external");
01247 
01248         /* scale 'em into local buffer */
01249         VSCALE( &vec[0*ELEMENTS_PER_VECT], eip->v, local2mm );
01250         VSCALE( &vec[1*ELEMENTS_PER_VECT], eip->a, local2mm );
01251         VSCALE( &vec[2*ELEMENTS_PER_VECT], eip->b, local2mm );
01252         VSCALE( &vec[3*ELEMENTS_PER_VECT], eip->c, local2mm );
01253 
01254         /* Convert from internal (host) to database (network) format */
01255         htond( ep->ext_buf, (unsigned char *)vec, ELEMENTS_PER_VECT*4 );
01256 
01257         return 0;
01258 }
01259 
01260 /**
01261  *                      R T _ E L L _ D E S C R I B E
01262  *
01263  *  Make human-readable formatted presentation of this solid.
01264  *  First line describes type of solid.
01265  *  Additional lines are indented one tab, and give parameter values.
01266  */
01267 int
01268 rt_ell_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
01269 {
01270         register struct rt_ell_internal *tip =
01271                 (struct rt_ell_internal *)ip->idb_ptr;
01272         fastf_t mag_a, mag_b, mag_c;
01273         char    buf[256];
01274         double  angles[5];
01275         vect_t  unitv;
01276 
01277         RT_ELL_CK_MAGIC(tip);
01278         bu_vls_strcat( str, "ellipsoid (ELL)\n");
01279 
01280         sprintf(buf, "\tV (%g, %g, %g)\n",
01281                 INTCLAMP(tip->v[X] * mm2local),
01282                 INTCLAMP(tip->v[Y] * mm2local),
01283                 INTCLAMP(tip->v[Z] * mm2local) );
01284         bu_vls_strcat( str, buf );
01285 
01286         mag_a = MAGNITUDE(tip->a);
01287         mag_b = MAGNITUDE(tip->b);
01288         mag_c = MAGNITUDE(tip->c);
01289 
01290         sprintf(buf, "\tA (%g, %g, %g) mag=%g\n",
01291                 INTCLAMP(tip->a[X] * mm2local),
01292                 INTCLAMP(tip->a[Y] * mm2local),
01293                 INTCLAMP(tip->a[Z] * mm2local),
01294                 INTCLAMP(mag_a * mm2local) );
01295         bu_vls_strcat( str, buf );
01296 
01297         sprintf(buf, "\tB (%g, %g, %g) mag=%g\n",
01298                 INTCLAMP(tip->b[X] * mm2local),
01299                 INTCLAMP(tip->b[Y] * mm2local),
01300                 INTCLAMP(tip->b[Z] * mm2local),
01301                 INTCLAMP(mag_b * mm2local) );
01302         bu_vls_strcat( str, buf );
01303 
01304         sprintf(buf, "\tC (%g, %g, %g) mag=%g\n",
01305                 INTCLAMP(tip->c[X] * mm2local),
01306                 INTCLAMP(tip->c[Y] * mm2local),
01307                 INTCLAMP(tip->c[Z] * mm2local),
01308                 INTCLAMP(mag_c * mm2local) );
01309         bu_vls_strcat( str, buf );
01310 
01311         if( !verbose )  return(0);
01312 
01313         VSCALE( unitv, tip->a, 1/mag_a );
01314         rt_find_fallback_angle( angles, unitv );
01315         rt_pr_fallback_angle( str, "\tA", angles );
01316 
01317         VSCALE( unitv, tip->b, 1/mag_b );
01318         rt_find_fallback_angle( angles, unitv );
01319         rt_pr_fallback_angle( str, "\tB", angles );
01320 
01321         VSCALE( unitv, tip->c, 1/mag_c );
01322         rt_find_fallback_angle( angles, unitv );
01323         rt_pr_fallback_angle( str, "\tC", angles );
01324 
01325         return(0);
01326 }
01327 
01328 /**
01329  *                      R T _ E L L _ I F R E E
01330  *
01331  *  Free the storage associated with the rt_db_internal version of this solid.
01332  */
01333 void
01334 rt_ell_ifree(struct rt_db_internal *ip)
01335 {
01336         RT_CK_DB_INTERNAL(ip);
01337         bu_free( ip->idb_ptr, "ell ifree" );
01338         ip->idb_ptr = GENPTR_NULL;
01339 }
01340 
01341 /*  The U parameter runs south to north.
01342  *  In order to orient loop CCW, need to start with 0,1-->0,0 transition
01343  *  at the south pole.
01344  */
01345 static const fastf_t rt_ell_uvw[5*ELEMENTS_PER_VECT] = {
01346         0, 1, 0,
01347         0, 0, 0,
01348         1, 0, 0,
01349         1, 1, 0,
01350         0, 1, 0
01351 };
01352 
01353 /**
01354  *                      R T _ E L L _ T N U R B
01355  */
01356 int
01357 rt_ell_tnurb(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct bn_tol *tol)
01358 {
01359         LOCAL mat_t     R;
01360         LOCAL mat_t     S;
01361         LOCAL mat_t     invR;
01362         LOCAL mat_t     invS;
01363         mat_t           invRinvS;
01364         mat_t           invRoS;
01365         LOCAL mat_t     unit2model;
01366         LOCAL mat_t     xlate;
01367         LOCAL vect_t    Au, Bu, Cu;     /* A,B,C with unit length */
01368         LOCAL fastf_t   Alen, Blen, Clen;
01369         LOCAL fastf_t   invAlen, invBlen, invClen;
01370         LOCAL fastf_t   magsq_a, magsq_b, magsq_c;
01371         LOCAL fastf_t   f;
01372         register int            i;
01373         fastf_t         radius;
01374         struct rt_ell_internal  *eip;
01375         struct vertex           *verts[8];
01376         struct vertex           **vertp[4];
01377         struct faceuse          *fu;
01378         struct shell            *s;
01379         struct loopuse          *lu;
01380         struct edgeuse          *eu;
01381         point_t                 pole;
01382 
01383         RT_CK_DB_INTERNAL(ip);
01384         eip = (struct rt_ell_internal *)ip->idb_ptr;
01385         RT_ELL_CK_MAGIC(eip);
01386 
01387         /* Validate that |A| > 0, |B| > 0, |C| > 0 */
01388         magsq_a = MAGSQ( eip->a );
01389         magsq_b = MAGSQ( eip->b );
01390         magsq_c = MAGSQ( eip->c );
01391         if( magsq_a < tol->dist || magsq_b < tol->dist || magsq_c < tol->dist ) {
01392                 bu_log("rt_ell_tess():  zero length A, B, or C vector\n");
01393                 return(-2);             /* BAD */
01394         }
01395 
01396         /* Create unit length versions of A,B,C */
01397         invAlen = 1.0/(Alen = sqrt(magsq_a));
01398         VSCALE( Au, eip->a, invAlen );
01399         invBlen = 1.0/(Blen = sqrt(magsq_b));
01400         VSCALE( Bu, eip->b, invBlen );
01401         invClen = 1.0/(Clen = sqrt(magsq_c));
01402         VSCALE( Cu, eip->c, invClen );
01403 
01404         /* Validate that A.B == 0, B.C == 0, A.C == 0 (check dir only) */
01405         f = VDOT( Au, Bu );
01406         if( ! NEAR_ZERO(f, tol->dist) )  {
01407                 bu_log("ell():  A not perpendicular to B, f=%f\n", f);
01408                 return(-3);             /* BAD */
01409         }
01410         f = VDOT( Bu, Cu );
01411         if( ! NEAR_ZERO(f, tol->dist) )  {
01412                 bu_log("ell():  B not perpendicular to C, f=%f\n", f);
01413                 return(-3);             /* BAD */
01414         }
01415         f = VDOT( Au, Cu );
01416         if( ! NEAR_ZERO(f, tol->dist) )  {
01417                 bu_log("ell():  A not perpendicular to C, f=%f\n", f);
01418                 return(-3);             /* BAD */
01419         }
01420 
01421         {
01422                 vect_t  axb;
01423                 VCROSS( axb, Au, Bu );
01424                 f = VDOT( axb, Cu );
01425                 if( f < 0 )  {
01426                         VREVERSE( Cu, Cu );
01427                         VREVERSE( eip->c, eip->c );
01428                 }
01429         }
01430 
01431         /* Compute R and Rinv matrices */
01432         MAT_IDN( R );
01433         VMOVE( &R[0], Au );
01434         VMOVE( &R[4], Bu );
01435         VMOVE( &R[8], Cu );
01436         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
01437 
01438         /* Compute S and invS matrices */
01439         /* invS is just 1/diagonal elements */
01440         MAT_IDN( S );
01441         S[ 0] = invAlen;
01442         S[ 5] = invBlen;
01443         S[10] = invClen;
01444         bn_mat_inv( invS, S );
01445 
01446         /* invRinvS, for converting points from unit sphere to model */
01447         bn_mat_mul( invRinvS, invR, invS );
01448 
01449         /* invRoS, for converting normals from unit sphere to model */
01450         bn_mat_mul( invRoS, invR, S );
01451 
01452         /* Compute radius of bounding sphere */
01453         radius = Alen;
01454         if( Blen > radius )
01455                 radius = Blen;
01456         if( Clen > radius )
01457                 radius = Clen;
01458 
01459         MAT_IDN( xlate );
01460         MAT_DELTAS_VEC( xlate, eip->v );
01461         bn_mat_mul( unit2model, xlate, invRinvS );
01462 
01463         /*
01464          *  --- Build Topology ---
01465          *
01466          *  There is a vertex at either pole, and a single longitude line.
01467          *  There is a single face, an snurb with singularities.
01468          *  vert[0] is the south pole, and is the first row of the ctl_points.
01469          *  vert[1] is the north pole, and is the last row of the ctl_points.
01470          *
01471          *  Somewhat surprisingly, the U parameter runs from south to north.
01472          */
01473         for( i=0; i<8; i++ )  verts[i] = (struct vertex *)0;
01474 
01475         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01476         s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01477 
01478         vertp[0] = &verts[0];
01479         vertp[1] = &verts[0];
01480         vertp[2] = &verts[1];
01481         vertp[3] = &verts[1];
01482 
01483         if( (fu = nmg_cmface( s, vertp, 4 )) == 0 )  {
01484                 bu_log("rt_ell_tnurb(%s): nmg_cmface() fail on face\n");
01485                 return -1;
01486         }
01487 
01488         /* March around the fu's loop assigning uv parameter values */
01489         lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
01490         NMG_CK_LOOPUSE(lu);
01491         eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
01492         NMG_CK_EDGEUSE(eu);
01493 
01494         /* Loop always has Counter-Clockwise orientation (CCW) */
01495         for( i=0; i < 4; i++ )  {
01496                 nmg_vertexuse_a_cnurb( eu->vu_p, &rt_ell_uvw[i*ELEMENTS_PER_VECT] );
01497                 nmg_vertexuse_a_cnurb( eu->eumate_p->vu_p, &rt_ell_uvw[(i+1)*ELEMENTS_PER_VECT] );
01498                 eu = BU_LIST_NEXT( edgeuse, &eu->l );
01499         }
01500 
01501         /* Associate vertex geometry */
01502         VSUB2( pole, eip->v, eip->c );          /* south pole */
01503         nmg_vertex_gv( verts[0], pole );
01504         VADD2( pole, eip->v, eip->c );
01505         nmg_vertex_gv( verts[1], pole );        /* north pole */
01506 
01507         /* Build snurb, transformed into final position */
01508         nmg_sphere_face_snurb( fu, unit2model );
01509 
01510         /* Associate edge geometry (trimming curve) -- linear in param space */
01511         eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
01512         NMG_CK_EDGEUSE(eu);
01513         for( i=0; i < 4; i++ )  {
01514 #if 0
01515 struct snurb sn;
01516 fastf_t param[4];
01517 bu_log("\neu=x%x, vu=x%x, v=x%x  ", eu, eu->vu_p, eu->vu_p->v_p);
01518 VPRINT("xyz", eu->vu_p->v_p->vg_p->coord);
01519 nmg_hack_snurb( &sn, fu->f_p->g.snurb_p );
01520 VPRINT("uv", eu->vu_p->a.cnurb_p->param);
01521 rt_nurb_s_eval( &sn, V2ARGS(eu->vu_p->a.cnurb_p->param), param );
01522 VPRINT("surf(u,v)", param);
01523 #endif
01524 
01525                 nmg_edge_g_cnurb_plinear(eu);
01526                 eu = BU_LIST_NEXT( edgeuse, &eu->l );
01527         }
01528 
01529         /* Compute "geometry" for region and shell */
01530         nmg_region_a( *r, tol );
01531 
01532         return 0;
01533 }
01534 
01535 /*
01536  *  u,v=(0,0) is supposed to be the south pole, at Z=-1.0
01537  *  The V direction runs from the south to the north pole.
01538  */
01539 static void
01540 nmg_sphere_face_snurb(struct faceuse *fu, const matp_t m)
01541 {
01542         struct face_g_snurb     *fg;
01543         FAST fastf_t root2_2;
01544         register fastf_t        *op;
01545 
01546         NMG_CK_FACEUSE(fu);
01547         root2_2 = sqrt(2.0)*0.5;
01548 
01549         /* Let the library allocate all the storage */
01550         /* The V direction runs from south to north pole */
01551         nmg_face_g_snurb( fu,
01552                 3, 3,           /* u,v order */
01553                 8, 12,          /* Number of knots, u,v */
01554                 NULL, NULL,     /* initial u,v knot vectors */
01555                 9, 5,           /* n_rows, n_cols */
01556                 RT_NURB_MAKE_PT_TYPE( 4, RT_NURB_PT_XYZ, RT_NURB_PT_RATIONAL ),
01557                 NULL );         /* initial mesh */
01558 
01559         fg = fu->f_p->g.snurb_p;
01560         NMG_CK_FACE_G_SNURB(fg);
01561 
01562         fg->v.knots[ 0] = 0;
01563         fg->v.knots[ 1] = 0;
01564         fg->v.knots[ 2] = 0;
01565         fg->v.knots[ 3] = 0.25;
01566         fg->v.knots[ 4] = 0.25;
01567         fg->v.knots[ 5] = 0.5;
01568         fg->v.knots[ 6] = 0.5;
01569         fg->v.knots[ 7] = 0.75;
01570         fg->v.knots[ 8] = 0.75;
01571         fg->v.knots[ 9] = 1;
01572         fg->v.knots[10] = 1;
01573         fg->v.knots[11] = 1;
01574 
01575         fg->u.knots[0] = 0;
01576         fg->u.knots[1] = 0;
01577         fg->u.knots[2] = 0;
01578         fg->u.knots[3] = 0.5;
01579         fg->u.knots[4] = 0.5;
01580         fg->u.knots[5] = 1;
01581         fg->u.knots[6] = 1;
01582         fg->u.knots[7] = 1;
01583 
01584         op = fg->ctl_points;
01585 
01586 /* Inspired by MAT4X4PNT */
01587 #define M(x,y,z,w)      { \
01588         *op++ = m[ 0]*(x) + m[ 1]*(y) + m[ 2]*(z) + m[ 3]*(w);\
01589         *op++ = m[ 4]*(x) + m[ 5]*(y) + m[ 6]*(z) + m[ 7]*(w);\
01590         *op++ = m[ 8]*(x) + m[ 9]*(y) + m[10]*(z) + m[11]*(w);\
01591         *op++ = m[12]*(x) + m[13]*(y) + m[14]*(z) + m[15]*(w); }
01592 
01593 
01594         M(   0     ,   0     ,-1.0     , 1.0     );
01595         M( root2_2 ,   0     ,-root2_2 , root2_2 );
01596         M( 1.0     ,   0     ,   0     , 1.0     );
01597         M( root2_2 ,   0     , root2_2 , root2_2 );
01598         M(   0     ,   0     , 1.0     , 1.0     );
01599 
01600         M(   0     ,   0     ,-root2_2 , root2_2 );
01601         M( 0.5     ,-0.5     ,-0.5     , 0.5     );
01602         M( root2_2 ,-root2_2 ,   0     , root2_2 );
01603         M( 0.5     ,-0.5     , 0.5     , 0.5     );
01604         M(   0     ,   0     , root2_2 , root2_2 );
01605 
01606         M(   0     ,   0     ,-1.0     , 1.0     );
01607         M(   0     ,-root2_2 ,-root2_2 , root2_2 );
01608         M(   0     ,-1.0     ,   0     , 1.0     );
01609         M(   0     ,-root2_2 , root2_2 , root2_2 );
01610         M(   0     ,   0     , 1.0     , 1.0     );
01611 
01612         M(   0     ,   0     ,-root2_2 , root2_2 );
01613         M(-0.5     ,-0.5     ,-0.5     , 0.5     );
01614         M(-root2_2 ,-root2_2 ,   0     , root2_2 );
01615         M(-0.5     ,-0.5     , 0.5     , 0.5     );
01616         M(   0     ,   0     , root2_2 , root2_2 );
01617 
01618         M(   0     ,   0     ,-1.0     , 1.0     );
01619         M(-root2_2 ,   0     ,-root2_2 , root2_2 );
01620         M(-1.0     ,   0     ,   0     , 1.0     );
01621         M(-root2_2 ,   0     , root2_2 , root2_2 );
01622         M(   0     ,   0     , 1.0     , 1.0     );
01623 
01624         M(   0     ,   0     ,-root2_2 , root2_2 );
01625         M(-0.5     , 0.5     ,-0.5     , 0.5     );
01626         M(-root2_2 , root2_2 ,   0     , root2_2 );
01627         M(-0.5     , 0.5     , 0.5     , 0.5     );
01628         M(   0     ,   0     , root2_2 , root2_2 );
01629 
01630         M(   0     ,   0     ,-1.0     , 1.0     );
01631         M(   0     , root2_2 ,-root2_2 , root2_2 );
01632         M(   0     , 1.0     ,   0     , 1.0     );
01633         M(   0     , root2_2 , root2_2 , root2_2 );
01634         M(   0     ,   0     , 1.0     , 1.0     );
01635 
01636         M(   0     ,   0     ,-root2_2 , root2_2 );
01637         M( 0.5     , 0.5     ,-0.5     , 0.5     );
01638         M( root2_2 , root2_2 ,   0     , root2_2 );
01639         M( 0.5     , 0.5     , 0.5     , 0.5     );
01640         M(   0     ,   0     , root2_2 , root2_2 );
01641 
01642         M(   0     ,   0     ,-1.0     , 1.0     );
01643         M( root2_2 ,   0     ,-root2_2 , root2_2 );
01644         M( 1.0     ,   0     ,   0     , 1.0     );
01645         M( root2_2 ,   0     , root2_2 , root2_2 );
01646         M(   0     ,   0     , 1.0     , 1.0     );
01647 }
01648 
01649 /*@}*/
01650 /*
01651  * Local Variables:
01652  * mode: C
01653  * tab-width: 8
01654  * c-basic-offset: 4
01655  * indent-tabs-mode: t
01656  * End:
01657  * ex: shiftwidth=4 tabstop=8
01658  */

Generated on Mon Sep 18 01:24:50 2006 for BRL-CAD by  doxygen 1.4.6