g_sph.c

Go to the documentation of this file.
00001 /*                         G _ S P H . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1985-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup g_ */
00023 
00024 /*@{*/
00025 /** @file g_sph.c
00026  *      Intersect a ray with a Sphere.
00027  *      Special case of the Generalized Ellipsoid
00028  *
00029  *  Authors -
00030  *      Phillip Dykstra
00031  *      Dave Becker             (Vectorization)
00032  *
00033  *  Source -
00034  *      SECAD/VLD Computing Consortium, Bldg 394
00035  *      The U. S. Army Ballistic Research Laboratory
00036  *      Aberdeen Proving Ground, Maryland  21005
00037  *
00038  */
00039 /*@}*/
00040 #ifndef lint
00041 static const char RCSsph[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_sph.c,v 14.10 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00042 #endif
00043 
00044 #include "common.h"
00045 
00046 
00047 
00048 #include <stdio.h>
00049 #ifdef HAVE_STRING_H
00050 #include <string.h>
00051 #endif
00052 #include <math.h>
00053 #include "machine.h"
00054 #include "vmath.h"
00055 #include "raytrace.h"
00056 #include "rtgeom.h"
00057 #include "./debug.h"
00058 
00059 /*
00060  *  Algorithm:
00061  *
00062  *  Given V, A, where |A| = Radius, there is a set of points on this sphere
00063  *
00064  *  { (x,y,z) | (x,y,z) is on sphere defined by V, A }
00065  *
00066  *  To find the intersection of a line with the sphere, consider
00067  *  the parametric line L:
00068  *
00069  *      L : { P(n) | P + t(n) . D }
00070  *
00071  *  Call W the actual point of intersection between L and the sphere.
00072  *
00073  *  NORMALS.  Given the point W on the sphere, what is the vector
00074  *  normal to the tangent plane at that point?
00075  *
00076  *  N = (W - V) / |A|
00077  */
00078 
00079 struct sph_specific {
00080         vect_t  sph_V;          /* Vector to center of sphere */
00081         fastf_t sph_radsq;      /* Radius squared */
00082         fastf_t sph_invrad;     /* Inverse radius (for normal) */
00083         fastf_t sph_rad;        /* Radius */
00084         mat_t   sph_SoR;        /* Rotate and scale for UV mapping */
00085 };
00086 
00087 /**
00088  *                      R T _ S P H _ P R E P
00089  *
00090  *  Given a pointer to a GED database record, and a transformation matrix,
00091  *  determine if this is a valid sphere, and if so, precompute various
00092  *  terms of the formula.
00093  *
00094  *  Returns -
00095  *      0       SPH is OK
00096  *      !0      Error in description
00097  *
00098  *  Implicit return -
00099  *      A struct sph_specific is created, and it's address is stored in
00100  *      stp->st_specific for use by rt_sph_shot().
00101  *      If the ELL is really a SPH, stp->st_id is modified to ID_SPH.
00102  */
00103 int
00104 rt_sph_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00105 {
00106         register struct sph_specific *sph;
00107         LOCAL fastf_t   magsq_a, magsq_b, magsq_c;
00108         LOCAL vect_t    Au, Bu, Cu;     /* A,B,C with unit length */
00109         LOCAL fastf_t   f;
00110         struct rt_ell_internal  *eip;
00111 
00112         eip = (struct rt_ell_internal *)ip->idb_ptr;
00113         RT_ELL_CK_MAGIC(eip);
00114 
00115         /* Validate that |A| > 0, |B| > 0, |C| > 0 */
00116         magsq_a = MAGSQ( eip->a );
00117         magsq_b = MAGSQ( eip->b );
00118         magsq_c = MAGSQ( eip->c );
00119         if( magsq_a < rtip->rti_tol.dist || magsq_b < rtip->rti_tol.dist || magsq_c < rtip->rti_tol.dist ) {
00120                 bu_log("sph(%s):  zero length A(%g), B(%g), or C(%g) vector\n",
00121                         stp->st_name, magsq_a, magsq_b, magsq_c );
00122                 return(1);              /* BAD */
00123         }
00124 
00125         /* Validate that |A|, |B|, and |C| are nearly equal */
00126         if( fabs(magsq_a - magsq_b) > 0.0001
00127             || fabs(magsq_a - magsq_c) > 0.0001 ) {
00128 #if 0
00129                 /* Ordinarily, don't say anything here, will handle as ELL */
00130                 bu_log("sph(%s):  non-equal length A, B, C vectors\n",
00131                         stp->st_name );
00132 #endif
00133                 return(1);              /* ELL, not SPH */
00134         }
00135 
00136         /* Create unit length versions of A,B,C */
00137         f = 1.0/sqrt(magsq_a);
00138         VSCALE( Au, eip->a, f );
00139         f = 1.0/sqrt(magsq_b);
00140         VSCALE( Bu, eip->b, f );
00141         f = 1.0/sqrt(magsq_c);
00142         VSCALE( Cu, eip->c, f );
00143 
00144         /* Validate that A.B == 0, B.C == 0, A.C == 0 (check dir only) */
00145         f = VDOT( Au, Bu );
00146         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00147                 bu_log("sph(%s):  A not perpendicular to B, f=%f\n",stp->st_name, f);
00148                 return(1);              /* BAD */
00149         }
00150         f = VDOT( Bu, Cu );
00151         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00152                 bu_log("sph(%s):  B not perpendicular to C, f=%f\n",stp->st_name, f);
00153                 return(1);              /* BAD */
00154         }
00155         f = VDOT( Au, Cu );
00156         if( ! NEAR_ZERO(f, rtip->rti_tol.dist) )  {
00157                 bu_log("sph(%s):  A not perpendicular to C, f=%f\n",stp->st_name, f);
00158                 return(1);              /* BAD */
00159         }
00160 
00161         /*
00162          *  This ELL is really an SPH
00163          */
00164         stp->st_id = ID_SPH;            /* "fix" soltab ID */
00165         stp->st_meth = &rt_functab[ID_SPH];
00166 
00167         /* Solid is OK, compute constant terms now */
00168         BU_GETSTRUCT( sph, sph_specific );
00169         stp->st_specific = (genptr_t)sph;
00170 
00171         VMOVE( sph->sph_V, eip->v );
00172 
00173         sph->sph_radsq = magsq_a;
00174         sph->sph_rad = sqrt(sph->sph_radsq);
00175         sph->sph_invrad = 1.0 / sph->sph_rad;
00176 
00177         /*
00178          * Save the matrix which rotates our ABC to world
00179          * XYZ respectively, and scales points on surface
00180          * to unit length.  Used here in UV mapping.
00181          * See ell.c for details.
00182          */
00183         MAT_IDN( sph->sph_SoR );
00184         VSCALE( &sph->sph_SoR[0], eip->a, 1.0/magsq_a );
00185         VSCALE( &sph->sph_SoR[4], eip->b, 1.0/magsq_b );
00186         VSCALE( &sph->sph_SoR[8], eip->c, 1.0/magsq_c );
00187 
00188         /* Compute bounding sphere */
00189         VMOVE( stp->st_center, sph->sph_V );
00190         stp->st_aradius = stp->st_bradius = sph->sph_rad;
00191 
00192         /* Compute bounding RPP */
00193         stp->st_min[X] = sph->sph_V[X] - sph->sph_rad;
00194         stp->st_max[X] = sph->sph_V[X] + sph->sph_rad;
00195         stp->st_min[Y] = sph->sph_V[Y] - sph->sph_rad;
00196         stp->st_max[Y] = sph->sph_V[Y] + sph->sph_rad;
00197         stp->st_min[Z] = sph->sph_V[Z] - sph->sph_rad;
00198         stp->st_max[Z] = sph->sph_V[Z] + sph->sph_rad;
00199 
00200         return(0);                      /* OK */
00201 }
00202 
00203 /**
00204  *                      R T _ S P H _ P R I N T
00205  */
00206 void
00207 rt_sph_print(register const struct soltab *stp)
00208 {
00209         register const struct sph_specific *sph =
00210                 (struct sph_specific *)stp->st_specific;
00211 
00212         VPRINT("V", sph->sph_V);
00213         bu_log("Rad %g\n", sph->sph_rad);
00214         bu_log("Radsq %g\n", sph->sph_radsq);
00215         bu_log("Invrad %g\n", sph->sph_invrad);
00216         bn_mat_print("S o R", sph->sph_SoR );
00217 }
00218 
00219 /**
00220  *                      R T _ S P H _ S H O T
00221  *
00222  *  Intersect a ray with a sphere.
00223  *  If an intersection occurs, a struct seg will be acquired
00224  *  and filled in.
00225  *  Notes: In the quadratic equation,
00226  *   A is MAGSQ(r_dir) which is always equal to 1, so it does not appear.
00227  *   The sign of B is reversed (vector is reversed) to save negation.
00228  *   We have factored out the 2 and 4 constants.
00229  *  Claim: The straight quadratic formula leads to precision problems
00230  *   if either A or C are small.  In our case A is always 1.  C is a
00231  *   radial distance of the ray origin from the sphere surface.  Thus
00232  *   if we are shooting from near the surface we may have problems.
00233  *   XXX - investigate this.
00234  *
00235  *  Returns -
00236  *      0       MISS
00237  *      >0      HIT
00238  */
00239 int
00240 rt_sph_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00241 {
00242         register struct sph_specific *sph =
00243                 (struct sph_specific *)stp->st_specific;
00244         register struct seg *segp;
00245         LOCAL vect_t    ov;             /* ray orgin to center (V - P) */
00246         FAST fastf_t    magsq_ov;       /* length squared of ov */
00247         FAST fastf_t    b;              /* second term of quadratic eqn */
00248         FAST fastf_t    root;           /* root of radical */
00249 
00250         VSUB2( ov, sph->sph_V, rp->r_pt );
00251         b = VDOT( rp->r_dir, ov );
00252         magsq_ov = MAGSQ(ov);
00253 
00254         if( magsq_ov >= sph->sph_radsq ) {
00255                 /* ray origin is outside of sphere */
00256                 if( b < 0 ) {
00257                         /* ray direction is away from sphere */
00258                         return(0);              /* No hit */
00259                 }
00260                 root = b*b - magsq_ov + sph->sph_radsq;
00261                 if( root <= 0 ) {
00262                         /* no real roots */
00263                         return(0);              /* No hit */
00264                 }
00265         } else {
00266                 root = b*b - magsq_ov + sph->sph_radsq;
00267         }
00268         root = sqrt(root);
00269 
00270         RT_GET_SEG(segp, ap->a_resource);
00271         segp->seg_stp = stp;
00272 
00273         /* we know root is positive, so we know the smaller t */
00274         segp->seg_in.hit_dist = b - root;
00275         segp->seg_out.hit_dist = b + root;
00276         BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00277         return(2);                      /* HIT */
00278 }
00279 
00280 #define SEG_MISS(SEG)           (SEG).seg_stp=(struct soltab *) 0;
00281 /**
00282  *                      R T _ S P H _ V S H O T
00283  *
00284  *  This is the Becker vectorized version
00285  */
00286 void
00287 rt_sph_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00288                                /* An array of solid pointers */
00289                                /* An array of ray pointers */
00290                                /* array of segs (results returned) */
00291                                /* Number of ray/object pairs */
00292 
00293 {
00294         register struct sph_specific *sph;
00295         LOCAL vect_t    ov;             /* ray orgin to center (V - P) */
00296         FAST fastf_t    magsq_ov;       /* length squared of ov */
00297         FAST fastf_t    b;              /* second term of quadratic eqn */
00298         FAST fastf_t    root;           /* root of radical */
00299         register int    i;
00300 
00301         /* for each ray/sphere pair */
00302 #       include "noalias.h"
00303         for(i = 0; i < n; i++){
00304 #if !CRAY       /* XXX currently prevents vectorization on cray */
00305                 if (stp[i] == 0) continue; /* stp[i] == 0 signals skip ray */
00306 #endif
00307 
00308                 sph = (struct sph_specific *)stp[i]->st_specific;
00309                 VSUB2( ov, sph->sph_V, rp[i]->r_pt );
00310                 b = VDOT( rp[i]->r_dir, ov );
00311                 magsq_ov = MAGSQ(ov);
00312 
00313                 if( magsq_ov >= sph->sph_radsq ) {
00314                         /* ray origin is outside of sphere */
00315                         if( b < 0 ) {
00316                                 /* ray direction is away from sphere */
00317                                 SEG_MISS(segp[i]);              /* No hit */
00318                                 continue;
00319                         }
00320                         root = b*b - magsq_ov + sph->sph_radsq;
00321                         if( root <= 0 ) {
00322                                 /* no real roots */
00323                                 SEG_MISS(segp[i]);              /* No hit */
00324                                 continue;
00325                         }
00326                 } else {
00327                         root = b*b - magsq_ov + sph->sph_radsq;
00328                 }
00329                 root = sqrt(root);
00330 
00331                 segp[i].seg_stp = stp[i];
00332 
00333                 /* we know root is positive, so we know the smaller t */
00334                 segp[i].seg_in.hit_dist = b - root;
00335                 segp[i].seg_out.hit_dist = b + root;
00336         }
00337 }
00338 
00339 /**
00340  *                      R T _ S P H _ N O R M
00341  *
00342  *  Given ONE ray distance, return the normal and entry/exit point.
00343  */
00344 void
00345 rt_sph_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00346 {
00347         register struct sph_specific *sph =
00348                 (struct sph_specific *)stp->st_specific;
00349 
00350         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00351         VSUB2( hitp->hit_normal, hitp->hit_point, sph->sph_V );
00352         VSCALE( hitp->hit_normal, hitp->hit_normal, sph->sph_invrad );
00353 }
00354 
00355 /**
00356  *                      R T _ S P H _ C U R V E
00357  *
00358  *  Return the curvature of the sphere.
00359  */
00360 void
00361 rt_sph_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00362 {
00363         register struct sph_specific *sph =
00364                 (struct sph_specific *)stp->st_specific;
00365 
00366         cvp->crv_c1 = cvp->crv_c2 = - sph->sph_invrad;
00367 
00368         /* any tangent direction */
00369         bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
00370 }
00371 
00372 /**
00373  *                      R T _ S P H _ U V
00374  *
00375  *  For a hit on the surface of an SPH, return the (u,v) coordinates
00376  *  of the hit point, 0 <= u,v <= 1.
00377  *  u = azimuth
00378  *  v = elevation
00379  */
00380 void
00381 rt_sph_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00382 {
00383         register struct sph_specific *sph =
00384                 (struct sph_specific *)stp->st_specific;
00385         LOCAL fastf_t r;
00386         LOCAL vect_t work;
00387         LOCAL vect_t pprime;
00388 
00389         /* hit_point is on surface;  project back to unit sphere,
00390          * creating a vector from vertex to hit point which always
00391          * has length=1.0
00392          */
00393         VSUB2( work, hitp->hit_point, sph->sph_V );
00394         MAT4X3VEC( pprime, sph->sph_SoR, work );
00395         /* Assert that pprime has unit length */
00396 
00397         /* U is azimuth, atan() range: -pi to +pi */
00398         uvp->uv_u = bn_atan2( pprime[Y], pprime[X] ) * bn_inv2pi;
00399         if( uvp->uv_u < 0 )
00400                 uvp->uv_u += 1.0;
00401         /*
00402          *  V is elevation, atan() range: -pi/2 to +pi/2,
00403          *  because sqrt() ensures that X parameter is always >0
00404          */
00405         uvp->uv_v = bn_atan2( pprime[Z],
00406                 sqrt( pprime[X] * pprime[X] + pprime[Y] * pprime[Y]) ) *
00407                 bn_invpi + 0.5;
00408 
00409         /* approximation: r / (circumference, 2 * pi * aradius) */
00410         r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
00411         uvp->uv_du = uvp->uv_dv =
00412                 bn_inv2pi * r / stp->st_aradius;
00413 }
00414 
00415 /**
00416  *              R T _ S P H _ F R E E
00417  */
00418 void
00419 rt_sph_free(register struct soltab *stp)
00420 {
00421         register struct sph_specific *sph =
00422                 (struct sph_specific *)stp->st_specific;
00423 
00424         bu_free( (char *)sph, "sph_specific" );
00425 }
00426 
00427 int
00428 rt_sph_class(void)
00429 {
00430         return(0);
00431 }
00432 
00433 /* ELL versions of the plot and tess functions are used */
00434 
00435 
00436 #if 0
00437 /**
00438  *                      R T _ S P H _ I M P O R T 5
00439  *
00440  *  Import a sphere from the v5 database format to
00441  *  the internal structure.
00442  *  Apply modeling transformations as well.
00443  */
00444 int
00445 rt_sph_import5( ip, ep, mat, dbip )
00446 struct rt_db_internal           *ip;
00447 const struct bu_external        *ep;
00448 register const mat_t            mat;
00449 const struct db_i               *dbip;
00450 {
00451         struct rt_sph_internal  *sip;
00452         LOCAL fastf_t           vec[3+1];
00453 
00454         BU_CK_EXTERNAL( ep );
00455 
00456         RT_CK_DB_INTERNAL( ip );
00457         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
00458         ip->idb_type = ID_SPH;
00459         ip->idb_meth = &rt_functab[ID_SPH];
00460         ip->idb_ptr = bu_malloc( sizeof(struct rt_sph_internal), "rt_sph_internal");
00461 
00462         sip = (struct rt_sph_internal *)ip->idb_ptr;
00463         sip->magic = RT_SPH_INTERNAL_MAGIC;
00464 
00465         /* Convert from database to internal format */
00466         htond( vec, ep->ext_buf, 3+1 );
00467 
00468         /* Apply modeling transformations */
00469         MAT4X3PNT( sip->v, mat, &vec[0*3] );
00470         MAT4XSCALOR( sip->r, mat, vec[1*3] );
00471 
00472         return(0);              /* OK */
00473 }
00474 
00475 /**
00476  *                      R T _ S P H _ E X P O R T 5
00477  */
00478 int
00479 rt_sph_export5( ep, ip, local2mm, dbip )
00480 struct bu_external              *ep;
00481 const struct rt_db_internal     *ip;
00482 double                          local2mm;
00483 const struct db_i               *dbip;
00484 {
00485         struct rt_sph_internal  *tip;
00486         union record            *rec;
00487 
00488         RT_CK_DB_INTERNAL(ip);
00489         if( ip->idb_type != ID_ELL )  return(-1);
00490         tip = (struct rt_sph_internal *)ip->idb_ptr;
00491         RT_ELL_CK_MAGIC(tip);
00492 
00493         BU_CK_EXTERNAL(ep);
00494         ep->ext_nbytes = sizeof(union record);
00495         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "sph external");
00496         rec = (union record *)ep->ext_buf;
00497 
00498         rec->s.s_id = ID_SOLID;
00499         rec->s.s_type = GENELL;
00500 
00501         /* NOTE: This also converts to dbfloat_t */
00502         VSCALE( &rec->s.s_values[0], tip->v, local2mm );
00503         VSCALE( &rec->s.s_values[3], tip->a, local2mm );
00504         VSCALE( &rec->s.s_values[6], tip->b, local2mm );
00505         VSCALE( &rec->s.s_values[9], tip->c, local2mm );
00506 
00507         return(0);
00508 }
00509 #endif
00510 
00511 /*
00512  * Local Variables:
00513  * mode: C
00514  * tab-width: 8
00515  * c-basic-offset: 4
00516  * indent-tabs-mode: t
00517  * End:
00518  * ex: shiftwidth=4 tabstop=8
00519  */

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