g_hf.c

Go to the documentation of this file.
00001 /*                          G _ H F . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1994-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_hf.c
00026  *      Intersect a ray with a height field,
00027  *      where the heights are imported from an external data file,
00028  *      and where some (or all) of the parameters of that data file
00029  *      may be read in from an external control file.
00030  *
00031  *  Authors -
00032  *      Michael John Muuss
00033  *      (Christopher T. Johnson, GSI)
00034  *
00035  *  Source -
00036  *      The U. S. Army Research Laboratory
00037  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00038  */
00039 
00040 #ifndef lint
00041 static char rt_hf_RcSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_hf.c,v 14.14 2006/09/16 02:04:24 lbutler Exp $ (ARL)";
00042 #endif
00043 
00044 #include "common.h"
00045 
00046 #include <stdlib.h>
00047 #include <stddef.h>
00048 #include <stdio.h>
00049 #include <math.h>
00050 #include <fcntl.h>
00051 #ifdef HAVE_STRING_H
00052 #  include <string.h>
00053 #else
00054 #  include <strings.h>
00055 #endif
00056 
00057 #include "machine.h"
00058 #include "vmath.h"
00059 #include "db.h"
00060 #include "nmg.h"
00061 #include "raytrace.h"
00062 #include "rtgeom.h"
00063 #include "./debug.h"
00064 
00065 
00066 /*
00067  *  Description of the external string description of the HF.
00068  *
00069  *  There are two versions of this parse table.
00070  *  The string solid in the .g file can set any parameter.
00071  *  The indirect control file (cfile) can only set parameters
00072  *  relating to dfile parameters, and not to the geometric
00073  *  position, orientation, and scale of the HF's bounding RPP.
00074  *
00075  *  In general, the cfile should be thought of as describing
00076  *  the data arrangement of the dfile, and the string solid should
00077  *  be thought of as describing the "geometry" of the height
00078  *  field's bounding RPP.
00079  *
00080  *  The string solid is parsed first.  If a cfile is present, it is
00081  *  parsed second, and any parameters specified in the cfile override
00082  *  the values taken from the string solid.
00083  */
00084 #define HF_O(m)                 bu_offsetof(struct rt_hf_internal, m)
00085 
00086 /* All fields valid in string solid */
00087 const struct bu_structparse rt_hf_parse[] = {
00088         {"%s",  128,    "cfile",        bu_offsetofarray(struct rt_hf_internal, cfile), BU_STRUCTPARSE_FUNC_NULL},
00089         {"%s",  128,    "dfile",        bu_offsetofarray(struct rt_hf_internal, dfile), BU_STRUCTPARSE_FUNC_NULL},
00090         {"%s",  8,      "fmt",          bu_offsetofarray(struct rt_hf_internal, fmt), BU_STRUCTPARSE_FUNC_NULL},
00091         {"%d",  1,      "w",            HF_O(w),                BU_STRUCTPARSE_FUNC_NULL },
00092         {"%d",  1,      "n",            HF_O(n),                BU_STRUCTPARSE_FUNC_NULL },
00093         {"%d",  1,      "shorts",       HF_O(shorts),           BU_STRUCTPARSE_FUNC_NULL },
00094         {"%f",  1,      "file2mm",      HF_O(file2mm),          BU_STRUCTPARSE_FUNC_NULL },
00095         {"%f",  3,      "v",            HF_O(v[0]),             BU_STRUCTPARSE_FUNC_NULL },
00096         {"%f",  3,      "x",            HF_O(x[0]),             BU_STRUCTPARSE_FUNC_NULL },
00097         {"%f",  3,      "y",            HF_O(y[0]),             BU_STRUCTPARSE_FUNC_NULL },
00098         {"%f",  1,      "xlen",         HF_O(xlen),             BU_STRUCTPARSE_FUNC_NULL },
00099         {"%f",  1,      "ylen",         HF_O(ylen),             BU_STRUCTPARSE_FUNC_NULL },
00100         {"%f",  1,      "zscale",       HF_O(zscale),           BU_STRUCTPARSE_FUNC_NULL },
00101         {"",    0,      (char *)0,      0,                      BU_STRUCTPARSE_FUNC_NULL }
00102 };
00103 /* Subset of fields found in cfile */
00104 const struct bu_structparse rt_hf_cparse[] = {
00105         {"%s",  128,    "dfile",        bu_offsetofarray(struct rt_hf_internal, dfile), BU_STRUCTPARSE_FUNC_NULL},
00106         {"%s",  8,      "fmt",          bu_offsetofarray(struct rt_hf_internal, fmt), BU_STRUCTPARSE_FUNC_NULL},
00107         {"%d",  1,      "w",            HF_O(w),                BU_STRUCTPARSE_FUNC_NULL },
00108         {"%d",  1,      "n",            HF_O(n),                BU_STRUCTPARSE_FUNC_NULL },
00109         {"%d",  1,      "shorts",       HF_O(shorts),           BU_STRUCTPARSE_FUNC_NULL },
00110         {"%f",  1,      "file2mm",      HF_O(file2mm),          BU_STRUCTPARSE_FUNC_NULL },
00111         {"",    0,      (char *)0,      0,                      BU_STRUCTPARSE_FUNC_NULL }
00112 };
00113 
00114 struct hf_specific {
00115         vect_t  hf_V;           /* min vertex/origin of HF */
00116         vect_t  hf_VO;          /* max vertex of HF */
00117         vect_t  hf_X;           /* X direction vector */
00118         fastf_t hf_Xlen;        /* magnitude of HF in X direction */
00119         vect_t  hf_Y;           /* Y Direction vector */
00120         fastf_t hf_Ylen;        /* magnitude of HF in Y direction */
00121         vect_t  hf_N;           /* dir of elevation */
00122         fastf_t hf_min;         /* bounding box of hf solid */
00123         fastf_t hf_max;
00124         fastf_t hf_file2mm;     /* scale file elevation units to mm */
00125         int     hf_w;           /* X dimension of file */
00126         int     hf_n;           /* Y dimension of file */
00127         int     hf_shorts;      /* Boolean: use shorts instead of double */
00128         struct bu_mapped_file *hf_mp;
00129 };
00130 
00131 /**
00132  *                      R T _ H F _ T O _ D S P
00133  *
00134  *      Convert in-memory form of a height-field (HF) to a displacement-map
00135  *      solid (DSP) in internal representation.
00136  *      There is no record in the V5 database for an HF.
00137  */
00138 int
00139 rt_hf_to_dsp(struct rt_db_internal *db_intern, struct resource *resp)
00140 {
00141         struct rt_hf_internal   *hip = (struct rt_hf_internal *)db_intern->idb_ptr;
00142         struct rt_dsp_internal  *dsp;
00143         vect_t                  tmp;
00144 
00145         RT_CK_DB_INTERNAL(db_intern);
00146         RT_CK_RESOURCE(resp);
00147         RT_HF_CK_MAGIC( hip );
00148 
00149         if (! hip->shorts) {
00150                 bu_log("cannot convert float HF to DSP\n");
00151                 return -1;
00152         }
00153 
00154         BU_GETSTRUCT( dsp, rt_dsp_internal );
00155         bu_vls_init( &dsp->dsp_name );
00156         bu_vls_strcat( &dsp->dsp_name, hip->dfile );
00157         dsp->dsp_xcnt = hip->w;
00158         dsp->dsp_ycnt = hip->n;
00159         dsp->dsp_smooth = 0;
00160         dsp->dsp_cuttype = DSP_CUT_DIR_ADAPT;
00161         if (RT_G_DEBUG & DEBUG_HF) {
00162             bu_log("Converting from cut-style lower-left/upper-right to adaptive\n");
00163         }
00164         dsp->dsp_datasrc = RT_DSP_SRC_FILE;
00165 
00166 
00167         MAT_IDN(dsp->dsp_stom);
00168         MAT_DELTAS_VEC(dsp->dsp_stom, hip->v);  /* translate */
00169 
00170         /* Apply modeling transformations */
00171         VUNITIZE( hip->x );
00172         VSCALE( tmp, hip->x, hip->xlen/(fastf_t)(hip->w - 1) );
00173         dsp->dsp_stom[0] = tmp[0];
00174         dsp->dsp_stom[4] = tmp[1];
00175         dsp->dsp_stom[8] = tmp[2];
00176         VUNITIZE( hip->y );
00177         VSCALE( tmp, hip->y, hip->ylen/(fastf_t)(hip->n - 1) );
00178         dsp->dsp_stom[1] = tmp[0];
00179         dsp->dsp_stom[5] = tmp[1];
00180         dsp->dsp_stom[9] = tmp[2];
00181         VCROSS( tmp, hip->x, hip->y );
00182         VUNITIZE( tmp );
00183 
00184         /* The next line should be:
00185          * VSCALE( tmp, tmp, hip->zscale * hip->file2mm );
00186          * This will make the converted DSP plot in MGED agree with what he HF looks like,
00187          * but the HF ignores 'zscale' in the shot routine.
00188          * So we choose to duplicate the raytrace behavior (ignore zscale)
00189          */
00190         VSCALE( tmp, tmp, hip->file2mm );
00191 
00192         dsp->dsp_stom[2] = tmp[0];
00193         dsp->dsp_stom[6] = tmp[1];
00194         dsp->dsp_stom[10] = tmp[2];
00195 
00196         bn_mat_inv( dsp->dsp_mtos, dsp->dsp_stom );
00197 
00198         dsp->magic = RT_DSP_INTERNAL_MAGIC;
00199 
00200         rt_db_free_internal( db_intern, resp );
00201 
00202         db_intern->idb_ptr = (genptr_t)dsp;
00203         db_intern->idb_major_type = DB5_MAJORTYPE_BRLCAD;
00204         db_intern->idb_type = ID_DSP;
00205         db_intern->idb_meth = &rt_functab[ID_DSP];
00206 
00207         return 0;
00208 
00209 }
00210 
00211 
00212 /**
00213  *                      R T _ H F _ P R E P
00214  *
00215  *  Given a pointer to a GED database record, and a transformation matrix,
00216  *  determine if this is a valid HF, and if so, precompute various
00217  *  terms of the formula.
00218  *
00219  *  Returns -
00220  *      0       HF is OK
00221  *      !0      Error in description
00222  *
00223  *  Implicit return -
00224  *      A struct hf_specific is created, and it's address is stored in
00225  *      stp->st_specific for use by hf_shot().
00226  */
00227 int
00228 rt_hf_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00229 {
00230         struct rt_hf_internal *hip;
00231         register struct hf_specific     *hf;
00232         const struct bn_tol             *tol = &rtip->rti_tol;
00233         double  dot;
00234         vect_t  height, work;
00235         static int first_time=1;
00236 
00237         if (first_time) {
00238 #ifndef lint
00239                 bu_log("%s\n",rt_hf_RcSid);
00240 #endif
00241                 first_time=0;
00242         }
00243         RT_CK_DB_INTERNAL(ip);
00244         hip = (struct rt_hf_internal *)ip->idb_ptr;
00245         RT_HF_CK_MAGIC(hip);
00246 
00247         BU_GETSTRUCT(hf, hf_specific);
00248         stp->st_specific = (genptr_t) hf;
00249         /*
00250          * The stuff that is given to us.
00251          */
00252         VMOVE(hf->hf_V, hip->v);
00253         VMOVE(hf->hf_X, hip->x);
00254         VUNITIZE(hf->hf_X);
00255         hf->hf_Xlen = hip->xlen;
00256         VMOVE(hf->hf_Y, hip->y);
00257         VUNITIZE(hf->hf_Y);
00258         hf->hf_Ylen = hip->ylen;
00259         hf->hf_file2mm = hip->file2mm;
00260         hf->hf_w = hip->w;
00261         hf->hf_n = hip->n;
00262         hf->hf_mp = hip->mp;
00263         bu_semaphore_acquire( RT_SEM_MODEL);
00264         ++hf->hf_mp->uses;
00265         bu_semaphore_release( RT_SEM_MODEL);
00266         hf->hf_shorts = hip->shorts;
00267         /*
00268          * From here down, we are calculating new values on a one time
00269          * basis.
00270          */
00271 
00272         /*
00273          * Start finding the location of the oposite vertex to V
00274          */
00275         VJOIN2(hf->hf_VO, hip->v, hip->xlen, hip->x, hip->ylen, hip->y);
00276 
00277         /*
00278          * get the normal.
00279          */
00280         dot = VDOT(hf->hf_X, hf->hf_Y);
00281         if (fabs(dot) >tol->perp) {     /* not perpendicular, bad hf */
00282                 bu_log("Hf(%s): X not perpendicular to Y.\n", stp->st_name);
00283                 bu_free((genptr_t)hf, "struct hf");
00284                 stp->st_specific = (genptr_t) 0;
00285                 return 1;       /* BAD */
00286         }
00287         VCROSS(hf->hf_N, hf->hf_X, hf->hf_Y);
00288         VUNITIZE(hf->hf_N);             /* Not needed (?) */
00289 
00290         /*
00291          * Locate the min-max of the HF for use in determining VO and
00292          * bounding boxes et so forth.
00293          */
00294         if (hf->hf_shorts) {
00295                 register int max, min;
00296                 register int len;
00297                 register unsigned short *sp;
00298                 register int i;
00299 
00300                 sp = (unsigned short *)hf->hf_mp->apbuf;
00301                 min = max = *sp++;
00302                 len = hf->hf_w * hf->hf_n;
00303                 for (i=1; i< len; i++, sp++) {
00304                         if ((int)*sp > max) max=*sp;
00305                         if ((int)*sp < min) min=*sp;
00306                 }
00307                 hf->hf_min = min * hf->hf_file2mm;
00308                 hf->hf_max = max * hf->hf_file2mm;
00309         } else {
00310                 fastf_t max, min;
00311                 register int len;
00312                 register int i;
00313                 fastf_t *fp;
00314 
00315                 fp = (fastf_t *) hf->hf_mp->apbuf;
00316                 min = max = *fp++;
00317                 len = hf->hf_w * hf->hf_n;
00318                 for (i=1; i < len; i++, fp++) {
00319                         if (*fp > max) max = *fp;
00320                         if (*fp < min) min = *fp;
00321                 }
00322                 hf->hf_min = min * hf->hf_file2mm;
00323                 hf->hf_max = max * hf->hf_file2mm;
00324         }
00325 
00326         VSCALE(height, hf->hf_N, hf->hf_max);
00327         VADD2(hf->hf_VO, hf->hf_VO, height);
00328 
00329         /*
00330          * Now we compute the bounding box and sphere.
00331          */
00332         VMOVE(stp->st_min, hf->hf_V);
00333         VMOVE(stp->st_max, hf->hf_V);
00334         VADD2(work, hf->hf_V, height);
00335         VMINMAX(stp->st_min, stp->st_max, work);
00336         VJOIN1(work, hf->hf_V, hf->hf_Xlen, hf->hf_X);
00337         VMINMAX(stp->st_min, stp->st_max, work);
00338         VADD2(work, work, height);
00339         VMINMAX(stp->st_min, stp->st_max, work);
00340         VJOIN1(work, hf->hf_V, hf->hf_Ylen, hf->hf_Y);
00341         VMINMAX(stp->st_min, stp->st_max, work);
00342         VADD2(work, work, height);
00343         VMINMAX(stp->st_min, stp->st_max, work);
00344         VJOIN2(work, hf->hf_V, hf->hf_Xlen, hf->hf_X, hf->hf_Ylen, hf->hf_Y);
00345         VMINMAX(stp->st_min, stp->st_max, work);
00346         VADD2(work, work, height);
00347         VMINMAX(stp->st_min, stp->st_max, work);
00348         /* Now find the center and radius for a bounding sphere. */
00349         {
00350                 LOCAL fastf_t   dx,dy,dz;
00351                 LOCAL fastf_t   f;
00352 
00353                 VADD2SCALE( stp->st_center, stp->st_max, stp->st_min, 0.5);
00354 
00355                 dx = (stp->st_max[X] - stp->st_min[X])/2;
00356                 dy = (stp->st_max[Y] - stp->st_min[Y])/2;
00357                 dz = (stp->st_max[Z] - stp->st_min[Z])/2;
00358                 f = dx;
00359                 if (dy > f) f = dy;
00360                 if (dz > f) f = dz;
00361                 stp->st_aradius = f;
00362                 stp->st_bradius = sqrt(dx*dx + dy*dy + dz*dz);
00363         }
00364         return 0;
00365 }
00366 
00367 /**
00368  *                      R T _ H F _ P R I N T
00369  */
00370 void
00371 rt_hf_print(register const struct soltab *stp)
00372 {
00373         register const struct hf_specific *hf =
00374                 (struct hf_specific *)stp->st_specific;
00375         VPRINT("V", hf->hf_V);
00376         VPRINT("X", hf->hf_X);
00377         VPRINT("Y", hf->hf_Y);
00378         VPRINT("N", hf->hf_N);
00379         bu_log("XL %g\n", hf->hf_Xlen);
00380         bu_log("YL %g\n", hf->hf_Ylen);
00381 }
00382 
00383 static int
00384 rt_hf_cell_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct hit *hitp, int xCell, int yCell)
00385 {
00386         register struct hf_specific *hfp =
00387                 (struct hf_specific *)stp->st_specific;
00388 
00389         fastf_t dn, abs_dn, k1st=0, k2nd=0, alpha, beta;
00390         int dir1st, dir2nd;
00391         vect_t wxb, xp;
00392         vect_t tri_wn1st, tri_wn2nd, tri_BA1st, tri_BA2nd;
00393         vect_t tri_CA1st, tri_CA2nd;
00394         vect_t xvect, yvect, tri_A, tri_B, tri_C;
00395         int fnd1, fnd2;
00396         register double hf2mm = hfp->hf_file2mm;
00397 
00398         if (RT_G_DEBUG & DEBUG_HF) {
00399                 bu_log("rt_hf_cell_shot(%s): %d, %d\n", stp->st_name,
00400                     xCell, yCell);
00401         }
00402         {
00403                 register fastf_t scale;
00404                 scale = hfp->hf_Xlen/((double)hfp->hf_w-1);
00405                 VSCALE(xvect, hfp->hf_X, scale);
00406                 scale = hfp->hf_Ylen/((double)hfp->hf_n-1);
00407                 VSCALE(yvect, hfp->hf_Y, scale);
00408         }
00409         if (hfp->hf_shorts) {
00410                 register unsigned short *sp;
00411                 sp = (unsigned short *)hfp->hf_mp->apbuf +
00412                          yCell * hfp->hf_w + xCell;
00413 
00414                 /* Get the points of one of the triangles */
00415 
00416                 /* 0,0 -> tri_A */
00417                 VJOIN3(tri_A, hfp->hf_V, *sp*hf2mm, hfp->hf_N, xCell+0, xvect,
00418                     yCell+0, yvect);
00419                 sp++;
00420                 /* 1, 0 -> tri_B */
00421                 VJOIN3(tri_B, hfp->hf_V, *sp*hf2mm, hfp->hf_N, xCell+1, xvect,
00422                     yCell+0, yvect);
00423                 sp += hfp->hf_w;
00424                 /* 1, 1 -> tri_C */
00425                 VJOIN3(tri_C, hfp->hf_V, *sp*hf2mm, hfp->hf_N, xCell+1, xvect,
00426                     yCell+1, yvect);
00427 
00428                 VSUB2(tri_CA1st, tri_C, tri_A);
00429                 VSUB2(tri_BA1st, tri_B, tri_A);
00430                 VCROSS(tri_wn1st, tri_BA1st, tri_CA1st);
00431                 VMOVE(tri_B, tri_C);            /* This can optimize down. */
00432                 --sp;
00433 
00434                 /* 0, 1 */
00435                 VJOIN3(tri_C, hfp->hf_V, *sp*hf2mm, hfp->hf_N, xCell+0, xvect,
00436                     yCell+1, yvect);
00437                 VSUB2(tri_CA2nd, tri_C, tri_A);
00438 
00439 /*              VMOVE(tri_BA2nd, tri_CA1st); */
00440 
00441                 VSUB2(tri_BA2nd, tri_B, tri_A);
00442                 VCROSS(tri_wn2nd, tri_BA2nd, tri_CA2nd);
00443         } else {
00444                 register double *fp;
00445                 fp = (double *)hfp->hf_mp->apbuf +
00446                         yCell * hfp->hf_w + xCell;
00447                 /* 0,0 -> A */
00448                 VJOIN3(tri_A, hfp->hf_V, *fp*hf2mm, hfp->hf_N, xCell+0, xvect,
00449                     yCell+0, yvect);
00450                 fp++;
00451                 /* 1, 0 */
00452                 VJOIN3(tri_B, hfp->hf_V, *fp*hf2mm, hfp->hf_N, xCell+1, xvect,
00453                     yCell+0, yvect);
00454                 fp += hfp->hf_w;
00455                 /* 1, 1 */
00456                 VJOIN3(tri_C, hfp->hf_V, *fp*hf2mm, hfp->hf_N, xCell+1, xvect,
00457                     yCell+1, yvect);
00458                 VSUB2(tri_CA1st, tri_C, tri_A);
00459                 VSUB2(tri_BA1st, tri_B, tri_A);
00460                 VCROSS(tri_wn1st, tri_BA1st, tri_CA1st);
00461                 VMOVE(tri_B, tri_C);            /* This can optimize down. */
00462                 --fp;
00463                 /* 0, 1 */
00464                 VJOIN3(tri_C, hfp->hf_V, *fp*hf2mm, hfp->hf_N, xCell+0, xvect,
00465                     yCell+1, yvect);
00466                 VSUB2(tri_CA2nd, tri_C, tri_A);
00467 /*              VMOVE(tri_BA2nd, tri_CA1st); */
00468                 VSUB2(tri_BA2nd, tri_B, tri_A);
00469                 VCROSS(tri_wn2nd, tri_BA2nd, tri_CA2nd);
00470         }
00471 
00472         /*      0,1             1,1
00473          *        o             o
00474          *                  _
00475          *        ^          //|
00476          *   CA2nd|         //
00477          *        |        //
00478          *        |  BA2nd//
00479          *        |      //
00480          *        |     // CA1st
00481          *        |    //
00482          *        |   //
00483          *        |  //
00484          *        | //
00485          *
00486          *        o  -------->  o
00487          *      0,0     BA1st   1,0
00488          *
00489          * wn1st and wn2nd are non-unit normal vectors pointing out of screen
00490          */
00491 
00492         fnd1 = fnd2 = 0;
00493 
00494 #if 0
00495         dn = VDOT(tri_wn1st, rp->r_dir); /* wn1st points out */
00496         abs_dn = (dn >= 0.0) ? dn : (-dn);
00497         if (abs_dn <SQRT_SMALL_FASTF)
00498                 goto other_half; /* ray parellel to plane */
00499 
00500         VSUB2( wxb, tri_A, rp->r_pt);
00501         VCROSS( xp, wxb, rp->r_dir);
00502         alpha = VDOT(tri_CA1st, xp);    /* alpha = dist along CA1 to isect pt */
00503         if (dn < 0.0) alpha = -alpha;
00504         if (alpha < 0.0 || alpha > abs_dn) goto other_half;
00505         beta = VDOT(tri_BA1st, xp);     /* beta = dist along BA1 to isect pt */
00506         if (dn > 0.0) beta = -beta;
00507         if (beta < 0.0 || beta > abs_dn) goto other_half;
00508         if (alpha + beta > abs_dn) goto other_half;
00509         k1st = VDOT(wxb, tri_wn1st) / dn;
00510         fnd1 = 1;
00511 #else
00512         /* Ray triangle intersection.
00513          * See: "Graphics Gems" An Efficient Ray-Polygon Intersection P:390
00514          */
00515 
00516         dn = VDOT(tri_wn1st, rp->r_dir); /* wn1st points out */
00517         if (dn >= 0.0) {
00518                 dir1st = 1;
00519                 abs_dn=dn;
00520         } else {
00521                 dir1st = 0;
00522                 abs_dn = -dn;
00523         }
00524 
00525         /* make sure ray not parallel to plane of triangle */
00526         if (abs_dn >= SQRT_SMALL_FASTF) {
00527                 VSUB2( wxb, tri_A, rp->r_pt);
00528                 VCROSS( xp, wxb, rp->r_dir);
00529 
00530                 /* alpha = dist along CA1 to isect pt */
00531                 alpha = VDOT(tri_CA1st, xp);
00532                 if (dn < 0.0) alpha = -alpha;
00533 
00534                 /* if pt before CA1st or beyond end of CA1st pt is
00535                  * outside triangle.
00536                  *
00537                  * XXX Can someone explain the "alpha <= abs_dn" part? -- Lee
00538                  *  I know it's supposed to be determining if the point
00539                  * is beyond the end of CA1st, but I don't see how the math
00540                  * here does that.
00541                  */
00542                 if (alpha >= 0.0 && alpha <= abs_dn) {
00543 
00544                         /* beta = dist along BA1 to isect pt */
00545                         beta = VDOT(tri_BA1st, xp);
00546                         if (dn > 0.0) beta = -beta;
00547 
00548                         if (beta >= 0.0 && beta <= abs_dn) {
00549                                 if (alpha + beta <= abs_dn) {
00550                                         k1st = VDOT(wxb, tri_wn1st) / dn;
00551                                         fnd1 = 1;
00552                                 }
00553                         }
00554                 }
00555         }
00556 #endif
00557 
00558 
00559 
00560 
00561 
00562 
00563         /* XXX This is really hard to read.  Need to fix this like above */
00564         dn = VDOT(tri_wn2nd, rp->r_dir);
00565         if (dn >= 0.0) {
00566                 dir2nd = 1;
00567                 abs_dn = dn;
00568         } else {
00569                 dir2nd = 0;
00570                 abs_dn = -dn;
00571         }
00572         if (abs_dn < SQRT_SMALL_FASTF) goto leave;
00573         VSUB2(wxb, tri_A, rp->r_pt);
00574         VCROSS(xp, wxb, rp->r_dir);
00575         alpha = VDOT(tri_CA2nd, xp);
00576         if (dn <0.0) alpha = -alpha;
00577         if (alpha < 0.0 || alpha > abs_dn) goto leave;
00578         beta = VDOT(tri_BA2nd, xp);
00579         if (dn > 0.0) beta = -beta;
00580         if (beta < 0.0 || beta > abs_dn) goto leave;
00581         if (alpha+beta > abs_dn) goto leave;
00582         k2nd = VDOT(wxb, tri_wn2nd)/ dn;
00583         fnd2 = 1;
00584 leave:
00585         if (!fnd1 && !fnd2) return 0;
00586 
00587         if (RT_G_DEBUG & DEBUG_HF) {
00588                 bu_log("rt_hf_cell_shot: hit(%d).\n",fnd1+fnd2);
00589         }
00590 
00591         /*
00592          * XXX - This is now wrong.
00593          *
00594          * We have now done the ray-triangle intersection.  dn1st
00595          * and dn tell us the direction of the normal, <0 is in
00596          * and >0 is out.  k1st and k2nd tell us the distance from
00597          * the start point.
00598          *
00599          * We are only interested in the closest hit. and that will
00600          * replace the out if dn>0 or the in if dn<0.
00601          */
00602 
00603 /* bu_log("cell: k1st=%g, k2nd=%g\n", k1st,k2nd); */
00604 
00605         if (!fnd2 ) {
00606                 hitp->hit_magic = RT_HIT_MAGIC;
00607                 hitp->hit_dist = k1st;
00608                 VMOVE(hitp->hit_normal, tri_wn1st);
00609                 VUNITIZE(hitp->hit_normal);
00610                 hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00611                 return 1;
00612         }
00613         if (!fnd1) {
00614                 hitp->hit_magic = RT_HIT_MAGIC;
00615                 hitp->hit_dist = k2nd;
00616                 VMOVE(hitp->hit_normal, tri_wn2nd);
00617                 VUNITIZE(hitp->hit_normal);
00618                 hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00619                 return 1;
00620         }
00621 #if 0
00622         if (fabs(k1st) < fabs(k2nd)) {
00623                 hitp->hit_magic = RT_HIT_MAGIC;
00624                 hitp->hit_dist = k1st;
00625                 VMOVE(hitp->hit_normal, tri_wn1st);
00626                 VUNITIZE(hitp->hit_normal);
00627                 hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00628                 return 1;
00629         } else {
00630                 hitp->hit_magic = RT_HIT_MAGIC;
00631                 hitp->hit_dist = k2nd;
00632                 VMOVE(hitp->hit_normal, tri_wn2nd);
00633                 VUNITIZE(hitp->hit_normal);
00634                 hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00635                 return 1;
00636         }
00637 #else
00638         /*
00639          * This is the two hit situation which can cause interesting
00640          * problems.  Three are basicly five different cases that must
00641          * be dealt with and each one requires that the ray be classified
00642          *
00643          * 1) The ray has hit two different planes at two different
00644          *    locations (k1st != k2nd).  Return both hit points.
00645          * 2) The ray is going from inside to outside, return one hit point.
00646          * 3) The ray is going from outside to inside, return one hit point.
00647          * 4) The ray is going from inside to inside, return two hit points.
00648          * 5) The ray is going from outside to outside, return two hit points.
00649          */
00650         hitp->hit_magic = RT_HIT_MAGIC;
00651         hitp->hit_dist = k1st;
00652         VMOVE(hitp->hit_normal, tri_wn1st);
00653         VUNITIZE(hitp->hit_normal);
00654         hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00655         hitp++;
00656 
00657         if ((fabs(k1st-k2nd) < SMALL_FASTF) &&
00658             (dir1st + dir2nd != 1)) return 1;
00659 
00660         hitp->hit_magic = RT_HIT_MAGIC;
00661         hitp->hit_dist = k2nd;
00662         VMOVE(hitp->hit_normal, tri_wn2nd);
00663         VUNITIZE(hitp->hit_normal);
00664         hitp->hit_surfno = yCell*hfp->hf_w+xCell;
00665         return 2;
00666 #endif
00667 }
00668 
00669 #define MAXHITS 128             /* # of surfaces hit, must be even */
00670 
00671 /*
00672  *      For the given plane of the bounding box of the hf solid, compute the
00673  *      hit distance and add it to the list of hits.  If the plane happens
00674  *      to be the "Zmax" face, then the hit is really in the elevation data,
00675  *      and we skip it.  That will be handled elsewhere.
00676  */
00677 static void
00678 axis_plane_isect(int plane, fastf_t inout, struct xray *rp, struct hf_specific *hf, double xWidth, double yWidth, struct hit **hp, int *nhits)
00679 {
00680         double left, right, xx=0, xright=0, answer;
00681         vect_t loc;
00682         int CellX=0, CellY=0;
00683 
00684         if (plane == -6) return;
00685 
00686         if (plane == -3) {
00687                 (*hp)->hit_magic = RT_HIT_MAGIC;
00688                 (*hp)->hit_dist = inout;
00689                 (*hp)->hit_surfno = plane;
00690                 (*hp)++;
00691                 if ((*nhits)++>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
00692                 return;
00693         }
00694 
00695         VJOIN1(loc, rp->r_pt, inout, rp->r_dir);
00696         VSUB2(loc,loc,hf->hf_V);
00697 
00698         /* find the left, right and xx */
00699         switch (plane) {
00700         case -1:
00701                 CellY = loc[Y]/ yWidth;
00702                 CellX = 0;
00703                 xright = yWidth;
00704                 xx = loc[Y] - (CellY* yWidth);
00705                 break;
00706         case -2:
00707                 CellY = 0;
00708                 CellX = loc[X]/ xWidth;
00709                 xright = xWidth;
00710                 xx = loc[X] - CellX * xWidth;
00711                 break;
00712         case -4:
00713                 CellY = loc[Y]/ yWidth;
00714                 CellX = hf->hf_n-1;
00715                 xright = yWidth;
00716                 xx = loc[Y] - (CellY* yWidth);
00717                 break;
00718         case -5:
00719                 CellY = hf->hf_w-1;
00720                 CellX = loc[X]/ xWidth;
00721                 xright = xWidth;
00722                 xx = loc[X] - CellX* xWidth;
00723                 break;
00724         }
00725 #if 1 /* What does this indicate that it generates so much noise? */
00726         if (xx < 0) {
00727                 bu_log("hf: xx < 0, plane = %d\n", plane);
00728         }
00729 #endif
00730 
00731         if (hf->hf_shorts) {
00732                 register unsigned short *sp;
00733                 sp = (unsigned short *)hf->hf_mp->apbuf +
00734                     CellY * hf->hf_w + CellX;
00735                 left = *sp;
00736                 if (plane == -2 || plane == -5) {
00737                         sp++;
00738                 } else {
00739                         sp += hf->hf_w;
00740                 }
00741                 right = *sp;
00742         } else {
00743                 register double *fp;
00744                 fp = (double *) hf->hf_mp->apbuf +
00745                     CellY * hf->hf_w + CellX;
00746                 left = *fp;
00747                 if (plane == -2 || plane == -5) {
00748                         fp++;
00749                 } else {
00750                         fp += hf->hf_w;
00751                 }
00752                 right = *fp;
00753         }
00754         left *= hf->hf_file2mm;
00755         right *= hf->hf_file2mm;
00756         answer = (right-left)/xright*xx+left;
00757 #if 0
00758 bu_log("inout: loc[Z]=%g, answer=%g, left=%g, right=%g, xright=%g, xx=%g\n",
00759     loc[Z], answer, left, right, xright, xx);
00760 #endif
00761 
00762         if (loc[Z]-SQRT_SMALL_FASTF < answer) {
00763                 (*hp)->hit_magic = RT_HIT_MAGIC;
00764                 (*hp)->hit_dist = inout;
00765                 (*hp)->hit_surfno = plane;
00766                 VJOIN1((*hp)->hit_point, rp->r_pt, inout, rp->r_dir);
00767                 (*hp)++;
00768                 if ((*nhits)++>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
00769         }
00770 }
00771 
00772 
00773 
00774 /**
00775  *                      R T _ H T F _ S H O T
00776  *
00777  *  Intersect a ray with a height field.
00778  *  If an intersection occurs, a struct seg will be acquired
00779  *  and filled in.
00780  *
00781  *  Returns -
00782  *      0       MISS
00783  *      >0      HIT
00784  */
00785 int
00786 rt_hf_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00787 {
00788         register struct hf_specific *hf =
00789                 (struct hf_specific *)stp->st_specific;
00790 
00791         LOCAL   struct hit      hits[MAXHITS];
00792         struct hit *hp;
00793         LOCAL   int             nhits;
00794         double  xWidth, yWidth;
00795 
00796         vect_t  peqn;
00797         fastf_t pdist=0;
00798         fastf_t allDist[6];     /* The hit point for all rays. */
00799         fastf_t cosine;
00800 
00801         LOCAL int       iplane, oplane, j;
00802         LOCAL fastf_t   in, out;
00803         vect_t aray, curloc;
00804 
00805 bzero(hits,sizeof(hits));
00806 
00807         in = -INFINITY;
00808         out = INFINITY;
00809         iplane = oplane = 0;
00810 
00811         nhits=0;
00812         hp = &hits[0];
00813 
00814 
00815         /*
00816          * First step in raytracing the HF is to find the intersection
00817          * of the ray with the bounding box.  Since the ray might not
00818          * even hit the box.
00819          *
00820          * The results of this intercept can be used to find which
00821          * cell of the dda is the start cell.
00822          */
00823         for (j=-1; j>-7; j--) {
00824                 FAST fastf_t    dn;     /* Direction dot Normal */
00825                 FAST fastf_t    dxbdn;  /* distence beteen d and b * dn */
00826                 FAST fastf_t    s;      /* actual distence in mm */
00827                 int allIndex;
00828 
00829                 switch (j) {
00830                 case -1:
00831                         /* Xmin plane */
00832                         VREVERSE(peqn, hf->hf_X);
00833                         pdist = VDOT(peqn, hf->hf_V);
00834                         break;
00835                 case -2:
00836                         /* Ymin plane */
00837                         VREVERSE(peqn, hf->hf_Y);
00838                         pdist = VDOT(peqn, hf->hf_V);
00839                         break;
00840                 case -3:
00841                         /* Zmin plane */
00842                         VREVERSE(peqn, hf->hf_N);
00843                         pdist = VDOT(peqn, hf->hf_V);
00844                         break;
00845                 case -4:
00846                         /* Xmax plane */
00847                         VMOVE(peqn, hf->hf_X);
00848                         pdist = VDOT(peqn, hf->hf_VO);
00849                         break;
00850                 case -5:
00851                         /* Ymax plane */
00852                         VMOVE(peqn, hf->hf_Y);
00853                         pdist = VDOT(peqn, hf->hf_VO);
00854                         break;
00855                 case -6:
00856                         /* Zmax plane */
00857                         VMOVE(peqn, hf->hf_N);
00858                         pdist = VDOT(peqn, hf->hf_VO);
00859                         break;
00860                 }
00861                 allIndex = abs(j)-1;
00862 
00863                 dxbdn = VDOT( peqn, rp->r_pt) - pdist;
00864                 dn = -VDOT( peqn, rp->r_dir);
00865 /*              allDist[allIndex] = s = dxbdn/dn; */
00866                 if (RT_G_DEBUG & DEBUG_HF) {
00867                         VPRINT("hf: Plane Equation", peqn);
00868                         bu_log("hf: dn=%g, dxbdn=%g, ", dn, dxbdn);
00869                 }
00870 
00871                 if (dn < -SQRT_SMALL_FASTF) {           /* Leaving */
00872                         allDist[allIndex] = s = dxbdn/dn;
00873                         if ( out > s ) {
00874                                 out = s;
00875                                 oplane = j;
00876                         }
00877                         if (RT_G_DEBUG & DEBUG_HF) {
00878                                 bu_log("s=%g out=%g\n", s, out);
00879                         }
00880                 } else if (dn > SQRT_SMALL_FASTF) {     /* entering */
00881                         allDist[allIndex] = s = dxbdn/dn;
00882                         if ( in < s ) {
00883                                 in = s;
00884                                 iplane = j;
00885                         }
00886                         if (RT_G_DEBUG & DEBUG_HF) {
00887                                 bu_log("s=%g in=%g\n", s, in);
00888                         }
00889                 } else {
00890                         /*
00891                          * if the ray is outside the solid, then this
00892                          * is a miss.
00893                          */
00894                         if (RT_G_DEBUG & DEBUG_HF) {
00895                                 bu_log("s=DIVIDE_BY_ZERO\n");
00896                         }
00897                         if ( dxbdn > SQRT_SMALL_FASTF) {
00898                                 return 0; /* MISS */
00899                         }
00900                         allDist[allIndex] = INFINITY;
00901                 }
00902                 if ( in > out) {
00903                         if (RT_G_DEBUG & DEBUG_HF) {
00904                                 bu_log("rt_hf_shoot(%s): in(%g) > out(%g)\n",
00905                                     stp->st_name, in, out);
00906                         }
00907                         return 0;       /* MISS */
00908                 }
00909         }
00910 
00911         if ( iplane >= 0 || oplane >= 0 ) {
00912                 bu_log("rt_hf_shoot(%s): 1 hit => MISS\n",
00913                     stp->st_name);
00914                 return 0;       /* MISS */
00915         }
00916 
00917         if ( fabs(in-out) < SMALL_FASTF  || out >= INFINITY ) {
00918                 if (RT_G_DEBUG & DEBUG_HF) {
00919                         bu_log("rt_hf_shoot(%s): in(%g) >= out(%g) || out >= INFINITY\n",
00920                             stp->st_name, in, out);
00921                 }
00922                 return 0;
00923         }
00924 
00925         /*
00926          * Once that translation is done, we start a DDA to walk across
00927          * the field.  Checking each "cell" and when changing cells in
00928          * the off direction, the 2 cells filling the corner are also
00929          * checked.
00930          */
00931 
00932         xWidth = hf->hf_Xlen/((double)(hf->hf_w-1));
00933         yWidth = hf->hf_Ylen/((double)(hf->hf_n-1));
00934 
00935         if (RT_G_DEBUG & DEBUG_HF) {
00936                 bu_log("hf: xWidth=%g, yWidth=%g, in=%g, out=%g\n", xWidth,
00937                        yWidth, in, out);
00938         }
00939 
00940 
00941         /*
00942          * add the sides, and bottom to the hit list.
00943          */
00944         axis_plane_isect(iplane,  in, rp, hf, xWidth, yWidth, &hp, &nhits);
00945         axis_plane_isect(oplane, out, rp, hf, xWidth, yWidth, &hp, &nhits);
00946 
00947         /*
00948          * Gee, we've gotten much closer, we know that we hit the
00949          * the solid. Now it's time to see which cell we hit.  The
00950          * Key here is to use a fast DDA to check ONLY the cells we
00951          * are interested in.  The basic idea and some of the pseudo
00952          * code comes from:
00953          *      Grid Tracing: Fast Ray Tracing for Height Fields
00954          *      By: F. Kenton Musgrave.
00955          */
00956 
00957         /*
00958          * Now we figure out which direction we are going to be moving,
00959          * X, Y or Z.
00960          */
00961         {
00962                 vect_t tmp;
00963                 VMOVE(tmp,rp->r_dir);
00964                 tmp[Z] = 0.0;   /* XXX Bogus?  Assumes X,Y in XY plane */
00965                 VUNITIZE(tmp);
00966                 cosine = VDOT(tmp, hf->hf_X);
00967         }
00968         if (fabs(cosine) < SMALL_FASTF) {       /* near enough to Z */
00969                 vect_t tmp;
00970                 int xCell, yCell, r;
00971                 if (RT_G_DEBUG & DEBUG_HF) {
00972                         bu_log("hf: Vertical shoot\n");
00973                 }
00974                 VSUB2(tmp, rp->r_pt, hf->hf_V);
00975                 xCell = tmp[X]/hf->hf_Xlen*hf->hf_w;
00976                 yCell = tmp[Y]/hf->hf_Ylen*hf->hf_n;
00977                 if (  (r=rt_hf_cell_shot(stp, rp, ap, hp, xCell, yCell)) ) {
00978                         if ((nhits+=r)>MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
00979                         hp+=r;
00980                 }
00981         } else if (cosine*cosine > 0.5) {
00982                 double tmp;
00983                 register double farZ, minZ, maxZ;
00984                 int     xCell, yCell, signX, signY;
00985                 double  highest,lowest, error,delta;
00986                 double  deltaZ;
00987 
00988                 vect_t  goesIN, goesOUT;
00989 
00990                 VJOIN1(goesIN, rp->r_pt, allDist[3], rp->r_dir); /* Xmax plane */
00991                 VJOIN1(goesOUT,rp->r_pt, allDist[0], rp->r_dir); /* Xmin plane */
00992                 VSUB2(aray, goesOUT, goesIN);
00993                 VSUB2(curloc, goesIN, hf->hf_V);
00994 
00995 
00996                 /*
00997                  * We will be stepping one "cell width" in the X direction
00998                  * each time through the loop.  In simple case we have???
00999                  * cell_width = htfp->htf_Xlen/(htfp->htf_i-1);
01000                  * deltaX = (Xdist*cell_width)/Xdist
01001                  * deltaY = (Ydist*cell_width)/Xdist;
01002                  */
01003                 tmp = xWidth/fabs(aray[X]);
01004 
01005 #if 0
01006 bu_log("hf: tmp=%g\n", tmp);
01007 bu_log("hf: before VSCALE... aray=(%g,%g,%g)\n",
01008                 aray[X], aray[Y], aray[Z]);
01009 #endif
01010 
01011                 VSCALE(aray, aray, tmp);
01012 
01013                 /*
01014                  * Some fudges here.  First, the size of the array of
01015                  * samples is iXj, but the size of the array of CELLS
01016                  * is (i-1)X(j-1), therefore number of CELLS is one less
01017                  * than number of samples and the scaling factor is used.
01018                  * Second, the math is nice to us.  IF we are entering at
01019                  * the far end (curloc[X] == Xlen || curloc[Y] == Ylen)
01020                  * then the result we will get back is of the cell
01021                  * following this (out of bounds)  So we add a check for
01022                  * that problem.
01023                  */
01024                 xCell = curloc[X]/xWidth;
01025                 tmp = curloc[Y];
01026                 if (tmp < 0) {
01027                         yCell = tmp/yWidth - 1;
01028                 } else {
01029                         yCell = tmp/yWidth;
01030                 }
01031 
01032                 signX = (aray[X] < 0.0) ? -1 : 1;
01033                 signY = (aray[Y] < 0.0) ? -1 : 1;
01034 
01035                 if (RT_G_DEBUG & DEBUG_HF ) {
01036                         bu_log("hf: curloc=(%g, %g, %g) aray=(%g,%g,%g)\n", curloc[X], curloc[Y],
01037                             curloc[Z], aray[X], aray[Y], aray[Z]);
01038                         bu_log("hf: from=(%g, %g) to=(%g, %g)\n",
01039                             goesIN[X]/xWidth,
01040                             goesIN[Y]/yWidth,
01041                             goesOUT[X]/xWidth,
01042                             goesOUT[Y]/yWidth);
01043                 }
01044                 error = curloc[Y]-yCell*yWidth;
01045                 error /= yWidth;
01046 
01047 /*              delta = aray[Y]/yWidth; */
01048                 delta = aray[Y]/fabs(aray[X]);
01049 
01050 #if 0
01051 bu_log("aray[Y]/aray[X]=%g\n", delta);
01052 #endif
01053 
01054                 if (delta < 0.0) {
01055                         delta = -delta;
01056                         error = -error;
01057                 } else {
01058                         error -= 1.0;
01059                 }
01060 
01061                 /*
01062                  * if at the far end (Xlen) then we need to move one
01063                  * step forward along aray.
01064                  */
01065                 if (xCell >= hf->hf_w-1) {
01066                         xCell+=signX;
01067                         error += delta;
01068                 }
01069                 if (RT_G_DEBUG & DEBUG_HF) {
01070                         bu_log("hf: delta=%g, error=%g, %d, %d\n",
01071                            delta, error, xCell, yCell);
01072                 }
01073 
01074 
01075                 deltaZ = (aray[Z] < 0) ? -aray[Z] : aray[Z];
01076                 do {
01077                         farZ = curloc[Z] + aray[Z];
01078                         maxZ = (curloc[Z] > farZ) ? curloc[Z] : farZ;
01079                         minZ = (curloc[Z] < farZ) ? curloc[Z] : farZ;
01080                         if (RT_G_DEBUG & DEBUG_HF) {
01081                                 bu_log("hf: cell %d,%d [%g -- %g]",
01082                                 xCell, yCell, minZ, maxZ);
01083                         }
01084                         /*
01085                          * Are we on the grid yet?  If not, then
01086                          * we will check for a side step and inc.
01087                          */
01088 /* CTJ - Or maxZ < hf->hf_min  then no chance to hit */
01089                         if (yCell < 0 || yCell > hf->hf_n-2) {
01090                                 if (error > -SQRT_SMALL_FASTF) {
01091                                         if (yCell >= -1) goto skip_first;
01092                                         yCell += signY;
01093                                         error -= 1.0;
01094                                 }
01095                                 xCell += signX;
01096                                 error += delta;
01097                                 VADD2(curloc, curloc, aray);
01098                                 continue;
01099                         }
01100 
01101                         /*
01102                          * Get the min/max of the four corners of
01103                          * a given cell.  Since the data in memory
01104                          * can be in one of two forms, unsigned short
01105                          * and double, we have this simple if statement
01106                          * around the find min/max to reference the data
01107                          * correctly.
01108                          */
01109                         if (hf->hf_shorts) {
01110                                 register unsigned short *sp;
01111                                 sp = (unsigned short *)hf->hf_mp->apbuf +
01112                                     yCell * hf->hf_w + xCell;
01113                                 /* 0,0 */
01114                                 highest = lowest = *sp++;
01115                                 /* 1,0 */
01116                                 if (lowest > (double)*sp) lowest=*sp;
01117                                 if (highest < (double)*sp) highest=*sp;
01118                                 sp+=hf->hf_w;
01119                                 /* 1,1 */
01120                                 if (lowest > (double)*sp) lowest=*sp;
01121                                 if (highest < (double)*sp) highest=*sp;
01122                                 sp--;
01123                                 /* 0,1 */
01124                                 if (lowest > (double)*sp) lowest = *sp;
01125                                 if (highest < (double)*sp) highest = *sp;
01126                                 lowest *= hf->hf_file2mm;
01127                                 highest *= hf->hf_file2mm;
01128                         } else {
01129                                 register double *fp;
01130                                 fp = (double *)hf->hf_mp->apbuf +
01131                                     yCell * hf->hf_w + xCell;
01132                                 /* 0,0 */
01133                                 highest = lowest = *fp++;
01134                                 /* 1,0 */
01135                                 if (lowest > *fp) lowest=*fp;
01136                                 if (highest < *fp) highest=*fp;
01137                                 fp+=hf->hf_w;
01138                                 /* 1,1 */
01139                                 if (lowest > *fp) lowest=*fp;
01140                                 if (highest < *fp) highest=*fp;
01141                                 fp--;
01142                                 /* 0,1 */
01143                                 if (lowest > *fp) lowest = *fp;
01144                                 if (highest < *fp) highest = *fp;
01145                                 lowest *= hf->hf_file2mm;
01146                                 highest *= hf->hf_file2mm;
01147                         }
01148 
01149                         if (RT_G_DEBUG & DEBUG_HF) {
01150                                 bu_log("lowest=%g, highest=%g\n",
01151                                     lowest, highest);
01152                         }
01153 
01154 /*
01155  * This is the primary test.  It is designed to get all cells that the
01156  * ray passes through.
01157  */
01158 #if 1
01159                         if (maxZ+deltaZ > lowest &&
01160                             minZ-deltaZ < highest ) {
01161 #else
01162                         {
01163 #endif
01164                                 int r;
01165                                 if ( (r=rt_hf_cell_shot(stp, rp, ap, hp, xCell, yCell)) ) {
01166                                         if ((nhits+=r)>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
01167                                         hp+=r;
01168                                 }
01169                         }
01170 /*
01171  * This is the DDA trying to fill in the corners as it walks the
01172  * path.
01173  */
01174 skip_first:
01175                         if (error > SQRT_SMALL_FASTF) {
01176                                 yCell += signY;
01177                                 if (RT_G_DEBUG & DEBUG_HF) {
01178                                         bu_log("hf: cell %d,%d ", xCell, yCell);
01179                                 }
01180                                 if ((yCell < 0) || yCell > hf->hf_n-2) {
01181                                         error -= 1.0;
01182                                         xCell += signX;
01183                                         error += delta;
01184                                         VADD2(curloc, curloc, aray);
01185                                         continue;
01186                                 }
01187                                 if (hf->hf_shorts) {
01188                                         register unsigned short *sp;
01189                                         sp = (unsigned short *)hf->hf_mp->apbuf +
01190                                             yCell * hf->hf_w + xCell;
01191                                         /* 0,0 */
01192                                         highest = lowest = *sp++;
01193                                         /* 1,0 */
01194                                         if (lowest > (double)*sp) lowest=*sp;
01195                                         if (highest < (double)*sp) highest=*sp;
01196                                         sp+=hf->hf_w;
01197                                         /* 1,1 */
01198                                         if (lowest > (double)*sp) lowest=*sp;
01199                                         if (highest < (double)*sp) highest=*sp;
01200                                         sp--;
01201                                         /* 0,1 */
01202                                         if (lowest > (double)*sp) lowest = *sp;
01203                                         if (highest < (double)*sp) highest = *sp;
01204                                         lowest *= hf->hf_file2mm;
01205                                         highest *= hf->hf_file2mm;
01206                                 } else {
01207                                         register double *fp;
01208                                         fp = (double *)hf->hf_mp->apbuf +
01209                                             yCell * hf->hf_w + xCell;
01210                                         /* 0,0 */
01211                                         highest = lowest = *fp++;
01212                                         /* 1,0 */
01213                                         if (lowest > *fp) lowest=*fp;
01214                                         if (highest < *fp) highest=*fp;
01215                                         fp+=hf->hf_w;
01216                                         /* 1,1 */
01217                                         if (lowest > *fp) lowest=*fp;
01218                                         if (highest < *fp) highest=*fp;
01219                                         fp--;
01220                                         /* 0,1 */
01221                                         if (lowest > *fp) lowest = *fp;
01222                                         if (highest < *fp) highest = *fp;
01223                                         lowest *= hf->hf_file2mm;
01224                                         highest *= hf->hf_file2mm;
01225                                 }
01226 #if 1
01227                                 if (maxZ+deltaZ > lowest &&
01228                                     minZ-deltaZ < highest) {
01229 #else
01230                                 {
01231 #endif /* 0 */
01232                                         int r;
01233                                         /* DO HIT */
01234                                         if ( (r=rt_hf_cell_shot(stp, rp, ap, hp, xCell, yCell)) ) {
01235                                                 if ((nhits+=r)>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
01236                                                 hp+=r;
01237                                         }
01238                                 }
01239                                 error -= 1.0;
01240                         } else if (error > -SQRT_SMALL_FASTF) {
01241                                 yCell += signY;
01242                                 error -= 1.0;
01243                         }
01244                         xCell += signX;
01245                         error += delta;
01246                         VADD2(curloc, curloc, aray);
01247                 } while (xCell >= 0 && xCell < hf->hf_w-1 );
01248                 if (RT_G_DEBUG & DEBUG_HF) {
01249                         bu_log("htf: leaving loop, %d, %d, %g vs. 0--%d, 0--%d, 0.0--%g\n",
01250                            xCell, yCell, curloc[Z], hf->hf_w-1, hf->hf_n-1, hf->hf_max);
01251                 }
01252 /* OTHER HALF */
01253         } else {
01254                 double tmp;
01255                 register double farZ, minZ, maxZ;
01256                 double  deltaZ;
01257                 int     xCell, yCell, signX, signY;
01258                 double  highest,lowest, error,delta;
01259 
01260                 vect_t  goesIN, goesOUT;
01261 
01262                 VJOIN1(goesIN, rp->r_pt, allDist[4], rp->r_dir);
01263                 VJOIN1(goesOUT,rp->r_pt, allDist[1], rp->r_dir);
01264                 VSUB2(aray, goesOUT, goesIN);
01265                 VSUB2(curloc, goesIN, hf->hf_V);
01266 
01267 
01268                 /*
01269                  * We will be stepping one "cell width" in the X direction
01270                  * each time through the loop.  In simple case we have???
01271                  * cell_width = htfp->htf_Xlen/(htfp->htf_i-1);
01272                  * deltaX = (Xdist*cell_width)/Xdist
01273                  * deltaY = (Ydist*cell_width)/Xdist;
01274                  */
01275                 tmp = yWidth/fabs(aray[Y]);
01276 
01277 #if 0
01278 bu_log("hf: tmp=%g\n", tmp);
01279 bu_log("hf: before VSCALE... aray=(%g,%g,%g)\n",
01280                 aray[X], aray[Y], aray[Z]);
01281 #endif
01282 
01283                 VSCALE(aray, aray, tmp);
01284 
01285                 /*
01286                  * Some fudges here.  First, the size of the array of
01287                  * samples is iXj, but the size of the array of CELLS
01288                  * is (i-1)X(j-1), therefore number of CELLS is one less
01289                  * than number of samples and the scaling factor is used.
01290                  * Second, the math is nice to us.  IF we are entering at
01291                  * the far end (curloc[X] == Xlen || curloc[Y] == Ylen)
01292                  * then the result we will get back is of the cell
01293                  * following this (out of bounds)  So we add a check for
01294                  * that problem.
01295                  */
01296                 yCell = curloc[Y]/yWidth;
01297                 tmp = curloc[X];
01298                 if (tmp < 0) {
01299                         xCell = tmp/xWidth - 1;
01300                 } else {
01301                         xCell = tmp/xWidth;
01302                 }
01303 
01304                 signX = (aray[X] < 0.0) ? -1 : 1;
01305                 signY = (aray[Y] < 0.0) ? -1 : 1;
01306 
01307                 if (RT_G_DEBUG & DEBUG_HF ) {
01308                         bu_log("hf: curloc=(%g, %g, %g) aray=(%g,%g,%g)\n", curloc[X], curloc[Y],
01309                             curloc[Z], aray[X], aray[Y], aray[Z]);
01310                         bu_log("hf: from=(%g, %g) to=(%g, %g)\n",
01311                             goesIN[X]/xWidth,
01312                             goesIN[Y]/yWidth,
01313                             goesOUT[X]/xWidth,
01314                             goesOUT[Y]/yWidth);
01315                 }
01316                 error = curloc[X]-xCell*xWidth;
01317                 error /= xWidth;
01318 
01319 /*              delta = aray[X]/xWidth; */
01320                 delta = aray[X]/fabs(aray[Y]);
01321 
01322 #if 0
01323 bu_log("aray[X]/aray[Y]=%g\n", delta);
01324 #endif
01325 
01326                 if (delta < 0.0) {
01327                         delta = -delta;
01328                         error = -error;
01329                 } else {
01330                         error -= 1.0;
01331                 }
01332 
01333                 /*
01334                  * if at the far end (Ylen) then we need to move one
01335                  * step forward along aray.
01336                  */
01337                 if (yCell >= hf->hf_n-1) {
01338                         yCell+=signY;
01339                         error += delta;
01340                 }
01341                 if (RT_G_DEBUG & DEBUG_HF) {
01342                         bu_log("hf: delta=%g, error=%g, %d, %d\n",
01343                            delta, error, xCell, yCell);
01344                 }
01345 
01346                 deltaZ = (aray[Z] < 0) ? -aray[Z] : aray[Z];
01347                 do {
01348                         farZ = curloc[Z] + aray[Z];
01349                         maxZ = (curloc[Z] > farZ) ? curloc[Z] : farZ;
01350                         minZ = (curloc[Z] < farZ) ? curloc[Z] : farZ;
01351                         if (RT_G_DEBUG & DEBUG_HF) {
01352                                 bu_log("hf: cell %d,%d [%g -- %g] ",
01353                                 xCell, yCell, minZ, maxZ);
01354                         }
01355 /* CTJ - Or maxZ < hf->hf_min */
01356                         if (xCell < 0 || xCell > hf->hf_w-2) {
01357                                 if (error > -SQRT_SMALL_FASTF) {
01358                                         if (xCell >= -1) goto skip_2nd;
01359                                         xCell += signX;
01360                                         error -= 1.0;
01361                                 }
01362                                 yCell += signY;
01363                                 error += delta;
01364                                 VADD2(curloc, curloc, aray);
01365                                 continue;
01366                         }
01367 
01368                         if (hf->hf_shorts) {
01369                                 register unsigned short *sp;
01370                                 sp = (unsigned short *)hf->hf_mp->apbuf +
01371                                     yCell * hf->hf_w + xCell;
01372                                 /* 0,0 */
01373                                 highest = lowest = *sp++;
01374                                 /* 1,0 */
01375                                 if (lowest > (double)*sp) lowest=*sp;
01376                                 if (highest < (double)*sp) highest=*sp;
01377                                 sp+=hf->hf_w;
01378                                 /* 1,1 */
01379                                 if (lowest > (double)*sp) lowest=*sp;
01380                                 if (highest < (double)*sp) highest=*sp;
01381                                 sp--;
01382                                 /* 0,1 */
01383                                 if (lowest > (double)*sp) lowest = *sp;
01384                                 if (highest < (double)*sp) highest = *sp;
01385                                 lowest *= hf->hf_file2mm;
01386                                 highest *= hf->hf_file2mm;
01387                         } else {
01388                                 register double *fp;
01389                                 fp = (double *)hf->hf_mp->apbuf +
01390                                     yCell * hf->hf_w + xCell;
01391                                 /* 0,0 */
01392                                 highest = lowest = *fp++;
01393                                 /* 1,0 */
01394                                 if (lowest > *fp) lowest=*fp;
01395                                 if (highest < *fp) highest=*fp;
01396                                 fp+=hf->hf_w;
01397                                 /* 1,1 */
01398                                 if (lowest > *fp) lowest=*fp;
01399                                 if (highest < *fp) highest=*fp;
01400                                 fp--;
01401                                 /* 0,1 */
01402                                 if (lowest > *fp) lowest = *fp;
01403                                 if (highest < *fp) highest = *fp;
01404                                 lowest *= hf->hf_file2mm;
01405                                 highest *= hf->hf_file2mm;
01406                         }
01407 
01408 
01409                         if (RT_G_DEBUG & DEBUG_HF) {
01410                                 bu_log("lowest=%g, highest=%g\n",
01411                                     lowest, highest);
01412                         }
01413 
01414 /*
01415  * This is the primary test.  It is designed to get all cells that the
01416  * ray passes through.
01417  */
01418 #if 1
01419                         if (maxZ+deltaZ > lowest &&
01420                             minZ-deltaZ < highest ) {
01421 #else
01422                         {
01423 #endif
01424                                 int r;
01425                                 if ( (r=rt_hf_cell_shot(stp, rp, ap, hp, xCell, yCell)) ) {
01426                                         if ((nhits+=r)>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
01427                                         hp+=r;
01428                                 }
01429                         }
01430 /*
01431  * This is the DDA trying to fill in the corners as it walks the
01432  * path.
01433  */
01434 skip_2nd:
01435                         if (error > SQRT_SMALL_FASTF) {
01436                                 xCell += signX;
01437                                 if (RT_G_DEBUG & DEBUG_HF) {
01438                                         bu_log("hf: cell %d,%d\n", xCell, yCell);
01439                                 }
01440                                 if ((xCell < 0) || xCell > hf->hf_w-2) {
01441                                         error -= 1.0;
01442                                         yCell += signY;
01443                                         error += delta;
01444                                         VADD2(curloc, curloc, aray);
01445                                         continue;
01446                                 }
01447                                 if (hf->hf_shorts) {
01448                                         register unsigned short *sp;
01449                                         sp = (unsigned short *)hf->hf_mp->apbuf +
01450                                             yCell * hf->hf_w + xCell;
01451                                         /* 0,0 */
01452                                         highest = lowest = *sp++;
01453                                         /* 1,0 */
01454                                         if (lowest > (double)*sp) lowest=*sp;
01455                                         if (highest < (double)*sp) highest=*sp;
01456                                         sp+=hf->hf_w;
01457                                         /* 1,1 */
01458                                         if (lowest > (double)*sp) lowest=*sp;
01459                                         if (highest < (double)*sp) highest=*sp;
01460                                         sp--;
01461                                         /* 0,1 */
01462                                         if (lowest > (double)*sp) lowest = *sp;
01463                                         if (highest < (double)*sp) highest = *sp;
01464                                         lowest *= hf->hf_file2mm;
01465                                         highest *= hf->hf_file2mm;
01466                                 } else {
01467                                         register double *fp;
01468                                         fp = (double *)hf->hf_mp->apbuf +
01469                                             yCell * hf->hf_w + xCell;
01470                                         /* 0,0 */
01471                                         highest = lowest = *fp++;
01472                                         /* 1,0 */
01473                                         if (lowest > *fp) lowest=*fp;
01474                                         if (highest < *fp) highest=*fp;
01475                                         fp+=hf->hf_w;
01476                                         /* 1,1 */
01477                                         if (lowest > *fp) lowest=*fp;
01478                                         if (highest < *fp) highest=*fp;
01479                                         fp--;
01480                                         /* 0,1 */
01481                                         if (lowest > *fp) lowest = *fp;
01482                                         if (highest < *fp) highest = *fp;
01483                                         lowest *= hf->hf_file2mm;
01484                                         highest *= hf->hf_file2mm;
01485                                 }
01486 #if 1
01487                                 if (maxZ+deltaZ > lowest &&
01488                                     minZ-deltaZ < highest) {
01489 #else
01490                                 {
01491 #endif
01492                                         int r;
01493                                         /* DO HIT */
01494                                         if ( (r=rt_hf_cell_shot(stp, rp, ap, hp, xCell, yCell)) ) {
01495                                                 if ((nhits+=r)>=MAXHITS) rt_bomb("g_hf.c: too many hits.\n");
01496                                                 hp+=r;
01497                                         }
01498                                 }
01499                                 error -= 1.0;
01500                         } else if (error > -SQRT_SMALL_FASTF) {
01501                                 xCell += signX;
01502                                 error -= 1.0;
01503                         }
01504                         yCell += signY;
01505                         error += delta;
01506                         VADD2(curloc, curloc, aray);
01507                 } while (yCell >= 0 && yCell < hf->hf_n-1 );
01508                 if (RT_G_DEBUG & DEBUG_HF) {
01509                         bu_log("htf: leaving loop, %d, %d, %g vs. 0--%d, 0--%d, 0.0--%g\n",
01510                            xCell, yCell, curloc[Z], hf->hf_w-1, hf->hf_n-1, hf->hf_max);
01511                 }
01512         }
01513         /* Sort hits, near to Far */
01514         {
01515                 register int i,j;
01516                 LOCAL struct hit tmp;
01517                 for ( i=0; i< nhits-1; i++) {
01518                         for (j=i+1; j<nhits; j++) {
01519                                 if (hits[i].hit_dist <= hits[j].hit_dist) continue;
01520                                 tmp = hits[j];
01521                                 hits[j]=hits[i];
01522                                 hits[i]=tmp;
01523                         }
01524                 }
01525         }
01526         if ( nhits & 1) {
01527                 register int i;
01528                 static int nerrors = 0;
01529                 hits[nhits] = hits[nhits-1];    /* struct copy*/
01530                 VREVERSE(hits[nhits].hit_normal, hits[nhits-1].hit_normal);
01531                 nhits++;
01532                 if (nerrors++ < 300 ) {
01533                         bu_log("rt_hf_shot(%s): %d hit(s)@ %d %d: ", stp->st_name, nhits-1,ap->a_x, ap->a_y);
01534                         for(i=0; i< nhits; i++) {
01535                                 bu_log("%f(%d), ",hits[i].hit_dist,hits[i].hit_surfno);
01536                         }
01537                         bu_log("\n");
01538                 }
01539 #if 0
01540 rt_g.debug |= DEBUG_HF;
01541 rt_bomb("Odd number of hits.");
01542 #endif
01543         }
01544         /* nhits is even, build segments */
01545         {
01546                 register struct seg *segp;
01547                 register int i;
01548                 for (i=0; i< nhits; i+=2) {
01549                         RT_GET_SEG(segp, ap->a_resource);
01550                         segp->seg_stp = stp;
01551                         segp->seg_in = hits[i];
01552                         segp->seg_out= hits[i+1];
01553                         BU_LIST_INSERT( &(seghead->l), &(segp->l));
01554                 }
01555         }
01556         return nhits;   /* hits or misses */
01557 }
01558 /**
01559  *                      R T _ H F _ N O R M
01560  *
01561  *  Given ONE ray distance, return the normal and entry/exit point.
01562  */
01563 void
01564 rt_hf_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
01565 {
01566         register struct hf_specific *hf =
01567                 (struct hf_specific *)stp->st_specific;
01568         register int j;
01569 
01570         j = hitp->hit_surfno;
01571 
01572         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
01573         if (j >= 0) {
01574                 /* Normals computed in rt_htf_shot, nothing to do here. */
01575                 return;
01576         }
01577 
01578         switch (j) {
01579         case -1:
01580                 VREVERSE(hitp->hit_normal, hf->hf_X);
01581                 break;
01582         case -2:
01583                 VREVERSE(hitp->hit_normal, hf->hf_Y);
01584                 break;
01585         case -3:
01586                 VREVERSE(hitp->hit_normal, hf->hf_N);
01587                 break;
01588         case -4:
01589                 VMOVE(hitp->hit_normal, hf->hf_X);
01590                 break;
01591         case -5:
01592                 VMOVE(hitp->hit_normal, hf->hf_Y);
01593                 break;
01594         case -6:
01595                 VMOVE(hitp->hit_normal, hf->hf_N);
01596                 break;
01597         }
01598         VUNITIZE(hitp->hit_normal);
01599 
01600 }
01601 
01602 /**
01603  *                      R T _ H F _ C U R V E
01604  *
01605  *  Return the curvature of the hf.
01606  */
01607 void
01608 rt_hf_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
01609 {
01610         cvp->crv_c1 = cvp->crv_c2 = 0;
01611 
01612         /* any tangent direction */
01613         bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
01614 }
01615 
01616 /**
01617  *                      R T _ H F _ U V
01618  *
01619  *  For a hit on the surface of an hf, return the (u,v) coordinates
01620  *  of the hit point, 0 <= u,v <= 1.
01621  *  u = azimuth
01622  *  v = elevation
01623  */
01624 void
01625 rt_hf_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
01626 {
01627         register struct hf_specific *hf =
01628                 (struct hf_specific *)stp->st_specific;
01629         vect_t delta;
01630         fastf_t r = 0;
01631 
01632         VSUB2(delta, hitp->hit_point, hf->hf_V);
01633 #if 0
01634         VUNITIZE(delta);
01635         uvp->uv_u = VDOT(delta, hf->hf_X);
01636         uvp->uv_v = VDOT(delta, hf->hf_Y);
01637         r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
01638 #else
01639         uvp->uv_u = delta[X] / hf->hf_Xlen;
01640         uvp->uv_v = delta[Y] / hf->hf_Ylen;
01641         r = 0.0;
01642 #endif
01643         if (uvp->uv_u < 0.0) uvp->uv_u=0.0;
01644         if (uvp->uv_u > 1.0) uvp->uv_u=1.0;
01645         if (uvp->uv_v < 0.0) uvp->uv_v=0.0;
01646         if (uvp->uv_v > 1.0) uvp->uv_v=1.0;
01647 
01648         uvp->uv_du = r;
01649         uvp->uv_dv = r;
01650 }
01651 
01652 /**
01653  *              R T _ H F _ F R E E
01654  */
01655 void
01656 rt_hf_free(register struct soltab *stp)
01657 {
01658         register struct hf_specific *hf =
01659                 (struct hf_specific *)stp->st_specific;
01660 
01661         if (hf->hf_mp) {
01662                 bu_close_mapped_file(hf->hf_mp);
01663                 hf->hf_mp = (struct bu_mapped_file *)0;
01664         }
01665         bu_free( (char *)hf, "hf_specific" );
01666 }
01667 
01668 /**
01669  *                      R T _ H F _ C L A S S
01670  */
01671 int
01672 rt_hf_class(void)
01673 {
01674         return(0);
01675 }
01676 
01677 /**
01678  *                      R T _ H F _ P L O T
01679  */
01680 int
01681 rt_hf_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01682 {
01683         LOCAL struct rt_hf_internal     *xip;
01684         register unsigned short         *sp = (unsigned short *)NULL;
01685         register double *dp;
01686         vect_t          xbasis;
01687         vect_t          ybasis;
01688         vect_t          zbasis;
01689         point_t         start;
01690         point_t         cur;
01691         int             x;
01692         int             y;
01693         int             cmd;
01694         int             step;
01695         int             half_step;
01696         int             goal;
01697 
01698         RT_CK_DB_INTERNAL(ip);
01699         xip = (struct rt_hf_internal *)ip->idb_ptr;
01700         RT_HF_CK_MAGIC(xip);
01701 
01702         VSCALE( xbasis, xip->x, xip->xlen / (xip->w - 1) );
01703         VSCALE( ybasis, xip->y, xip->ylen / (xip->n - 1) );
01704         VCROSS( zbasis, xip->x, xip->y );
01705         VSCALE( zbasis, zbasis, xip->zscale * xip->file2mm );
01706 
01707         /* XXX This should be set from the tessellation tolerance */
01708         goal = 20000;
01709 
01710         /* Draw the 4 corners of the base plate */
01711         RT_ADD_VLIST( vhead, xip->v, BN_VLIST_LINE_MOVE );
01712 
01713         VJOIN1( start, xip->v, xip->xlen, xip->x );
01714         RT_ADD_VLIST( vhead, start, BN_VLIST_LINE_DRAW );
01715 
01716         VJOIN2( start, xip->v, xip->xlen, xip->x, xip->ylen, xip->y );
01717         RT_ADD_VLIST( vhead, start, BN_VLIST_LINE_DRAW );
01718 
01719         VJOIN1( start, xip->v, xip->ylen, xip->y );
01720         RT_ADD_VLIST( vhead, start, BN_VLIST_LINE_DRAW );
01721 
01722         RT_ADD_VLIST( vhead, xip->v, BN_VLIST_LINE_DRAW );
01723         goal -= 5;
01724 
01725 #define HF_GET(_p,_x,_y)        ((_p)[(_y)*xip->w+(_x)])
01726         /*
01727          *  Draw the four "ridge lines" at full resolution, for edge matching.
01728          */
01729         if (xip->shorts) {
01730                 /* X direction, Y=0, with edges down to base */
01731                 RT_ADD_VLIST( vhead, xip->v, BN_VLIST_LINE_MOVE );
01732                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, 0, 0 );
01733                 for( x = 0; x < xip->w; x++ )  {
01734                         VJOIN2( cur, xip->v, x, xbasis, *sp, zbasis );
01735                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01736                         sp++;
01737                 }
01738                 VJOIN1( cur, xip->v, xip->xlen, xip->x );
01739                 RT_ADD_VLIST( vhead, cur, BN_VLIST_LINE_DRAW );
01740 
01741                 /* X direction, Y=n-1, with edges down to base */
01742                 VJOIN1( start, xip->v, xip->ylen, xip->y );
01743                 RT_ADD_VLIST( vhead, start, BN_VLIST_LINE_MOVE );
01744                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, 0, xip->n - 1 );
01745                 VJOIN1( start, xip->v, xip->ylen, xip->y );
01746                 for( x = 0; x < xip->w; x++ )  {
01747                         VJOIN2( cur, start, x, xbasis, *sp, zbasis );
01748                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01749                         sp++;
01750                 }
01751                 VJOIN2( cur, xip->v, xip->xlen, xip->x, xip->ylen, xip->y );
01752                 RT_ADD_VLIST( vhead, cur, BN_VLIST_LINE_DRAW );
01753 
01754                 /* Y direction, X=0 */
01755                 cmd = BN_VLIST_LINE_MOVE;
01756                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, 0, 0 );
01757                 for( y = 0; y < xip->n; y++ )  {
01758                         VJOIN2( cur, xip->v, y, ybasis, *sp, zbasis );
01759                         RT_ADD_VLIST(vhead, cur, cmd );
01760                         cmd = BN_VLIST_LINE_DRAW;
01761                         sp += xip->w;
01762                 }
01763 
01764                 /* Y direction, X=w-1 */
01765                 cmd = BN_VLIST_LINE_MOVE;
01766                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, xip->w - 1, 0 );
01767                 VJOIN1( start, xip->v, xip->xlen, xip->x );
01768                 for( y = 0; y < xip->n; y++ )  {
01769                         VJOIN2( cur, start, y, ybasis, *sp, zbasis );
01770                         RT_ADD_VLIST(vhead, cur, cmd );
01771                         cmd = BN_VLIST_LINE_DRAW;
01772                         sp += xip->w;
01773                 }
01774         } else {
01775                 /* X direction, Y=0, with edges down to base */
01776                 RT_ADD_VLIST( vhead, xip->v, BN_VLIST_LINE_MOVE );
01777                 dp = &HF_GET((double *)xip->mp->apbuf, 0, 0 );
01778                 for( x = 0; x < xip->w; x++ )  {
01779                         VJOIN2( cur, xip->v, x, xbasis, *dp, zbasis );
01780                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01781                         dp++;
01782                 }
01783                 VJOIN1( cur, xip->v, xip->xlen, xip->x );
01784                 RT_ADD_VLIST( vhead, cur, BN_VLIST_LINE_DRAW );
01785 
01786                 /* X direction, Y=n-1, with edges down to base */
01787                 VJOIN1( start, xip->v, xip->ylen, xip->y );
01788                 RT_ADD_VLIST( vhead, start, BN_VLIST_LINE_MOVE );
01789                 dp = &HF_GET((double *)xip->mp->apbuf, 0, xip->n - 1 );
01790                 VJOIN1( start, xip->v, xip->ylen, xip->y );
01791                 for( x = 0; x < xip->w; x++ )  {
01792                         VJOIN2( cur, start, x, xbasis, *dp, zbasis );
01793                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01794                         dp++;
01795                 }
01796                 VJOIN2( cur, xip->v, xip->xlen, xip->x, xip->ylen, xip->y );
01797                 RT_ADD_VLIST( vhead, cur, BN_VLIST_LINE_DRAW );
01798 
01799                 /* Y direction, X=0 */
01800                 cmd = BN_VLIST_LINE_MOVE;
01801                 dp = &HF_GET((double *)xip->mp->apbuf, 0, 0 );
01802                 for( y = 0; y < xip->n; y++ )  {
01803                         VJOIN2( cur, xip->v, y, ybasis, *dp, zbasis );
01804                         RT_ADD_VLIST(vhead, cur, cmd );
01805                         cmd = BN_VLIST_LINE_DRAW;
01806                         sp += xip->w;
01807                 }
01808 
01809                 /* Y direction, X=w-1 */
01810                 cmd = BN_VLIST_LINE_MOVE;
01811                 dp = &HF_GET((double *)xip->mp->apbuf, xip->w - 1, 0 );
01812                 VJOIN1( start, xip->v, xip->xlen, xip->x );
01813                 for( y = 0; y < xip->n; y++ )  {
01814                         VJOIN2( cur, start, y, ybasis, *dp, zbasis );
01815                         RT_ADD_VLIST(vhead, cur, cmd );
01816                         cmd = BN_VLIST_LINE_DRAW;
01817                         dp += xip->w;
01818                 }
01819         }
01820         goal -= 4 + 2 * (xip->w + xip->n);
01821 
01822         /* Apply relative tolerance, if specified */
01823         if( ttol->rel )  {
01824                 int     rstep;
01825                 rstep = xip->w;
01826                 V_MAX( rstep, xip->n );
01827                 step = (int)(ttol->rel * rstep);
01828         } else {
01829                 /* No relative tol specified, limit drawing to 'goal' # of vectors */
01830                 if( goal <= 0 )  return 0;              /* no vectors for interior */
01831 
01832                 /* Compute data stride based upon producing no more than 'goal' vectors */
01833                 step = ceil(sqrt( 2*(xip->w-1)*(xip->n-1) / (double)goal ));
01834         }
01835         if( step < 1 )  step = 1;
01836         if( (half_step = step/2) < 1 )  half_step = 1;
01837 
01838         /* Draw the contour lines in W (x) direction.  Don't redo ridges. */
01839         for( y = half_step; y < xip->n-half_step; y += step )  {
01840                 VJOIN1( start, xip->v, y, ybasis );
01841                 x = 0;
01842                 if (xip->shorts) {
01843                         sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01844                         VJOIN2( cur, start, x, xbasis, *sp, zbasis );
01845                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_MOVE );
01846                         x += half_step;
01847                         sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01848                         for( ; x < xip->w; x += step )  {
01849                                 VJOIN2( cur, start, x, xbasis, *sp, zbasis );
01850                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01851                                 sp += step;
01852                         }
01853                         if( x != step+xip->w-1+step )  {
01854                                 x = xip->w - 1;
01855                                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01856                                 VJOIN2( cur, start, x, xbasis, *sp, zbasis );
01857                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01858                         }
01859                 } else { /* doubles */
01860                         dp = &HF_GET((double *)xip->mp->apbuf, x, y );
01861                         VJOIN2( cur, start, x, xbasis, *dp, zbasis );
01862                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_MOVE );
01863                         x += half_step;
01864                         dp = &HF_GET((double *)xip->mp->apbuf, x, y);
01865                         for (; x < xip->w; x+=step) {
01866                                 VJOIN2( cur, start, x, xbasis, *dp, zbasis);
01867                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW);
01868                                 dp += step;
01869                         }
01870                         if (x != step+xip->w-1+step) {
01871                                 x = xip->w - 1;
01872                                 dp = &HF_GET((double *)xip->mp->apbuf, x, y);
01873                                 VJOIN2( cur, start, x, xbasis, *dp, zbasis );
01874                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW);
01875                         }
01876                 }
01877         }
01878 
01879         /* Draw the contour lines in the N (y) direction */
01880         if (xip->shorts) {
01881                 for( x = half_step; x < xip->w-half_step; x += step )  {
01882                         VJOIN1( start, xip->v, x, xbasis );
01883                         y = 0;
01884                         sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01885                         VJOIN2( cur, start, y, ybasis, *sp, zbasis );
01886                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_MOVE );
01887                         y += half_step;
01888                         for( ; y < xip->n; y += step )  {
01889                                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01890                                 VJOIN2( cur, start, y, ybasis, *sp, zbasis );
01891                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01892                         }
01893                         if( y != step+xip->n-1+step )  {
01894                                 y = xip->n - 1;
01895                                 sp = &HF_GET((unsigned short *)xip->mp->apbuf, x, y );
01896                                 VJOIN2( cur, start, y, ybasis, *sp, zbasis );
01897                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01898                         }
01899                 }
01900         } else { /* doubles */
01901                 for( x = half_step; x < xip->w-half_step; x += step )  {
01902                         VJOIN1( start, xip->v, x, xbasis );
01903                         y = 0;
01904                         dp = &HF_GET((double *)xip->mp->apbuf, x, y );
01905                         VJOIN2( cur, start, y, ybasis, *dp, zbasis );
01906                         RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_MOVE );
01907                         y += half_step;
01908                         for( ; y < xip->n; y += step )  {
01909                                 dp = &HF_GET((double *)xip->mp->apbuf, x, y );
01910                                 VJOIN2( cur, start, y, ybasis, *dp, zbasis );
01911                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01912                         }
01913                         if( y != step+xip->n-1+step )  {
01914                                 y = xip->n - 1;
01915                                 dp = &HF_GET((double *)xip->mp->apbuf, x, y );
01916                                 VJOIN2( cur, start, y, ybasis, *dp, zbasis );
01917                                 RT_ADD_VLIST(vhead, cur, BN_VLIST_LINE_DRAW );
01918                         }
01919                 }
01920         }
01921         return 0;
01922 }
01923 
01924 /**
01925  *                      R T _ H F _ T E S S
01926  *
01927  *  Returns -
01928  *      -1      failure
01929  *       0      OK.  *r points to nmgregion that holds this tessellation.
01930  */
01931 int
01932 rt_hf_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01933 {
01934         LOCAL struct rt_hf_internal     *xip;
01935 
01936         RT_CK_DB_INTERNAL(ip);
01937         xip = (struct rt_hf_internal *)ip->idb_ptr;
01938         RT_HF_CK_MAGIC(xip);
01939 
01940         return(-1);
01941 }
01942 
01943 /**
01944  *                      R T _ H F _ I M P O R T
01945  *
01946  *  Import an HF from the database format to the internal format.
01947  *  Apply modeling transformations as well.
01948  */
01949 int
01950 rt_hf_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01951 {
01952         LOCAL struct rt_hf_internal     *xip;
01953         union record                    *rp;
01954         struct bu_vls                   str;
01955         struct bu_mapped_file           *mp;
01956         vect_t                          tmp;
01957         int                             in_cookie;      /* format cookie */
01958         int                             in_len;
01959         int                             out_cookie;
01960         int                             count;
01961         int                             got;
01962 
01963         BU_CK_EXTERNAL( ep );
01964         rp = (union record *)ep->ext_buf;
01965         /* Check record type */
01966         if( rp->u_id != DBID_STRSOL )  {
01967                 bu_log("rt_hf_import: defective record\n");
01968                 return(-1);
01969         }
01970 
01971         RT_CK_DB_INTERNAL( ip );
01972         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01973         ip->idb_type = ID_HF;
01974         ip->idb_meth = &rt_functab[ID_HF];
01975         ip->idb_ptr = bu_calloc( 1, sizeof(struct rt_hf_internal), "rt_hf_internal");
01976         xip = (struct rt_hf_internal *)ip->idb_ptr;
01977         xip->magic = RT_HF_INTERNAL_MAGIC;
01978 
01979         /* Provide defaults.  Only non-defaulted fields are dfile, w, n */
01980         xip->shorts = 1;                /* for now */
01981         xip->file2mm = 1.0;
01982         VSETALL( xip->v, 0 );
01983         VSET( xip->x, 1, 0, 0 );
01984         VSET( xip->y, 0, 1, 0 );
01985         xip->xlen = 1000;
01986         xip->ylen = 1000;
01987         xip->zscale = 1;
01988         strcpy( xip->fmt, "nd" );
01989 
01990         /* Process parameters found in .g file */
01991         bu_vls_init( &str );
01992         bu_vls_strcpy( &str, rp->ss.ss_args );
01993         if( bu_struct_parse( &str, rt_hf_parse, (char *)xip ) < 0 )  {
01994                 bu_vls_free( &str );
01995 err1:
01996                 bu_free( (char *)xip , "rt_hf_import: xip" );
01997                 ip->idb_type = ID_NULL;
01998                 ip->idb_ptr = (genptr_t)NULL;
01999                 return -2;
02000         }
02001         bu_vls_free( &str );
02002 
02003         /* If "cfile" was specified, process parameters from there */
02004         if( xip->cfile[0] )  {
02005                 FILE    *fp;
02006 
02007                 bu_semaphore_acquire( BU_SEM_SYSCALL );
02008                 fp = fopen( xip->cfile, "r" );
02009                 bu_semaphore_release( BU_SEM_SYSCALL );
02010                 if( !fp )  {
02011                         perror(xip->cfile);
02012                         bu_log("rt_hf_import() unable to open cfile=%s\n", xip->cfile);
02013                         goto err1;
02014                 }
02015                 bu_vls_init( &str );
02016                 while( bu_vls_gets( &str, fp ) >= 0 )
02017                         bu_vls_strcat( &str, " " );
02018                 bu_semaphore_acquire( BU_SEM_SYSCALL );
02019                 fclose(fp);
02020                 bu_semaphore_release( BU_SEM_SYSCALL );
02021                 if( bu_struct_parse( &str, rt_hf_cparse, (char *)xip ) < 0 )  {
02022                         bu_log("rt_hf_import() parse error in cfile input '%s'\n",
02023                                 bu_vls_addr(&str) );
02024                         bu_vls_free( &str );
02025                         goto err1;
02026                 }
02027         }
02028 
02029         /* Check for reasonable values */
02030         if( !xip->dfile[0] )  {
02031                 /* XXX Should create 2x2 data file instead, for positioning use (FPO) */
02032                 bu_log("rt_hf_import() no dfile specified\n");
02033                 goto err1;
02034         }
02035         if( xip->w < 2 || xip->n < 2 )  {
02036                 bu_log("rt_hf_import() w=%d, n=%d too small\n");
02037                 goto err1;
02038         }
02039         if( xip->xlen <= 0 || xip->ylen <= 0 )  {
02040                 bu_log("rt_hf_import() xlen=%g, ylen=%g too small\n", xip->xlen, xip->ylen);
02041                 goto err1;
02042         }
02043 
02044         /* Apply modeling transformations */
02045         MAT4X3PNT( tmp, mat, xip->v );
02046         VMOVE( xip->v, tmp );
02047         MAT4X3VEC( tmp, mat, xip->x );
02048         VMOVE( xip->x, tmp );
02049         MAT4X3VEC( tmp, mat, xip->y );
02050         VMOVE( xip->y, tmp );
02051         xip->xlen /= mat[15];
02052         xip->ylen /= mat[15];
02053         xip->zscale /= mat[15];
02054 
02055         VUNITIZE(xip->x);
02056         VUNITIZE(xip->y);
02057 
02058         /* Prepare for cracking input file format */
02059         if( (in_cookie = bu_cv_cookie( xip->fmt )) == 0 )  {
02060                 bu_log("rt_hf_import() fmt='%s' unknown\n", xip->fmt);
02061                 goto err1;
02062         }
02063         in_len = bu_cv_itemlen( in_cookie );
02064 
02065         /*
02066          *  Load data file, and transform to internal format
02067          */
02068         if( !(mp = bu_open_mapped_file( xip->dfile, "hf" )) )  {
02069                 bu_log("rt_hf_import() unable to open '%s'\n", xip->dfile);
02070                 goto err1;
02071         }
02072         xip->mp = mp;
02073         count = mp->buflen / in_len;
02074 
02075         /* If this data has already been mapped, all done */
02076         if( mp->apbuf )  return 0;              /* OK */
02077 
02078         /* Transform external data to internal format -- short or double */
02079         if( xip->shorts )  {
02080                 mp->apbuflen = sizeof(unsigned short) * count;
02081                 out_cookie = bu_cv_cookie("hus");
02082         } else {
02083                 mp->apbuflen = sizeof(double) * count;
02084                 out_cookie = bu_cv_cookie("hd");
02085         }
02086 
02087         if( bu_cv_optimize(in_cookie) == bu_cv_optimize(out_cookie) )  {
02088                 /* Don't replicate the data, just re-use the pointer */
02089                 mp->apbuf = mp->buf;
02090                 return 0;               /* OK */
02091         }
02092 
02093         mp->apbuf = (genptr_t)bu_malloc( mp->apbuflen, "rt_hf_import apbuf[]" );
02094         got = bu_cv_w_cookie( mp->apbuf, out_cookie, mp->apbuflen,
02095                 mp->buf, in_cookie, count );
02096         if( got != count )  {
02097                 bu_log("rt_hf_import(%s) bu_cv_w_cookie count=%d, got=%d\n",
02098                         xip->dfile, count, got );
02099         }
02100 
02101         return(0);                      /* OK */
02102 }
02103 
02104 /**
02105  *                      R T _ H F _ E X P O R T
02106  *
02107  *  The name is added by the caller, in the usual place.
02108  *
02109  *  The meaning of the export here is slightly different than that of
02110  *  most other solids.  The cfile and dfile are not modified, only
02111  *  changes to the string solid parameters are placed back into the .g file.
02112  *  Note that any parameters taken from a cfile are included in the new
02113  *  string solid.  This isn't a problem, because if the cfile is changed
02114  *  (perhaps to substitute a different resolution height field of the same
02115  *  location in space), it's new parameters will override those stored
02116  *  in the string solid (including the dfile name).
02117  */
02118 int
02119 rt_hf_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
02120 {
02121         struct rt_hf_internal   *xip;
02122         union record            *rec;
02123         struct bu_vls           str;
02124 
02125         RT_CK_DB_INTERNAL(ip);
02126         if( ip->idb_type != ID_HF )  return(-1);
02127         xip = (struct rt_hf_internal *)ip->idb_ptr;
02128         RT_HF_CK_MAGIC(xip);
02129 
02130         /* Apply any scale transformation */
02131         xip->xlen /= local2mm;
02132         xip->ylen /= local2mm;
02133         xip->zscale /= local2mm;
02134 
02135         BU_CK_EXTERNAL(ep);
02136         ep->ext_nbytes = sizeof(union record) * DB_SS_NGRAN;
02137         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "hf external");
02138         rec = (union record *)ep->ext_buf;
02139 
02140         bu_vls_init( &str );
02141         bu_vls_struct_print( &str, rt_hf_parse, (char *)xip );
02142 
02143         /* Any changes made by solid editing affect .g file only,
02144          * and not the cfile, if specified.
02145          */
02146 
02147         rec->s.s_id = DBID_STRSOL;
02148         strncpy( rec->ss.ss_keyword, "hf", NAMESIZE-1 );
02149         strncpy( rec->ss.ss_args, bu_vls_addr(&str), DB_SS_LEN-1 );
02150         bu_vls_free( &str );
02151 
02152         return(0);
02153 }
02154 
02155 int
02156 rt_hf_import5(struct rt_db_internal *ip, const struct bu_external *ep, const fastf_t *mat, const struct db_i *dbip)
02157 {
02158         bu_log( "As of release 6.0 the HF primitive is superceded by the DSP primitive.\n" );
02159         bu_log( "\tTo convert HF primitives to DSP, use 'dbupgrade'.\n");
02160         /* The rt_hf_to_dsp() routine can also be used */
02161         return -1;
02162 }
02163 
02164 int
02165 rt_hf_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
02166 {
02167         bu_log( "As of release 6.0 the HF primitive is superceded by the DSP primitive.\n" );
02168         bu_log( "\tTo convert HF primitives to DSP, use 'dbupgrade'.\n" );
02169         /* The rt_hf_to_dsp() routine can also be used */
02170         return -1;
02171 }
02172 
02173 /**
02174  *                      R T _ H F _ D E S C R I B E
02175  *
02176  *  Make human-readable formatted presentation of this solid.
02177  *  First line describes type of solid.
02178  *  Additional lines are indented one tab, and give parameter values.
02179  */
02180 int
02181 rt_hf_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
02182 {
02183 #ifndef NO_MAGIC_CHECKING
02184         register struct rt_hf_internal  *xip =
02185                 (struct rt_hf_internal *)ip->idb_ptr;
02186 
02187         BU_CK_VLS(str);
02188         RT_HF_CK_MAGIC(xip);
02189 #endif
02190         bu_vls_printf( str, "Height Field (HF)  mm2local=%g\n", mm2local);
02191         bu_vls_struct_print( str, rt_hf_parse, ip->idb_ptr );
02192         bu_vls_strcat( str, "\n" );
02193 
02194         return(0);
02195 }
02196 
02197 /**
02198  *                      R T _ H F _ I F R E E
02199  *
02200  *  Free the storage associated with the rt_db_internal version of this solid.
02201  */
02202 void
02203 rt_hf_ifree(struct rt_db_internal *ip)
02204 {
02205         register struct rt_hf_internal  *xip;
02206 
02207         RT_CK_DB_INTERNAL(ip);
02208         xip = (struct rt_hf_internal *)ip->idb_ptr;
02209         RT_HF_CK_MAGIC(xip);
02210         xip->magic = 0;                 /* sanity */
02211 
02212         if( xip->mp )
02213         {
02214                 BU_CK_MAPPED_FILE(xip->mp);
02215                 bu_close_mapped_file(xip->mp);
02216         }
02217 
02218         bu_free( (char *)xip, "hf ifree" );
02219         ip->idb_ptr = GENPTR_NULL;      /* sanity */
02220 }
02221 
02222 /*@}*/
02223 /*
02224  * Local Variables:
02225  * mode: C
02226  * tab-width: 8
02227  * c-basic-offset: 4
02228  * indent-tabs-mode: t
02229  * End:
02230  * ex: shiftwidth=4 tabstop=8
02231  */

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