g_part.c

Go to the documentation of this file.
00001 /*                        G _ P A R T . 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_part.c
00026  *      Intersect a ray with a "particle" solid, which can have
00027  *      three main forms:  sphere, hemisphere-tipped cylinder (lozenge),
00028  *      and hemisphere-tipped cone.
00029  *      This code draws on the examples of g_rec (Davisson) & g_sph (Dykstra).
00030  *
00031  *  Authors -
00032  *      Michael John Muuss
00033  *      Paul Tanenbaum
00034  *
00035  *  Source -
00036  *      SECAD/VLD Computing Consortium, Bldg 394
00037  *      The U. S. Army Ballistic Research Laboratory
00038  *      Aberdeen Proving Ground, Maryland  21005-5066
00039  *
00040  *
00041  *  Algorithm for the hemisphere-tipped cylinder and cone cases -
00042  *
00043  *  Given V, H, vrad, and hrad, there is a set of points on this cylinder
00044  *
00045  *  { (x,y,z) | (x,y,z) is on cylinder }
00046  *
00047  *  Through a series of Affine Transformations, this set of points will be
00048  *  transformed into a set of points on a unit cylinder (or cone)
00049  *  with the transformed base (V') located at the origin
00050  *  with a transformed radius of 1 (vrad').
00051  *  The height of the cylinder (or cone) along the +Z axis is +1
00052  *  (ie, H' = (0,0,1) ), with a transformed radius of hrad/vrad.
00053  *
00054  *
00055  *  { (x',y',z') | (x',y',z') is on cylinder at origin }
00056  *
00057  *  The transformation from X to X' is accomplished by:
00058  *
00059  *  finding two unit vectors A and B mutually perpendicular, and perp. to H.
00060  *
00061  *  X' = S(R( X - V ))
00062  *
00063  *  where R(X) rotates H to the +Z axis, and S(X) scales vrad' to 1
00064  *  and |H'| to 1.
00065  *
00066  *  where R(X) =  ( A/(|A|) )
00067  *               (  B/(|B|)  ) . X
00068  *                ( H/(|H|) )
00069  *
00070  *  and S(X) =   (  1/|A|   0     0   )
00071  *              (    0    1/|B|   0    ) . X
00072  *               (   0      0   1/|H| )
00073  *
00074  *  To find the intersection of a line with the surface of the cylinder,
00075  *  consider the parametric line L:
00076  *
00077  *      L : { P(n) | P + t(n) . D }
00078  *
00079  *  Call W the actual point of intersection between L and the cylinder.
00080  *  Let W' be the point of intersection between L' and the unit cylinder.
00081  *
00082  *      L' : { P'(n) | P' + t(n) . D' }
00083  *
00084  *  W = invR( invS( W' ) ) + V
00085  *
00086  *  Where W' = k D' + P'.
00087  *
00088  *  If Dx' and Dy' are both 0, then there is no hit on the cylinder;
00089  *  but the end spheres need checking.
00090  *
00091  *  The equation for the unit cylinder ranging along Z is
00092  *
00093  *      x**2 + y**2 - r**2 = 0
00094  *
00095  *  and the equation for a unit cone ranging along Z is
00096  *
00097  *      x**2 + y**2 - f(z)**2 = 0
00098  *
00099  *  where in this case f(z) linearly interpolates the radius of the
00100  *  cylinder from vrad (r1) to hrad (r2) as z ranges from 0 to 1, i.e.:
00101  *
00102  *      f(z) = (r2-r1)/1 * z + r1
00103  *
00104  *  let m = (r2-r1)/1, and substitute:
00105  *
00106  *      x**2 + y**2 - (m*z+r1)**2 = 0 .
00107  *
00108  *  For the cylinder case, r1 == r2, so m == 0, and everything simplifies.
00109  *
00110  *  The parametric formulation for line L' is P' + t * D', or
00111  *
00112  *      x = Px' + t * Dx'
00113  *      y = Py' + t * Dy'
00114  *      z = Pz' + t * Dz' .
00115  *
00116  *  Substituting these definitions into the formula for the unit cone gives
00117  *
00118  *      (Px'+t*Dx')**2 + (Py'+t*Dy')**2 + (m*(Pz'+t*Dz')+r1)**2 = 0
00119  *
00120  *  Expanding and regrouping terms gives a quadratic in "t"
00121  *  which has the form
00122  *
00123  *      a * t**2 + b * t + c = 0
00124  *
00125  *  where
00126  *
00127  *      a = Dx'**2 + Dy'**2 - m**2 * Dz'**2
00128  *      b = 2 * (Px'*Dx' + Py'*Dy' - m**2 * Pz'*Dz' - m*r1*Dz')
00129  *      c = Px'**2 + Py'**2 - m**2 * Pz'**2 - 2*m*r1*Pz' - r1**2
00130  *
00131  *  Line L' hits the infinitely tall unit cone at point(s) W'
00132  *  which correspond to the roots of the quadratic.
00133  *  The quadratic formula yields values for "t"
00134  *
00135  *      t = [ -b +/- sqrt( b** - 4 * a * c ) ] / ( 2 * a )
00136  *
00137  *  This parameter "t" can be substituted into the formulas for either
00138  *  L' or L, because affine transformations preserve distances along lines.
00139  *
00140  *  Now, D' = S( R( D ) )
00141  *  and  P' = S( R( P - V ) )
00142  *
00143  *  Substituting,
00144  *
00145  *  W = V + invR( invS[ k *( S( R( D ) ) ) + S( R( P - V ) ) ] )
00146  *    = V + invR( ( k * R( D ) ) + R( P - V ) )
00147  *    = V + k * D + P - V
00148  *    = k * D + P
00149  *
00150  *  Note that ``t'' is constant, and is the same in the formulations
00151  *  for both W and W'.
00152  *
00153  *  The hit at ``t'' is a hit on the height=1 unit cylinder IFF
00154  *  0 <= Wz' <= 1.
00155  *
00156  *  NORMALS.  Given the point W on the surface of the cylinder,
00157  *  what is the vector normal to the tangent plane at that point?
00158  *
00159  *  Map W onto the unit cylinder, ie:  W' = S( R( W - V ) ).
00160  *
00161  *  Plane on unit cylinder at W' has a normal vector N' of the same value
00162  *  as W' in x and y, with z set to zero, ie, (Wx', Wy', 0)
00163  *
00164  *  The plane transforms back to the tangent plane at W, and this
00165  *  new plane (on the original cylinder) has a normal vector of N, viz:
00166  *
00167  *  N = inverse[ transpose(invR o invS) ] ( N' )
00168  *    = inverse[ transpose(invS) o transpose(invR) ] ( N' )
00169  *    = inverse[ inverse(S) o R ] ( N' )
00170  *    = invR o S ( N' )
00171  *
00172  *  Note that the normal vector produced above will not have unit length.
00173  *
00174  *  THE HEMISPHERES.
00175  *
00176  *  THE "EQUIVALENT CONE":
00177  *
00178  *  In order to have exact matching of the surface normals at the join
00179  *  between the conical body of the particle and the hemispherical end,
00180  *  it is necessary to alter the cone to form an "equivalent cone",
00181  *  where the end caps of the cone are both shifted away from the
00182  *  large hemisphere and towards the smaller one.
00183  *  This makes the cone end where it is tangent to the hemisphere.
00184  *  The calculation for theta come from a diagram drawn by PJT on 18-Nov-99.
00185  */
00186 /*@}*/
00187 #ifndef lint
00188 static const char RCSpart[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_part.c,v 14.13 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00189 #endif
00190 
00191 #include "common.h"
00192 
00193 #include <stddef.h>
00194 #include <stdio.h>
00195 #ifdef HAVE_STRING_H
00196 #  include <string.h>
00197 #else
00198 #  include <strings.h>
00199 #endif
00200 #include <math.h>
00201 
00202 #include "machine.h"
00203 #include "vmath.h"
00204 #include "db.h"
00205 #include "raytrace.h"
00206 #include "nmg.h"
00207 #include "rtgeom.h"
00208 #include "./debug.h"
00209 
00210 struct part_specific {
00211         struct rt_part_internal part_int;
00212         mat_t                   part_SoR;       /* Scale(Rot(vect)) */
00213         mat_t                   part_invRoS;    /* invRot(Scale(vect)) */
00214         fastf_t                 part_vrad_prime;
00215         fastf_t                 part_hrad_prime;
00216         /* For the "equivalent cone" */
00217         fastf_t                 part_v_hdist;   /* dist of base plate on unit cone */
00218         fastf_t                 part_h_hdist;
00219         fastf_t                 part_v_erad;    /* radius of equiv. particle */
00220         fastf_t                 part_h_erad;
00221 };
00222 
00223 /* hit_surfno flags for which end was hit */
00224 #define RT_PARTICLE_SURF_VSPHERE        1
00225 #define RT_PARTICLE_SURF_BODY           2
00226 #define RT_PARTICLE_SURF_HSPHERE        3
00227 
00228 const struct bu_structparse rt_part_parse[] = {
00229     { "%f", 3, "V",  bu_offsetof(struct rt_part_internal, part_V[X]), BU_STRUCTPARSE_FUNC_NULL },
00230     { "%f", 3, "H",  bu_offsetof(struct rt_part_internal, part_H[X]), BU_STRUCTPARSE_FUNC_NULL },
00231     { "%f", 1, "r_v",bu_offsetof(struct rt_part_internal, part_vrad), BU_STRUCTPARSE_FUNC_NULL },
00232     { "%f", 1, "r_h",bu_offsetof(struct rt_part_internal, part_hrad), BU_STRUCTPARSE_FUNC_NULL },
00233     { {'\0','\0','\0','\0'}, 0, (char *)NULL, 0, BU_STRUCTPARSE_FUNC_NULL }
00234  };
00235 
00236 BU_EXTERN( void rt_part_ifree, (struct rt_db_internal *ip) );
00237 
00238 /**
00239  *                      R T _ P A R T _ P R E P
00240  *
00241  *  Given a pointer to a GED database record, and a transformation matrix,
00242  *  determine if this is a valid particle, and if so, precompute various
00243  *  terms of the formula.
00244  *
00245  *  Returns -
00246  *      0       particle is OK
00247  *      !0      Error in description
00248  *
00249  *  Implicit return -
00250  *      A struct part_specific is created, and it's address is stored in
00251  *      stp->st_specific for use by part_shot().
00252  */
00253 int
00254 rt_part_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00255 {
00256         register struct part_specific *part;
00257         struct rt_part_internal *pip;
00258         vect_t          Hunit;
00259         vect_t          a, b;
00260         mat_t           R, Rinv;
00261         mat_t           S;
00262         vect_t          max, min;
00263         vect_t          tip;
00264         fastf_t         inv_hlen;
00265         fastf_t         hlen;
00266         fastf_t         hlen_sq;
00267         fastf_t         r_diff;
00268         fastf_t         r_diff_sq;
00269         fastf_t         sin_theta;
00270         fastf_t         cos_theta;
00271 
00272         RT_CK_DB_INTERNAL( ip );
00273         pip = (struct rt_part_internal *)ip->idb_ptr;
00274         RT_PART_CK_MAGIC(pip);
00275 
00276         BU_GETSTRUCT( part, part_specific );
00277         stp->st_specific = (genptr_t)part;
00278         part->part_int = *pip;                  /* struct copy */
00279         pip = &part->part_int;
00280 
00281         if( pip->part_type == RT_PARTICLE_TYPE_SPHERE )  {
00282                 /* XXX Ought to hand off to rt_sph_prep() here */
00283                 /* Compute bounding sphere and RPP */
00284                 VMOVE( stp->st_center, pip->part_V );
00285                 stp->st_aradius = stp->st_bradius = pip->part_vrad;
00286                 stp->st_min[X] = pip->part_V[X] - pip->part_vrad;
00287                 stp->st_max[X] = pip->part_V[X] + pip->part_vrad;
00288                 stp->st_min[Y] = pip->part_V[Y] - pip->part_vrad;
00289                 stp->st_max[Y] = pip->part_V[Y] + pip->part_vrad;
00290                 stp->st_min[Z] = pip->part_V[Z] - pip->part_vrad;
00291                 stp->st_max[Z] = pip->part_V[Z] + pip->part_vrad;
00292                 return(0);              /* OK */
00293         }
00294 
00295         /* Compute some essential terms */
00296         hlen_sq = MAGSQ(pip->part_H );
00297         if( hlen_sq < SMALL )  {
00298                 bu_log("part(%s): 0-length H vector\n", stp->st_dp->d_namep);
00299                 return 1;               /* BAD */
00300         }
00301         hlen = sqrt(hlen_sq);
00302         inv_hlen = 1/hlen;
00303         VSCALE( Hunit, pip->part_H, inv_hlen );
00304         bn_vec_ortho( a, Hunit );
00305         VCROSS( b, Hunit, a );
00306 
00307         /*
00308          *  Compute parameters for the "equivalent cone"
00309          */
00310 
00311         /* Calculate changes in terms of the "unit particle" */
00312         if( pip->part_vrad >= pip->part_hrad )  {
00313                 /* V end is larger, H end is smaller */
00314                 r_diff = (pip->part_vrad - pip->part_hrad) * inv_hlen;
00315                 r_diff_sq = r_diff * r_diff;
00316                 if( r_diff_sq > 1 )  {
00317                         /* No "equivalent cone" is possible, theta=90deg */
00318                         sin_theta = 1;
00319                         cos_theta = 0;
00320                 } else {
00321                         sin_theta = sqrt( 1 - r_diff_sq );
00322                         cos_theta = fabs( r_diff );
00323                 }
00324 
00325                 part->part_v_erad = pip->part_vrad / sin_theta;
00326                 part->part_h_erad = pip->part_hrad / sin_theta;
00327 
00328                 /* Move both plates towards H hemisphere */
00329                 part->part_v_hdist = cos_theta * pip->part_vrad * inv_hlen;
00330                 part->part_h_hdist = 1 + cos_theta * pip->part_hrad * inv_hlen;
00331         } else {
00332                 /* H end is larger, V end is smaller */
00333                 r_diff = (pip->part_hrad - pip->part_vrad) * inv_hlen;
00334                 r_diff_sq = r_diff * r_diff;
00335                 if( r_diff_sq > 1 )  {
00336                         /* No "equivalent cone" is possible, theta=90deg */
00337                         sin_theta = 1;
00338                         cos_theta = 0;
00339                 } else {
00340                         sin_theta = sqrt( 1 - r_diff_sq );
00341                         cos_theta = fabs( r_diff );
00342                 }
00343 
00344                 part->part_v_erad = pip->part_vrad / sin_theta;
00345                 part->part_h_erad = pip->part_hrad / sin_theta;
00346 
00347                 /* Move both plates towards V hemisphere */
00348                 part->part_v_hdist = -cos_theta * pip->part_vrad * inv_hlen;
00349                 part->part_h_hdist = 1 - cos_theta * pip->part_hrad * inv_hlen;
00350         }
00351         /* Thanks to matrix S, vrad_prime is always 1 */
00352 /*#define VRAD_PRIME    1 */
00353 /*#define HRAD_PRIME    (part->part_int.part_hrad / part->part_int.part_vrad) */
00354         part->part_vrad_prime = 1;
00355         part->part_hrad_prime = part->part_h_erad / part->part_v_erad;
00356 
00357         /* Compute R and Rinv */
00358         MAT_IDN( R );
00359         VMOVE( &R[0], a );              /* has unit length */
00360         VMOVE( &R[4], b );              /* has unit length */
00361         VMOVE( &R[8], Hunit );
00362         bn_mat_trn( Rinv, R );
00363 
00364         /* Compute scale matrix S, where |A| = |B| = equiv_Vradius */
00365         MAT_IDN( S );
00366         S[ 0] = 1.0 / part->part_v_erad;
00367         S[ 5] = S[0];
00368         S[10] = inv_hlen;
00369 
00370         bn_mat_mul( part->part_SoR, S, R );
00371         bn_mat_mul( part->part_invRoS, Rinv, S );
00372 
00373         /* RPP and bounding sphere */
00374         VJOIN1( stp->st_center, pip->part_V, 0.5, pip->part_H );
00375 
00376         stp->st_aradius = stp->st_bradius = pip->part_vrad;
00377 
00378         stp->st_min[X] = pip->part_V[X] - pip->part_vrad;
00379         stp->st_max[X] = pip->part_V[X] + pip->part_vrad;
00380         stp->st_min[Y] = pip->part_V[Y] - pip->part_vrad;
00381         stp->st_max[Y] = pip->part_V[Y] + pip->part_vrad;
00382         stp->st_min[Z] = pip->part_V[Z] - pip->part_vrad;
00383         stp->st_max[Z] = pip->part_V[Z] + pip->part_vrad;
00384 
00385         VADD2( tip, pip->part_V, pip->part_H );
00386         min[X] = tip[X] - pip->part_hrad;
00387         max[X] = tip[X] + pip->part_hrad;
00388         min[Y] = tip[Y] - pip->part_hrad;
00389         max[Y] = tip[Y] + pip->part_hrad;
00390         min[Z] = tip[Z] - pip->part_hrad;
00391         max[Z] = tip[Z] + pip->part_hrad;
00392         VMINMAX( stp->st_min, stp->st_max, min );
00393         VMINMAX( stp->st_min, stp->st_max, max );
00394 
00395         /* Determine bounding sphere from the RPP */
00396         {
00397                 register fastf_t        f;
00398                 vect_t                  work;
00399                 VSUB2SCALE( work, stp->st_max, stp->st_min, 0.5 );      /* radius */
00400                 f = work[X];
00401                 if( work[Y] > f )  f = work[Y];
00402                 if( work[Z] > f )  f = work[Z];
00403                 stp->st_aradius = f;
00404                 stp->st_bradius = MAGNITUDE(work);
00405         }
00406         return(0);                      /* OK */
00407 }
00408 
00409 /**
00410  *                      R T _ P A R T _ P R I N T
00411  */
00412 void
00413 rt_part_print(register const struct soltab *stp)
00414 {
00415         register const struct part_specific *part =
00416                 (struct part_specific *)stp->st_specific;
00417 
00418         VPRINT("part_V", part->part_int.part_V );
00419         VPRINT("part_H", part->part_int.part_H );
00420         bu_log("part_vrad=%g\n", part->part_int.part_vrad );
00421         bu_log("part_hrad=%g\n", part->part_int.part_hrad );
00422 
00423         switch( part->part_int.part_type )  {
00424         case RT_PARTICLE_TYPE_SPHERE:
00425                 bu_log("part_type = SPHERE\n");
00426                 break;
00427         case RT_PARTICLE_TYPE_CYLINDER:
00428                 bu_log("part_type = CYLINDER\n");
00429                 bn_mat_print("part_SoR", part->part_SoR );
00430                 bn_mat_print("part_invRoS", part->part_invRoS );
00431                 break;
00432         case RT_PARTICLE_TYPE_CONE:
00433                 bu_log("part_type = CONE\n");
00434                 bn_mat_print("part_SoR", part->part_SoR );
00435                 bn_mat_print("part_invRoS", part->part_invRoS );
00436                 break;
00437         default:
00438                 bu_log("part_type = %d ???\n", part->part_int.part_type );
00439                 break;
00440         }
00441 }
00442 
00443 /**
00444  *                      R T _ P A R T _ S H O T
00445  *
00446  *  Intersect a ray with a part.
00447  *  If an intersection occurs, a struct seg will be acquired
00448  *  and filled in.
00449  *
00450  *  Returns -
00451  *      0       MISS
00452  *      >0      HIT
00453  */
00454 int
00455 rt_part_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00456 {
00457         register struct part_specific *part =
00458                 (struct part_specific *)stp->st_specific;
00459         register struct seg *segp;
00460         LOCAL vect_t    dprime;         /* D' */
00461         LOCAL point_t   pprime;         /* P' */
00462         LOCAL point_t   xlated;         /* translated ray start point */
00463         LOCAL fastf_t   t1, t2;         /* distance constants of solution */
00464         LOCAL fastf_t   f;
00465         LOCAL struct hit hits[4];       /* 4 potential hit points */
00466         register struct hit *hitp = &hits[0];
00467         int             check_v, check_h;
00468 
00469         if( part->part_int.part_type == RT_PARTICLE_TYPE_SPHERE )  {
00470                 LOCAL vect_t    ov;             /* ray orgin to center (V - P) */
00471                 FAST fastf_t    vrad_sq;
00472                 FAST fastf_t    magsq_ov;       /* length squared of ov */
00473                 FAST fastf_t    b;              /* second term of quadratic eqn */
00474                 FAST fastf_t    root;           /* root of radical */
00475 
00476                 VSUB2( ov, part->part_int.part_V, rp->r_pt );
00477                 b = VDOT( rp->r_dir, ov );
00478                 magsq_ov = MAGSQ(ov);
00479 
00480                 if( magsq_ov >= (vrad_sq = part->part_int.part_vrad *
00481                     part->part_int.part_vrad) ) {
00482                         /* ray origin is outside of sphere */
00483                         if( b < 0 ) {
00484                                 /* ray direction is away from sphere */
00485                                 return(0);              /* No hit */
00486                         }
00487                         root = b*b - magsq_ov + vrad_sq;
00488                         if( root <= 0 ) {
00489                                 /* no real roots */
00490                                 return(0);              /* No hit */
00491                         }
00492                 } else {
00493                         root = b*b - magsq_ov + vrad_sq;
00494                 }
00495                 root = sqrt(root);
00496 
00497                 RT_GET_SEG(segp, ap->a_resource);
00498                 segp->seg_stp = stp;
00499 
00500                 /* we know root is positive, so we know the smaller t */
00501                 segp->seg_in.hit_magic = RT_HIT_MAGIC;
00502                 segp->seg_in.hit_dist = b - root;
00503                 segp->seg_in.hit_surfno = RT_PARTICLE_SURF_VSPHERE;
00504                 segp->seg_out.hit_magic = RT_HIT_MAGIC;
00505                 segp->seg_out.hit_dist = b + root;
00506                 segp->seg_out.hit_surfno = RT_PARTICLE_SURF_VSPHERE;
00507                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00508                 return(2);                      /* HIT */
00509         }
00510 
00511         /* Transform ray to coordinate system of unit cone at origin */
00512         MAT4X3VEC( dprime, part->part_SoR, rp->r_dir );
00513         VSUB2( xlated, rp->r_pt, part->part_int.part_V );
00514         MAT4X3VEC( pprime, part->part_SoR, xlated );
00515 
00516         if( NEAR_ZERO(dprime[X], SMALL) && NEAR_ZERO(dprime[Y], SMALL) )  {
00517                 check_v = check_h = 1;
00518                 goto check_hemispheres;
00519         }
00520         check_v = check_h = 0;
00521 
00522         /* Find roots of the equation, using forumla for quadratic */
00523         /* Note that vrad' = 1 and hrad' = hrad/vrad */
00524         if( part->part_int.part_type == RT_PARTICLE_TYPE_CYLINDER )  {
00525                 /* Cylinder case, hrad == vrad, m = 0 */
00526                 FAST fastf_t    a, b, c;
00527                 FAST fastf_t    root;           /* root of radical */
00528 
00529                 a = dprime[X]*dprime[X] + dprime[Y]*dprime[Y];
00530                 b = dprime[X]*pprime[X] + dprime[Y]*pprime[Y];
00531                 c = pprime[X]*pprime[X] + pprime[Y]*pprime[Y] - 1;
00532                 if( (root = b*b - a * c) <= 0 )
00533                         goto check_hemispheres;
00534                 root = sqrt(root);
00535                 t1 = (root-b) / a;
00536                 t2 = -(root+b) / a;
00537         } else {
00538                 /* Cone case */
00539                 FAST fastf_t    a, b, c;
00540                 FAST fastf_t    root;           /* root of radical */
00541                 FAST fastf_t    m, msq;
00542 
00543                 m = part->part_hrad_prime - part->part_vrad_prime;
00544 
00545                 /* This quadratic has had a factor of 2 divided out of "b"
00546                  * throughout.  More efficient, but the same answers.
00547                  */
00548                 a = dprime[X]*dprime[X] + dprime[Y]*dprime[Y] -
00549                         (msq = m*m) * dprime[Z]*dprime[Z];
00550                 b = dprime[X]*pprime[X] + dprime[Y]*pprime[Y] -
00551                         msq * dprime[Z]*pprime[Z] -
00552                         m * dprime[Z];          /* * part->part_vrad_prime */
00553                 c = pprime[X]*pprime[X] + pprime[Y]*pprime[Y] -
00554                         msq * pprime[Z]*pprime[Z] -
00555                         2 * m * pprime[Z] - 1;
00556                         /* was: ... -2m * vrad' * Pz' - vrad'**2 */
00557 
00558                 if( (root = b*b - a * c) <= 0 )
00559                         goto check_hemispheres;
00560                 root = sqrt(root);
00561 
00562                 t1 = (root-b) / a;
00563                 t2 = -(root+b) / a;
00564         }
00565 
00566         /*
00567          *  t1 and t2 are potential solutions to intersection with side.
00568          *  Find hit' point, see if Z values fall in range.
00569          */
00570         if( (f = pprime[Z] + t1 * dprime[Z]) >= part->part_v_hdist )  {
00571                 check_h = 1;            /* may also hit off end */
00572                 if( f <= part->part_h_hdist )  {
00573                         hitp->hit_magic = RT_HIT_MAGIC;
00574                         /** VJOIN1( hitp->hit_vpriv, pprime, t1, dprime ); **/
00575                         hitp->hit_vpriv[X] = pprime[X] + t1 * dprime[X];
00576                         hitp->hit_vpriv[Y] = pprime[Y] + t1 * dprime[Y];
00577                         hitp->hit_vpriv[Z] = f;
00578                         hitp->hit_dist = t1;
00579                         hitp->hit_surfno = RT_PARTICLE_SURF_BODY;
00580                         hitp++;
00581                 }
00582         } else {
00583                 check_v = 1;
00584         }
00585 
00586         if( (f = pprime[Z] + t2 * dprime[Z]) >= part->part_v_hdist )  {
00587                 check_h = 1;            /* may also hit off end */
00588                 if( f <= part->part_h_hdist )  {
00589                         hitp->hit_magic = RT_HIT_MAGIC;
00590                         /** VJOIN1( hitp->hit_vpriv, pprime, t2, dprime ); **/
00591                         hitp->hit_vpriv[X] = pprime[X] + t2 * dprime[X];
00592                         hitp->hit_vpriv[Y] = pprime[Y] + t2 * dprime[Y];
00593                         hitp->hit_vpriv[Z] = f;
00594                         hitp->hit_dist = t2;
00595                         hitp->hit_surfno = RT_PARTICLE_SURF_BODY;
00596                         hitp++;
00597                 }
00598         } else {
00599                 check_v = 1;
00600         }
00601 
00602         /*
00603          *  Check for hitting the end hemispheres.
00604          */
00605 check_hemispheres:
00606         if( check_v )  {
00607                 LOCAL vect_t    ov;             /* ray orgin to center (V - P) */
00608                 FAST fastf_t    rad_sq;
00609                 FAST fastf_t    magsq_ov;       /* length squared of ov */
00610                 FAST fastf_t    b;
00611                 FAST fastf_t    root;           /* root of radical */
00612 
00613                 /*
00614                  *  First, consider a hit on V hemisphere.
00615                  */
00616                 VSUB2( ov, part->part_int.part_V, rp->r_pt );
00617                 b = VDOT( rp->r_dir, ov );
00618                 magsq_ov = MAGSQ(ov);
00619                 if( magsq_ov >= (rad_sq = part->part_int.part_vrad *
00620                     part->part_int.part_vrad) ) {
00621                         /* ray origin is outside of sphere */
00622                         if( b < 0 ) {
00623                                 /* ray direction is away from sphere */
00624                                 goto do_check_h;
00625                         }
00626                         root = b*b - magsq_ov + rad_sq;
00627                         if( root <= 0 ) {
00628                                 /* no real roots */
00629                                 goto do_check_h;
00630                         }
00631                 } else {
00632                         root = b*b - magsq_ov + rad_sq;
00633                 }
00634                 root = sqrt(root);
00635                 t1 = b - root;
00636                 /* see if hit'[Z] is below V end of cylinder */
00637                 if( pprime[Z] + t1 * dprime[Z] <= part->part_v_hdist )  {
00638                         hitp->hit_magic = RT_HIT_MAGIC;
00639                         hitp->hit_dist = t1;
00640                         hitp->hit_surfno = RT_PARTICLE_SURF_VSPHERE;
00641                         hitp++;
00642                 }
00643                 t2 = b + root;
00644                 if( pprime[Z] + t2 * dprime[Z] <= part->part_v_hdist )  {
00645                         hitp->hit_magic = RT_HIT_MAGIC;
00646                         hitp->hit_dist = t2;
00647                         hitp->hit_surfno = RT_PARTICLE_SURF_VSPHERE;
00648                         hitp++;
00649                 }
00650         }
00651 
00652 do_check_h:
00653         if( check_h )  {
00654                 LOCAL vect_t    ov;             /* ray orgin to center (V - P) */
00655                 FAST fastf_t    rad_sq;
00656                 FAST fastf_t    magsq_ov;       /* length squared of ov */
00657                 FAST fastf_t    b;              /* second term of quadratic eqn */
00658                 FAST fastf_t    root;           /* root of radical */
00659 
00660                 /*
00661                  *  Next, consider a hit on H hemisphere
00662                  */
00663                 VADD2( ov, part->part_int.part_V, part->part_int.part_H );
00664                 VSUB2( ov, ov, rp->r_pt );
00665                 b = VDOT( rp->r_dir, ov );
00666                 magsq_ov = MAGSQ(ov);
00667                 if( magsq_ov >= (rad_sq = part->part_int.part_hrad *
00668                     part->part_int.part_hrad) ) {
00669                         /* ray origin is outside of sphere */
00670                         if( b < 0 ) {
00671                                 /* ray direction is away from sphere */
00672                                 goto out;
00673                         }
00674                         root = b*b - magsq_ov + rad_sq;
00675                         if( root <= 0 ) {
00676                                 /* no real roots */
00677                                 goto out;
00678                         }
00679                 } else {
00680                         root = b*b - magsq_ov + rad_sq;
00681                 }
00682                 root = sqrt(root);
00683                 t1 = b - root;
00684                 /* see if hit'[Z] is above H end of cylinder */
00685                 if( pprime[Z] + t1 * dprime[Z] >= part->part_h_hdist )  {
00686                         hitp->hit_magic = RT_HIT_MAGIC;
00687                         hitp->hit_dist = t1;
00688                         hitp->hit_surfno = RT_PARTICLE_SURF_HSPHERE;
00689                         hitp++;
00690                 }
00691                 t2 = b + root;
00692                 if( pprime[Z] + t2 * dprime[Z] >= part->part_h_hdist )  {
00693                         hitp->hit_magic = RT_HIT_MAGIC;
00694                         hitp->hit_dist = t2;
00695                         hitp->hit_surfno = RT_PARTICLE_SURF_HSPHERE;
00696                         hitp++;
00697                 }
00698         }
00699 out:
00700         if( hitp == &hits[0] )
00701                 return(0);      /* MISS */
00702         if( hitp == &hits[1] )  {
00703                 /* Only one hit, make it a 0-thickness segment */
00704                 hits[1] = hits[0];              /* struct copy */
00705                 hitp++;
00706         } else if( hitp > &hits[2] )  {
00707                 /*
00708                  *  More than two intersections found.
00709                  *  This can happen when a ray grazes down along a tangent
00710                  *  line; the intersection interval from the hemisphere
00711                  *  may not quite join up with the interval from the cone.
00712                  *  Since particles are convex, all we need to do is to
00713                  *  return the maximum extent of the ray.
00714                  *  Do this by sorting the intersections,
00715                  *  and using the minimum and maximum values.
00716                  */
00717                 rt_hitsort( hits, hitp - &hits[0] );
00718 
00719                 /* [0] is minimum, make [1] be maximum (hitp is +1 off end) */
00720                 hits[1] = hitp[-1];     /* struct copy */
00721         }
00722 
00723         if( hits[0].hit_dist < hits[1].hit_dist )  {
00724                 /* entry is [0], exit is [1] */
00725                 register struct seg *segp;
00726 
00727                 RT_GET_SEG(segp, ap->a_resource);
00728                 segp->seg_stp = stp;
00729                 segp->seg_in = hits[0];         /* struct copy */
00730                 segp->seg_out = hits[1];        /* struct copy */
00731                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00732         } else {
00733                 /* entry is [1], exit is [0] */
00734                 register struct seg *segp;
00735 
00736                 RT_GET_SEG(segp, ap->a_resource);
00737                 segp->seg_stp = stp;
00738                 segp->seg_in = hits[1];         /* struct copy */
00739                 segp->seg_out = hits[0];        /* struct copy */
00740                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00741         }
00742         return(2);                      /* HIT */
00743 }
00744 
00745 #define SEG_MISS(SEG)           (SEG).seg_stp=(struct soltab *) 0;
00746 
00747 /**
00748  *                      R T _ P A R T _ V S H O T
00749  *
00750  *  Vectorized version.
00751  */
00752 void
00753 rt_part_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00754                                /* An array of solid pointers */
00755                                /* An array of ray pointers */
00756                                /* array of segs (results returned) */
00757                                /* Number of ray/object pairs */
00758 
00759 {
00760         rt_vstub( stp, rp, segp, n, ap );
00761 }
00762 
00763 /**
00764  *                      R T _ P A R T _ N O R M
00765  *
00766  *  Given ONE ray distance, return the normal and entry/exit point.
00767  */
00768 void
00769 rt_part_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00770 {
00771         register struct part_specific *part =
00772                 (struct part_specific *)stp->st_specific;
00773 
00774         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00775         switch( hitp->hit_surfno )  {
00776         case RT_PARTICLE_SURF_VSPHERE:
00777                 VSUB2( hitp->hit_normal, hitp->hit_point, part->part_int.part_V );
00778                 VUNITIZE( hitp->hit_normal );
00779                 break;
00780         case RT_PARTICLE_SURF_HSPHERE:
00781                 VSUB3( hitp->hit_normal, hitp->hit_point,
00782                         part->part_int.part_V, part->part_int.part_H );
00783                 VUNITIZE( hitp->hit_normal );
00784                 break;
00785         case RT_PARTICLE_SURF_BODY:
00786                 /* compute it */
00787                 if( part->part_int.part_type == RT_PARTICLE_TYPE_CYLINDER )  {
00788                         /* The X' and Y' components of hit' are N' */
00789                         hitp->hit_vpriv[Z] = 0;
00790                         MAT4X3VEC( hitp->hit_normal, part->part_invRoS,
00791                                 hitp->hit_vpriv );
00792                         VUNITIZE( hitp->hit_normal );
00793                 } else {
00794                         /* The cone case */
00795                         FAST fastf_t    s, m;
00796                         vect_t unorm;
00797                         /* vpriv[Z] ranges from 0 to 1 (roughly) */
00798                         /* Rescale X' and Y' into unit circle */
00799                         m = part->part_hrad_prime - part->part_vrad_prime;
00800                         s = 1/(part->part_vrad_prime + m * hitp->hit_vpriv[Z]);
00801                         unorm[X] = hitp->hit_vpriv[X] * s;
00802                         unorm[Y] = hitp->hit_vpriv[Y] * s;
00803                         /* Z' is constant, from slope of cylinder wall*/
00804                         unorm[Z] = -m / sqrt(m*m+1);
00805                         MAT4X3VEC( hitp->hit_normal, part->part_invRoS, unorm );
00806                         VUNITIZE( hitp->hit_normal );
00807                 }
00808                 break;
00809         }
00810 }
00811 
00812 /**
00813  *                      R T _ P A R T _ C U R V E
00814  *
00815  *  Return the curvature of the particle.
00816  *  There are two cases:  hitting a hemisphere, and hitting the cylinder.
00817  */
00818 void
00819 rt_part_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00820 {
00821         register struct part_specific *part =
00822                 (struct part_specific *)stp->st_specific;
00823         point_t hit_local;      /* hit_point, with V as origin */
00824         point_t hit_unit;       /* hit_poit in unit coords, +Z along H */
00825 
00826         switch( hitp->hit_surfno )  {
00827         case RT_PARTICLE_SURF_VSPHERE:
00828                 bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
00829                 cvp->crv_c1 = cvp->crv_c2 = -part->part_int.part_vrad;
00830                 break;
00831         case RT_PARTICLE_SURF_HSPHERE:
00832                 bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
00833                 cvp->crv_c1 = cvp->crv_c2 = -part->part_int.part_hrad;
00834                 break;
00835         case RT_PARTICLE_SURF_BODY:
00836                 /* Curvature in only one direction, around H */
00837                 VCROSS( cvp->crv_pdir, hitp->hit_normal, part->part_int.part_H );
00838                 VUNITIZE( cvp->crv_pdir );
00839                 /* Interpolate radius between vrad and hrad */
00840                 VSUB2( hit_local, hitp->hit_point, part->part_int.part_V );
00841                 MAT4X3VEC( hit_unit, part->part_SoR, hit_local );
00842                 /* hit_unit[Z] ranges from 0 at V to 1 at H, interpolate */
00843                 cvp->crv_c1 = -(
00844                         part->part_v_erad * hit_unit[Z] +
00845                         part->part_h_erad * (1 - hit_unit[Z]) );
00846                 cvp->crv_c2 = 0;
00847                 break;
00848         }
00849 }
00850 
00851 /**
00852  *                      R T _ P A R T _ U V
00853  *
00854  *  For a hit on the surface of a particle, return the (u,v) coordinates
00855  *  of the hit point, 0 <= u,v <= 1.
00856  *  u = azimuth
00857  *  v = elevation along H
00858  *
00859  *  The 'u' coordinate wraps around the particle, once.
00860  *  The 'v' coordinate covers the 'height' of the particle,
00861  *  from V-r1 to (V+H)+r2.
00862  *
00863  *  hit_point has already been computed.
00864  */
00865 void
00866 rt_part_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00867 {
00868         register const struct part_specific *part =
00869                 (struct part_specific *)stp->st_specific;
00870         point_t hit_local;      /* hit_point, with V as origin */
00871         point_t hit_unit;       /* hit_poit in unit coords, +Z along H */
00872         fastf_t hsize;
00873         fastf_t hmag_inv;
00874         fastf_t vrad_unit;
00875         fastf_t r;
00876         fastf_t minrad;
00877 
00878         RT_PART_CK_MAGIC(&part->part_int.part_magic);
00879 
00880         hmag_inv = 1.0/MAGNITUDE(part->part_int.part_H);
00881         hsize = 1 + (vrad_unit = part->part_v_erad*hmag_inv) +
00882                 part->part_h_erad*hmag_inv;
00883 
00884         /* Transform hit point into unit particle coords */
00885         VSUB2( hit_local, hitp->hit_point, part->part_int.part_V );
00886         MAT4X3VEC( hit_unit, part->part_SoR, hit_local );
00887         /* normalize 0..1 */
00888         uvp->uv_v = (hit_unit[Z] + vrad_unit) / hsize;
00889 
00890         /* U is azimuth, atan() range: -pi to +pi */
00891         uvp->uv_u = bn_atan2( hit_unit[Y], hit_unit[X] ) * bn_inv2pi;
00892         if( uvp->uv_u < 0 )
00893                 uvp->uv_u += 1.0;
00894 
00895         /* approximation: beam_r / (solid_circumference = 2 * pi * radius) */
00896         minrad = part->part_v_erad;
00897         V_MIN(minrad, part->part_h_erad);
00898         r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
00899         uvp->uv_du = uvp->uv_dv =
00900                 bn_inv2pi * r / minrad;
00901 }
00902 
00903 /**
00904  *              R T _ P A R T _ F R E E
00905  */
00906 void
00907 rt_part_free(register struct soltab *stp)
00908 {
00909         register struct part_specific *part =
00910                 (struct part_specific *)stp->st_specific;
00911 
00912         bu_free( (char *)part, "part_specific" );
00913         stp->st_specific = GENPTR_NULL;
00914 }
00915 
00916 /**
00917  *                      R T _ P A R T _ C L A S S
00918  */
00919 int
00920 rt_part_class(void)
00921 {
00922         return(0);
00923 }
00924 
00925 /**
00926  *                      R T _ P A R T _ H E M I S P H E R E 8
00927  *
00928  *  Produce a crude approximation to a hemisphere,
00929  *  8 points around the rim [0]..[7],
00930  *  4 points around a midway latitude [8]..[11], and
00931  *  1 point at the pole [12].
00932  *
00933  *  For the dome, connect up:
00934  *      0 8 12 10 4
00935  *      2 9 12 11 6
00936  */
00937 HIDDEN void
00938 rt_part_hemisphere(register point_t (*ov), register fastf_t *v, fastf_t *a, fastf_t *b, fastf_t *h)
00939 {
00940         register float cos45 = 0.707107;
00941 
00942         /* This is the top of the dome */
00943         VADD2( ov[12], v, h );
00944 
00945         VADD2( ov[0], v, a );
00946         VJOIN2( ov[1], v, cos45, a, cos45, b );
00947         VADD2( ov[2], v, b );
00948         VJOIN2( ov[3], v, -cos45, a, cos45, b );
00949         VSUB2( ov[4], v, a );
00950         VJOIN2( ov[5], v, -cos45, a, -cos45, b );
00951         VSUB2( ov[6], v, b );
00952         VJOIN2( ov[7], v, cos45, a, -cos45, b );
00953 
00954         VJOIN2( ov[8], v, cos45, a, cos45, h );
00955         VJOIN2( ov[10], v, -cos45, a, cos45, h );
00956 
00957         VJOIN2( ov[9], v, cos45, b, cos45, h );
00958         VJOIN2( ov[11], v, -cos45, b, cos45, h );
00959         /* Obviously, this could be optimized quite a lot more */
00960 }
00961 
00962 /**
00963  *                      R T _ P A R T _ P L O T
00964  */
00965 int
00966 rt_part_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00967 {
00968         struct rt_part_internal *pip;
00969         point_t         tail;
00970         point_t         sphere_rim[16];
00971         point_t         vhemi[13];
00972         point_t         hhemi[13];
00973         vect_t          a, b, c;                /* defines coord sys of part */
00974         vect_t          as, bs, hs;             /* scaled by radius */
00975         vect_t          Hunit;
00976         register int    i;
00977 
00978         RT_CK_DB_INTERNAL(ip);
00979         pip = (struct rt_part_internal *)ip->idb_ptr;
00980         RT_PART_CK_MAGIC(pip);
00981 
00982         if( pip->part_type == RT_PARTICLE_TYPE_SPHERE )  {
00983                 /* For the sphere, 3 rings of 16 points */
00984                 VSET( a, pip->part_vrad, 0, 0 );
00985                 VSET( b, 0, pip->part_vrad, 0 );
00986                 VSET( c, 0, 0, pip->part_vrad );
00987 
00988                 rt_ell_16pts( &sphere_rim[0][X], pip->part_V, a, b );
00989                 RT_ADD_VLIST( vhead, sphere_rim[15], BN_VLIST_LINE_MOVE );
00990                 for( i=0; i<16; i++ )  {
00991                         RT_ADD_VLIST( vhead, sphere_rim[i], BN_VLIST_LINE_DRAW );
00992                 }
00993 
00994                 rt_ell_16pts( &sphere_rim[0][X], pip->part_V, b, c );
00995                 RT_ADD_VLIST( vhead, sphere_rim[15], BN_VLIST_LINE_MOVE );
00996                 for( i=0; i<16; i++ )  {
00997                         RT_ADD_VLIST( vhead, sphere_rim[i], BN_VLIST_LINE_DRAW );
00998                 }
00999 
01000                 rt_ell_16pts( &sphere_rim[0][X], pip->part_V, a, c );
01001                 RT_ADD_VLIST( vhead, sphere_rim[15], BN_VLIST_LINE_MOVE );
01002                 for( i=0; i<16; i++ )  {
01003                         RT_ADD_VLIST( vhead, sphere_rim[i], BN_VLIST_LINE_DRAW );
01004                 }
01005                 return(0);              /* OK */
01006         }
01007 
01008         VMOVE( Hunit, pip->part_H );
01009         VUNITIZE( Hunit );
01010         bn_vec_perp( a, Hunit );
01011         VUNITIZE(a);
01012         VCROSS( b, Hunit, a );
01013         VUNITIZE(b);
01014 
01015         VSCALE( as, a, pip->part_vrad );
01016         VSCALE( bs, b, pip->part_vrad );
01017         VSCALE( hs, Hunit, -pip->part_vrad );
01018         rt_part_hemisphere( vhemi, pip->part_V, as, bs, hs );
01019 
01020         VADD2( tail, pip->part_V, pip->part_H );
01021         VSCALE( as, a, pip->part_hrad );
01022         VSCALE( bs, b, pip->part_hrad );
01023         VSCALE( hs, Hunit, pip->part_hrad );
01024         rt_part_hemisphere( hhemi, tail, as, bs, hs );
01025 
01026         /* Draw V end hemisphere */
01027         RT_ADD_VLIST( vhead, vhemi[0], BN_VLIST_LINE_MOVE );
01028         for( i=7; i >= 0; i-- )  {
01029                 RT_ADD_VLIST( vhead, vhemi[i], BN_VLIST_LINE_DRAW );
01030         }
01031         RT_ADD_VLIST( vhead, vhemi[8], BN_VLIST_LINE_DRAW );
01032         RT_ADD_VLIST( vhead, vhemi[12], BN_VLIST_LINE_DRAW );
01033         RT_ADD_VLIST( vhead, vhemi[10], BN_VLIST_LINE_DRAW );
01034         RT_ADD_VLIST( vhead, vhemi[4], BN_VLIST_LINE_DRAW );
01035         RT_ADD_VLIST( vhead, vhemi[2], BN_VLIST_LINE_MOVE );
01036         RT_ADD_VLIST( vhead, vhemi[9], BN_VLIST_LINE_DRAW );
01037         RT_ADD_VLIST( vhead, vhemi[12], BN_VLIST_LINE_DRAW );
01038         RT_ADD_VLIST( vhead, vhemi[11], BN_VLIST_LINE_DRAW );
01039         RT_ADD_VLIST( vhead, vhemi[6], BN_VLIST_LINE_DRAW );
01040 
01041         /* Draw H end hemisphere */
01042         RT_ADD_VLIST( vhead, hhemi[0], BN_VLIST_LINE_MOVE );
01043         for( i=7; i >= 0; i-- )  {
01044                 RT_ADD_VLIST( vhead, hhemi[i], BN_VLIST_LINE_DRAW );
01045         }
01046         RT_ADD_VLIST( vhead, hhemi[8], BN_VLIST_LINE_DRAW );
01047         RT_ADD_VLIST( vhead, hhemi[12], BN_VLIST_LINE_DRAW );
01048         RT_ADD_VLIST( vhead, hhemi[10], BN_VLIST_LINE_DRAW );
01049         RT_ADD_VLIST( vhead, hhemi[4], BN_VLIST_LINE_DRAW );
01050         RT_ADD_VLIST( vhead, hhemi[2], BN_VLIST_LINE_MOVE );
01051         RT_ADD_VLIST( vhead, hhemi[9], BN_VLIST_LINE_DRAW );
01052         RT_ADD_VLIST( vhead, hhemi[12], BN_VLIST_LINE_DRAW );
01053         RT_ADD_VLIST( vhead, hhemi[11], BN_VLIST_LINE_DRAW );
01054         RT_ADD_VLIST( vhead, hhemi[6], BN_VLIST_LINE_DRAW );
01055 
01056         /* Draw 4 connecting lines */
01057         RT_ADD_VLIST( vhead, vhemi[0], BN_VLIST_LINE_MOVE );
01058         RT_ADD_VLIST( vhead, hhemi[0], BN_VLIST_LINE_DRAW );
01059         RT_ADD_VLIST( vhead, vhemi[2], BN_VLIST_LINE_MOVE );
01060         RT_ADD_VLIST( vhead, hhemi[2], BN_VLIST_LINE_DRAW );
01061         RT_ADD_VLIST( vhead, vhemi[4], BN_VLIST_LINE_MOVE );
01062         RT_ADD_VLIST( vhead, hhemi[4], BN_VLIST_LINE_DRAW );
01063         RT_ADD_VLIST( vhead, vhemi[6], BN_VLIST_LINE_MOVE );
01064         RT_ADD_VLIST( vhead, hhemi[6], BN_VLIST_LINE_DRAW );
01065 
01066         return(0);
01067 }
01068 
01069 struct part_state {
01070         struct shell    *s;
01071         mat_t           upper_invRinvS;
01072         mat_t           upper_invRoS;
01073         mat_t           lower_invRinvS;
01074         mat_t           lower_invRoS;
01075         fastf_t         theta_tol;
01076 };
01077 
01078 struct part_vert_strip {
01079         int             nverts_per_strip;
01080         int             nverts;
01081         struct vertex   **vp;
01082         vect_t          *norms;
01083         int             nfaces;
01084         struct faceuse  **fu;
01085 };
01086 
01087 /**
01088  *                      R T _ P A R T _ T E S S
01089  *
01090  *  Based upon the tesselator for the ellipsoid.
01091  *
01092  *  Break the particle into three parts:
01093  *      Upper hemisphere        0..nsegs                        H       North
01094  *      middle cylinder         nsegs..nsegs+1
01095  *      lower hemisphere        nsegs+1..nstrips-1              V       South
01096  */
01097 int
01098 rt_part_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01099 {
01100         struct rt_part_internal *pip;
01101         LOCAL mat_t     R;
01102         LOCAL mat_t     S;
01103         LOCAL mat_t     invR;
01104         LOCAL mat_t     invS;
01105         LOCAL vect_t    zz;
01106         LOCAL vect_t    hcenter;
01107         struct part_state       state;
01108         register int            i;
01109         fastf_t         radius;
01110         int             nsegs;
01111         int             nstrips;
01112         struct part_vert_strip  *strips;
01113         int             j;
01114         struct vertex           **vertp[5];
01115         int     faceno;
01116         int     stripno;
01117         int     boff;           /* base offset */
01118         int     toff;           /* top offset */
01119         int     blim;           /* base subscript limit */
01120         int     tlim;           /* top subscrpit limit */
01121         fastf_t rel;            /* Absolutized relative tolerance */
01122 
01123         RT_CK_DB_INTERNAL(ip);
01124         pip = (struct rt_part_internal *)ip->idb_ptr;
01125         RT_PART_CK_MAGIC(pip);
01126 
01127         if( pip->part_type == RT_PARTICLE_TYPE_SPHERE )
01128                 return(-1);
01129         /* For now, concentrate on the most important kind. */
01130 
01131         VADD2( hcenter, pip->part_V, pip->part_H );
01132 
01133         /* Compute R and Rinv matrices */
01134         /* R is the same for both upper and lower hemispheres */
01135         /* R is rotation from model coords to unit sphere */
01136         /* For rotation of the particle, +H (model) becomes +Z (unit sph) */
01137         VSET( zz, 0, 0, 1 );
01138         bn_mat_fromto( R, pip->part_H, zz );
01139         bn_mat_trn( invR, R );                  /* inv of rot mat is trn */
01140 
01141         /*** Upper (H) ***/
01142 
01143         /* Compute S and invS matrices */
01144         /* invS is just 1/diagonal elements */
01145         MAT_IDN( S );
01146         S[ 0] = S[ 5] = S[10] = 1/pip->part_hrad;
01147         bn_mat_inv( invS, S );
01148 
01149         /* invRinvS, for converting points from unit sphere to model */
01150         bn_mat_mul( state.upper_invRinvS, invR, invS );
01151 
01152         /* invRoS, for converting normals from unit sphere to model */
01153         bn_mat_mul( state.upper_invRoS, invR, S );
01154 
01155         /*** Lower (V) ***/
01156 
01157         /* Compute S and invS matrices */
01158         /* invS is just 1/diagonal elements */
01159         MAT_IDN( S );
01160         S[ 0] = S[ 5] = S[10] = 1/pip->part_vrad;
01161         bn_mat_inv( invS, S );
01162 
01163         /* invRinvS, for converting points from unit sphere to model */
01164         bn_mat_mul( state.lower_invRinvS, invR, invS );
01165 
01166         /* invRoS, for converting normals from unit sphere to model */
01167         bn_mat_mul( state.lower_invRoS, invR, S );
01168 
01169         /* Find the larger of two hemispheres */
01170         radius = pip->part_vrad;
01171         if( pip->part_hrad > radius )
01172                 radius = pip->part_hrad;
01173 
01174         /*
01175          *  Establish tolerances
01176          */
01177         if( ttol->rel <= 0.0 || ttol->rel >= 1.0 )  {
01178                 rel = 0.0;              /* none */
01179         } else {
01180                 /* Convert rel to absolute by scaling by radius */
01181                 rel = ttol->rel * radius;
01182         }
01183         if( ttol->abs <= 0.0 )  {
01184                 if( rel <= 0.0 )  {
01185                         /* No tolerance given, use a default */
01186                         rel = 0.10 * radius;    /* 10% */
01187                 } else {
01188                         /* Use absolute-ized relative tolerance */
01189                 }
01190         } else {
01191                 /* Absolute tolerance was given, pick smaller */
01192                 if( ttol->rel <= 0.0 || rel > ttol->abs )
01193                 {
01194                         rel = ttol->abs;
01195                         if( rel > radius )
01196                                 rel = radius;
01197                 }
01198         }
01199 
01200         /*
01201          *  Converte distance tolerance into a maximum permissible
01202          *  angle tolerance.  'radius' is largest radius.
01203          */
01204         state.theta_tol = 2 * acos( 1.0 - rel / radius );
01205 
01206         /* To ensure normal tolerance, remain below this angle */
01207         if( ttol->norm > 0.0 && ttol->norm < state.theta_tol )  {
01208                 state.theta_tol = ttol->norm;
01209         }
01210 
01211         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01212         state.s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01213 
01214         /* Find the number of segments to divide 90 degrees worth into */
01215         nsegs = bn_halfpi / state.theta_tol + 0.999;
01216         if( nsegs < 2 )  nsegs = 2;
01217 
01218         /*  Find total number of strips of vertices that will be needed.
01219          *  nsegs for each hemisphere, plus one equator each.
01220          *  The two equators will be stitched together to make the cylinder.
01221          *  Note that faces are listed in the the stripe ABOVE, ie, toward
01222          *  the poles.  Thus, strips[0] will have 4 faces.
01223          */
01224         nstrips = 2 * nsegs + 2;
01225         strips = (struct part_vert_strip *)bu_calloc( nstrips,
01226                 sizeof(struct part_vert_strip), "strips[]" );
01227 
01228         /* North pole (Upper hemisphere, H end) */
01229         strips[0].nverts = 1;
01230         strips[0].nverts_per_strip = 0;
01231         strips[0].nfaces = 4;
01232         /* South pole (Lower hemispehre, V end) */
01233         strips[nstrips-1].nverts = 1;
01234         strips[nstrips-1].nverts_per_strip = 0;
01235         strips[nstrips-1].nfaces = 4;
01236         /* upper equator (has faces) */
01237         strips[nsegs].nverts = nsegs * 4;
01238         strips[nsegs].nverts_per_strip = nsegs;
01239         strips[nsegs].nfaces = nsegs * 4;
01240         /* lower equator (no faces) */
01241         strips[nsegs+1].nverts = nsegs * 4;
01242         strips[nsegs+1].nverts_per_strip = nsegs;
01243         strips[nsegs+1].nfaces = 0;
01244 
01245         for( i=1; i<nsegs; i++ )  {
01246                 strips[i].nverts_per_strip =
01247                         strips[nstrips-1-i].nverts_per_strip = i;
01248                 strips[i].nverts =
01249                         strips[nstrips-1-i].nverts = i * 4;
01250                 strips[i].nfaces =
01251                         strips[nstrips-1-i].nfaces = (2 * i + 1)*4;
01252         }
01253         /* All strips have vertices and normals */
01254         for( i=0; i<nstrips; i++ )  {
01255                 strips[i].vp = (struct vertex **)bu_calloc( strips[i].nverts,
01256                         sizeof(struct vertex *), "strip vertex[]" );
01257                 strips[i].norms = (vect_t *)bu_calloc( strips[i].nverts,
01258                         sizeof( vect_t ), "strip normals[]" );
01259         }
01260         /* All strips have faces, except for the one (marked) equator */
01261         for( i=0; i < nstrips; i++ )  {
01262                 if( strips[i].nfaces <= 0 )  {
01263                         strips[i].fu = (struct faceuse **)NULL;
01264                         continue;
01265                 }
01266                 strips[i].fu = (struct faceuse **)bu_calloc( strips[i].nfaces,
01267                         sizeof(struct faceuse *), "strip faceuse[]" );
01268         }
01269 
01270         /* First, build the triangular mesh topology */
01271         /* Do the top. "toff" in i-1 is UP, towards +B */
01272         for( i = 1; i <= nsegs; i++ )  {
01273                 faceno = 0;
01274                 tlim = strips[i-1].nverts;
01275                 blim = strips[i].nverts;
01276                 for( stripno=0; stripno<4; stripno++ )  {
01277                         toff = stripno * strips[i-1].nverts_per_strip;
01278                         boff = stripno * strips[i].nverts_per_strip;
01279 
01280                         /* Connect this quarter strip */
01281                         for( j = 0; j < strips[i].nverts_per_strip; j++ )  {
01282 
01283                                 /* "Right-side-up" triangle */
01284                                 vertp[0] = &(strips[i].vp[j+boff]);
01285                                 vertp[1] = &(strips[i].vp[(j+1+boff)%blim]);
01286                                 vertp[2] = &(strips[i-1].vp[(j+toff)%tlim]);
01287                                 if( (strips[i-1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
01288                                         bu_log("rt_part_tess() nmg_cmface failure\n");
01289                                         goto fail;
01290                                 }
01291                                 if( j+1 >= strips[i].nverts_per_strip )  break;
01292 
01293                                 /* Follow with interior "Up-side-down" triangle */
01294                                 vertp[0] = &(strips[i].vp[(j+1+boff)%blim]);
01295                                 vertp[1] = &(strips[i-1].vp[(j+1+toff)%tlim]);
01296                                 vertp[2] = &(strips[i-1].vp[(j+toff)%tlim]);
01297                                 if( (strips[i-1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
01298                                         bu_log("rt_part_tess() nmg_cmface failure\n");
01299                                         goto fail;
01300                                 }
01301                         }
01302                 }
01303         }
01304 
01305         /* Connect the two equators with rectangular (4-point) faces */
01306         i = nsegs+1;
01307         {
01308                 faceno = 0;
01309                 tlim = strips[i-1].nverts;
01310                 blim = strips[i].nverts;
01311                 {
01312                         /* Connect this whole strip */
01313                         for( j = 0; j < strips[i].nverts; j++ )  {
01314 
01315                                 /* "Right-side-up" rectangle */
01316                                 vertp[3] = &(strips[i].vp[(j)%blim]);
01317                                 vertp[2] = &(strips[i-1].vp[(j)%tlim]);
01318                                 vertp[1] = &(strips[i-1].vp[(j+1)%tlim]);
01319                                 vertp[0] = &(strips[i].vp[(j+1)%blim]);
01320                                 if( (strips[i-1].fu[faceno++] = nmg_cmface(state.s, vertp, 4 )) == 0 )  {
01321                                         bu_log("rt_part_tess() nmg_cmface failure\n");
01322                                         goto fail;
01323                                 }
01324                         }
01325                 }
01326         }
01327 
01328         /* Do the bottom.  Everything is upside down. "toff" in i+1 is DOWN */
01329         for( i = nsegs+1; i < nstrips; i++ )  {
01330                 faceno = 0;
01331                 tlim = strips[i+1].nverts;
01332                 blim = strips[i].nverts;
01333                 for( stripno=0; stripno<4; stripno++ )  {
01334                         toff = stripno * strips[i+1].nverts_per_strip;
01335                         boff = stripno * strips[i].nverts_per_strip;
01336 
01337                         /* Connect this quarter strip */
01338                         for( j = 0; j < strips[i].nverts_per_strip; j++ )  {
01339 
01340                                 /* "Right-side-up" triangle */
01341                                 vertp[0] = &(strips[i].vp[j+boff]);
01342                                 vertp[1] = &(strips[i+1].vp[(j+toff)%tlim]);
01343                                 vertp[2] = &(strips[i].vp[(j+1+boff)%blim]);
01344                                 if( (strips[i+1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
01345                                         bu_log("rt_part_tess() nmg_cmface failure\n");
01346                                         goto fail;
01347                                 }
01348                                 if( j+1 >= strips[i].nverts_per_strip )  break;
01349 
01350                                 /* Follow with interior "Up-side-down" triangle */
01351                                 vertp[0] = &(strips[i].vp[(j+1+boff)%blim]);
01352                                 vertp[1] = &(strips[i+1].vp[(j+toff)%tlim]);
01353                                 vertp[2] = &(strips[i+1].vp[(j+1+toff)%tlim]);
01354                                 if( (strips[i+1].fu[faceno++] = nmg_cmface(state.s, vertp, 3 )) == 0 )  {
01355                                         bu_log("rt_part_tess() nmg_cmface failure\n");
01356                                         goto fail;
01357                                 }
01358                         }
01359                 }
01360         }
01361 
01362         /*  Compute the geometry of each vertex.
01363          *  Start with the location in the unit sphere, and project back.
01364          *  i=0 is "straight up" along +H.
01365          */
01366         for( i=0; i < nstrips; i++ )  {
01367                 double  alpha;          /* decline down from B to A */
01368                 double  beta;           /* angle around equator (azimuth) */
01369                 fastf_t         cos_alpha, sin_alpha;
01370                 fastf_t         cos_beta, sin_beta;
01371                 point_t         sphere_pt;
01372                 point_t         model_pt;
01373 
01374                 /* Compensate for extra equator */
01375                 if( i <= nsegs )
01376                         alpha = (((double)i) / (nstrips-1-1));
01377                 else
01378                         alpha = (((double)i-1) / (nstrips-1-1));
01379                 cos_alpha = cos(alpha*bn_pi);
01380                 sin_alpha = sin(alpha*bn_pi);
01381                 for( j=0; j < strips[i].nverts; j++ )  {
01382 
01383                         beta = ((double)j) / strips[i].nverts;
01384                         cos_beta = cos(beta*bn_twopi);
01385                         sin_beta = sin(beta*bn_twopi);
01386                         VSET( sphere_pt,
01387                                 cos_beta * sin_alpha,
01388                                 sin_beta * sin_alpha,
01389                                 cos_alpha );
01390                         /* Convert from ideal sphere coordinates */
01391                         if( i <= nsegs )  {
01392                                 MAT4X3PNT( model_pt, state.upper_invRinvS, sphere_pt );
01393                                 VADD2( model_pt, model_pt, hcenter );
01394                                 /* Convert sphere normal to ellipsoid normal */
01395                                 MAT4X3VEC( strips[i].norms[j], state.upper_invRoS, sphere_pt );
01396                         } else {
01397                                 MAT4X3PNT( model_pt, state.lower_invRinvS, sphere_pt );
01398                                 VADD2( model_pt, model_pt, pip->part_V );
01399                                 /* Convert sphere normal to ellipsoid normal */
01400                                 MAT4X3VEC( strips[i].norms[j], state.lower_invRoS, sphere_pt );
01401                         }
01402 
01403                         /* May not be unit length anymore */
01404                         VUNITIZE( strips[i].norms[j] );
01405                         /* Associate vertex geometry */
01406                         nmg_vertex_gv( strips[i].vp[j], model_pt );
01407                 }
01408         }
01409 
01410         /* Associate face geometry.  lower Equator has no faces */
01411         for( i=0; i < nstrips; i++ )  {
01412                 for( j=0; j < strips[i].nfaces; j++ )  {
01413                         if( nmg_fu_planeeqn( strips[i].fu[j], tol ) < 0 )
01414                                 goto fail;
01415                 }
01416         }
01417 
01418         /* Associate normals with vertexuses */
01419         for( i=0; i < nstrips; i++ )
01420         {
01421                 for( j=0; j < strips[i].nverts; j++ )
01422                 {
01423                         struct faceuse *fu;
01424                         struct vertexuse *vu;
01425                         vect_t norm_opp;
01426 
01427                         NMG_CK_VERTEX( strips[i].vp[j] );
01428                         VREVERSE( norm_opp , strips[i].norms[j] )
01429 
01430                         for( BU_LIST_FOR( vu , vertexuse , &strips[i].vp[j]->vu_hd ) )
01431                         {
01432                                 fu = nmg_find_fu_of_vu( vu );
01433                                 NMG_CK_FACEUSE( fu );
01434                                 /* get correct direction of normals depending on
01435                                  * faceuse orientation
01436                                  */
01437                                 if( fu->orientation == OT_SAME )
01438                                         nmg_vertexuse_nv( vu , strips[i].norms[j] );
01439                                 else if( fu->orientation == OT_OPPOSITE )
01440                                         nmg_vertexuse_nv( vu , norm_opp );
01441                         }
01442                 }
01443         }
01444 
01445         /* Compute "geometry" for region and shell */
01446         nmg_region_a( *r, tol );
01447 
01448         /* Release memory */
01449         /* All strips have vertices and normals */
01450         for( i=0; i<nstrips; i++ )  {
01451                 bu_free( (char *)strips[i].vp, "strip vertex[]" );
01452                 bu_free( (char *)strips[i].norms, "strip norms[]" );
01453         }
01454         /* All strips have faces, except for equator */
01455         for( i=0; i < nstrips; i++ )  {
01456                 if( strips[i].fu == (struct faceuse **)0 )  continue;
01457                 bu_free( (char *)strips[i].fu, "strip faceuse[]" );
01458         }
01459         bu_free( (char *)strips, "strips[]" );
01460         return(0);
01461 fail:
01462         /* Release memory */
01463         /* All strips have vertices and normals */
01464         for( i=0; i<nstrips; i++ )  {
01465                 bu_free( (char *)strips[i].vp, "strip vertex[]" );
01466                 bu_free( (char *)strips[i].norms, "strip norms[]" );
01467         }
01468         /* All strips have faces, except for equator */
01469         for( i=0; i < nstrips; i++ )  {
01470                 if( strips[i].fu == (struct faceuse **)0 )  continue;
01471                 bu_free( (char *)strips[i].fu, "strip faceuse[]" );
01472         }
01473         bu_free( (char *)strips, "strips[]" );
01474         return(-1);
01475 }
01476 
01477 /**
01478  *                      R T _ P A R T _ I M P O R T
01479  */
01480 int
01481 rt_part_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01482 {
01483         point_t         v;
01484         vect_t          h;
01485         double          vrad;
01486         double          hrad;
01487         fastf_t         maxrad, minrad;
01488         union record    *rp;
01489         struct rt_part_internal *part;
01490 
01491         BU_CK_EXTERNAL( ep );
01492         rp = (union record *)ep->ext_buf;
01493         /* Check record type */
01494         if( rp->u_id != DBID_PARTICLE )  {
01495                 bu_log("rt_part_import: defective record\n");
01496                 return(-1);
01497         }
01498 
01499         /* Convert from database to internal format */
01500         ntohd( (unsigned char *)v, rp->part.p_v, 3 );
01501         ntohd( (unsigned char *)h, rp->part.p_h, 3 );
01502         ntohd( (unsigned char *)&vrad, rp->part.p_vrad, 1 );
01503         ntohd( (unsigned char *)&hrad, rp->part.p_hrad, 1 );
01504 
01505         RT_CK_DB_INTERNAL( ip );
01506         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01507         ip->idb_type = ID_PARTICLE;
01508         ip->idb_meth = &rt_functab[ID_PARTICLE];
01509         ip->idb_ptr = bu_malloc( sizeof(struct rt_part_internal), "rt_part_internal");
01510         part = (struct rt_part_internal *)ip->idb_ptr;
01511         part->part_magic = RT_PART_INTERNAL_MAGIC;
01512 
01513         /* Apply modeling transformations */
01514         MAT4X3PNT( part->part_V, mat, v );
01515         MAT4X3VEC( part->part_H, mat, h );
01516         if( (part->part_vrad = vrad / mat[15]) < 0 )  {
01517           bu_log("unable to import particle, negative v radius\n");
01518           bu_free( ip->idb_ptr, "rt_part_internal" );
01519           ip->idb_ptr=NULL;
01520           return(-2);
01521         }
01522         if( (part->part_hrad = hrad / mat[15]) < 0 )  {
01523           bu_log("unable to import particle, negative h radius\n");
01524           bu_free( ip->idb_ptr, "rt_part_internal" );
01525           ip->idb_ptr=NULL;
01526           return(-3);
01527         }
01528 
01529         if( part->part_vrad > part->part_hrad )  {
01530                 maxrad = part->part_vrad;
01531                 minrad = part->part_hrad;
01532         } else {
01533                 maxrad = part->part_hrad;
01534                 minrad = part->part_vrad;
01535         }
01536         if( maxrad <= 0 )  {
01537           bu_log("unable to import particle, negative radius\n");
01538           bu_free( ip->idb_ptr, "rt_part_internal" );
01539           ip->idb_ptr=NULL;
01540           return(-4);
01541         }
01542 
01543         if( MAGSQ( part->part_H ) * 1000000 < maxrad * maxrad )  {
01544                 /* Height vector is insignificant, particle is a sphere */
01545                 part->part_vrad = part->part_hrad = maxrad;
01546                 VSETALL( part->part_H, 0 );             /* sanity */
01547                 part->part_type = RT_PARTICLE_TYPE_SPHERE;
01548                 return(0);              /* OK */
01549         }
01550 
01551         if( (maxrad - minrad) / maxrad < 0.001 )  {
01552                 /* radii are nearly equal, particle is a cylinder (lozenge) */
01553                 part->part_vrad = part->part_hrad = maxrad;
01554                 part->part_type = RT_PARTICLE_TYPE_CYLINDER;
01555                 return(0);              /* OK */
01556         }
01557 
01558         part->part_type = RT_PARTICLE_TYPE_CONE;
01559         return(0);              /* OK */
01560 }
01561 
01562 /**
01563  *                      R T _ P A R T _ E X P O R T
01564  */
01565 int
01566 rt_part_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01567 {
01568         struct rt_part_internal *pip;
01569         union record            *rec;
01570         point_t         vert;
01571         vect_t          hi;
01572         fastf_t         vrad;
01573         fastf_t         hrad;
01574 
01575         RT_CK_DB_INTERNAL(ip);
01576         if( ip->idb_type != ID_PARTICLE )  return(-1);
01577         pip = (struct rt_part_internal *)ip->idb_ptr;
01578         RT_PART_CK_MAGIC(pip);
01579 
01580         BU_CK_EXTERNAL(ep);
01581         ep->ext_nbytes = sizeof(union record);
01582         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "part external");
01583         rec = (union record *)ep->ext_buf;
01584 
01585         /* Convert from user units to mm */
01586         VSCALE( vert, pip->part_V, local2mm );
01587         VSCALE( hi, pip->part_H, local2mm );
01588         vrad = pip->part_vrad * local2mm;
01589         hrad = pip->part_hrad * local2mm;
01590         /* pip->part_type is not converted -- internal only */
01591 
01592         rec->part.p_id = DBID_PARTICLE;
01593         htond( rec->part.p_v, (unsigned char *)vert, 3 );
01594         htond( rec->part.p_h, (unsigned char *)hi, 3 );
01595         htond( rec->part.p_vrad, (unsigned char *)&vrad, 1 );
01596         htond( rec->part.p_hrad, (unsigned char *)&hrad, 1 );
01597 
01598         return(0);
01599 }
01600 
01601 
01602 /**
01603  *                      R T _ P A R T _ I M P O R T 5
01604  */
01605 int
01606 rt_part_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01607 {
01608         fastf_t                 maxrad, minrad;
01609         struct rt_part_internal *part;
01610         fastf_t                 vec[8];
01611 
01612         BU_CK_EXTERNAL( ep );
01613 
01614         BU_ASSERT_LONG( ep->ext_nbytes, ==, SIZEOF_NETWORK_DOUBLE * 8 );
01615 
01616         RT_CK_DB_INTERNAL( ip );
01617         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01618         ip->idb_type = ID_PARTICLE;
01619         ip->idb_meth = &rt_functab[ID_PARTICLE];
01620         ip->idb_ptr = bu_malloc( sizeof(struct rt_part_internal), "rt_part_internal");
01621 
01622         part = (struct rt_part_internal *)ip->idb_ptr;
01623         part->part_magic = RT_PART_INTERNAL_MAGIC;
01624 
01625         /* Convert from database (network) to internal (host) format */
01626         ntohd( (unsigned char *)vec, ep->ext_buf, 8 );
01627 
01628         /* Apply modeling transformations */
01629         MAT4X3PNT( part->part_V, mat, &vec[0*3] );
01630         MAT4X3VEC( part->part_H, mat, &vec[1*3] );
01631         if( (part->part_vrad = vec[2*3] / mat[15]) < 0 )  {
01632           bu_free( ip->idb_ptr, "rt_part_internal" );
01633           ip->idb_ptr=NULL;
01634           bu_log("unable to import particle, negative v radius\n");
01635           return(-2);
01636         }
01637         if( (part->part_hrad = vec[2*3+1] / mat[15]) < 0 )  {
01638           bu_free( ip->idb_ptr, "rt_part_internal" );
01639           ip->idb_ptr=NULL;
01640           bu_log("unable to import particle, negative h radius\n");
01641           return(-3);
01642         }
01643 
01644         if( part->part_vrad > part->part_hrad )  {
01645                 maxrad = part->part_vrad;
01646                 minrad = part->part_hrad;
01647         } else {
01648                 maxrad = part->part_hrad;
01649                 minrad = part->part_vrad;
01650         }
01651         if( maxrad <= 0 )  {
01652           bu_free( ip->idb_ptr, "rt_part_internal" );
01653           ip->idb_ptr=NULL;
01654           bu_log("unable to import particle, negative radius\n");
01655           return(-4);
01656         }
01657 
01658         if( MAGSQ( part->part_H ) * 1000000 < maxrad * maxrad )  {
01659                 /* Height vector is insignificant, particle is a sphere */
01660                 part->part_vrad = part->part_hrad = maxrad;
01661                 VSETALL( part->part_H, 0 );             /* sanity */
01662                 part->part_type = RT_PARTICLE_TYPE_SPHERE;
01663                 return(0);              /* OK */
01664         }
01665 
01666         if( (maxrad - minrad) / maxrad < 0.001 )  {
01667                 /* radii are nearly equal, particle is a cylinder (lozenge) */
01668                 part->part_vrad = part->part_hrad = maxrad;
01669                 part->part_type = RT_PARTICLE_TYPE_CYLINDER;
01670                 return(0);              /* OK */
01671         }
01672 
01673         part->part_type = RT_PARTICLE_TYPE_CONE;
01674         return(0);              /* OK */
01675 }
01676 
01677 /**
01678  *                      R T _ P A R T _ E X P O R T 5
01679  */
01680 int
01681 rt_part_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01682 {
01683         struct rt_part_internal *pip;
01684         fastf_t                 vec[8];
01685 
01686         RT_CK_DB_INTERNAL(ip);
01687         if( ip->idb_type != ID_PARTICLE )  return(-1);
01688         pip = (struct rt_part_internal *)ip->idb_ptr;
01689         RT_PART_CK_MAGIC(pip);
01690 
01691         BU_CK_EXTERNAL(ep);
01692         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE * 8;
01693         ep->ext_buf = (genptr_t)bu_malloc( ep->ext_nbytes, "part external");
01694 
01695         /* scale 'em into local buffer */
01696         VSCALE( &vec[0*3], pip->part_V, local2mm );
01697         VSCALE( &vec[1*3], pip->part_H, local2mm );
01698         vec[2*3] = pip->part_vrad * local2mm;
01699         vec[2*3+1] = pip->part_hrad * local2mm;
01700 
01701         /* !!! should make sure the values are proper (no negative radius) */
01702 
01703         /* Convert from internal (host) to database (network) format */
01704         htond( ep->ext_buf, (unsigned char *)vec, 8 );
01705 
01706         return(0);
01707 }
01708 
01709 /**
01710  *                      R T _ P A R T _ D E S C R I B E
01711  *
01712  *  Make human-readable formatted presentation of this solid.
01713  *  First line describes type of solid.
01714  *  Additional lines are indented one tab, and give parameter values.
01715  */
01716 int
01717 rt_part_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
01718 {
01719         register struct rt_part_internal        *pip =
01720                 (struct rt_part_internal *)ip->idb_ptr;
01721         char    buf[256];
01722 
01723         RT_PART_CK_MAGIC(pip);
01724         switch( pip->part_type )  {
01725         case RT_PARTICLE_TYPE_SPHERE:
01726                 bu_vls_strcat( str, "spherical particle\n");
01727                 sprintf(buf, "\tV (%g, %g, %g)\n",
01728                         INTCLAMP(pip->part_V[X] * mm2local),
01729                         INTCLAMP(pip->part_V[Y] * mm2local),
01730                         INTCLAMP(pip->part_V[Z] * mm2local) );
01731                 bu_vls_strcat( str, buf );
01732                 sprintf(buf, "\tradius = %g\n",
01733                         INTCLAMP(pip->part_vrad * mm2local) );
01734                 bu_vls_strcat( str, buf );
01735                 break;
01736         case RT_PARTICLE_TYPE_CYLINDER:
01737                 bu_vls_strcat( str, "cylindrical particle (lozenge)\n");
01738                 sprintf(buf, "\tV (%g, %g, %g)\n",
01739                         INTCLAMP(pip->part_V[X] * mm2local),
01740                         INTCLAMP(pip->part_V[Y] * mm2local),
01741                         INTCLAMP(pip->part_V[Z] * mm2local) );
01742                 bu_vls_strcat( str, buf );
01743                 sprintf(buf, "\tH (%g, %g, %g)\n",
01744                         INTCLAMP(pip->part_H[X] * mm2local),
01745                         INTCLAMP(pip->part_H[Y] * mm2local),
01746                         INTCLAMP(pip->part_H[Z] * mm2local) );
01747                 bu_vls_strcat( str, buf );
01748                 sprintf(buf, "\tradius = %g\n",
01749                         INTCLAMP(pip->part_vrad * mm2local) );
01750                 bu_vls_strcat( str, buf );
01751                 break;
01752         case RT_PARTICLE_TYPE_CONE:
01753                 bu_vls_strcat( str, "conical particle\n");
01754                 sprintf(buf, "\tV (%g, %g, %g)\n",
01755                         INTCLAMP(pip->part_V[X] * mm2local),
01756                         INTCLAMP(pip->part_V[Y] * mm2local),
01757                         INTCLAMP(pip->part_V[Z] * mm2local) );
01758                 bu_vls_strcat( str, buf );
01759                 sprintf(buf, "\tH (%g, %g, %g)\n",
01760                         INTCLAMP(pip->part_H[X] * mm2local),
01761                         INTCLAMP(pip->part_H[Y] * mm2local),
01762                         INTCLAMP(pip->part_H[Z] * mm2local) );
01763                 bu_vls_strcat( str, buf );
01764                 sprintf(buf, "\tv end radius = %g\n",
01765                         INTCLAMP(pip->part_vrad * mm2local) );
01766                 bu_vls_strcat( str, buf );
01767                 sprintf(buf, "\th end radius = %g\n",
01768                         INTCLAMP(pip->part_hrad * mm2local) );
01769                 bu_vls_strcat( str, buf );
01770                 break;
01771         default:
01772                 bu_vls_strcat( str, "Unknown particle type\n");
01773                 return(-1);
01774         }
01775         return(0);
01776 }
01777 
01778 /**
01779  *                      R T _ P A R T _ I F R E E
01780  *
01781  *  Free the storage associated with the rt_db_internal version of this solid.
01782  */
01783 void
01784 rt_part_ifree(struct rt_db_internal *ip)
01785 {
01786         RT_CK_DB_INTERNAL(ip);
01787         bu_free( ip->idb_ptr, "particle ifree" );
01788         ip->idb_ptr = GENPTR_NULL;
01789 }
01790 
01791 /*
01792  * Local Variables:
01793  * mode: C
01794  * tab-width: 8
01795  * c-basic-offset: 4
01796  * indent-tabs-mode: t
01797  * End:
01798  * ex: shiftwidth=4 tabstop=8
01799  */

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