g_epa.c

Go to the documentation of this file.
00001 /*                         G _ E P A . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1990-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_epa.c
00026  *      Intersect a ray with an Elliptical Paraboloid.
00027  *
00028  *  Algorithm -
00029  *
00030  *  Given V, H, R, and B, there is a set of points on this epa
00031  *
00032  *  { (x,y,z) | (x,y,z) is on epa }
00033  *
00034  *  Through a series of Affine Transformations, this set of points will be
00035  *  transformed into a set of points on an epa located at the origin
00036  *  with a semi-major axis R1 along the +Y axis, a semi-minor axis R2
00037  *  along the -X axis, a height H along the -Z axis, and a vertex V at
00038  *  the origin.
00039  *
00040  *
00041  *  { (x',y',z') | (x',y',z') is on epa at origin }
00042  *
00043  *  The transformation from X to X' is accomplished by:
00044  *
00045  *  X' = S(R( X - V ))
00046  *
00047  *  where R(X) =  ( R1/(-|R1|) )
00048  *               (  R2/( |R2|)  ) . X
00049  *                ( H /(-|H |) )
00050  *
00051  *  and S(X) =   (  1/|R1|   0     0   )
00052  *              (    0    1/|R2|   0    ) . X
00053  *               (   0      0   1/|H | )
00054  *
00055  *  To find the intersection of a line with the surface of the epa,
00056  *  consider the parametric line L:
00057  *
00058  *      L : { P(n) | P + t(n) . D }
00059  *
00060  *  Call W the actual point of intersection between L and the epa.
00061  *  Let W' be the point of intersection between L' and the unit epa.
00062  *
00063  *      L' : { P'(n) | P' + t(n) . D' }
00064  *
00065  *  W = invR( invS( W' ) ) + V
00066  *
00067  *  Where W' = k D' + P'.
00068  *
00069  *  If Dy' and Dz' are both 0, then there is no hit on the epa;
00070  *  but the end plates need checking.  If there is now only 1 hit
00071  *  point, the top plate needs to be checked as well.
00072  *
00073  *  Line L' hits the infinitely long epa at W' when
00074  *
00075  *      A * k**2 + B * k + C = 0
00076  *
00077  *  where
00078  *
00079  *  A = Dx'**2 + Dy'**2
00080  *  B = 2 * (Dx' * Px' + Dy' * Py') - Dz'
00081  *  C = Px'**2 + Py'**2 - Pz' - 1
00082  *  b = |Breadth| = 1.0
00083  *  h = |Height| = 1.0
00084  *  r = 1.0
00085  *
00086  *  The quadratic formula yields k (which is constant):
00087  *
00088  *  k = [ -B +/- sqrt( B**2 - 4 * A * C )] / (2.0 * A)
00089  *
00090  *  Now, D' = S( R( D ) )
00091  *  and  P' = S( R( P - V ) )
00092  *
00093  *  Substituting,
00094  *
00095  *  W = V + invR( invS[ k *( S( R( D ) ) ) + S( R( P - V ) ) ] )
00096  *    = V + invR( ( k * R( D ) ) + R( P - V ) )
00097  *    = V + k * D + P - V
00098  *    = k * D + P
00099  *
00100  *  Note that ``k'' is constant, and is the same in the formulations
00101  *  for both W and W'.
00102  *
00103  *  The hit at ``k'' is a hit on the canonical epa IFF
00104  *  Wz' <= 0.
00105  *
00106  *  NORMALS.  Given the point W on the surface of the epa,
00107  *  what is the vector normal to the tangent plane at that point?
00108  *
00109  *  Map W onto the unit epa, ie:  W' = S( R( W - V ) ).
00110  *
00111  *  Plane on unit epa at W' has a normal vector N' where
00112  *
00113  *  N' = <Wx', Wy', -.5>.
00114  *
00115  *  The plane transforms back to the tangent plane at W, and this
00116  *  new plane (on the original epa) has a normal vector of N, viz:
00117  *
00118  *  N = inverse[ transpose( inverse[ S o R ] ) ] ( N' )
00119  *
00120  *  because if H is perpendicular to plane Q, and matrix M maps from
00121  *  Q to Q', then inverse[ transpose(M) ] (H) is perpendicular to Q'.
00122  *  Here, H and Q are in "prime space" with the unit sphere.
00123  *  [Somehow, the notation here is backwards].
00124  *  So, the mapping matrix M = inverse( S o R ), because
00125  *  S o R maps from normal space to the unit sphere.
00126  *
00127  *  N = inverse[ transpose( inverse[ S o R ] ) ] ( N' )
00128  *    = inverse[ transpose(invR o invS) ] ( N' )
00129  *    = inverse[ transpose(invS) o transpose(invR) ] ( N' )
00130  *    = inverse[ inverse(S) o R ] ( N' )
00131  *    = invR o S ( N' )
00132  *
00133  *  because inverse(R) = transpose(R), so R = transpose( invR ),
00134  *  and S = transpose( S ).
00135  *
00136  *  Note that the normal vector produced above will not have unit length.
00137  *
00138  *  THE TOP PLATE.
00139  *
00140  *  If Dz' == 0, line L' is parallel to the top plate, so there is no
00141  *  hit on the top plate.  Otherwise, rays intersect the top plate
00142  *  with k = (0 - Pz')/Dz'.  The solution is within the top plate
00143  *  IFF Wx'**2 + Wy'**2 <= 1.
00144  *
00145  *  The normal for a hit on the top plate is -Hunit.
00146  *
00147  *  Authors -
00148  *      Michael J. Markowski
00149  *
00150  *  Source -
00151  *      SECAD/VLD Computing Consortium, Bldg 394
00152  *      The U. S. Army Ballistic Research Laboratory
00153  *      Aberdeen Proving Ground, Maryland  21005-5066
00154  *
00155  */
00156 
00157 #ifndef lint
00158 static const char RCSepa[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_epa.c,v 14.12 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00159 #endif
00160 
00161 #include "common.h"
00162 
00163 #include <stddef.h>
00164 #include <stdio.h>
00165 #ifdef HAVE_STRING_H
00166 #  include <string.h>
00167 #else
00168 #  include <strings.h>
00169 #endif
00170 #include <math.h>
00171 
00172 #include "machine.h"
00173 #include "vmath.h"
00174 #include "db.h"
00175 #include "nmg.h"
00176 #include "raytrace.h"
00177 #include "rtgeom.h"
00178 #include "./debug.h"
00179 
00180 struct epa_specific {
00181         point_t epa_V;          /* vector to epa origin */
00182         vect_t  epa_Hunit;      /* unit H vector */
00183         vect_t  epa_Aunit;      /* unit vector along semi-major axis */
00184         vect_t  epa_Bunit;      /* unit vector, A x H */
00185         fastf_t epa_h;          /* |H| */
00186         fastf_t epa_inv_r1sq;   /* 1/(r1 * r1) */
00187         fastf_t epa_inv_r2sq;   /* 1/(r2 * r2) */
00188         mat_t   epa_SoR;        /* Scale(Rot(vect)) */
00189         mat_t   epa_invRoS;     /* invRot(Scale(vect)) */
00190 };
00191 
00192 const struct bu_structparse rt_epa_parse[] = {
00193     { "%f", 3, "V",   bu_offsetof(struct rt_epa_internal, epa_V[X]),  BU_STRUCTPARSE_FUNC_NULL },
00194     { "%f", 3, "H",   bu_offsetof(struct rt_epa_internal, epa_H[X]),  BU_STRUCTPARSE_FUNC_NULL },
00195     { "%f", 3, "A",   bu_offsetof(struct rt_epa_internal, epa_Au[X]), BU_STRUCTPARSE_FUNC_NULL },
00196     { "%f", 1, "r_1", bu_offsetof(struct rt_epa_internal, epa_r1),    BU_STRUCTPARSE_FUNC_NULL },
00197     { "%f", 1, "r_2", bu_offsetof(struct rt_epa_internal, epa_r2),    BU_STRUCTPARSE_FUNC_NULL },
00198     { {'\0','\0','\0','\0'}, 0, (char *)NULL, 0, BU_STRUCTPARSE_FUNC_NULL }
00199 };
00200 
00201 /**
00202  *                      R T _ E P A _ P R E P
00203  *
00204  *  Given a pointer to a GED database record, and a transformation matrix,
00205  *  determine if this is a valid EPA, and if so, precompute various
00206  *  terms of the formula.
00207  *
00208  *  Returns -
00209  *      0       EPA is OK
00210  *      !0      Error in description
00211  *
00212  *  Implicit return -
00213  *      A struct epa_specific is created, and it's address is stored in
00214  *      stp->st_specific for use by epa_shot().
00215  */
00216 int
00217 rt_epa_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00218 {
00219         struct rt_epa_internal          *xip;
00220         register struct epa_specific    *epa;
00221 #ifndef NO_MAGIC_CHECKING
00222         const struct bn_tol             *tol = &rtip->rti_tol;
00223 #endif
00224         LOCAL fastf_t   magsq_h;
00225         LOCAL fastf_t   mag_a, mag_h;
00226         LOCAL fastf_t   f, r1, r2;
00227         LOCAL mat_t     R;
00228         LOCAL mat_t     Rinv;
00229         LOCAL mat_t     S;
00230 
00231 #ifndef NO_MAGIC_CHECKING
00232         RT_CK_DB_INTERNAL(ip);
00233         BN_CK_TOL(tol);
00234 #endif
00235         xip = (struct rt_epa_internal *)ip->idb_ptr;
00236         RT_EPA_CK_MAGIC(xip);
00237 
00238         /* compute |A| |H| */
00239         mag_a = sqrt( MAGSQ( xip->epa_Au ) );
00240         mag_h = sqrt( magsq_h = MAGSQ( xip->epa_H ) );
00241         r1 = xip->epa_r1;
00242         r2 = xip->epa_r2;
00243         /* Check for |H| > 0, |A| == 1, r1 >  0, r2 > 0 */
00244         if( NEAR_ZERO(mag_h, RT_LEN_TOL)
00245                 || !NEAR_ZERO(mag_a - 1.0, RT_LEN_TOL)
00246                 || r1 < 0.0 || r2 < 0.0 )  {
00247                 return(1);              /* BAD, too small */
00248         }
00249 
00250         /* Check for A.H == 0 */
00251         f = VDOT( xip->epa_Au, xip->epa_H ) / mag_h;
00252         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
00253                 return(1);              /* BAD */
00254         }
00255 
00256         /*
00257          *  EPA is ok
00258          */
00259         stp->st_id = ID_EPA;            /* set soltab ID */
00260         stp->st_meth = &rt_functab[ID_EPA];
00261 
00262         BU_GETSTRUCT( epa, epa_specific );
00263         stp->st_specific = (genptr_t)epa;
00264 
00265         epa->epa_h = mag_h;
00266         epa->epa_inv_r1sq = 1 / (r1 * r1);
00267         epa->epa_inv_r2sq = 1 / (r2 * r2);
00268 
00269         /* make unit vectors in A, H, and BxH directions */
00270         VMOVE(    epa->epa_Hunit, xip->epa_H );
00271         VUNITIZE( epa->epa_Hunit );
00272         VMOVE(    epa->epa_Aunit, xip->epa_Au );
00273         VCROSS(   epa->epa_Bunit, epa->epa_Aunit, epa->epa_Hunit );
00274 
00275         VMOVE( epa->epa_V, xip->epa_V );
00276 
00277         /* Compute R and Rinv matrices */
00278         MAT_IDN( R );
00279         VREVERSE( &R[0], epa->epa_Bunit );
00280         VMOVE(    &R[4], epa->epa_Aunit );
00281         VREVERSE( &R[8], epa->epa_Hunit );
00282         bn_mat_trn( Rinv, R );                  /* inv of rot mat is trn */
00283 
00284         /* Compute S */
00285         MAT_IDN( S );
00286         S[ 0] = 1.0/r2;
00287         S[ 5] = 1.0/r1;
00288         S[10] = 1.0/mag_h;
00289 
00290         /* Compute SoR and invRoS */
00291         bn_mat_mul( epa->epa_SoR, S, R );
00292         bn_mat_mul( epa->epa_invRoS, Rinv, S );
00293 
00294         /* Compute bounding sphere and RPP */
00295         /* bounding sphere center */
00296         VJOIN1( stp->st_center, epa->epa_V, mag_h / 2.0, epa->epa_Hunit );
00297         /* bounding radius */
00298         stp->st_bradius = sqrt(0.25*magsq_h + r2*r2 + r1*r1);
00299         /* approximate bounding radius */
00300         stp->st_aradius = stp->st_bradius;
00301 
00302         /* cheat, make bounding RPP by enclosing bounding sphere */
00303         stp->st_min[X] = stp->st_center[X] - stp->st_bradius;
00304         stp->st_max[X] = stp->st_center[X] + stp->st_bradius;
00305         stp->st_min[Y] = stp->st_center[Y] - stp->st_bradius;
00306         stp->st_max[Y] = stp->st_center[Y] + stp->st_bradius;
00307         stp->st_min[Z] = stp->st_center[Z] - stp->st_bradius;
00308         stp->st_max[Z] = stp->st_center[Z] + stp->st_bradius;
00309 
00310         return(0);                      /* OK */
00311 }
00312 
00313 /**
00314  *                      R T _ E P A _ P R I N T
00315  */
00316 void
00317 rt_epa_print(register const struct soltab *stp)
00318 {
00319         register const struct epa_specific *epa =
00320                 (struct epa_specific *)stp->st_specific;
00321 
00322         VPRINT("V", epa->epa_V);
00323         VPRINT("Hunit", epa->epa_Hunit);
00324         VPRINT("Aunit", epa->epa_Aunit);
00325         VPRINT("Bunit", epa->epa_Bunit);
00326         bn_mat_print("S o R", epa->epa_SoR );
00327         bn_mat_print("invR o S", epa->epa_invRoS );
00328 }
00329 
00330 /* hit_surfno is set to one of these */
00331 #define EPA_NORM_BODY   (1)             /* compute normal */
00332 #define EPA_NORM_TOP    (2)             /* copy epa_N */
00333 
00334 /**
00335  *                      R T _ E P A _ S H O T
00336  *
00337  *  Intersect a ray with a epa.
00338  *  If an intersection occurs, a struct seg will be acquired
00339  *  and filled in.
00340  *
00341  *  Returns -
00342  *      0       MISS
00343  *      >0      HIT
00344  */
00345 int
00346 rt_epa_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00347 {
00348         register struct epa_specific *epa =
00349                 (struct epa_specific *)stp->st_specific;
00350         LOCAL vect_t    dprime;         /* D' */
00351         LOCAL vect_t    pprime;         /* P' */
00352         LOCAL fastf_t   k1, k2;         /* distance constants of solution */
00353         LOCAL vect_t    xlated;         /* translated vector */
00354         LOCAL struct hit hits[3];       /* 2 potential hit points */
00355         register struct hit *hitp;      /* pointer to hit point */
00356 
00357         hitp = &hits[0];
00358 
00359         /* out, Mat, vect */
00360         MAT4X3VEC( dprime, epa->epa_SoR, rp->r_dir );
00361         VSUB2( xlated, rp->r_pt, epa->epa_V );
00362         MAT4X3VEC( pprime, epa->epa_SoR, xlated );
00363 
00364         /* Find roots of the equation, using formula for quadratic */
00365         {
00366                 FAST fastf_t    a, b, c;        /* coeffs of polynomial */
00367                 FAST fastf_t    disc;           /* disc of radical */
00368 
00369                 a = dprime[X] * dprime[X] + dprime[Y] * dprime[Y];
00370                 b = 2*(dprime[X] * pprime[X] + dprime[Y] * pprime[Y])
00371                         - dprime[Z];
00372                 c = pprime[X] * pprime[X]
00373                         + pprime[Y] * pprime[Y] - pprime[Z] - 1.0;
00374                 if ( !NEAR_ZERO(a, RT_PCOEF_TOL) ) {
00375                         disc = b*b - 4 * a * c;
00376                         if (disc <= 0)
00377                                 goto check_plates;
00378                         disc = sqrt(disc);
00379 
00380                         k1 = (-b + disc) / (2.0 * a);
00381                         k2 = (-b - disc) / (2.0 * a);
00382 
00383                         /*
00384                          *  k1 and k2 are potential solutions to intersection
00385                          *  with side.  See if they fall in range.
00386                          */
00387                         VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00388                         if( hitp->hit_vpriv[Z] <= 0.0 ) {
00389                                 hitp->hit_magic = RT_HIT_MAGIC;
00390                                 hitp->hit_dist = k1;
00391                                 hitp->hit_surfno = EPA_NORM_BODY;       /* compute N */
00392                                 hitp++;
00393                         }
00394 
00395                         VJOIN1( hitp->hit_vpriv, pprime, k2, dprime );  /* hit' */
00396                         if( hitp->hit_vpriv[Z] <= 0.0 ) {
00397                                 hitp->hit_magic = RT_HIT_MAGIC;
00398                                 hitp->hit_dist = k2;
00399                                 hitp->hit_surfno = EPA_NORM_BODY;       /* compute N */
00400                                 hitp++;
00401                         }
00402                 } else if ( !NEAR_ZERO(b, RT_PCOEF_TOL) ) {
00403                         k1 = -c/b;
00404                         VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00405                         if( hitp->hit_vpriv[Z] <= 0.0 ) {
00406                                 hitp->hit_magic = RT_HIT_MAGIC;
00407                                 hitp->hit_dist = k1;
00408                                 hitp->hit_surfno = EPA_NORM_BODY;       /* compute N */
00409                                 hitp++;
00410                         }
00411                 }
00412         }
00413 
00414 
00415         /*
00416          * Check for hitting the top plate.
00417          */
00418 check_plates:
00419         /* check top plate */
00420         if( hitp == &hits[1]  &&  !NEAR_ZERO(dprime[Z], SMALL) )  {
00421                 /* 1 hit so far, this is worthwhile */
00422                 k1 = -pprime[Z] / dprime[Z];            /* top plate */
00423 
00424                 VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00425                 if( hitp->hit_vpriv[X] * hitp->hit_vpriv[X] +
00426                         hitp->hit_vpriv[Y] * hitp->hit_vpriv[Y] <= 1.0 ) {
00427                         hitp->hit_magic = RT_HIT_MAGIC;
00428                         hitp->hit_dist = k1;
00429                         hitp->hit_surfno = EPA_NORM_TOP;        /* -H */
00430                         hitp++;
00431                 }
00432         }
00433 
00434         if( hitp != &hits[2] )
00435                 return(0);      /* MISS */
00436 
00437         if( hits[0].hit_dist < hits[1].hit_dist )  {
00438                 /* entry is [0], exit is [1] */
00439                 register struct seg *segp;
00440 
00441                 RT_GET_SEG(segp, ap->a_resource);
00442                 segp->seg_stp = stp;
00443                 segp->seg_in = hits[0];         /* struct copy */
00444                 segp->seg_out = hits[1];        /* struct copy */
00445                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00446         } else {
00447                 /* entry is [1], exit is [0] */
00448                 register struct seg *segp;
00449 
00450                 RT_GET_SEG(segp, ap->a_resource);
00451                 segp->seg_stp = stp;
00452                 segp->seg_in = hits[1];         /* struct copy */
00453                 segp->seg_out = hits[0];        /* struct copy */
00454                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00455         }
00456         return(2);                      /* HIT */
00457 }
00458 
00459 #define RT_EPA_SEG_MISS(SEG)    (SEG).seg_stp=RT_SOLTAB_NULL
00460 
00461 /**
00462  *                      R T _ E P A _ V S H O T
00463  *
00464  *  Vectorized version.
00465  */
00466 void
00467 rt_epa_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00468                                /* An array of solid pointers */
00469                                /* An array of ray pointers */
00470                                /* array of segs (results returned) */
00471                                /* Number of ray/object pairs */
00472 
00473 {
00474         rt_vstub( stp, rp, segp, n, ap );
00475 }
00476 
00477 /**
00478  *                      R T _ E P A _ N O R M
00479  *
00480  *  Given ONE ray distance, return the normal and entry/exit point.
00481  */
00482 void
00483 rt_epa_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00484 {
00485         fastf_t scale;
00486         vect_t  can_normal;     /* normal to canonical epa */
00487         register struct epa_specific *epa =
00488                 (struct epa_specific *)stp->st_specific;
00489 
00490         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00491         switch( hitp->hit_surfno )  {
00492         case EPA_NORM_BODY:
00493                 VSET( can_normal,
00494                         hitp->hit_vpriv[X],
00495                         hitp->hit_vpriv[Y],
00496                         -0.5 );
00497                 MAT4X3VEC( hitp->hit_normal, epa->epa_invRoS, can_normal );
00498                 scale = 1.0 / MAGNITUDE( hitp->hit_normal );
00499                 VSCALE( hitp->hit_normal, hitp->hit_normal, scale );
00500 
00501                 /* tuck away this scale for the curvature routine */
00502                 hitp->hit_vpriv[X] = scale;
00503                 break;
00504         case EPA_NORM_TOP:
00505                 VREVERSE( hitp->hit_normal, epa->epa_Hunit );
00506                 break;
00507         default:
00508                 bu_log("rt_epa_norm: surfno=%d bad\n", hitp->hit_surfno);
00509                 break;
00510         }
00511 }
00512 
00513 /**
00514  *                      R T _ E P A _ C U R V E
00515  *
00516  *  Return the curvature of the epa.
00517  */
00518 void
00519 rt_epa_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00520 {
00521         fastf_t a, b, c, scale;
00522         mat_t   M1, M2;
00523         register struct epa_specific *epa =
00524                 (struct epa_specific *)stp->st_specific;
00525         vect_t  u, v;                   /* basis vectors (with normal) */
00526         vect_t  vec1, vec2;             /* eigen vectors */
00527         vect_t  tmp;
00528 
00529         switch( hitp->hit_surfno )  {
00530         case EPA_NORM_BODY:
00531                 /*
00532                  * choose a tangent plane coordinate system
00533                  *  (u, v, normal) form a right-handed triple
00534                  */
00535                 bn_vec_ortho( u, hitp->hit_normal );
00536                 VCROSS( v, hitp->hit_normal, u );
00537 
00538                 /* get the saved away scale factor */
00539                 scale = - hitp->hit_vpriv[X];
00540 
00541                 MAT_IDN( M1 );
00542                 M1[10] = 0;     /* M1[3,3] = 0 */
00543                 /* M1 = invR * S * M1 * S * R */
00544                 bn_mat_mul( M2, epa->epa_invRoS, M1);
00545                 bn_mat_mul( M1, M2, epa->epa_SoR );
00546 
00547                 /* find the second fundamental form */
00548                 MAT4X3VEC( tmp, M1, u );
00549                 a = VDOT(u, tmp) * scale;
00550                 b = VDOT(v, tmp) * scale;
00551                 MAT4X3VEC( tmp, M1, v );
00552                 c = VDOT(v, tmp) * scale;
00553 
00554                 eigen2x2( &cvp->crv_c1, &cvp->crv_c2, vec1, vec2, a, b, c );
00555                 VCOMB2( cvp->crv_pdir, vec1[X], u, vec1[Y], v );
00556                 VUNITIZE( cvp->crv_pdir );
00557                 break;
00558         case EPA_NORM_TOP:
00559                 cvp->crv_c1 = cvp->crv_c2 = 0;
00560                 /* any tangent direction */
00561                 bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
00562                 break;
00563         }
00564 }
00565 
00566 /**
00567  *                      R T _ E P A _ U V
00568  *
00569  *  For a hit on the surface of an epa, return the (u,v) coordinates
00570  *  of the hit point, 0 <= u,v <= 1.
00571  *  u = azimuth
00572  *  v = elevation
00573  */
00574 void
00575 rt_epa_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00576 {
00577         register struct epa_specific *epa =
00578                 (struct epa_specific *)stp->st_specific;
00579         LOCAL vect_t work;
00580         LOCAL vect_t pprime;
00581         FAST fastf_t len;
00582 
00583         /*
00584          * hit_point is on surface;  project back to unit epa,
00585          * creating a vector from vertex to hit point.
00586          */
00587         VSUB2( work, hitp->hit_point, epa->epa_V );
00588         MAT4X3VEC( pprime, epa->epa_SoR, work );
00589 
00590         switch( hitp->hit_surfno )  {
00591         case EPA_NORM_BODY:
00592                 /* top plate, polar coords */
00593                 if (pprime[Z] == -1.0) {        /* bottom pt of body */
00594                         uvp->uv_u = 0;
00595                 } else {
00596                         len = sqrt(pprime[X]*pprime[X] + pprime[Y]*pprime[Y]);
00597                         uvp->uv_u = acos(pprime[X]/len) * bn_inv2pi;
00598                 }
00599                 uvp->uv_v = -pprime[Z];
00600                 break;
00601         case EPA_NORM_TOP:
00602                 /* top plate, polar coords */
00603                 len = sqrt(pprime[X]*pprime[X] + pprime[Y]*pprime[Y]);
00604                 uvp->uv_u = acos(pprime[X]/len) * bn_inv2pi;
00605                 uvp->uv_v = 1.0 - len;
00606                 break;
00607         }
00608         /* Handle other half of acos() domain */
00609         if( pprime[Y] < 0 )
00610                 uvp->uv_u = 1.0 - uvp->uv_u;
00611 
00612         /* uv_du should be relative to rotation, uv_dv relative to height */
00613         uvp->uv_du = uvp->uv_dv = 0;
00614 }
00615 
00616 /**
00617  *              R T _ E P A _ F R E E
00618  */
00619 void
00620 rt_epa_free(register struct soltab *stp)
00621 {
00622         register struct epa_specific *epa =
00623                 (struct epa_specific *)stp->st_specific;
00624 
00625 
00626         bu_free( (char *)epa, "epa_specific" );
00627 }
00628 
00629 /**
00630  *                      R T _ E P A _ C L A S S
00631  */
00632 int
00633 rt_epa_class(void)
00634 {
00635         return(0);
00636 }
00637 
00638 /**
00639  *                      R T _ E P A _ P L O T
00640  */
00641 int
00642 rt_epa_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00643 {
00644         fastf_t         dtol, f, mag_a, mag_h, ntol, r1, r2;
00645         fastf_t         **ellipses, theta_new, theta_prev, rt_ell_ang(fastf_t *p1, fastf_t a, fastf_t b, fastf_t dtol, fastf_t ntol);
00646         int             *pts_dbl, i, j, nseg;
00647         int             jj, na, nb, nell, recalc_b;
00648         LOCAL mat_t     R;
00649         LOCAL mat_t     invR;
00650         LOCAL struct rt_epa_internal    *xip;
00651         point_t         p1;
00652         struct rt_pt_node       *pos_a, *pos_b, *pts_a, *pts_b, *rt_ptalloc(void);
00653         vect_t          A, Au, B, Bu, Hu, V, Work;
00654 
00655 #ifndef NO_MAGIC_CHECKING
00656         RT_CK_DB_INTERNAL(ip);
00657 #endif
00658 
00659         xip = (struct rt_epa_internal *)ip->idb_ptr;
00660         RT_EPA_CK_MAGIC(xip);
00661 
00662         /*
00663          *      make sure epa description is valid
00664          */
00665 
00666         /* compute |A| |H| */
00667         mag_a = MAGSQ( xip->epa_Au );   /* should already be unit vector */
00668         mag_h = MAGNITUDE( xip->epa_H );
00669         r1 = xip->epa_r1;
00670         r2 = xip->epa_r2;
00671         /* Check for |H| > 0, |A| == 1, r1 > 0, r2 > 0 */
00672         if( NEAR_ZERO(mag_h, RT_LEN_TOL)
00673                 || !NEAR_ZERO(mag_a - 1.0, RT_LEN_TOL)
00674                 || r1 <= 0.0 || r2 <= 0.0 )  {
00675                 return(-2);             /* BAD */
00676         }
00677 
00678         /* Check for A.H == 0 */
00679         f = VDOT( xip->epa_Au, xip->epa_H ) / mag_h;
00680         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
00681                 return(-2);             /* BAD */
00682         }
00683 
00684         /* make unit vectors in A, H, and BxH directions */
00685         VMOVE(    Hu, xip->epa_H );
00686         VUNITIZE( Hu );
00687         VMOVE(    Au, xip->epa_Au );
00688         VCROSS(   Bu, Au, Hu );
00689 
00690         /* Compute R and Rinv matrices */
00691         MAT_IDN( R );
00692         VREVERSE( &R[0], Bu );
00693         VMOVE(    &R[4], Au );
00694         VREVERSE( &R[8], Hu );
00695         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
00696 
00697         /*
00698          *  Establish tolerances
00699          */
00700         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )
00701                 dtol = 0.0;             /* none */
00702         else
00703                 /* Convert rel to absolute by scaling by smallest side */
00704                 dtol = ttol->rel * 2 * r2;
00705         if( ttol->abs <= 0.0 )  {
00706                 if( dtol <= 0.0 )
00707                         /* No tolerance given, use a default */
00708                         dtol = 2 * 0.10 * r2;   /* 10% */
00709                 else
00710                         /* Use absolute-ized relative tolerance */
00711                         ;
00712         } else {
00713                 /* Absolute tolerance was given, pick smaller */
00714                 if( ttol->rel <= 0.0 || dtol > ttol->abs )
00715                         dtol = ttol->abs;
00716         }
00717 
00718         /* To ensure normal tolerance, remain below this angle */
00719         if( ttol->norm > 0.0 )
00720                 ntol = ttol->norm;
00721         else
00722                 /* tolerate everything */
00723                 ntol = bn_pi;
00724 
00725         /*
00726          *      build epa from 2 parabolas
00727          */
00728 
00729         /* approximate positive half of parabola along semi-minor axis */
00730         pts_b = rt_ptalloc();
00731         pts_b->next = rt_ptalloc();
00732         pts_b->next->next = NULL;
00733         VSET( pts_b->p,       0, 0, -mag_h);
00734         VSET( pts_b->next->p, 0, r2, 0);
00735         /* 2 endpoints in 1st approximation */
00736         nb = 2;
00737         /* recursively break segment 'til within error tolerances */
00738         nb += rt_mk_parabola( pts_b, r2, mag_h, dtol, ntol );
00739         nell = nb - 1;  /* # of ellipses needed */
00740 
00741         /* construct positive half of parabola along semi-major axis
00742          * of epa using same z coords as parab along semi-minor axis
00743          */
00744         pts_a = rt_ptalloc();
00745         VMOVE(pts_a->p, pts_b->p);      /* 1st pt is the apex */
00746         pts_a->next = NULL;
00747         pos_b = pts_b->next;
00748         pos_a = pts_a;
00749         while (pos_b) {
00750                 /* copy node from b_parabola to a_parabola */
00751                 pos_a->next = rt_ptalloc();
00752                 pos_a = pos_a->next;
00753                 pos_a->p[Z] = pos_b->p[Z];
00754                 /* at given z, find y on parabola */
00755                 pos_a->p[Y] = r1*sqrt( pos_a->p[Z] / mag_h + 1 );
00756                 pos_a->p[X] = 0;
00757                 pos_b = pos_b->next;
00758         }
00759         pos_a->next = NULL;
00760 
00761         /* see if any segments need further breaking up */
00762         recalc_b = 0;
00763         na = 0;
00764         pos_a = pts_a;
00765         while (pos_a->next) {
00766                 na = rt_mk_parabola( pos_a, r1, mag_h, dtol, ntol );
00767                 if (na != 0) {
00768                         recalc_b = 1;
00769                         nell += na;
00770                 }
00771                 pos_a = pos_a->next;
00772         }
00773         /* if any were broken, recalculate parabola 'a' */
00774         if ( recalc_b ) {
00775                 /* free mem for old approximation of parabola */
00776                 pos_b = pts_b;
00777                 while ( pos_b ) {
00778                         struct rt_pt_node *next;
00779 
00780                         /* get next node before freeing */
00781                         next = pos_b->next;
00782                         bu_free( (char *)pos_b, "rt_pt_node" );
00783                         pos_b = next;
00784                 }
00785                 /* construct parabola along semi-major axis of epa
00786                  * using same z coords as parab along semi-minor axis
00787                  */
00788                 pts_b = rt_ptalloc();
00789                 pts_b->p[Z] = pts_a->p[Z];
00790                 pts_b->next = NULL;
00791                 pos_a = pts_a->next;
00792                 pos_b = pts_b;
00793                 while (pos_a) {
00794                         /* copy node from a_parabola to b_parabola */
00795                         pos_b->next = rt_ptalloc();
00796                         pos_b = pos_b->next;
00797                         pos_b->p[Z] = pos_a->p[Z];
00798                         /* at given z, find y on parabola */
00799                         pos_b->p[Y] = r2*sqrt( pos_b->p[Z] / mag_h + 1 );
00800                         pos_b->p[X] = 0;
00801                         pos_a = pos_a->next;
00802                 }
00803                 pos_b->next = NULL;
00804         }
00805 
00806         /* make array of ptrs to epa ellipses */
00807         ellipses = (fastf_t **)bu_malloc( nell * sizeof(fastf_t *), "fastf_t ell[]");
00808         /* keep track of whether pts in each ellipse are doubled or not */
00809         pts_dbl = (int *)bu_malloc( nell * sizeof(int), "dbl ints" );
00810 
00811         /* make ellipses at each z level */
00812         i = 0;
00813         nseg = 0;
00814         theta_prev = bn_twopi;
00815         pos_a = pts_a->next;    /* skip over apex of epa */
00816         pos_b = pts_b->next;
00817         while (pos_a) {
00818                 VSCALE( A, Au, pos_a->p[Y] );   /* semimajor axis */
00819                 VSCALE( B, Bu, pos_b->p[Y] );   /* semiminor axis */
00820                 VJOIN1( V, xip->epa_V, -pos_a->p[Z], Hu );
00821 
00822                 VSET( p1, 0., pos_b->p[Y], 0. );
00823                 theta_new = rt_ell_ang(p1, pos_a->p[Y], pos_b->p[Y], dtol, ntol);
00824                 if (nseg == 0) {
00825                         nseg = (int)(bn_twopi / theta_new) + 1;
00826                         pts_dbl[i] = 0;
00827                 } else if (theta_new < theta_prev) {
00828                         nseg *= 2;
00829                         pts_dbl[i] = 1;
00830                 } else
00831                         pts_dbl[i] = 0;
00832                 theta_prev = theta_new;
00833 
00834                 ellipses[i] = (fastf_t *)bu_malloc(3*(nseg+1)*sizeof(fastf_t),
00835                         "pts ell");
00836                 rt_ell( ellipses[i], V, A, B, nseg );
00837 
00838                 i++;
00839                 pos_a = pos_a->next;
00840                 pos_b = pos_b->next;
00841         }
00842         /* Draw the top ellipse */
00843         RT_ADD_VLIST( vhead,
00844                 &ellipses[nell-1][(nseg-1)*ELEMENTS_PER_VECT],
00845                 BN_VLIST_LINE_MOVE );
00846         for( i = 0; i < nseg; i++ )  {
00847                 RT_ADD_VLIST( vhead,
00848                         &ellipses[nell-1][i*ELEMENTS_PER_VECT],
00849                         BN_VLIST_LINE_DRAW );
00850         }
00851 
00852         /* connect ellipses */
00853         for (i = nell-2; i >= 0; i--) { /* skip top ellipse */
00854                 int bottom, top;
00855 
00856                 top = i + 1;
00857                 bottom = i;
00858                 if (pts_dbl[top])
00859                         nseg /= 2;      /* # segs in 'bottom' ellipse */
00860 
00861                 /* Draw the current ellipse */
00862                 RT_ADD_VLIST( vhead,
00863                         &ellipses[bottom][(nseg-1)*ELEMENTS_PER_VECT],
00864                         BN_VLIST_LINE_MOVE );
00865                 for( j = 0; j < nseg; j++ )  {
00866                         RT_ADD_VLIST( vhead,
00867                                 &ellipses[bottom][j*ELEMENTS_PER_VECT],
00868                                 BN_VLIST_LINE_DRAW );
00869                 }
00870 
00871                 /* make connections between ellipses */
00872                 for (j = 0; j < nseg; j++) {
00873                         if (pts_dbl[top])
00874                                 jj = j + j;     /* top ellipse index */
00875                         else
00876                                 jj = j;
00877                         RT_ADD_VLIST( vhead,
00878                                 &ellipses[bottom][j*ELEMENTS_PER_VECT],
00879                                 BN_VLIST_LINE_MOVE );
00880                         RT_ADD_VLIST( vhead,
00881                                 &ellipses[top][jj*ELEMENTS_PER_VECT],
00882                                 BN_VLIST_LINE_DRAW );
00883                 }
00884         }
00885 
00886         VADD2( Work, xip->epa_V, xip->epa_H );
00887         for (i = 0; i < nseg; i++) {
00888                 /* Draw connector */
00889                 RT_ADD_VLIST( vhead, Work, BN_VLIST_LINE_MOVE );
00890                 RT_ADD_VLIST( vhead,
00891                         &ellipses[0][i*ELEMENTS_PER_VECT],
00892                         BN_VLIST_LINE_DRAW );
00893         }
00894 
00895         /* free mem */
00896         for (i = 0; i < nell; i++) {
00897                 bu_free( (char *)ellipses[i], "pts ell");
00898         }
00899         bu_free( (char *)ellipses, "fastf_t ell[]");
00900         bu_free( (char *)pts_dbl, "dbl ints" );
00901 
00902         return(0);
00903 }
00904 
00905 #define ELLOUT(n)       ov+(n-1)*3
00906 
00907 void
00908 rt_ell_norms(register fastf_t *ov, fastf_t *A, fastf_t *B, fastf_t *h_vec, fastf_t t, int sides)
00909 {
00910         fastf_t ang, theta, x, y, sqrt_1mt;
00911         int     n;
00912         vect_t partial_t, partial_ang;
00913 
00914         sqrt_1mt = sqrt( 1.0 - t );
00915         if( sqrt_1mt < SMALL_FASTF )
00916                 rt_bomb( "rt_epa_tess: rt_ell_norms: sqrt( 1.0 -t ) is zero\n" );
00917         theta = 2 * bn_pi / sides;
00918         ang = 0.;
00919 
00920         for (n = 1; n <= sides; n++, ang += theta) {
00921                 x = cos( ang );
00922                 y = sin( ang );
00923                 VJOIN2( partial_t, h_vec, -x/(2.0*sqrt_1mt), A, -y/(2.0*sqrt_1mt), B );
00924                 VBLEND2( partial_ang, x*sqrt_1mt, B, -y*sqrt_1mt, A );
00925                 VCROSS( ELLOUT(n), partial_t, partial_ang );
00926                 VUNITIZE( ELLOUT(n) );
00927         }
00928         VMOVE(ELLOUT(n), ELLOUT(1));
00929 }
00930 
00931 
00932 /**
00933  *                      R T _ E L L
00934  *
00935  *  Generate an ellipsoid with the specified number of sides approximating it.
00936  */
00937 void
00938 rt_ell(register fastf_t *ov, register const fastf_t *V, const fastf_t *A, const fastf_t *B, int sides)
00939 {
00940         fastf_t ang, theta, x, y;
00941         int     n;
00942 
00943         theta = 2 * bn_pi / sides;
00944         ang = 0.;
00945         /* make ellipse regardless of whether it meets req's */
00946         for (n = 1; n <= sides; n++, ang += theta) {
00947                 x = cos( ang );
00948                 y = sin( ang );
00949                 VJOIN2( ELLOUT(n), V, x, A, y, B );
00950         }
00951         VMOVE(ELLOUT(n), ELLOUT(1));
00952 }
00953 
00954 /**
00955  *      R T _ E L L _ A N G
00956  *
00957  *      Return angle required for smallest side to fall within
00958  *      tolerances for ellipse.  Smallest side is a side with
00959  *      an endpoint at (a, 0, 0) where a is the semi-major axis.
00960  */
00961 fastf_t
00962 rt_ell_ang(fastf_t *p1, fastf_t a, fastf_t b, fastf_t dtol, fastf_t ntol)
00963 {
00964         fastf_t dist, intr, m, theta0, theta1;
00965         point_t mpt, p0;
00966         vect_t  norm_line, norm_ell;
00967 
00968         VSET( p0, a, 0., 0. );
00969         /* slope and intercept of segment */
00970         m = ( p1[Y] - p0[Y] ) / ( p1[X] - p0[X] );
00971         intr = p0[Y] - m * p0[X];
00972         /* point on ellipse with max dist between ellipse and line */
00973         mpt[X] = a / sqrt( b*b / (m*m*a*a) + 1 );
00974         mpt[Y] = b * sqrt( 1 - mpt[X] * mpt[X] / (a*a) );
00975         mpt[Z] = 0;
00976         /* max distance between that point and line */
00977         dist = fabs( m * mpt[X] - mpt[Y] + intr ) / sqrt( m * m + 1 );
00978         /* angles between normal of line and of ellipse at line endpoints */
00979         VSET( norm_line, m, -1., 0.);
00980         VSET( norm_ell, b * b * p0[X], a * a * p0[Y], 0. );
00981         VUNITIZE( norm_line );
00982         VUNITIZE( norm_ell );
00983         theta0 = fabs( acos( VDOT( norm_line, norm_ell )));
00984         VSET( norm_ell, b * b * p1[X], a * a * p1[Y], 0. );
00985         VUNITIZE( norm_ell );
00986         theta1 = fabs( acos( VDOT( norm_line, norm_ell )));
00987         /* split segment at widest point if not within error tolerances */
00988         if ( dist > dtol || theta0 > ntol || theta1 > ntol ) {
00989                 /* split segment */
00990                 return( rt_ell_ang( mpt, a, b, dtol, ntol ) );
00991         } else
00992                 return( acos( VDOT(p0, p1)
00993                         / ( MAGNITUDE(p0) * MAGNITUDE(p1) ) ));
00994 }
00995 
00996 /**
00997  *                      R T _ E P A _ T E S S
00998  *
00999  *  Returns -
01000  *      -1      failure
01001  *       0      OK.  *r points to nmgregion that holds this tessellation.
01002  */
01003 int
01004 rt_epa_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01005 {
01006         fastf_t         dtol, f, mag_a, mag_h, ntol, r1, r2;
01007         fastf_t         **ellipses, **normals, theta_new, theta_prev;
01008         int             *pts_dbl, face, i, j, nseg;
01009         int             *segs_per_ell;
01010         int             jj, na, nb, nell, recalc_b;
01011         LOCAL mat_t     R;
01012         LOCAL mat_t     invR;
01013         LOCAL struct rt_epa_internal    *xip;
01014         point_t         p1;
01015         struct rt_pt_node       *pos_a, *pos_b, *pts_a, *pts_b, *rt_ptalloc(void);
01016         struct shell    *s;
01017         struct faceuse  **outfaceuses = NULL;
01018         struct vertex   *vertp[3];
01019         struct vertex   ***vells = (struct vertex ***)NULL;
01020         vect_t          A, Au, B, Bu, Hu, V;
01021         vect_t          apex_norm,rev_norm;
01022         vect_t          A_orig,B_orig;
01023         struct vertex   *apex_v;
01024         struct vertexuse *vu;
01025         struct faceuse *fu;
01026 
01027         RT_CK_DB_INTERNAL(ip);
01028         xip = (struct rt_epa_internal *)ip->idb_ptr;
01029         RT_EPA_CK_MAGIC(xip);
01030 
01031         /*
01032          *      make sure epa description is valid
01033          */
01034 
01035         /* compute |A| |H| */
01036         mag_a = MAGSQ( xip->epa_Au );   /* should already be unit vector */
01037         mag_h = MAGNITUDE( xip->epa_H );
01038         r1 = xip->epa_r1;
01039         r2 = xip->epa_r2;
01040         /* Check for |H| > 0, |A| == 1, r1 > 0, r2 > 0 */
01041         if( NEAR_ZERO(mag_h, RT_LEN_TOL)
01042                 || !NEAR_ZERO(mag_a - 1.0, RT_LEN_TOL)
01043                 || r1 <= 0.0 || r2 <= 0.0 )  {
01044                 return(-2);             /* BAD */
01045         }
01046 
01047         /* Check for A.H == 0 */
01048         f = VDOT( xip->epa_Au, xip->epa_H ) / mag_h;
01049         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
01050                 return(-2);             /* BAD */
01051         }
01052 
01053         /* make unit vectors in A, H, and BxH directions */
01054         VMOVE(    Hu, xip->epa_H );
01055         VUNITIZE( Hu );
01056         VMOVE(    Au, xip->epa_Au );
01057         VCROSS(   Bu, Au, Hu );
01058 
01059         VSCALE( A_orig , Au , xip->epa_r1 );
01060         VSCALE( B_orig , Bu , xip->epa_r2 );
01061 
01062         /* Compute R and Rinv matrices */
01063         MAT_IDN( R );
01064         VREVERSE( &R[0], Bu );
01065         VMOVE(    &R[4], Au );
01066         VREVERSE( &R[8], Hu );
01067         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
01068 
01069         /*
01070          *  Establish tolerances
01071          */
01072         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )
01073                 dtol = 0.0;             /* none */
01074         else
01075                 /* Convert rel to absolute by scaling by smallest side */
01076                 dtol = ttol->rel * 2 * r2;
01077         if( ttol->abs <= 0.0 )  {
01078                 if( dtol <= 0.0 )
01079                         /* No tolerance given, use a default */
01080                         dtol = 2 * 0.10 * r2;   /* 10% */
01081                 else
01082                         /* Use absolute-ized relative tolerance */
01083                         ;
01084         } else {
01085                 /* Absolute tolerance was given, pick smaller */
01086                 if( ttol->rel <= 0.0 || dtol > ttol->abs )
01087                         dtol = ttol->abs;
01088         }
01089 
01090         /* To ensure normal tolerance, remain below this angle */
01091         if( ttol->norm > 0.0 )
01092                 ntol = ttol->norm;
01093         else
01094                 /* tolerate everything */
01095                 ntol = bn_pi;
01096 
01097         /*
01098          *      build epa from 2 parabolas
01099          */
01100 
01101         /* approximate positive half of parabola along semi-minor axis */
01102         pts_b = rt_ptalloc();
01103         pts_b->next = rt_ptalloc();
01104         pts_b->next->next = NULL;
01105         VSET( pts_b->p,       0, 0, -mag_h);
01106         VSET( pts_b->next->p, 0, r2, 0);
01107         /* 2 endpoints in 1st approximation */
01108         nb = 2;
01109         /* recursively break segment 'til within error tolerances */
01110         nb += rt_mk_parabola( pts_b, r2, mag_h, dtol, ntol );
01111         nell = nb - 1;  /* # of ellipses needed */
01112 
01113         /* construct positive half of parabola along semi-major axis
01114          * of epa using same z coords as parab along semi-minor axis
01115          */
01116         pts_a = rt_ptalloc();
01117         VMOVE(pts_a->p, pts_b->p);      /* 1st pt is the apex */
01118         pts_a->next = NULL;
01119         pos_b = pts_b->next;
01120         pos_a = pts_a;
01121         while (pos_b) {
01122                 /* copy node from b_parabola to a_parabola */
01123                 pos_a->next = rt_ptalloc();
01124                 pos_a = pos_a->next;
01125                 pos_a->p[Z] = pos_b->p[Z];
01126                 /* at given z, find y on parabola */
01127                 pos_a->p[Y] = r1*sqrt( pos_a->p[Z] / mag_h + 1 );
01128                 pos_a->p[X] = 0;
01129                 pos_b = pos_b->next;
01130         }
01131         pos_a->next = NULL;
01132 
01133         /* see if any segments need further breaking up */
01134         recalc_b = 0;
01135         na = 0;
01136         pos_a = pts_a;
01137         while (pos_a->next) {
01138                 na = rt_mk_parabola( pos_a, r1, mag_h, dtol, ntol );
01139                 if (na != 0) {
01140                         recalc_b = 1;
01141                         nell += na;
01142                 }
01143                 pos_a = pos_a->next;
01144         }
01145         /* if any were broken, recalculate parabola 'a' */
01146         if ( recalc_b ) {
01147                 /* free mem for old approximation of parabola */
01148                 pos_b = pts_b;
01149                 while ( pos_b ) {
01150                         struct rt_pt_node *tmp;
01151 
01152                         tmp = pos_b->next;
01153                         bu_free( (char *)pos_b, "rt_pt_node" );
01154                         pos_b = tmp;
01155                 }
01156                 /* construct parabola along semi-major axis of epa
01157                  * using same z coords as parab along semi-minor axis
01158                  */
01159                 pts_b = rt_ptalloc();
01160                 pts_b->p[Z] = pts_a->p[Z];
01161                 pts_b->next = NULL;
01162                 pos_a = pts_a->next;
01163                 pos_b = pts_b;
01164                 while (pos_a) {
01165                         /* copy node from a_parabola to b_parabola */
01166                         pos_b->next = rt_ptalloc();
01167                         pos_b = pos_b->next;
01168                         pos_b->p[Z] = pos_a->p[Z];
01169                         /* at given z, find y on parabola */
01170                         pos_b->p[Y] = r2*sqrt( pos_b->p[Z] / mag_h + 1 );
01171                         pos_b->p[X] = 0;
01172                         pos_a = pos_a->next;
01173                 }
01174                 pos_b->next = NULL;
01175         }
01176 
01177         /* make array of ptrs to epa ellipses */
01178         ellipses = (fastf_t **)bu_malloc( nell * sizeof(fastf_t *), "fastf_t ell[]");
01179         /* keep track of whether pts in each ellipse are doubled or not */
01180         pts_dbl = (int *)bu_malloc( nell * sizeof(int), "dbl ints" );
01181         /* I don't understand this pts_dbl, so here is an array containing the length of
01182          * each ellipses array
01183          */
01184         segs_per_ell = (int *)bu_calloc( nell , sizeof( int ) , "rt_epa_tess: segs_per_ell" );
01185 
01186         /* and an array of normals */
01187         normals = (fastf_t **)bu_malloc( nell * sizeof(fastf_t *), "fastf_t normals[]");
01188 
01189         /* make ellipses at each z level */
01190         i = 0;
01191         nseg = 0;
01192         theta_prev = bn_twopi;
01193         pos_a = pts_a->next;    /* skip over apex of epa */
01194         pos_b = pts_b->next;
01195         while (pos_a) {
01196                 fastf_t t;
01197 
01198                 t = (-pos_a->p[Z] / mag_h);
01199                 VSCALE( A, Au, pos_a->p[Y] );   /* semimajor axis */
01200                 VSCALE( B, Bu, pos_b->p[Y] );   /* semiminor axis */
01201                 VJOIN1( V, xip->epa_V, -pos_a->p[Z], Hu );
01202 
01203                 VSET( p1, 0., pos_b->p[Y], 0. );
01204                 theta_new = rt_ell_ang(p1, pos_a->p[Y], pos_b->p[Y], dtol, ntol);
01205                 if (nseg == 0) {
01206                         nseg = (int)(bn_twopi / theta_new) + 1;
01207                         pts_dbl[i] = 0;
01208                         /* maximum number of faces needed for epa */
01209                         face = nseg*(1 + 3*((1 << (nell-1)) - 1));
01210                         /* array for each triangular face */
01211                         outfaceuses = (struct faceuse **)
01212                         bu_malloc( (face+1) * sizeof(struct faceuse *), "faceuse []" );
01213                 } else if (theta_new < theta_prev) {
01214                         nseg *= 2;
01215                         pts_dbl[i] = 1;
01216                 } else {
01217                         pts_dbl[i] = 0;
01218                 }
01219                 theta_prev = theta_new;
01220 
01221                 ellipses[i] = (fastf_t *)bu_malloc(3*(nseg+1)*sizeof(fastf_t),
01222                         "pts ell");
01223                 segs_per_ell[i] = nseg;
01224                 normals[i] = (fastf_t *)bu_malloc(3*(nseg+1)*sizeof(fastf_t), "rt_epa_tess_ normals" );
01225                 rt_ell( ellipses[i], V, A, B, nseg );
01226                 rt_ell_norms( normals[i], A_orig, B_orig, xip->epa_H, t, nseg );
01227 
01228                 i++;
01229                 pos_a = pos_a->next;
01230                 pos_b = pos_b->next;
01231         }
01232 
01233         /*
01234          *      put epa geometry into nmg data structures
01235          */
01236 
01237         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01238         s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01239 
01240         /* vertices of ellipses of epa */
01241         vells = (struct vertex ***)
01242                 bu_malloc(nell*sizeof(struct vertex **), "vertex [][]");
01243         j = nseg;
01244         for (i = nell-1; i >= 0; i--) {
01245                 vells[i] = (struct vertex **)
01246                         bu_malloc(j*sizeof(struct vertex *), "vertex []");
01247                 if (i && pts_dbl[i])
01248                         j /= 2;
01249         }
01250 
01251         /* top face of epa */
01252         for (i = 0; i < nseg; i++)
01253                 vells[nell-1][i] = (struct vertex *)0;
01254         face = 0;
01255         if ( (outfaceuses[face++] = nmg_cface(s, vells[nell-1], nseg)) == 0) {
01256                 bu_log("rt_epa_tess() failure, top face\n");
01257                 goto fail;
01258         }
01259         for (i = 0; i < nseg; i++) {
01260                 NMG_CK_VERTEX( vells[nell-1][i] );
01261                 nmg_vertex_gv( vells[nell-1][i], &ellipses[nell-1][3*i] );
01262         }
01263 
01264         /* Mark the edges of this face as real, this is the only real edge */
01265         (void)nmg_mark_edges_real( &outfaceuses[0]->l.magic );
01266 
01267         /* connect ellipses with triangles */
01268         for (i = nell-2; i >= 0; i--) { /* skip top ellipse */
01269                 int bottom, top;
01270 
01271                 top = i + 1;
01272                 bottom = i;
01273                 if (pts_dbl[top])
01274                         nseg /= 2;      /* # segs in 'bottom' ellipse */
01275                 vertp[0] = (struct vertex *)0;
01276 
01277                 /* make triangular faces */
01278                 for (j = 0; j < nseg; j++) {
01279                         jj = j + j;     /* top ellipse index */
01280 
01281                         if (pts_dbl[top]) {
01282                                 /* first triangle */
01283                                 vertp[1] = vells[top][jj+1];
01284                                 vertp[2] = vells[top][jj];
01285                                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01286                                         bu_log("rt_epa_tess() failure\n");
01287                                         goto fail;
01288                                 }
01289                                 if (j == 0)
01290                                         vells[bottom][j] = vertp[0];
01291 
01292                                 /* second triangle */
01293                                 vertp[2] = vertp[1];
01294                                 if (j == nseg-1)
01295                                         vertp[1] = vells[bottom][0];
01296                                 else
01297                                         vertp[1] = (struct vertex *)0;
01298                                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01299                                         bu_log("rt_epa_tess() failure\n");
01300                                         goto fail;
01301                                 }
01302                                 if (j != nseg-1)
01303                                         vells[bottom][j+1] = vertp[1];
01304 
01305                                 /* third triangle */
01306                                 vertp[0] = vertp[1];
01307                                 if (j == nseg-1)
01308                                         vertp[1] = vells[top][0];
01309                                 else
01310                                         vertp[1] = vells[top][jj+2];
01311                                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01312                                         bu_log("rt_epa_tess() failure\n");
01313                                         goto fail;
01314                                 }
01315                         } else {
01316                                 /* first triangle */
01317                                 if (j == nseg-1)
01318                                         vertp[1] = vells[top][0];
01319                                 else
01320                                         vertp[1] = vells[top][j+1];
01321                                 vertp[2] = vells[top][j];
01322                                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01323                                         bu_log("rt_epa_tess() failure\n");
01324                                         goto fail;
01325                                 }
01326                                 if (j == 0)
01327                                         vells[bottom][j] = vertp[0];
01328 
01329                                 /* second triangle */
01330                                 vertp[2] = vertp[0];
01331                                 if (j == nseg-1)
01332                                         vertp[0] = vells[bottom][0];
01333                                 else
01334                                         vertp[0] = (struct vertex *)0;
01335                                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01336                                         bu_log("rt_epa_tess() failure\n");
01337                                         goto fail;
01338                                 }
01339                                 if (j != nseg-1)
01340                                         vells[bottom][j+1] = vertp[0];
01341                         }
01342                 }
01343 
01344                 /* associate geometry with each vertex */
01345                 for (j = 0; j < nseg; j++) {
01346                         NMG_CK_VERTEX( vells[bottom][j] );
01347                         nmg_vertex_gv( vells[bottom][j],
01348                                 &ellipses[bottom][3*j] );
01349                 }
01350         }
01351 
01352         /* connect bottom of ellipse to apex of epa */
01353         VADD2(V, xip->epa_V, xip->epa_H);
01354         vertp[0] = (struct vertex *)0;
01355         vertp[1] = vells[0][1];
01356         vertp[2] = vells[0][0];
01357         if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01358                 bu_log("rt_epa_tess() failure\n");
01359                 goto fail;
01360         }
01361         /* associate geometry with topology */
01362         NMG_CK_VERTEX(vertp[0]);
01363         nmg_vertex_gv( vertp[0], (fastf_t *)V );
01364         apex_v = vertp[0];
01365         /* create rest of faces around apex */
01366         for (i = 1; i < nseg; i++) {
01367                 vertp[2] = vertp[1];
01368                 if (i == nseg-1)
01369                         vertp[1] = vells[0][0];
01370                 else
01371                         vertp[1] = vells[0][i+1];
01372                 if ( (outfaceuses[face++] = nmg_cface(s, vertp, 3)) == 0) {
01373                         bu_log("rt_epa_tess() failure\n");
01374                         goto fail;
01375                 }
01376         }
01377 
01378         /* Associate the face geometry */
01379         for (i=0 ; i < face ; i++) {
01380                 if( nmg_fu_planeeqn( outfaceuses[i], tol ) < 0 )
01381                         goto fail;
01382         }
01383 
01384         /* Associate vertexuse normals */
01385         for( i=0 ; i<nell ; i++ )
01386         {
01387                 for( j=0 ; j<segs_per_ell[i] ; j++ )
01388                 {
01389                         VREVERSE( rev_norm , &normals[i][j*3] );
01390                         for( BU_LIST_FOR( vu , vertexuse , &vells[i][j]->vu_hd ) )
01391                         {
01392 
01393                                 fu = nmg_find_fu_of_vu( vu );
01394                                 NMG_CK_FACEUSE( fu );
01395 
01396                                 if( fu == outfaceuses[0] || fu->fumate_p == outfaceuses[0] )
01397                                         continue;       /* don't assign normals to top faceuse (flat) */
01398 
01399                                 if( fu->orientation == OT_SAME )
01400                                         nmg_vertexuse_nv( vu , &normals[i][j*3] );
01401                                 else if( fu->orientation == OT_OPPOSITE )
01402                                         nmg_vertexuse_nv( vu , rev_norm );
01403                         }
01404                 }
01405         }
01406         /* and don't forget the apex */
01407         VMOVE( apex_norm , xip->epa_H );
01408         VUNITIZE( apex_norm );
01409         VREVERSE( rev_norm , apex_norm );
01410         for( BU_LIST_FOR( vu , vertexuse , &apex_v->vu_hd ) )
01411         {
01412                 NMG_CK_VERTEXUSE( vu );
01413                 fu = nmg_find_fu_of_vu( vu );
01414                 NMG_CK_FACEUSE( fu );
01415                 if( fu->orientation == OT_SAME )
01416                         nmg_vertexuse_nv( vu , apex_norm );
01417                 else if( fu->orientation == OT_OPPOSITE )
01418                         nmg_vertexuse_nv( vu , rev_norm );
01419         }
01420 
01421         /* Glue the edges of different outward pointing face uses together */
01422         nmg_gluefaces( outfaceuses, face, tol );
01423 
01424         /* Compute "geometry" for region and shell */
01425         nmg_region_a( *r, tol );
01426 
01427         /* XXX just for testing, to make up for loads of triangles ... */
01428         nmg_shell_coplanar_face_merge( s, tol, 1 );
01429 
01430         /* free mem */
01431         bu_free( (char *)outfaceuses, "faceuse []");
01432         for (i = 0; i < nell; i++) {
01433                 bu_free( (char *)ellipses[i], "pts ell");
01434                 bu_free( (char *)vells[i], "vertex []");
01435         }
01436         bu_free( (char *)ellipses, "fastf_t ell[]");
01437         bu_free( (char *)pts_dbl, "dbl ints" );
01438         bu_free( (char *)vells, "vertex [][]");
01439 
01440         return(0);
01441 
01442 fail:
01443         /* free mem */
01444         bu_free( (char *)outfaceuses, "faceuse []");
01445         for (i = 0; i < nell; i++) {
01446                 bu_free( (char *)ellipses[i], "pts ell");
01447                 bu_free( (char *)vells[i], "vertex []");
01448         }
01449         bu_free( (char *)ellipses, "fastf_t ell[]");
01450         bu_free( (char *)pts_dbl, "dbl ints" );
01451         bu_free( (char *)vells, "vertex [][]");
01452 
01453         return(-1);
01454 }
01455 
01456 /**
01457  *                      R T _ E P A _ I M P O R T
01458  *
01459  *  Import an EPA from the database format to the internal format.
01460  *  Apply modeling transformations as well.
01461  */
01462 int
01463 rt_epa_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01464 {
01465         LOCAL struct rt_epa_internal    *xip;
01466         union record                    *rp;
01467 
01468         BU_CK_EXTERNAL( ep );
01469         rp = (union record *)ep->ext_buf;
01470         /* Check record type */
01471         if( rp->u_id != ID_SOLID )  {
01472                 bu_log("rt_epa_import: defective record\n");
01473                 return(-1);
01474         }
01475 
01476         RT_CK_DB_INTERNAL( ip );
01477         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01478         ip->idb_type = ID_EPA;
01479         ip->idb_meth = &rt_functab[ID_EPA];
01480         ip->idb_ptr = bu_malloc( sizeof(struct rt_epa_internal), "rt_epa_internal");
01481         xip = (struct rt_epa_internal *)ip->idb_ptr;
01482         xip->epa_magic = RT_EPA_INTERNAL_MAGIC;
01483 
01484         /* Warning:  type conversion */
01485         MAT4X3PNT( xip->epa_V, mat, &rp->s.s_values[0*3] );
01486         MAT4X3VEC( xip->epa_H, mat, &rp->s.s_values[1*3] );
01487         MAT4X3VEC( xip->epa_Au, mat, &rp->s.s_values[2*3] );
01488         VUNITIZE( xip->epa_Au );
01489         xip->epa_r1 = rp->s.s_values[3*3] / mat[15];
01490         xip->epa_r2 = rp->s.s_values[3*3+1] / mat[15];
01491 
01492         if( xip->epa_r1 < SMALL_FASTF || xip->epa_r2 < SMALL_FASTF )
01493         {
01494                 bu_log( "rt_epa_import: r1 or r2 are zero\n" );
01495                 bu_free( (char *)ip->idb_ptr , "rt_epa_import: ip->idb_ptr" );
01496                 return( -1 );
01497         }
01498 
01499         return(0);                      /* OK */
01500 }
01501 
01502 /**
01503  *                      R T _ E P A _ E X P O R T
01504  *
01505  *  The name is added by the caller, in the usual place.
01506  */
01507 int
01508 rt_epa_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01509 {
01510         struct rt_epa_internal  *xip;
01511         union record            *epa;
01512         fastf_t                 mag_h;
01513 
01514         RT_CK_DB_INTERNAL(ip);
01515         if( ip->idb_type != ID_EPA )  return(-1);
01516         xip = (struct rt_epa_internal *)ip->idb_ptr;
01517         RT_EPA_CK_MAGIC(xip);
01518 
01519         BU_CK_EXTERNAL(ep);
01520         ep->ext_nbytes = sizeof(union record);
01521         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "epa external");
01522         epa = (union record *)ep->ext_buf;
01523 
01524         epa->s.s_id = ID_SOLID;
01525         epa->s.s_type = EPA;
01526 
01527         if (!NEAR_ZERO( MAGNITUDE(xip->epa_Au) - 1., RT_LEN_TOL)) {
01528                 bu_log("rt_epa_export: Au not a unit vector!\n");
01529                 return(-1);
01530         }
01531 
01532         mag_h = MAGNITUDE(xip->epa_H);
01533 
01534         if ( mag_h < RT_LEN_TOL
01535                 || xip->epa_r1 < RT_LEN_TOL
01536                 || xip->epa_r2 < RT_LEN_TOL) {
01537                 bu_log("rt_epa_export: not all dimensions positive!\n");
01538                 return(-1);
01539         }
01540 
01541         if ( !NEAR_ZERO( VDOT(xip->epa_Au, xip->epa_H)/mag_h, RT_DOT_TOL) ) {
01542                 bu_log("rt_epa_export: Au and H are not perpendicular!\n");
01543                 return(-1);
01544         }
01545 
01546         if (xip->epa_r2 > xip->epa_r1) {
01547                 bu_log("rt_epa_export: semi-minor axis cannot be longer than semi-major axis!\n");
01548                 return(-1);
01549         }
01550 
01551         /* Warning:  type conversion */
01552         VSCALE( &epa->s.s_values[0*3], xip->epa_V, local2mm );
01553         VSCALE( &epa->s.s_values[1*3], xip->epa_H, local2mm );
01554         VMOVE( &epa->s.s_values[2*3], xip->epa_Au ); /* don't scale a unit vector */
01555         epa->s.s_values[3*3] = xip->epa_r1 * local2mm;
01556         epa->s.s_values[3*3+1] = xip->epa_r2 * local2mm;
01557 
01558         return(0);
01559 }
01560 
01561 /**
01562  *                      R T _ E P A _ I M P O R T 5
01563  *
01564  *  Import an EPA from the database format to the internal format.
01565  *  Apply modeling transformations as well.
01566  */
01567 int
01568 rt_epa_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01569 {
01570         LOCAL struct rt_epa_internal    *xip;
01571         fastf_t                         vec[11];
01572 
01573         BU_CK_EXTERNAL( ep );
01574 
01575         BU_ASSERT_LONG( ep->ext_nbytes, ==, SIZEOF_NETWORK_DOUBLE * 11 );
01576 
01577         RT_CK_DB_INTERNAL( ip );
01578         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01579         ip->idb_type = ID_EPA;
01580         ip->idb_meth = &rt_functab[ID_EPA];
01581         ip->idb_ptr = bu_malloc( sizeof(struct rt_epa_internal), "rt_epa_internal");
01582 
01583         xip = (struct rt_epa_internal *)ip->idb_ptr;
01584         xip->epa_magic = RT_EPA_INTERNAL_MAGIC;
01585 
01586         /* Convert from database (network) to internal (host) format */
01587         ntohd( (unsigned char *)vec, ep->ext_buf, 11 );
01588 
01589         /* Apply modeling transformations */
01590         MAT4X3PNT( xip->epa_V, mat, &vec[0*3] );
01591         MAT4X3VEC( xip->epa_H, mat, &vec[1*3] );
01592         MAT4X3VEC( xip->epa_Au, mat, &vec[2*3] );
01593         VUNITIZE( xip->epa_Au );
01594         xip->epa_r1 = vec[3*3] / mat[15];
01595         xip->epa_r2 = vec[3*3+1] / mat[15];
01596 
01597         if( xip->epa_r1 < SMALL_FASTF || xip->epa_r2 < SMALL_FASTF )
01598         {
01599                 bu_log( "rt_epa_import: r1 or r2 are zero\n" );
01600                 bu_free( (char *)ip->idb_ptr , "rt_epa_import: ip->idb_ptr" );
01601                 return( -1 );
01602         }
01603 
01604         return(0);                      /* OK */
01605 }
01606 
01607 /**
01608  *                      R T _ E P A _ E X P O R T 5
01609  *
01610  *  The name is added by the caller, in the usual place.
01611  */
01612 int
01613 rt_epa_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01614 {
01615         struct rt_epa_internal  *xip;
01616         fastf_t                 vec[11];
01617         fastf_t                 mag_h;
01618 
01619         RT_CK_DB_INTERNAL(ip);
01620         if( ip->idb_type != ID_EPA )  return(-1);
01621         xip = (struct rt_epa_internal *)ip->idb_ptr;
01622         RT_EPA_CK_MAGIC(xip);
01623 
01624         BU_CK_EXTERNAL(ep);
01625         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE * 11;
01626         ep->ext_buf = (genptr_t)bu_malloc( ep->ext_nbytes, "epa external");
01627 
01628         if (!NEAR_ZERO( MAGNITUDE(xip->epa_Au) - 1., RT_LEN_TOL)) {
01629                 bu_log("rt_epa_export: Au not a unit vector!\n");
01630                 return(-1);
01631         }
01632 
01633         mag_h = MAGNITUDE(xip->epa_H);
01634 
01635         if ( mag_h < RT_LEN_TOL
01636                 || xip->epa_r1 < RT_LEN_TOL
01637                 || xip->epa_r2 < RT_LEN_TOL) {
01638                 bu_log("rt_epa_export: not all dimensions positive!\n");
01639                 return(-1);
01640         }
01641 
01642         if ( !NEAR_ZERO( VDOT(xip->epa_Au, xip->epa_H)/mag_h, RT_DOT_TOL) ) {
01643                 bu_log("rt_epa_export: Au and H are not perpendicular!\n");
01644                 return(-1);
01645         }
01646 
01647         if (xip->epa_r2 > xip->epa_r1) {
01648                 bu_log("rt_epa_export: semi-minor axis cannot be longer than semi-major axis!\n");
01649                 return(-1);
01650         }
01651 
01652         /* scale 'em into local buffer */
01653         VSCALE( &vec[0*3], xip->epa_V, local2mm );
01654         VSCALE( &vec[1*3], xip->epa_H, local2mm );
01655         VMOVE( &vec[2*3], xip->epa_Au ); /* don't scale a unit vector */
01656         vec[3*3] = xip->epa_r1 * local2mm;
01657         vec[3*3+1] = xip->epa_r2 * local2mm;
01658 
01659         /* Convert from internal (host) to database (network) format */
01660         htond( ep->ext_buf, (unsigned char *)vec, 11 );
01661 
01662         return(0);
01663 }
01664 
01665 /**
01666  *                      R T _ E P A _ D E S C R I B E
01667  *
01668  *  Make human-readable formatted presentation of this solid.
01669  *  First line describes type of solid.
01670  *  Additional lines are indented one tab, and give parameter values.
01671  */
01672 int
01673 rt_epa_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
01674 {
01675         register struct rt_epa_internal *xip =
01676                 (struct rt_epa_internal *)ip->idb_ptr;
01677         char    buf[256];
01678 
01679         RT_EPA_CK_MAGIC(xip);
01680         bu_vls_strcat( str, "Elliptical Paraboloid (EPA)\n");
01681 
01682         sprintf(buf, "\tV (%g, %g, %g)\n",
01683                 INTCLAMP(xip->epa_V[X] * mm2local),
01684                 INTCLAMP(xip->epa_V[Y] * mm2local),
01685                 INTCLAMP(xip->epa_V[Z] * mm2local) );
01686         bu_vls_strcat( str, buf );
01687 
01688         sprintf(buf, "\tH (%g, %g, %g) mag=%g\n",
01689                 INTCLAMP(xip->epa_H[X] * mm2local),
01690                 INTCLAMP(xip->epa_H[Y] * mm2local),
01691                 INTCLAMP(xip->epa_H[Z] * mm2local),
01692                 INTCLAMP(MAGNITUDE(xip->epa_H) * mm2local) );
01693         bu_vls_strcat( str, buf );
01694 
01695         sprintf(buf, "\tA=%g\n", INTCLAMP(xip->epa_r1 * mm2local));
01696         bu_vls_strcat( str, buf );
01697 
01698         sprintf(buf, "\tB=%g\n", INTCLAMP(xip->epa_r2 * mm2local));
01699         bu_vls_strcat( str, buf );
01700 
01701         return(0);
01702 }
01703 
01704 /**
01705  *                      R T _ E P A _ I F R E E
01706  *
01707  *  Free the storage associated with the rt_db_internal version of this solid.
01708  */
01709 void
01710 rt_epa_ifree(struct rt_db_internal *ip)
01711 {
01712         register struct rt_epa_internal *xip;
01713 
01714         RT_CK_DB_INTERNAL(ip);
01715         xip = (struct rt_epa_internal *)ip->idb_ptr;
01716         RT_EPA_CK_MAGIC(xip);
01717         xip->epa_magic = 0;             /* sanity */
01718 
01719         bu_free( (char *)xip, "epa ifree" );
01720         ip->idb_ptr = GENPTR_NULL;      /* sanity */
01721 }
01722 
01723 /*@}*/
01724 /*
01725  * Local Variables:
01726  * mode: C
01727  * tab-width: 8
01728  * c-basic-offset: 4
01729  * indent-tabs-mode: t
01730  * End:
01731  * ex: shiftwidth=4 tabstop=8
01732  */

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