g_rpc.c

Go to the documentation of this file.
00001 /*                         G _ R P C . 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_rpc.c
00026  *      Intersect a ray with a Right Parabolic Cylinder.
00027  *
00028  *  Algorithm -
00029  *
00030  *  Given V, H, R, and B, there is a set of points on this rpc
00031  *
00032  *  { (x,y,z) | (x,y,z) is on rpc }
00033  *
00034  *  Through a series of Affine Transformations, this set of points will be
00035  *  transformed into a set of points on an rpc located at the origin
00036  *  with a rectangular halfwidth R of 1 along the Y axis, a height H of +1
00037  *  along the -X axis, a distance B of 1 along the -Z axis between the
00038  *  vertex V and the tip of the parabola.
00039  *
00040  *
00041  *  { (x',y',z') | (x',y',z') is on rpc 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) =  ( H/(-|H|) )
00048  *               (  R/( |R|)  ) . X
00049  *                ( B/(-|B|) )
00050  *
00051  *  and S(X) =   (  1/|H|   0     0   )
00052  *              (    0    1/|R|   0    ) . X
00053  *               (   0      0   1/|B| )
00054  *
00055  *  To find the intersection of a line with the surface of the rpc,
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 rpc.
00061  *  Let W' be the point of intersection between L' and the unit rpc.
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 rpc;
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 canonical rpc at W' when
00074  *
00075  *      A * k**2 + B * k + C = 0
00076  *
00077  *  where
00078  *
00079  *  A = Dy'**2
00080  *  B = (2 * Dy' * Py') - Dz'
00081  *  C = 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*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 rpc IFF
00104  *  -1 <= Wx' <= 0 and -1 <= Wz' <= 0.
00105  *
00106  *  NORMALS.  Given the point W on the surface of the rpc,
00107  *  what is the vector normal to the tangent plane at that point?
00108  *
00109  *  Map W onto the unit rpc, ie:  W' = S( R( W - V ) ).
00110  *
00111  *  Plane on unit rpc at W' has a normal vector N' where
00112  *
00113  *  N' = <0, Wy', -.5>.
00114  *
00115  *  The plane transforms back to the tangent plane at W, and this
00116  *  new plane (on the original rpc) 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 AND END PLATES.
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  -1 <= Wx' <= 0 and -1 <= Wy' <= 1.
00144  *
00145  *  If Dx' == 0, line L' is parallel to the end plates, so there is no
00146  *  hit on the end plates.  Otherwise, rays intersect the front plate
00147  *  with k = (0 - Px') / Dx' and the back plate with k = (-1 - Px') / Dx'.
00148  *
00149  *  The solution W' is within an end plate IFF
00150  *
00151  *      Wy'**2 + Wz' <= 1.0  and  Wz' <= 1.0
00152  *
00153  *  The normal for a hit on the top plate is -Bunit.
00154  *  The normal for a hit on the front plate is -Hunit, and
00155  *  the normal for a hit on the back plate is +Hunit.
00156  *
00157  *  Authors -
00158  *      Michael J. Markowski
00159  *
00160  *  Source -
00161  *      SECAD/VLD Computing Consortium, Bldg 394
00162  *      The U. S. Army Ballistic Research Laboratory
00163  *      Aberdeen Proving Ground, Maryland  21005-5066
00164  *
00165  */
00166 /*@}*/
00167 
00168 #ifndef lint
00169 static const char RCSrpc[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_rpc.c,v 14.13 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00170 #endif
00171 
00172 #include "common.h"
00173 
00174 #include <stdlib.h>
00175 #include <stddef.h>
00176 #include <stdio.h>
00177 #ifdef HAVE_STRING_H
00178 #  include <string.h>
00179 #else
00180 #  include <strings.h>
00181 #endif
00182 #include <math.h>
00183 
00184 #include "machine.h"
00185 #include "vmath.h"
00186 #include "db.h"
00187 #include "nmg.h"
00188 #include "raytrace.h"
00189 #include "rtgeom.h"
00190 #include "./debug.h"
00191 
00192 
00193 struct rpc_specific {
00194         point_t rpc_V;          /* vector to rpc origin */
00195         vect_t  rpc_Bunit;      /* unit B vector */
00196         vect_t  rpc_Hunit;      /* unit H vector */
00197         vect_t  rpc_Runit;      /* unit vector, B x H */
00198         fastf_t rpc_b;          /* |B| */
00199         fastf_t rpc_inv_rsq;    /* 1/(r * r) */
00200         mat_t   rpc_SoR;        /* Scale(Rot(vect)) */
00201         mat_t   rpc_invRoS;     /* invRot(Scale(vect)) */
00202 };
00203 
00204 const struct bu_structparse rt_rpc_parse[] = {
00205     { "%f", 3, "V", bu_offsetof(struct rt_rpc_internal, rpc_V[X]), BU_STRUCTPARSE_FUNC_NULL },
00206     { "%f", 3, "H", bu_offsetof(struct rt_rpc_internal, rpc_H[X]), BU_STRUCTPARSE_FUNC_NULL },
00207     { "%f", 3, "B", bu_offsetof(struct rt_rpc_internal, rpc_B[X]), BU_STRUCTPARSE_FUNC_NULL },
00208     { "%f", 1, "r", bu_offsetof(struct rt_rpc_internal, rpc_r),    BU_STRUCTPARSE_FUNC_NULL },
00209     { {'\0','\0','\0','\0'}, 0, (char *)NULL, 0, BU_STRUCTPARSE_FUNC_NULL }
00210  };
00211 
00212 /**
00213  *                      R T _ R P C _ P R E P
00214  *
00215  *  Given a pointer to a GED database record, and a transformation matrix,
00216  *  determine if this is a valid RPC, and if so, precompute various
00217  *  terms of the formula.
00218  *
00219  *  Returns -
00220  *      0       RPC is OK
00221  *      !0      Error in description
00222  *
00223  *  Implicit return -
00224  *      A struct rpc_specific is created, and it's address is stored in
00225  *      stp->st_specific for use by rpc_shot().
00226  */
00227 int
00228 rt_rpc_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00229 {
00230         struct rt_rpc_internal          *xip;
00231         register struct rpc_specific    *rpc;
00232 #ifndef NO_BOMBING_MACROS
00233         const struct bn_tol             *tol = &rtip->rti_tol;  /* only used to check a if tolerance is valid */
00234 #endif
00235         LOCAL fastf_t   magsq_b, magsq_h, magsq_r;
00236         LOCAL fastf_t   mag_b, mag_h, mag_r;
00237         LOCAL fastf_t   f;
00238         LOCAL mat_t     R;
00239         LOCAL mat_t     Rinv;
00240         LOCAL mat_t     S;
00241         LOCAL vect_t    invsq;  /* [ 1/(|H|**2), 1/(|R|**2), 1/(|B|**2) ] */
00242 
00243         RT_CK_DB_INTERNAL(ip);
00244         BN_CK_TOL(tol);
00245         xip = (struct rt_rpc_internal *)ip->idb_ptr;
00246         RT_RPC_CK_MAGIC(xip);
00247 
00248         /* compute |B| |H| */
00249         mag_b = sqrt( magsq_b = MAGSQ( xip->rpc_B ) );
00250         mag_h = sqrt( magsq_h = MAGSQ( xip->rpc_H ) );
00251         mag_r = xip->rpc_r;
00252         magsq_r = mag_r * mag_r;
00253 
00254         /* Check for |H| > 0, |B| > 0, |R| > 0 */
00255         if( NEAR_ZERO(mag_h, RT_LEN_TOL) || NEAR_ZERO(mag_b, RT_LEN_TOL)
00256          || NEAR_ZERO(mag_r, RT_LEN_TOL) )  {
00257                 return(1);              /* BAD, too small */
00258         }
00259 
00260         /* Check for B.H == 0 */
00261         f = VDOT( xip->rpc_B, xip->rpc_H ) / (mag_b * mag_h);
00262         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
00263                 return(1);              /* BAD */
00264         }
00265 
00266         /*
00267          *  RPC is ok
00268          */
00269         stp->st_id = ID_RPC;            /* set soltab ID */
00270         stp->st_meth = &rt_functab[ID_RPC];
00271 
00272         BU_GETSTRUCT( rpc, rpc_specific );
00273         stp->st_specific = (genptr_t)rpc;
00274         rpc->rpc_b = mag_b;
00275         rpc->rpc_inv_rsq = 1 / magsq_r;
00276 
00277         /* make unit vectors in B, H, and BxH directions */
00278         VMOVE(    rpc->rpc_Hunit, xip->rpc_H );
00279         VUNITIZE( rpc->rpc_Hunit );
00280         VMOVE(    rpc->rpc_Bunit, xip->rpc_B );
00281         VUNITIZE( rpc->rpc_Bunit );
00282         VCROSS(   rpc->rpc_Runit, rpc->rpc_Bunit, rpc->rpc_Hunit );
00283 
00284         VMOVE( rpc->rpc_V, xip->rpc_V );
00285 
00286         /* Compute R and Rinv matrices */
00287         MAT_IDN( R );
00288         VREVERSE( &R[0], rpc->rpc_Hunit );
00289         VMOVE(    &R[4], rpc->rpc_Runit );
00290         VREVERSE( &R[8], rpc->rpc_Bunit );
00291         bn_mat_trn( Rinv, R );                  /* inv of rot mat is trn */
00292 
00293         /* Compute S */
00294         VSET( invsq, 1.0/magsq_h, 1.0/magsq_r, 1.0/magsq_b );
00295         MAT_IDN( S );
00296         S[ 0] = sqrt( invsq[0] );
00297         S[ 5] = sqrt( invsq[1] );
00298         S[10] = sqrt( invsq[2] );
00299 
00300         /* Compute SoR and invRoS */
00301         bn_mat_mul( rpc->rpc_SoR, S, R );
00302         bn_mat_mul( rpc->rpc_invRoS, Rinv, S );
00303 
00304         /* Compute bounding sphere and RPP */
00305         /* bounding sphere center */
00306         VJOIN2( stp->st_center, rpc->rpc_V,
00307                 mag_h / 2.0,    rpc->rpc_Hunit,
00308                 mag_b / 2.0,    rpc->rpc_Bunit );
00309         /* bounding radius */
00310         stp->st_bradius = 0.5 * sqrt(magsq_h + 4.0*magsq_r + magsq_b);
00311         /* approximate bounding radius */
00312         stp->st_aradius = stp->st_bradius;
00313 
00314         /* cheat, make bounding RPP by enclosing bounding sphere */
00315         stp->st_min[X] = stp->st_center[X] - stp->st_bradius;
00316         stp->st_max[X] = stp->st_center[X] + stp->st_bradius;
00317         stp->st_min[Y] = stp->st_center[Y] - stp->st_bradius;
00318         stp->st_max[Y] = stp->st_center[Y] + stp->st_bradius;
00319         stp->st_min[Z] = stp->st_center[Z] - stp->st_bradius;
00320         stp->st_max[Z] = stp->st_center[Z] + stp->st_bradius;
00321 
00322         return(0);                      /* OK */
00323 }
00324 
00325 /**
00326  *                      R T _ R P C _ P R I N T
00327  */
00328 void
00329 rt_rpc_print(register const struct soltab *stp)
00330 {
00331         register const struct rpc_specific *rpc =
00332                 (struct rpc_specific *)stp->st_specific;
00333 
00334         VPRINT("V", rpc->rpc_V);
00335         VPRINT("Bunit", rpc->rpc_Bunit);
00336         VPRINT("Hunit", rpc->rpc_Hunit);
00337         VPRINT("Runit", rpc->rpc_Runit);
00338         bn_mat_print("S o R", rpc->rpc_SoR );
00339         bn_mat_print("invR o S", rpc->rpc_invRoS );
00340 }
00341 
00342 /* hit_surfno is set to one of these */
00343 #define RPC_NORM_BODY   (1)             /* compute normal */
00344 #define RPC_NORM_TOP    (2)             /* copy tgc_N */
00345 #define RPC_NORM_FRT    (3)             /* copy reverse tgc_N */
00346 #define RPC_NORM_BACK   (4)
00347 
00348 /**
00349  *                      R T _ R P C _ S H O T
00350  *
00351  *  Intersect a ray with a rpc.
00352  *  If an intersection occurs, a struct seg will be acquired
00353  *  and filled in.
00354  *
00355  *  Returns -
00356  *      0       MISS
00357  *      >0      HIT
00358  */
00359 int
00360 rt_rpc_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00361 {
00362         register struct rpc_specific *rpc =
00363                 (struct rpc_specific *)stp->st_specific;
00364         LOCAL vect_t    dprime;         /* D' */
00365         LOCAL vect_t    pprime;         /* P' */
00366         LOCAL fastf_t   k1, k2;         /* distance constants of solution */
00367         LOCAL vect_t    xlated;         /* translated vector */
00368         LOCAL struct hit hits[3];       /* 2 potential hit points */
00369         register struct hit *hitp;      /* pointer to hit point */
00370 /*????? const struct bn_tol     *tol = &rtip->rti_tol; ?????*/
00371 
00372         hitp = &hits[0];
00373 
00374         /* out, Mat, vect */
00375         MAT4X3VEC( dprime, rpc->rpc_SoR, rp->r_dir );
00376         VSUB2( xlated, rp->r_pt, rpc->rpc_V );
00377         MAT4X3VEC( pprime, rpc->rpc_SoR, xlated );
00378 
00379         /* Find roots of the equation, using formula for quadratic */
00380         if ( !NEAR_ZERO(dprime[Y], RT_PCOEF_TOL) ) {
00381                 FAST fastf_t    a, b, c;        /* coeffs of polynomial */
00382                 FAST fastf_t    disc;           /* disc of radical */
00383 
00384                 a = dprime[Y] * dprime[Y];
00385                 b = 2.0 * dprime[Y] * pprime[Y] - dprime[Z];
00386                 c = pprime[Y] * pprime[Y] - pprime[Z] - 1;
00387                 disc = b*b - 4 * a * c;
00388                 if (disc <= 0)
00389                         goto check_plates;
00390                 disc = sqrt(disc);
00391 
00392                 k1 = (-b + disc) / (2.0 * a);
00393                 k2 = (-b - disc) / (2.0 * a);
00394 
00395                 /*
00396                  *  k1 and k2 are potential solutions to intersection with
00397                  *  side.  See if they fall in range.
00398                  */
00399                 VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00400                 if( hitp->hit_vpriv[X] >= -1.0 && hitp->hit_vpriv[X] <= 0.0
00401                         && hitp->hit_vpriv[Z] <= 0.0 ) {
00402                         hitp->hit_magic = RT_HIT_MAGIC;
00403                         hitp->hit_dist = k1;
00404                         hitp->hit_surfno = RPC_NORM_BODY;       /* compute N */
00405                         hitp++;
00406                 }
00407 
00408                 VJOIN1( hitp->hit_vpriv, pprime, k2, dprime );  /* hit' */
00409                 if( hitp->hit_vpriv[X] >= -1.0 && hitp->hit_vpriv[X] <= 0.0
00410                         && hitp->hit_vpriv[Z] <= 0.0 ) {
00411                         hitp->hit_magic = RT_HIT_MAGIC;
00412                         hitp->hit_dist = k2;
00413                         hitp->hit_surfno = RPC_NORM_BODY;       /* compute N */
00414                         hitp++;
00415                 }
00416         } else if ( !NEAR_ZERO(dprime[Z], RT_PCOEF_TOL) ) {
00417                 k1 = (pprime[Y] * pprime[Y] - pprime[Z] - 1.0) / dprime[Z];
00418                 VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00419                 if( hitp->hit_vpriv[X] >= -1.0 && hitp->hit_vpriv[X] <= 0.0
00420                         && hitp->hit_vpriv[Z] <= 0.0 ) {
00421                         hitp->hit_magic = RT_HIT_MAGIC;
00422                         hitp->hit_dist = k1;
00423                         hitp->hit_surfno = RPC_NORM_BODY;       /* compute N */
00424                         hitp++;
00425                 }
00426         }
00427 
00428         /*
00429          * Check for hitting the end plates.
00430          */
00431 
00432 check_plates:
00433         /* check front and back plates */
00434         if( hitp < &hits[2]  &&  !NEAR_ZERO(dprime[X], RT_PCOEF_TOL) )  {
00435                 /* 0 or 1 hits so far, this is worthwhile */
00436                 k1 = -pprime[X] / dprime[X];            /* front plate */
00437                 k2 = (-1.0 - pprime[X]) / dprime[X];    /* back plate */
00438 
00439                 VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00440                 if( hitp->hit_vpriv[Y] * hitp->hit_vpriv[Y]
00441                         - hitp->hit_vpriv[Z] <= 1.0
00442                         && hitp->hit_vpriv[Z] <= 0.0)  {
00443                         hitp->hit_magic = RT_HIT_MAGIC;
00444                         hitp->hit_dist = k1;
00445                         hitp->hit_surfno = RPC_NORM_FRT;        /* -H */
00446                         hitp++;
00447                 }
00448 
00449                 VJOIN1( hitp->hit_vpriv, pprime, k2, dprime );  /* hit' */
00450                 if( hitp->hit_vpriv[Y] * hitp->hit_vpriv[Y]
00451                         - hitp->hit_vpriv[Z] <= 1.0
00452                         && hitp->hit_vpriv[Z] <= 0.0)  {
00453                         hitp->hit_magic = RT_HIT_MAGIC;
00454                         hitp->hit_dist = k2;
00455                         hitp->hit_surfno = RPC_NORM_BACK;       /* +H */
00456                         hitp++;
00457                 }
00458         }
00459 
00460         /* check top plate */
00461         if( hitp == &hits[1]  &&  !NEAR_ZERO(dprime[Z], RT_PCOEF_TOL) )  {
00462                 /* 1 hit so far, this is worthwhile */
00463                 k1 = -pprime[Z] / dprime[Z];            /* top plate */
00464 
00465                 VJOIN1( hitp->hit_vpriv, pprime, k1, dprime );  /* hit' */
00466                 if( hitp->hit_vpriv[X] >= -1.0 &&  hitp->hit_vpriv[X] <= 0.0
00467                         && hitp->hit_vpriv[Y] >= -1.0
00468                         && hitp->hit_vpriv[Y] <= 1.0 ) {
00469                         hitp->hit_magic = RT_HIT_MAGIC;
00470                         hitp->hit_dist = k1;
00471                         hitp->hit_surfno = RPC_NORM_TOP;        /* -B */
00472                         hitp++;
00473                 }
00474         }
00475 
00476         if( hitp != &hits[2] )
00477                 return(0);      /* MISS */
00478 
00479         if( hits[0].hit_dist < hits[1].hit_dist )  {
00480                 /* entry is [0], exit is [1] */
00481                 register struct seg *segp;
00482 
00483                 RT_GET_SEG(segp, ap->a_resource);
00484                 segp->seg_stp = stp;
00485                 segp->seg_in = hits[0];         /* struct copy */
00486                 segp->seg_out = hits[1];        /* struct copy */
00487                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00488         } else {
00489                 /* entry is [1], exit is [0] */
00490                 register struct seg *segp;
00491 
00492                 RT_GET_SEG(segp, ap->a_resource);
00493                 segp->seg_stp = stp;
00494                 segp->seg_in = hits[1];         /* struct copy */
00495                 segp->seg_out = hits[0];        /* struct copy */
00496                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00497         }
00498         return(2);                      /* HIT */
00499 }
00500 
00501 #define RT_RPC_SEG_MISS(SEG)    (SEG).seg_stp=RT_SOLTAB_NULL
00502 
00503 /**
00504  *                      R T _ R P C _ V S H O T
00505  *
00506  *  Vectorized version.
00507  */
00508 void
00509 rt_rpc_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00510                                /* An array of solid pointers */
00511                                /* An array of ray pointers */
00512                                /* array of segs (results returned) */
00513                                /* Number of ray/object pairs */
00514 
00515 {
00516         rt_vstub( stp, rp, segp, n, ap );
00517 }
00518 
00519 /**
00520  *                      R T _ R P C _ N O R M
00521  *
00522  *  Given ONE ray distance, return the normal and entry/exit point.
00523  */
00524 void
00525 rt_rpc_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00526 {
00527         vect_t can_normal;      /* normal to canonical rpc */
00528         register struct rpc_specific *rpc =
00529                 (struct rpc_specific *)stp->st_specific;
00530 
00531         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00532         switch( hitp->hit_surfno )  {
00533         case RPC_NORM_BODY:
00534                 VSET( can_normal, 0.0, hitp->hit_vpriv[Y], -0.5 );
00535                 MAT4X3VEC( hitp->hit_normal, rpc->rpc_invRoS, can_normal );
00536                 VUNITIZE( hitp->hit_normal );
00537                 break;
00538         case RPC_NORM_TOP:
00539                 VREVERSE( hitp->hit_normal, rpc->rpc_Bunit );
00540                 break;
00541         case RPC_NORM_FRT:
00542                 VREVERSE( hitp->hit_normal, rpc->rpc_Hunit );
00543                 break;
00544         case RPC_NORM_BACK:
00545                 VMOVE( hitp->hit_normal, rpc->rpc_Hunit );
00546                 break;
00547         default:
00548                 bu_log("rt_rpc_norm: surfno=%d bad\n", hitp->hit_surfno);
00549                 break;
00550         }
00551 }
00552 
00553 /**
00554  *                      R T _ R P C _ C U R V E
00555  *
00556  *  Return the curvature of the rpc.
00557  */
00558 void
00559 rt_rpc_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00560 {
00561         fastf_t zp1, zp2;       /* 1st & 2nd derivatives */
00562         register struct rpc_specific *rpc =
00563                 (struct rpc_specific *)stp->st_specific;
00564 
00565         switch( hitp->hit_surfno )  {
00566         case RPC_NORM_BODY:
00567                 /* most nearly flat direction */
00568                 VMOVE( cvp->crv_pdir, rpc->rpc_Hunit );
00569                 cvp->crv_c1 = 0;
00570                 /* k = z'' / (1 + z'^2) ^ 3/2 */
00571                 zp2 = 2 * rpc->rpc_b * rpc->rpc_inv_rsq;
00572                 zp1 = zp2 * hitp->hit_point[Y];
00573                 cvp->crv_c2 = zp2 / pow( (1 + zp1*zp1), 1.5);
00574                 break;
00575         case RPC_NORM_BACK:
00576         case RPC_NORM_FRT:
00577         case RPC_NORM_TOP:
00578                 /* any tangent direction */
00579                 bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
00580                 cvp->crv_c1 = cvp->crv_c2 = 0;
00581                 break;
00582         }
00583 }
00584 
00585 /**
00586  *                      R T _ R P C _ U V
00587  *
00588  *  For a hit on the surface of an rpc, return the (u,v) coordinates
00589  *  of the hit point, 0 <= u,v <= 1.
00590  *  u = azimuth
00591  *  v = elevation
00592  */
00593 void
00594 rt_rpc_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00595 {
00596         register struct rpc_specific *rpc =
00597                 (struct rpc_specific *)stp->st_specific;
00598         LOCAL vect_t work;
00599         LOCAL vect_t pprime;
00600         FAST fastf_t len;
00601 
00602         /*
00603          * hit_point is on surface;  project back to unit rpc,
00604          * creating a vector from vertex to hit point.
00605          */
00606         VSUB2( work, hitp->hit_point, rpc->rpc_V );
00607         MAT4X3VEC( pprime, rpc->rpc_SoR, work );
00608 
00609         switch( hitp->hit_surfno )  {
00610         case RPC_NORM_BODY:
00611                 /* Skin.  x,y coordinates define rotation.  radius = 1 */
00612                 len = sqrt(pprime[Y]*pprime[Y] + pprime[Z]*pprime[Z]);
00613                 uvp->uv_u = acos(pprime[Y]/len) * bn_invpi;
00614                 uvp->uv_v = -pprime[X];         /* height */
00615                 break;
00616         case RPC_NORM_FRT:
00617         case RPC_NORM_BACK:
00618                 /* end plates - circular mapping, not seamless w/body, top */
00619                 len = sqrt(pprime[Y]*pprime[Y] + pprime[Z]*pprime[Z]);
00620                 uvp->uv_u = acos(pprime[Y]/len) * bn_invpi;
00621                 uvp->uv_v = len;        /* rim v = 1 for both plates */
00622                 break;
00623         case RPC_NORM_TOP:
00624                 uvp->uv_u = 1.0 - (pprime[Y] + 1.0)/2.0;
00625                 uvp->uv_v = -pprime[X];         /* height */
00626                 break;
00627         }
00628 
00629         /* uv_du should be relative to rotation, uv_dv relative to height */
00630         uvp->uv_du = uvp->uv_dv = 0;
00631 }
00632 
00633 /**
00634  *              R T _ R P C _ F R E E
00635  */
00636 void
00637 rt_rpc_free(register struct soltab *stp)
00638 {
00639         register struct rpc_specific *rpc =
00640                 (struct rpc_specific *)stp->st_specific;
00641 
00642         bu_free( (char *)rpc, "rpc_specific" );
00643 }
00644 
00645 /**
00646  *                      R T _ R P C _ C L A S S
00647  */
00648 int
00649 rt_rpc_class(void)
00650 {
00651         return(0);
00652 }
00653 
00654 /**
00655  *                      R T _ R P C _ P L O T
00656  */
00657 int
00658 rt_rpc_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00659 {
00660         LOCAL struct rt_rpc_internal    *xip;
00661         fastf_t *front;
00662         fastf_t *back;
00663         fastf_t b, dtol, f, h, ntol, rh;
00664         int     i, n;
00665         LOCAL mat_t     R;
00666         LOCAL mat_t     invR;
00667         struct rt_pt_node       *old, *pos, *pts;
00668         vect_t  Bu, Hu, Ru;
00669 
00670         RT_CK_DB_INTERNAL(ip);
00671         xip = (struct rt_rpc_internal *)ip->idb_ptr;
00672         RT_RPC_CK_MAGIC(xip);
00673 
00674         /* compute |B| |H| */
00675         b = MAGNITUDE( xip->rpc_B );    /* breadth */
00676         rh = xip->rpc_r;                /* rectangular halfwidth */
00677         h = MAGNITUDE( xip->rpc_H );    /* height */
00678 
00679         /* Check for |H| > 0, |B| > 0, |R| > 0 */
00680         if( NEAR_ZERO(h, RT_LEN_TOL) || NEAR_ZERO(b, RT_LEN_TOL)
00681          || NEAR_ZERO(rh, RT_LEN_TOL) )  {
00682                 bu_log("rt_rpc_plot():  zero length H, B, or rh\n");
00683                 return(-2);             /* BAD */
00684         }
00685 
00686         /* Check for B.H == 0 */
00687         f = VDOT( xip->rpc_B, xip->rpc_H ) / (b * h);
00688         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
00689                 bu_log("rt_rpc_plot(): B not perpendicular to H, f=%f\n", f);
00690                 return(-3);             /* BAD */
00691         }
00692 
00693         /* make unit vectors in B, H, and BxH directions */
00694         VMOVE(    Hu, xip->rpc_H );
00695         VUNITIZE( Hu );
00696         VMOVE(    Bu, xip->rpc_B );
00697         VUNITIZE( Bu );
00698         VCROSS(   Ru, Bu, Hu );
00699 
00700         /* Compute R and Rinv matrices */
00701         MAT_IDN( R );
00702         VREVERSE( &R[0], Hu );
00703         VMOVE(    &R[4], Ru );
00704         VREVERSE( &R[8], Bu );
00705         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
00706 
00707         /*
00708          *  Establish tolerances
00709          */
00710         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )  {
00711                 dtol = 0.0;             /* none */
00712         } else {
00713                 /* Convert rel to absolute by scaling by smallest side */
00714                 if (rh < b)
00715                         dtol = ttol->rel * 2 * rh;
00716                 else
00717                         dtol = ttol->rel * 2 * b;
00718         }
00719         if( ttol->abs <= 0.0 )  {
00720                 if( dtol <= 0.0 )  {
00721                         /* No tolerance given, use a default */
00722                         if (rh < b)
00723                                 dtol = 2 * 0.10 * rh;   /* 10% */
00724                         else
00725                                 dtol = 2 * 0.10 * b;    /* 10% */
00726                 } else {
00727                         /* Use absolute-ized relative tolerance */
00728                 }
00729         } else {
00730                 /* Absolute tolerance was given, pick smaller */
00731                 if( ttol->rel <= 0.0 || dtol > ttol->abs )
00732                         dtol = ttol->abs;
00733         }
00734 
00735         /* To ensure normal tolerance, remain below this angle */
00736         if( ttol->norm > 0.0 )
00737                 ntol = ttol->norm;
00738         else
00739                 /* tolerate everything */
00740                 ntol = bn_pi;
00741 
00742 #if 1
00743         /* initial parabola approximation is a single segment */
00744         pts = rt_ptalloc();
00745         pts->next = rt_ptalloc();
00746         pts->next->next = NULL;
00747         VSET( pts->p,       0, -rh, 0);
00748         VSET( pts->next->p, 0,  rh, 0);
00749         /* 2 endpoints in 1st approximation */
00750         n = 2;
00751         /* recursively break segment 'til within error tolerances */
00752         n += rt_mk_parabola( pts, rh, b, dtol, ntol );
00753 
00754         /* get mem for arrays */
00755         front = (fastf_t *)bu_malloc(3*n * sizeof(fastf_t), "fastf_t");
00756         back  = (fastf_t *)bu_malloc(3*n * sizeof(fastf_t), "fastf_t");
00757 
00758         /* generate front & back plates in world coordinates */
00759         pos = pts;
00760         i = 0;
00761         while (pos) {
00762                 /* rotate back to original position */
00763                 MAT4X3VEC( &front[i], invR, pos->p );
00764                 /* move to origin vertex origin */
00765                 VADD2( &front[i], &front[i], xip->rpc_V );
00766                 /* extrude front to create back plate */
00767                 VADD2( &back[i], &front[i], xip->rpc_H );
00768                 i += 3;
00769                 old = pos;
00770                 pos = pos->next;
00771                 bu_free( (char *)old, "rt_pt_node" );
00772         }
00773 #else
00774         /* initial parabola approximation is a single segment */
00775         pts = rt_ptalloc();
00776         pts->next = rt_ptalloc();
00777         pts->next->next = NULL;
00778         VSET( pts->p,       0,   0, -b);
00779         VSET( pts->next->p, 0,  rh,  0);
00780         /* 2 endpoints in 1st approximation */
00781         n = 2;
00782         /* recursively break segment 'til within error tolerances */
00783         n += rt_mk_parabola( pts, rh, b, dtol, ntol );
00784 
00785         /* get mem for arrays */
00786         front = (fastf_t *)bu_malloc((2*3*n-1) * sizeof(fastf_t), "fastf_t");
00787         back  = (fastf_t *)bu_malloc((2*3*n-1) * sizeof(fastf_t), "fastf_t");
00788 
00789         /* generate front & back plates in world coordinates */
00790         pos = pts;
00791         i = 0;
00792         while (pos) {
00793                 /* rotate back to original position */
00794                 MAT4X3VEC( &front[i], invR, pos->p );
00795                 /* move to origin vertex origin */
00796                 VADD2( &front[i], &front[i], xip->rpc_V );
00797                 /* extrude front to create back plate */
00798                 VADD2( &back[i], &front[i], xip->rpc_H );
00799                 i += 3;
00800                 old = pos;
00801                 pos = pos->next;
00802                 bu_free( (char *)old, "rt_pt_node" );
00803         }
00804         for (i = 3*n; i < 6*n-3; i+=3) {
00805                 VMOVE( &front[i], &front[6*n-i-6] );
00806                 front[i+1] = -front[i+1];
00807                 VMOVE( &back[i], &back[6*n-i-6] );
00808                 back[i+1] = -back[i+1];
00809         }
00810         n = 2*n - 1;
00811 #endif
00812 
00813         /* Draw the front */
00814         RT_ADD_VLIST( vhead, &front[(n-1)*ELEMENTS_PER_VECT],
00815                 BN_VLIST_LINE_MOVE );
00816         for( i = 0; i < n; i++ )  {
00817                 RT_ADD_VLIST( vhead, &front[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00818         }
00819 
00820         /* Draw the back */
00821         RT_ADD_VLIST( vhead, &back[(n-1)*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00822         for( i = 0; i < n; i++ )  {
00823                 RT_ADD_VLIST( vhead, &back[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00824         }
00825 
00826         /* Draw connections */
00827         for( i = 0; i < n; i++ )  {
00828                 RT_ADD_VLIST( vhead, &front[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00829                 RT_ADD_VLIST( vhead, &back[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00830         }
00831 
00832         bu_free( (char *)front, "fastf_t" );
00833         bu_free( (char *)back,  "fastf_t" );
00834 
00835         return(0);
00836 }
00837 
00838 /**
00839  *      R T _ M K _ P A R A B O L A
00840  *
00841  *      Approximate a parabola with line segments.  The initial single
00842  *      segment is broken at the point farthest from the parabola if
00843  *      that point is not aleady within the distance and normal error
00844  *      tolerances.  The two resulting segments are passed recursively
00845  *      to this routine until each segment is within tolerance.
00846  */
00847 int
00848 rt_mk_parabola(struct rt_pt_node *pts, fastf_t r, fastf_t b, fastf_t dtol, fastf_t ntol)
00849 {
00850         fastf_t dist, intr, m, theta0, theta1;
00851         int     n;
00852         point_t mpt, p0, p1;
00853         vect_t  norm_line, norm_parab;
00854         struct rt_pt_node *new, *rt_ptalloc(void);
00855 
00856 #define RPC_TOL .0001
00857         /* endpoints of segment approximating parabola */
00858         VMOVE( p0, pts->p );
00859         VMOVE( p1, pts->next->p );
00860         /* slope and intercept of segment */
00861         m = ( p1[Z] - p0[Z] ) / ( p1[Y] - p0[Y] );
00862         intr = p0[Z] - m * p0[Y];
00863         /* point on parabola with max dist between parabola and line */
00864         mpt[X] = 0;
00865         mpt[Y] = (r * r * m) / (2 * b);
00866         if (NEAR_ZERO( mpt[Y], RPC_TOL))
00867                 mpt[Y] = 0.;
00868         mpt[Z] = (mpt[Y] * m / 2) - b;
00869         if (NEAR_ZERO( mpt[Z], RPC_TOL))
00870                 mpt[Z] = 0.;
00871         /* max distance between that point and line */
00872         dist = fabs( mpt[Z] + b + b + intr ) / sqrt( m * m + 1 );
00873         /* angles between normal of line and of parabola at line endpoints */
00874         VSET( norm_line, m, -1., 0.);
00875         VSET( norm_parab, 2. * b / (r * r) * p0[Y], -1., 0.);
00876         VUNITIZE( norm_line );
00877         VUNITIZE( norm_parab );
00878         theta0 = fabs( acos( VDOT( norm_line, norm_parab )));
00879         VSET( norm_parab, 2. * b / (r * r) * p1[Y], -1., 0.);
00880         VUNITIZE( norm_parab );
00881         theta1 = fabs( acos( VDOT( norm_line, norm_parab )));
00882         /* split segment at widest point if not within error tolerances */
00883         if ( dist > dtol || theta0 > ntol || theta1 > ntol ) {
00884                 /* split segment */
00885                 new = rt_ptalloc();
00886                 VMOVE( new->p, mpt );
00887                 new->next = pts->next;
00888                 pts->next = new;
00889                 /* keep track of number of pts added */
00890                 n = 1;
00891                 /* recurse on first new segment */
00892                 n += rt_mk_parabola( pts, r, b, dtol, ntol );
00893                 /* recurse on second new segment */
00894                 n += rt_mk_parabola( new, r, b, dtol, ntol );
00895         } else
00896                 n  = 0;
00897         return( n );
00898 }
00899 
00900 /*
00901  *                      R T _ P T A L L O C
00902  */
00903 struct rt_pt_node *
00904 rt_ptalloc(void)
00905 {
00906         struct rt_pt_node *mem;
00907 
00908         mem = (struct rt_pt_node *)bu_malloc(sizeof(struct rt_pt_node), "rt_pt_node");
00909         return(mem);
00910 }
00911 
00912 /**
00913  *                      R T _ R P C _ T E S S
00914  *
00915  *  Returns -
00916  *      -1      failure
00917  *       0      OK.  *r points to nmgregion that holds this tessellation.
00918  */
00919 int
00920 rt_rpc_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00921 {
00922         int             i, j, n;
00923         fastf_t         b, *back, f, *front, h, rh;
00924         fastf_t         dtol, ntol;
00925         vect_t          Bu, Hu, Ru;
00926         LOCAL mat_t     R;
00927         LOCAL mat_t     invR;
00928         LOCAL struct rt_rpc_internal    *xip;
00929         struct rt_pt_node       *old, *pos, *pts;
00930         struct shell    *s;
00931         struct faceuse  **outfaceuses;
00932         struct vertex   **vfront, **vback, **vtemp, *vertlist[4];
00933         vect_t          *norms;
00934         fastf_t         r_sq_over_b;
00935 
00936         NMG_CK_MODEL( m );
00937         BN_CK_TOL( tol );
00938         RT_CK_TESS_TOL( ttol );
00939 
00940         RT_CK_DB_INTERNAL(ip);
00941         xip = (struct rt_rpc_internal *)ip->idb_ptr;
00942         RT_RPC_CK_MAGIC(xip);
00943 
00944         /* compute |B| |H| */
00945         b = MAGNITUDE( xip->rpc_B );    /* breadth */
00946         rh = xip->rpc_r;                /* rectangular halfwidth */
00947         h = MAGNITUDE( xip->rpc_H );    /* height */
00948 
00949         /* Check for |H| > 0, |B| > 0, |R| > 0 */
00950         if( NEAR_ZERO(h, RT_LEN_TOL) || NEAR_ZERO(b, RT_LEN_TOL)
00951          || NEAR_ZERO(rh, RT_LEN_TOL) )  {
00952                 bu_log("rt_rpc_tess():  zero length H, B, or rh\n");
00953                 return(-2);             /* BAD */
00954         }
00955 
00956         /* Check for B.H == 0 */
00957         f = VDOT( xip->rpc_B, xip->rpc_H ) / (b * h);
00958         if( ! NEAR_ZERO(f, RT_DOT_TOL) )  {
00959                 bu_log("rt_rpc_tess(): B not perpendicular to H, f=%f\n", f);
00960                 return(-3);             /* BAD */
00961         }
00962 
00963         /* make unit vectors in B, H, and BxH directions */
00964         VMOVE(    Hu, xip->rpc_H );
00965         VUNITIZE( Hu );
00966         VMOVE(    Bu, xip->rpc_B );
00967         VUNITIZE( Bu );
00968         VCROSS(   Ru, Bu, Hu );
00969 
00970         /* Compute R and Rinv matrices */
00971         MAT_IDN( R );
00972         VREVERSE( &R[0], Hu );
00973         VMOVE(    &R[4], Ru );
00974         VREVERSE( &R[8], Bu );
00975         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
00976 
00977         /*
00978          *  Establish tolerances
00979          */
00980         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )  {
00981                 dtol = 0.0;             /* none */
00982         } else {
00983                 /* Convert rel to absolute by scaling by smallest side */
00984                 if (rh < b)
00985                         dtol = ttol->rel * 2 * rh;
00986                 else
00987                         dtol = ttol->rel * 2 * b;
00988         }
00989         if( ttol->abs <= 0.0 )  {
00990                 if( dtol <= 0.0 )  {
00991                         /* No tolerance given, use a default */
00992                         if (rh < b)
00993                                 dtol = 2 * 0.10 * rh;   /* 10% */
00994                         else
00995                                 dtol = 2 * 0.10 * b;    /* 10% */
00996                 } else {
00997                         /* Use absolute-ized relative tolerance */
00998                 }
00999         } else {
01000                 /* Absolute tolerance was given, pick smaller */
01001                 if( ttol->rel <= 0.0 || dtol > ttol->abs )
01002                         dtol = ttol->abs;
01003         }
01004 
01005         /* To ensure normal tolerance, remain below this angle */
01006         if( ttol->norm > 0.0 )
01007                 ntol = ttol->norm;
01008         else
01009                 /* tolerate everything */
01010                 ntol = bn_pi;
01011 
01012         /* initial parabola approximation is a single segment */
01013         pts = rt_ptalloc();
01014         pts->next = rt_ptalloc();
01015         pts->next->next = NULL;
01016         VSET( pts->p,       0, -rh, 0);
01017         VSET( pts->next->p, 0,  rh, 0);
01018         /* 2 endpoints in 1st approximation */
01019         n = 2;
01020         /* recursively break segment 'til within error tolerances */
01021         n += rt_mk_parabola( pts, rh, b, dtol, ntol );
01022 
01023         /* get mem for arrays */
01024         front = (fastf_t *)bu_malloc(3*n * sizeof(fastf_t), "fastf_t");
01025         back  = (fastf_t *)bu_malloc(3*n * sizeof(fastf_t), "fastf_t");
01026         norms = (vect_t *)bu_calloc( n , sizeof( vect_t ) , "rt_rpc_tess: norms" );
01027         vfront = (struct vertex **)bu_malloc((n+1) * sizeof(struct vertex *), "vertex *");
01028         vback = (struct vertex **)bu_malloc((n+1) * sizeof(struct vertex *), "vertex *");
01029         vtemp = (struct vertex **)bu_malloc((n+1) * sizeof(struct vertex *), "vertex *");
01030         outfaceuses = (struct faceuse **)bu_malloc((n+2) * sizeof(struct faceuse *), "faceuse *");
01031 
01032         /* generate front & back plates in world coordinates */
01033         r_sq_over_b = rh * rh / b;
01034         pos = pts;
01035         i = 0;
01036         j = 0;
01037         while (pos) {
01038                 vect_t tmp_norm;
01039 
01040                 VSET( tmp_norm , 0.0 , 2.0 * pos->p[Y] , -r_sq_over_b );
01041                 MAT4X3VEC( norms[j] , invR , tmp_norm );
01042                 VUNITIZE( norms[j] );
01043                 /* rotate back to original position */
01044                 MAT4X3VEC( &front[i], invR, pos->p );
01045                 /* move to origin vertex origin */
01046                 VADD2( &front[i], &front[i], xip->rpc_V );
01047                 /* extrude front to create back plate */
01048                 VADD2( &back[i], &front[i], xip->rpc_H );
01049                 i += 3;
01050                 j++;
01051                 old = pos;
01052                 pos = pos->next;
01053                 bu_free( (char *)old, "rt_pt_node" );
01054         }
01055 
01056         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01057         s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01058 
01059         for( i=0; i<n; i++ )  {
01060                 vfront[i] = vtemp[i] = (struct vertex *)0;
01061         }
01062 
01063         /* Front face topology.  Verts are considered to go CCW */
01064         outfaceuses[0] = nmg_cface(s, vfront, n);
01065 
01066         (void)nmg_mark_edges_real( &outfaceuses[0]->l.magic );
01067 
01068         /* Back face topology.  Verts must go in opposite dir (CW) */
01069         outfaceuses[1] = nmg_cface(s, vtemp, n);
01070 
01071         (void)nmg_mark_edges_real( &outfaceuses[1]->l.magic );
01072 
01073         for( i=0; i<n; i++ )  vback[i] = vtemp[n-1-i];
01074 
01075         /* Duplicate [0] as [n] to handle loop end condition, below */
01076         vfront[n] = vfront[0];
01077         vback[n] = vback[0];
01078 
01079         /* Build topology for all the rectangular side faces (n of them)
01080          * connecting the front and back faces.
01081          * increasing indices go towards counter-clockwise (CCW).
01082          */
01083         for( i=0; i<n; i++ )  {
01084                 vertlist[0] = vfront[i];        /* from top, */
01085                 vertlist[1] = vback[i];         /* straight down, */
01086                 vertlist[2] = vback[i+1];       /* to left, */
01087                 vertlist[3] = vfront[i+1];      /* straight up. */
01088                 outfaceuses[2+i] = nmg_cface(s, vertlist, 4);
01089         }
01090 
01091         (void)nmg_mark_edges_real( &outfaceuses[n+1]->l.magic );
01092 
01093         for( i=0; i<n; i++ )  {
01094                 NMG_CK_VERTEX(vfront[i]);
01095                 NMG_CK_VERTEX(vback[i]);
01096         }
01097 
01098         /* Associate the vertex geometry, CCW */
01099         for( i=0; i<n; i++ )  {
01100                 nmg_vertex_gv( vfront[i], &front[3*(i)] );
01101         }
01102         for( i=0; i<n; i++ )  {
01103                 nmg_vertex_gv( vback[i], &back[3*(i)] );
01104         }
01105 
01106         /* Associate the face geometry */
01107         for (i=0 ; i < n+2 ; i++) {
01108                 if( nmg_fu_planeeqn( outfaceuses[i], tol ) < 0 )
01109                 {
01110                         /* free mem */
01111                         bu_free( (char *)front, "fastf_t");
01112                         bu_free( (char *)back, "fastf_t");
01113                         bu_free( (char*)vfront, "vertex *");
01114                         bu_free( (char*)vback, "vertex *");
01115                         bu_free( (char*)vtemp, "vertex *");
01116                         bu_free( (char*)outfaceuses, "faceuse *");
01117 
01118                         return(-1);             /* FAIL */
01119                 }
01120         }
01121 
01122         /* Associate vertexuse normals */
01123         for( i=0 ; i<n ; i++ )
01124         {
01125                 struct vertexuse *vu;
01126                 struct faceuse *fu;
01127                 vect_t rev_norm;
01128 
01129                 VREVERSE( rev_norm , norms[i] );
01130 
01131                 /* do "front" vertices */
01132                 NMG_CK_VERTEX( vfront[i] );
01133                 for( BU_LIST_FOR( vu , vertexuse , &vfront[i]->vu_hd ) )
01134                 {
01135                         NMG_CK_VERTEXUSE( vu );
01136                         fu = nmg_find_fu_of_vu( vu );
01137                         NMG_CK_FACEUSE( fu );
01138                         if( fu->f_p == outfaceuses[0]->f_p ||
01139                             fu->f_p == outfaceuses[1]->f_p ||
01140                             fu->f_p == outfaceuses[n+1]->f_p )
01141                                         continue;       /* skip flat faces */
01142 
01143                         if( fu->orientation == OT_SAME )
01144                                 nmg_vertexuse_nv( vu , norms[i] );
01145                         else if( fu->orientation == OT_OPPOSITE )
01146                                 nmg_vertexuse_nv( vu , rev_norm );
01147                 }
01148 
01149                 /* and "back" vertices */
01150                 NMG_CK_VERTEX( vback[i] );
01151                 for( BU_LIST_FOR( vu , vertexuse , &vback[i]->vu_hd ) )
01152                 {
01153                         NMG_CK_VERTEXUSE( vu );
01154                         fu = nmg_find_fu_of_vu( vu );
01155                         NMG_CK_FACEUSE( fu );
01156                         if( fu->f_p == outfaceuses[0]->f_p ||
01157                             fu->f_p == outfaceuses[1]->f_p ||
01158                             fu->f_p == outfaceuses[n+1]->f_p )
01159                                         continue;       /* skip flat faces */
01160 
01161                         if( fu->orientation == OT_SAME )
01162                                 nmg_vertexuse_nv( vu , norms[i] );
01163                         else if( fu->orientation == OT_OPPOSITE )
01164                                 nmg_vertexuse_nv( vu , rev_norm );
01165                 }
01166         }
01167 
01168         /* Glue the edges of different outward pointing face uses together */
01169         nmg_gluefaces( outfaceuses, n+2, tol );
01170 
01171         /* Compute "geometry" for region and shell */
01172         nmg_region_a( *r, tol );
01173 
01174         /* free mem */
01175         bu_free( (char *)front, "fastf_t");
01176         bu_free( (char *)back, "fastf_t");
01177         bu_free( (char*)vfront, "vertex *");
01178         bu_free( (char*)vback, "vertex *");
01179         bu_free( (char*)vtemp, "vertex *");
01180         bu_free( (char*)outfaceuses, "faceuse *");
01181 
01182         return(0);
01183 }
01184 
01185 /**
01186  *                      R T _ R P C _ I M P O R T
01187  *
01188  *  Import an RPC from the database format to the internal format.
01189  *  Apply modeling transformations as well.
01190  */
01191 int
01192 rt_rpc_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01193 {
01194         LOCAL struct rt_rpc_internal    *xip;
01195         union record                    *rp;
01196 
01197         BU_CK_EXTERNAL( ep );
01198         rp = (union record *)ep->ext_buf;
01199         /* Check record type */
01200         if( rp->u_id != ID_SOLID )  {
01201                 bu_log("rt_rpc_import: defective record\n");
01202                 return(-1);
01203         }
01204 
01205         RT_CK_DB_INTERNAL( ip );
01206         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01207         ip->idb_type = ID_RPC;
01208         ip->idb_meth = &rt_functab[ID_RPC];
01209         ip->idb_ptr = bu_malloc( sizeof(struct rt_rpc_internal), "rt_rpc_internal");
01210         xip = (struct rt_rpc_internal *)ip->idb_ptr;
01211         xip->rpc_magic = RT_RPC_INTERNAL_MAGIC;
01212 
01213         /* Warning:  type conversion */
01214         MAT4X3PNT( xip->rpc_V, mat, &rp->s.s_values[0*3] );
01215         MAT4X3VEC( xip->rpc_H, mat, &rp->s.s_values[1*3] );
01216         MAT4X3VEC( xip->rpc_B, mat, &rp->s.s_values[2*3] );
01217         xip->rpc_r = rp->s.s_values[3*3] / mat[15];
01218 
01219         if( xip->rpc_r < SMALL_FASTF )
01220         {
01221                 bu_log( "rt_rpc_import: r is zero\n" );
01222                 bu_free( (char *)ip->idb_ptr , "rt_rpc_import: ip->idp_ptr" );
01223                 return( -1 );
01224         }
01225 
01226         return(0);                      /* OK */
01227 }
01228 
01229 /**
01230  *                      R T _ R P C _ E X P O R T
01231  *
01232  *  The name is added by the caller, in the usual place.
01233  */
01234 int
01235 rt_rpc_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01236 {
01237         struct rt_rpc_internal  *xip;
01238         union record            *rpc;
01239         fastf_t                 f,mag_b,mag_h;
01240 
01241         RT_CK_DB_INTERNAL(ip);
01242         if( ip->idb_type != ID_RPC )  return(-1);
01243         xip = (struct rt_rpc_internal *)ip->idb_ptr;
01244         RT_RPC_CK_MAGIC(xip);
01245 
01246         BU_CK_EXTERNAL(ep);
01247         ep->ext_nbytes = sizeof(union record);
01248         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "rpc external");
01249         rpc = (union record *)ep->ext_buf;
01250 
01251         rpc->s.s_id = ID_SOLID;
01252         rpc->s.s_type = RPC;
01253 
01254         mag_b = MAGNITUDE( xip->rpc_B );
01255         mag_h = MAGNITUDE( xip->rpc_H );
01256 
01257         if( mag_b < RT_LEN_TOL || mag_h < RT_LEN_TOL || xip->rpc_r < RT_LEN_TOL) {
01258                 bu_log("rt_rpc_export: not all dimensions positive!\n");
01259                 return(-1);
01260         }
01261 
01262         f = VDOT(xip->rpc_B, xip->rpc_H) / (mag_b * mag_h );
01263         if ( !NEAR_ZERO( f , RT_DOT_TOL) ) {
01264                 bu_log("rt_rpc_export: B and H are not perpendicular! (dot = %g)\n",f );
01265                 return(-1);
01266         }
01267 
01268         /* Warning:  type conversion */
01269         VSCALE( &rpc->s.s_values[0*3], xip->rpc_V, local2mm );
01270         VSCALE( &rpc->s.s_values[1*3], xip->rpc_H, local2mm );
01271         VSCALE( &rpc->s.s_values[2*3], xip->rpc_B, local2mm );
01272         rpc->s.s_values[3*3] = xip->rpc_r * local2mm;
01273 
01274         return(0);
01275 }
01276 
01277 /**
01278  *                      R T _ R P C _ I M P O R T 5
01279  *
01280  *  Import an RPC from the database format to the internal format.
01281  *  Apply modeling transformations as well.
01282  */
01283 int
01284 rt_rpc_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01285 {
01286         struct rt_rpc_internal  *xip;
01287         fastf_t                 vec[10];
01288 
01289         BU_CK_EXTERNAL( ep );
01290 
01291         BU_ASSERT_LONG( ep->ext_nbytes, ==, SIZEOF_NETWORK_DOUBLE * 10 );
01292 
01293         RT_CK_DB_INTERNAL( ip );
01294         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01295         ip->idb_type = ID_RPC;
01296         ip->idb_meth = &rt_functab[ID_RPC];
01297         ip->idb_ptr = bu_malloc( sizeof(struct rt_rpc_internal), "rt_rpc_internal");
01298 
01299         xip = (struct rt_rpc_internal *)ip->idb_ptr;
01300         xip->rpc_magic = RT_RPC_INTERNAL_MAGIC;
01301 
01302         /* Convert from database (network) to internal (host) format */
01303         ntohd( (unsigned char *)vec, ep->ext_buf, 10 );
01304 
01305         /* Apply modeling transformations */
01306         MAT4X3PNT( xip->rpc_V, mat, &vec[0*3] );
01307         MAT4X3VEC( xip->rpc_H, mat, &vec[1*3] );
01308         MAT4X3VEC( xip->rpc_B, mat, &vec[2*3] );
01309         xip->rpc_r = vec[3*3] / mat[15];
01310 
01311         if( xip->rpc_r < SMALL_FASTF )
01312         {
01313                 bu_log( "rt_rpc_import: r is zero\n" );
01314                 bu_free( (char *)ip->idb_ptr , "rt_rpc_import: ip->idp_ptr" );
01315                 return( -1 );
01316         }
01317 
01318         return(0);                      /* OK */
01319 }
01320 
01321 /**
01322  *                      R T _ R P C _ E X P O R T 5
01323  *
01324  *  The name is added by the caller, in the usual place.
01325  */
01326 int
01327 rt_rpc_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01328 {
01329         struct rt_rpc_internal  *xip;
01330         fastf_t                 vec[10];
01331         fastf_t                 f,mag_b,mag_h;
01332 
01333         RT_CK_DB_INTERNAL(ip);
01334         if( ip->idb_type != ID_RPC )  return(-1);
01335         xip = (struct rt_rpc_internal *)ip->idb_ptr;
01336         RT_RPC_CK_MAGIC(xip);
01337 
01338         BU_CK_EXTERNAL(ep);
01339         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE * 10;
01340         ep->ext_buf = (genptr_t)bu_malloc( ep->ext_nbytes, "rpc external");
01341 
01342         mag_b = MAGNITUDE( xip->rpc_B );
01343         mag_h = MAGNITUDE( xip->rpc_H );
01344 
01345         if( mag_b < RT_LEN_TOL || mag_h < RT_LEN_TOL || xip->rpc_r < RT_LEN_TOL) {
01346                 bu_log("rt_rpc_export: not all dimensions positive!\n");
01347                 return(-1);
01348         }
01349 
01350         f = VDOT(xip->rpc_B, xip->rpc_H) / (mag_b * mag_h );
01351         if ( !NEAR_ZERO( f , RT_DOT_TOL) ) {
01352                 bu_log("rt_rpc_export: B and H are not perpendicular! (dot = %g)\n",f );
01353                 return(-1);
01354         }
01355 
01356         /* scale 'em into local buffer */
01357         VSCALE( &vec[0*3], xip->rpc_V, local2mm );
01358         VSCALE( &vec[1*3], xip->rpc_H, local2mm );
01359         VSCALE( &vec[2*3], xip->rpc_B, local2mm );
01360         vec[3*3] = xip->rpc_r * local2mm;
01361 
01362         /* Convert from internal (host) to database (network) format */
01363         htond( ep->ext_buf, (unsigned char *)vec, 10 );
01364 
01365         return(0);
01366 }
01367 
01368 /**
01369  *                      R T _ R P C _ D E S C R I B E
01370  *
01371  *  Make human-readable formatted presentation of this solid.
01372  *  First line describes type of solid.
01373  *  Additional lines are indented one tab, and give parameter values.
01374  */
01375 int
01376 rt_rpc_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
01377 {
01378         register struct rt_rpc_internal *xip =
01379                 (struct rt_rpc_internal *)ip->idb_ptr;
01380         char    buf[256];
01381 
01382         RT_RPC_CK_MAGIC(xip);
01383         bu_vls_strcat( str, "Right Parabolic Cylinder (RPC)\n");
01384 
01385         sprintf(buf, "\tV (%g, %g, %g)\n",
01386                 INTCLAMP(xip->rpc_V[X] * mm2local),
01387                 INTCLAMP(xip->rpc_V[Y] * mm2local),
01388                 INTCLAMP(xip->rpc_V[Z] * mm2local) );
01389         bu_vls_strcat( str, buf );
01390 
01391         sprintf(buf, "\tB (%g, %g, %g) mag=%g\n",
01392                 INTCLAMP(xip->rpc_B[X] * mm2local),
01393                 INTCLAMP(xip->rpc_B[Y] * mm2local),
01394                 INTCLAMP(xip->rpc_B[Z] * mm2local),
01395                 INTCLAMP(MAGNITUDE(xip->rpc_B) * mm2local));
01396         bu_vls_strcat( str, buf );
01397 
01398         sprintf(buf, "\tH (%g, %g, %g) mag=%g\n",
01399                 INTCLAMP(xip->rpc_H[X] * mm2local),
01400                 INTCLAMP(xip->rpc_H[Y] * mm2local),
01401                 INTCLAMP(xip->rpc_H[Z] * mm2local),
01402                 INTCLAMP(MAGNITUDE(xip->rpc_H) * mm2local));
01403         bu_vls_strcat( str, buf );
01404 
01405         sprintf(buf, "\tr=%g\n", INTCLAMP(xip->rpc_r * mm2local));
01406         bu_vls_strcat( str, buf );
01407 
01408         return(0);
01409 }
01410 
01411 /**
01412  *                      R T _ R P C _ I F R E E
01413  *
01414  *  Free the storage associated with the rt_db_internal version of this solid.
01415  */
01416 void
01417 rt_rpc_ifree(struct rt_db_internal *ip)
01418 {
01419         register struct rt_rpc_internal *xip;
01420 
01421         RT_CK_DB_INTERNAL(ip);
01422         xip = (struct rt_rpc_internal *)ip->idb_ptr;
01423         RT_RPC_CK_MAGIC(xip);
01424         xip->rpc_magic = 0;             /* sanity */
01425 
01426         bu_free( (char *)xip, "rpc ifree" );
01427         ip->idb_ptr = GENPTR_NULL;      /* sanity */
01428 }
01429 
01430 /*
01431  * Local Variables:
01432  * mode: C
01433  * tab-width: 8
01434  * c-basic-offset: 4
01435  * indent-tabs-mode: t
01436  * End:
01437  * ex: shiftwidth=4 tabstop=8
01438  */

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