g_metaball.c

Go to the documentation of this file.
00001 /*                      G _ M E T A B A L L . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1985-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup g_  */
00023 
00024 /*@{*/
00025 /** @file g_metaball.c
00026  * Intersect a ray with a metaball implicit surface.
00027  *
00028  * NOTICE: this primitive is incomplete and should be considered
00029  *        experimental.
00030  *
00031  * Authors -
00032  *      Erik Greenwald <erikg@arl.army.mil>
00033  *
00034  * Source -
00035  *      ARL/SLAD/SDB Bldg 238
00036  *      The U. S. Army Ballistic Research Laboratory
00037  *      Aberdeen Proving Ground, Maryland  21005
00038  *
00039  */
00040 /*@}*/
00041 
00042 #ifndef lint
00043 static const char RCSmetaball[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_metaball.c,v 14.17 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00044 #endif
00045 
00046 #include "common.h"
00047 
00048 #include <stddef.h>
00049 #include <stdio.h>
00050 #ifdef HAVE_STRING_H
00051 #  include <string.h>
00052 #else
00053 #  include <strings.h>
00054 #endif
00055 #include <math.h>
00056 
00057 #include "machine.h"
00058 #include "vmath.h"
00059 #include "db.h"
00060 #include "nmg.h"
00061 #include "raytrace.h"
00062 #include "nurb.h"
00063 #include "rtgeom.h"
00064 #include "wdb.h"
00065 #include "./debug.h"
00066 
00067 
00068 /*
00069  *  Algorithm:
00070  *      completely punted at the moment :D
00071  */
00072 
00073 /* compute the bounding sphere for a metaball cluster. center is filled, and the
00074  * radius is returned. */
00075 fastf_t rt_metaball_get_bounding_sphere(point_t *center, fastf_t threshold, struct bu_list *points)
00076 {
00077         struct wdb_metaballpt *mbpt, *mbpt2;
00078         point_t min, max, d;
00079         fastf_t r = 0.0, dist, mag;
00080         int i;
00081 
00082         /* find a bounding box for the POINTS (NOT the surface) */
00083         VSETALL(min,+INFINITY);
00084         VSETALL(max,-INFINITY);
00085         for(BU_LIST_FOR(mbpt, wdb_metaballpt, points))
00086                 for(i=0;i<3;i++) {
00087                         if(mbpt->coord[i] < min[i])
00088                                 min[i] = mbpt->coord[i];
00089                         if (mbpt->coord[i] > max[i])
00090                                 max[i] = mbpt->coord[i];
00091                 }
00092 
00093         /* compute the center of the generated box, call that the center */
00094         VADD2(*center, min, max);
00095         VSCALE(*center, *center, 0.5);
00096 
00097         /* start looking for the radius... */
00098         for(BU_LIST_FOR(mbpt, wdb_metaballpt, points)){
00099                 VSUB2(d,mbpt->coord,*center);
00100                 /* since the surface is where threshold=fldstr/mag,
00101                    mag=fldstr/threshold, so make that the initial value */
00102                 dist = MAGNITUDE(d) + mbpt->fldstr/threshold;
00103                 /* and add all the contribution */
00104                 for(BU_LIST_FOR(mbpt2, wdb_metaballpt, points))
00105                         if(mbpt2 != mbpt){
00106                                 fastf_t additive;
00107                                 VSUB2(d, mbpt2->coord, mbpt->coord);
00108                                 mag = MAGNITUDE(d) + dist;
00109                                 additive = (mbpt2->fldstr*mbpt2->fldstr) / mag;
00110                                 dist += additive;
00111                         }
00112                 /* then see if this is a 'defining' point */
00113                 if(dist > r)
00114                         r = dist;
00115         }
00116         return r;
00117 }
00118 
00119 /**
00120  *                      R T _ M E T A B A L L _ P R E P
00121  *
00122  * prep and build bounding volumes... unfortunately, generating the bounding
00123  * sphere is too 'loose' (I think) and O(n^2).
00124  */
00125 int
00126 rt_metaball_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00127 {
00128         struct rt_metaball_internal *mb, *nmb;
00129         struct wdb_metaballpt *mbpt, *nmbpt;
00130         int i;
00131 
00132         mb = ip->idb_ptr;
00133         RT_METABALL_CK_MAGIC(mb);
00134 
00135         /* generate a copy of the metaball */
00136         nmb = bu_malloc(sizeof(struct rt_metaball_internal), "rt_metaball_prep: nmb");
00137         BU_LIST_INIT( &nmb->metaball_ctrl_head );
00138         nmb->threshold = mb->threshold;
00139 
00140         /* and copy the list of control points */
00141         for(BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) {
00142                 nmbpt = (struct wdb_metaballpt *)bu_malloc(sizeof(struct wdb_metaballpt), "rt_metaball_prep: nmbpt");
00143                 nmbpt->fldstr = mbpt->fldstr;
00144                 VMOVE(nmbpt->coord,mbpt->coord);
00145                 BU_LIST_INSERT( &nmb->metaball_ctrl_head, &nmbpt->l );
00146         }
00147 
00148         /* find the bounding sphere */
00149         stp->st_aradius = rt_metaball_get_bounding_sphere(&stp->st_center, mb->threshold, &mb->metaball_ctrl_head);
00150         stp->st_bradius = stp->st_aradius * 1.01;
00151         /* generate a bounding box around the sphere... 
00152          * XXX this can be optimized greatly to reduce the BSP presense... */
00153         VSET(stp->st_min, 
00154                 stp->st_center[X] - stp->st_aradius, 
00155                 stp->st_center[Y] - stp->st_aradius, 
00156                 stp->st_center[Z] - stp->st_aradius);
00157         VSET(stp->st_max, 
00158                 stp->st_center[X] + stp->st_aradius, 
00159                 stp->st_center[Y] + stp->st_aradius, 
00160                 stp->st_center[Z] + stp->st_aradius);
00161         stp->st_specific = (void *)nmb;
00162         return 0;
00163 }
00164 
00165 /**
00166  *                      R T _ M E T A B A L L _ P R I N T
00167  */
00168 void
00169 rt_metaball_print(register const struct soltab *stp)
00170 {
00171         bu_log("rt_metaball_print called\n");
00172         return;
00173 }
00174 
00175 fastf_t
00176 rt_metaball_point_value(point_t *p, struct bu_list *points)
00177 {
00178         struct wdb_metaballpt *mbpt;
00179         fastf_t ret = 0.0;
00180         point_t v;
00181 
00182         for(BU_LIST_FOR(mbpt, wdb_metaballpt, points)) {
00183                 VSUB2(v, mbpt->coord, *p);
00184                 ret += (mbpt->fldstr*mbpt->fldstr) / MAGSQ(v);  /* f^2/r^2 */
00185         }
00186         return ret;
00187 }
00188 
00189 /*
00190  *                      R T _ M E T A B A L L _ S H O T
00191  */
00192 int
00193 rt_metaball_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00194 {
00195         int stat=0, retval = 0, segsleft = abs(ap->a_onehit);
00196         struct rt_metaball_internal *mb = (struct rt_metaball_internal *)stp->st_specific;
00197         struct seg *segp = NULL;
00198         point_t p, inc, delta;
00199         fastf_t initstep = stp->st_aradius / 20.0, finalstep = stp->st_aradius / 1e8, step = initstep, distleft = (rp->r_max-rp->r_min);
00200         
00201         VJOIN1(p, rp->r_pt, rp->r_min, rp->r_dir);
00202         VSCALE(inc, rp->r_dir, step); /* assume it's normalized and we want to creep at step */
00203 
00204 /* we hit, but not as fine-grained as we want. So back up one step, cut the 
00205  * step size in half and start over... Note that once we're happily inside, we
00206  * do NOT change the step size back! */
00207 #define STEPBACK { distleft += step; VSUB2(p, p, inc); step *= .5; VSCALE(inc,inc,.5); }
00208 #define STEPIN(x) { --segsleft; ++retval; VSUB2(delta, p, rp->r_pt); segp->seg_##x.hit_dist = MAGNITUDE(delta); }
00209         while( distleft >= 0.0 ) {
00210                 distleft -= step; 
00211                 VADD2(p, p, inc);
00212                 if(stat == 1) {
00213                         if(rt_metaball_point_value(&p, &mb->metaball_ctrl_head) < mb->threshold ) {
00214                                 if(step<=finalstep) {
00215                                         STEPIN(out);
00216                                         stat = 0;
00217                                         if(segsleft <= 0) {
00218                                             return retval;
00219                                         }
00220                                 } else
00221                                         STEPBACK
00222                         }
00223                 } else {
00224                         if(rt_metaball_point_value(&p, &mb->metaball_ctrl_head) > mb->threshold ) {
00225                                 if(step<=finalstep) {
00226                                         RT_GET_SEG(segp, ap->a_resource);
00227                                         segp->seg_stp = stp;
00228                                         STEPIN(in);
00229                                         segp->seg_out.hit_dist = segp->seg_in.hit_dist + .1; /* cope with silliness */
00230                                         BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00231                                         if(segsleft <= 0){      /* exit now if we're one-hit (like visual rendering) */
00232                                                 return retval;
00233                                         }
00234                                         /* reset the ray-walk shtuff */
00235                                         stat = 1;
00236                                         VSUB2(p, p, inc); 
00237                                         VSCALE(inc, rp->r_dir, step);
00238                                         step = initstep;
00239                                 } else
00240                                         STEPBACK
00241                         }
00242                 }
00243         }
00244 #undef STEPBACK
00245 #undef STEPIN
00246         return retval;
00247 }
00248 
00249 /**
00250  *                      R T _ M E T A B A L L _ N O R M
00251  *
00252  *  Given ONE ray distance, return the normal and entry/exit point.
00253  */
00254 void
00255 rt_metaball_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00256 {
00257         struct rt_metaball_internal *mb = stp->st_specific;
00258         struct wdb_metaballpt *mbpt;
00259         vect_t v;
00260         fastf_t f, a;
00261 
00262         VSETALL(hitp->hit_normal, 0);
00263         for(BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)){
00264                 VSUB2(v, hitp->hit_point, mbpt->coord);
00265                 f = mbpt->fldstr * mbpt->fldstr;        /* f^2 */
00266                 a = MAGNITUDE(v);
00267                 f /= (a*a*a);                           /* f^2 / r^3 */
00268                 VUNITIZE(v);                                            
00269                 VJOIN1(hitp->hit_normal, hitp->hit_normal, f, v);       
00270         }
00271         VUNITIZE(hitp->hit_normal);                                     
00272         return;
00273 }
00274 
00275 /**
00276  *                      R T _ M E T A B A L L _ C U R V E
00277  *
00278  *  Return the curvature of the metaball.
00279  */
00280 void
00281 rt_metaball_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00282 {
00283         bu_log("called rt_metaball_curve!\n");
00284         return;
00285 }
00286 
00287 
00288 /**
00289  *                      R T _ M E T A B A L L _ U V
00290  *
00291  *  For a hit on the surface of an METABALL, return the (u,v) coordinates
00292  *  of the hit point, 0 <= u,v <= 1.
00293  *  u = azimuth
00294  *  v = elevation
00295  */
00296 void
00297 rt_metaball_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
00298 {
00299         bu_log("called rt_metaball_uv!\n");
00300         return;
00301 }
00302 
00303 /**
00304  *                      R T _ M E T A B A L L _ F R E E
00305  */
00306 void
00307 rt_metaball_free(register struct soltab *stp)
00308 {
00309         /* seems to be unused? */
00310         bu_log("rt_metaball_free called\n");
00311         return;
00312 }
00313 
00314 
00315 /* I have no clue what this function is supposed to do */
00316 int
00317 rt_metaball_class(void)
00318 {
00319         return RT_CLASSIFY_UNIMPLEMENTED;       /* "assume the worst" */
00320 }
00321 
00322 
00323 void
00324 rt_metaball_plot_sph(struct bu_list *vhead, point_t *center, fastf_t radius)
00325 {
00326         fastf_t top[16*3], middle[16*3], bottom[16*3];
00327         point_t a, b, c;
00328         int i;
00329 
00330         VSET(a, radius, 0, 0);
00331         VSET(b, 0, radius, 0);
00332         VSET(c, 0, 0, radius);
00333         rt_ell_16pts( top, *center, a, b );
00334         rt_ell_16pts( bottom, *center, b, c );
00335         rt_ell_16pts( middle, *center, a, c );
00336 
00337         RT_ADD_VLIST( vhead, &top[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00338         for( i=0; i<16; i++ ) RT_ADD_VLIST( vhead, &top[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00339         RT_ADD_VLIST( vhead, &bottom[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00340         for( i=0; i<16; i++ ) RT_ADD_VLIST( vhead, &bottom[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00341         RT_ADD_VLIST( vhead, &middle[15*ELEMENTS_PER_VECT], BN_VLIST_LINE_MOVE );
00342         for( i=0; i<16; i++ ) RT_ADD_VLIST( vhead, &middle[i*ELEMENTS_PER_VECT], BN_VLIST_LINE_DRAW );
00343 }
00344 
00345 /**
00346  *                      R T _ M E T A B A L L _ P L O T
00347  */
00348 int
00349 rt_metaball_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00350 {
00351         struct rt_metaball_internal *mb;
00352         struct wdb_metaballpt *mbpt;
00353         point_t bsc;
00354         fastf_t rad;
00355 
00356         RT_CK_DB_INTERNAL(ip);
00357         mb = (struct rt_metaball_internal *)ip->idb_ptr;
00358         RT_METABALL_CK_MAGIC(mb);
00359         rad = rt_metaball_get_bounding_sphere(&bsc, mb->threshold, &mb->metaball_ctrl_head);
00360         rt_metaball_plot_sph(vhead, &bsc, rad);
00361         for(BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head))
00362                 rt_metaball_plot_sph(vhead, &mbpt->coord, mbpt->fldstr);
00363         return 0;
00364 }
00365 
00366 /**
00367  *                      R T _ M E T A B A L L _ T E S S
00368  *
00369  *  Tessellate a metaball.
00370  */
00371 int
00372 rt_metaball_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
00373 {
00374         bu_log("rt_metaball_tess called!\n");
00375         return -1;
00376 }
00377 
00378 /**
00379  *                      R T _ M E T A B A L L _ I M P O R T 5
00380  *
00381  *  Import an metaball/sphere from the database format to the internal 
00382  *  structure. Apply modeling transformations as well.
00383  */
00384 int
00385 rt_metaball_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
00386 {
00387         struct wdb_metaballpt *mbpt;
00388         struct rt_metaball_internal *mb;
00389         fastf_t *buf;
00390         int metaball_count = 0, i;
00391 
00392         BU_CK_EXTERNAL( ep );
00393         metaball_count = bu_glong((unsigned char *)ep->ext_buf);
00394         buf = (fastf_t *)bu_malloc((metaball_count*4+2)*SIZEOF_NETWORK_DOUBLE,"rt_metaball_import5: buf");
00395         ntohd((unsigned char *)buf, (unsigned char *)ep->ext_buf+SIZEOF_NETWORK_LONG, metaball_count*4+2);
00396 
00397         RT_CK_DB_INTERNAL( ip );
00398         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
00399         ip->idb_type = ID_METABALL;
00400         ip->idb_meth = &rt_functab[ID_METABALL];
00401         ip->idb_ptr = bu_malloc( sizeof(struct rt_metaball_internal), "rt_metaball_internal");
00402         mb = (struct rt_metaball_internal *)ip->idb_ptr;
00403         mb->magic = RT_METABALL_INTERNAL_MAGIC;
00404         mb->threshold = buf[0];
00405         mb->method = ((long *)buf)[1];
00406         BU_LIST_INIT( &mb->metaball_ctrl_head );
00407         for(i=2 ; i<=metaball_count*4 ; i+=4) {
00408                         /* Apply modeling transformations */
00409                 BU_GETSTRUCT( mbpt, wdb_metaballpt );
00410                 mbpt->l.magic = WDB_METABALLPT_MAGIC;
00411                 MAT4X3PNT( mbpt->coord, mat, &buf[i] );
00412                 mbpt->fldstr = buf[i+3] / mat[15];
00413                 BU_LIST_INSERT( &mb->metaball_ctrl_head, &mbpt->l );
00414         }
00415 
00416         bu_free((genptr_t)buf, "rt_metaball_import5: buf");
00417         return 0;               /* OK */
00418 }
00419 
00420 /**
00421  *                      R T _ M E T A B A L L _ E X P O R T 5
00422  *
00423  * storage is something like
00424  * long         numpoints
00425  * fastf_t      threshold
00426  * long         method
00427  *      fastf_t X1      (start point)
00428  *      fastf_t Y1
00429  *      fastf_t Z1
00430  *      fastf_t fldstr1 (end point)
00431  *      fastf_t X2      (start point)
00432  *      ...
00433  */
00434 int
00435 rt_metaball_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
00436 {
00437         struct rt_metaball_internal *mb;
00438         struct wdb_metaballpt *mbpt;
00439         int metaball_count = 0, i = 2;
00440         fastf_t *buf;
00441 
00442         RT_CK_DB_INTERNAL(ip);
00443         if( ip->idb_type != ID_METABALL )  
00444                 return(-1);
00445         mb = (struct rt_metaball_internal *)ip->idb_ptr;
00446         RT_METABALL_CK_MAGIC(mb);
00447         if (mb->metaball_ctrl_head.magic == 0) return -1;
00448 
00449         /* Count number of points */
00450         for(BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) metaball_count++;
00451 
00452         BU_CK_EXTERNAL(ep);
00453         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE*(1+4*metaball_count) + 2*SIZEOF_NETWORK_LONG;
00454         ep->ext_buf = (genptr_t)bu_malloc(ep->ext_nbytes, "metaball external");
00455         bu_plong((unsigned char *)ep->ext_buf, metaball_count);
00456 
00457         /* pack the point data */
00458         buf = (fastf_t *)bu_malloc((metaball_count*4+1)*SIZEOF_NETWORK_DOUBLE,"rt_metaball_export5: buf");
00459         buf[0] = mb->threshold;
00460         ((long *)buf)[1] = mb->method;
00461         for(BU_LIST_FOR( mbpt, wdb_metaballpt, &mb->metaball_ctrl_head), i+=4){
00462                 VSCALE(&buf[i], mbpt->coord, local2mm);
00463                 buf[i+3] = mbpt->fldstr * local2mm;
00464         }
00465         htond((unsigned char *)ep->ext_buf + SIZEOF_NETWORK_LONG, (unsigned char *)buf, 2 + 4 * metaball_count);
00466         bu_free(buf,"rt_metaball_export5: buf");
00467 
00468         return 0;
00469 }
00470 
00471 /**
00472  *                      R T _ M E T A B A L L _ D E S C R I B E
00473  *
00474  *  Make human-readable formatted presentation of this solid. First line 
00475  *  describes type of solid. Additional lines are indented one tab, and give 
00476  *  parameter values.
00477  */
00478 int
00479 rt_metaball_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
00480 {
00481         int metaball_count = 0;
00482         char buf[BUFSIZ];
00483         struct rt_metaball_internal *mb;
00484         struct wdb_metaballpt *mbpt;
00485 
00486         RT_CK_DB_INTERNAL(ip);
00487         mb = (struct rt_metaball_internal *)ip->idb_ptr;
00488         RT_METABALL_CK_MAGIC(mb);
00489         for(BU_LIST_FOR(mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)) metaball_count++;
00490 
00491         snprintf(buf, BUFSIZ, "Metaball with %d points and a threshold of %g\n", metaball_count, mb->threshold);
00492         bu_vls_strcat(str,buf);
00493         if(!verbose)return 0;
00494         metaball_count=0;
00495         for( BU_LIST_FOR( mbpt, wdb_metaballpt, &mb->metaball_ctrl_head)){
00496                 snprintf(buf,BUFSIZ,"\t%d: %g field strength at (%g, %g, %g)\n",
00497                         ++metaball_count, mbpt->fldstr, V3ARGS(mbpt->coord));
00498                 bu_vls_strcat(str,buf);
00499         }
00500         return 0;
00501 }
00502 
00503 /**
00504  *                      R T _ M E T A B A L L _ I F R E E
00505  *
00506  *  Free the storage associated with the rt_db_internal version of this solid.
00507  *  This only effects the in-memory copy.
00508  */
00509 void
00510 rt_metaball_ifree(struct rt_db_internal *ip)
00511 {
00512         register struct rt_metaball_internal    *metaball;
00513         register struct wdb_metaballpt  *mbpt;
00514 
00515         RT_CK_DB_INTERNAL(ip);
00516         metaball = (struct rt_metaball_internal*)ip->idb_ptr;
00517         RT_METABALL_CK_MAGIC(metaball);
00518 
00519         if (metaball->metaball_ctrl_head.magic != 0)
00520                 while(BU_LIST_WHILE(mbpt, wdb_metaballpt, &metaball->metaball_ctrl_head)) {
00521                         BU_LIST_DEQUEUE(&(mbpt->l));
00522                         bu_free((char *)mbpt, "wdb_metaballpt");
00523                 }
00524         bu_free( ip->idb_ptr, "metaball ifree" );
00525         ip->idb_ptr = GENPTR_NULL;
00526 }
00527 
00528 /**
00529  *                      R T _ M E T A B A L L _ T N U R B
00530  */
00531 int
00532 rt_metaball_tnurb(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct bn_tol *tol)
00533 {
00534         bu_log("rt_metaball_tnurb called!\n");
00535         return 0;
00536 }
00537 
00538 /*
00539  * Local Variables:
00540  * mode: C
00541  * tab-width: 8
00542  * c-basic-offset: 4
00543  * indent-tabs-mode: t
00544  * End:
00545  * ex: shiftwidth=4 tabstop=8
00546  */

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