g_arb.c

Go to the documentation of this file.
00001 /*                         G _ A R B . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1985-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup g_  */
00023 /*@{*/
00024 /** @file g_arb.c
00025  *  Intersect a ray with an Arbitrary Regular Polyhedron with as many as 8 vertices.
00026  *
00027  *  An ARB is a convex volume bounded by 4 (pyramid), 5 (wedge), or 6 (box)
00028  *  planes.  This analysis depends on the properties of objects with convex
00029  *  hulls.  Let the ray in question be defined such that any point X on the
00030  *  ray may be expressed as X = P + k D.  Intersect the ray with each of the
00031  *  planes bounding the ARB as discussed above, and record the values of the
00032  *  parametric distance k along the ray.
00033  *
00034  *  With outward pointing normal vectors,
00035  *  note that the ray enters the half-space defined by a plane when D cdot N <
00036  *  0, is parallel to the plane when D cdot N = 0, and exits otherwise.  Find
00037  *  the entry point farthest away from the starting point bold P, i.e.  it has
00038  *  the largest value of k among the entry points.
00039  *  The ray enters the solid at this point.
00040  *  Similarly, find the exit point closest to point P, i.e. it has
00041  *  the smallest value of k among the exit points.  The ray exits the solid
00042  *  here.
00043  *
00044  *  This algorithm is due to Cyrus & Beck, USAF.
00045  *
00046  *  Author -
00047  *      Michael John Muuss
00048  *
00049  *  Source -
00050  *      SECAD/VLD Computing Consortium, Bldg 394
00051  *      The U. S. Army Ballistic Research Laboratory
00052  *      Aberdeen Proving Ground, Maryland  21005
00053  *
00054  */
00055 #ifndef lint
00056 static const char RCSarb[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/g_arb.c,v 14.15 2006/09/16 02:04:24 lbutler Exp $ (BRL)";
00057 #endif
00058 
00059 #include "common.h"
00060 
00061 #include <stddef.h>
00062 #include <stdio.h>
00063 #include <math.h>
00064 #ifdef HAVE_STRING_H
00065 #  include <string.h>
00066 #else
00067 #  include <strings.h>
00068 #endif
00069 #include "machine.h"
00070 #include "bu.h"
00071 #include "vmath.h"
00072 #include "bn.h"
00073 #include "nmg.h"
00074 #include "db.h"
00075 #include "rtgeom.h"
00076 #include "raytrace.h"
00077 #include "nurb.h"
00078 #include "./debug.h"
00079 
00080 
00081 #define RT_SLOPPY_DOT_TOL       0.0087  /* inspired by RT_DOT_TOL, but less tight (.5 deg) */
00082 
00083 /* Optionally, one of these for each face.  (Lazy evaluation) */
00084 struct oface {
00085         fastf_t arb_UVorig[3];          /* origin of UV coord system */
00086         fastf_t arb_U[3];               /* unit U vector (along B-A) */
00087         fastf_t arb_V[3];               /* unit V vector (perp to N and U) */
00088         fastf_t arb_Ulen;               /* length of U basis (for du) */
00089         fastf_t arb_Vlen;               /* length of V basis (for dv) */
00090 };
00091 
00092 /* One of these for each face */
00093 struct aface {
00094         fastf_t A[3];                   /* "A" point */
00095         plane_t peqn;                   /* Plane equation, unit normal */
00096 };
00097 
00098 /* One of these for each ARB, custom allocated to size */
00099 struct arb_specific  {
00100         int             arb_nmfaces;    /* number of faces */
00101         struct oface    *arb_opt;       /* pointer to optional info */
00102         struct aface    arb_face[4];    /* May really be up to [6] faces */
00103 };
00104 
00105 /* These hold temp values for the face being prep'ed */
00106 struct prep_arb {
00107         vect_t          pa_center;      /* center point */
00108         int             pa_faces;       /* Number of faces done so far */
00109         int             pa_npts[6];     /* # of points on face's plane */
00110         int             pa_pindex[4][6]; /* subscr in arbi_pt[] */
00111         int             pa_clockwise[6];        /* face normal was flipped */
00112         struct aface    pa_face[6];     /* required face info work area */
00113         struct oface    pa_opt[6];      /* optional face info work area */
00114         /* These elements must be initialized before using */
00115         fastf_t         pa_tol_sq;      /* points-are-equal tol sq */
00116         int             pa_doopt;       /* compute pa_opt[] stuff */
00117 };
00118 
00119 /*
00120  *  Layout of arb in input record.
00121  *  Points are listed in "clockwise" order,
00122  *  to make proper outward-pointing face normals.
00123  *  (Although the cross product wants counter-clockwise order)
00124  */
00125 struct arb_info {
00126         char    *ai_title;
00127         int     ai_sub[4];
00128 };
00129 static const struct arb_info rt_arb_info[6] = {
00130         { "1234", {3, 2, 1, 0} },               /* "bottom" face */
00131         { "8765", {4, 5, 6, 7} },               /* "top" face */
00132         { "1485", {4, 7, 3, 0} },
00133         { "2673", {2, 6, 5, 1} },
00134         { "1562", {1, 5, 4, 0} },
00135         { "4378", {7, 6, 2, 3} }
00136 };
00137 
00138 BU_EXTERN(void rt_arb_ifree, (struct rt_db_internal *) );
00139 
00140 const struct bu_structparse rt_arb_parse[] = {
00141     { "%f", 3, "V1", bu_offsetof(struct rt_arb_internal, pt[0][X]), BU_STRUCTPARSE_FUNC_NULL },
00142     { "%f", 3, "V2", bu_offsetof(struct rt_arb_internal, pt[1][X]), BU_STRUCTPARSE_FUNC_NULL },
00143     { "%f", 3, "V3", bu_offsetof(struct rt_arb_internal, pt[2][X]), BU_STRUCTPARSE_FUNC_NULL },
00144     { "%f", 3, "V4", bu_offsetof(struct rt_arb_internal, pt[3][X]), BU_STRUCTPARSE_FUNC_NULL },
00145     { "%f", 3, "V5", bu_offsetof(struct rt_arb_internal, pt[4][X]), BU_STRUCTPARSE_FUNC_NULL },
00146     { "%f", 3, "V6", bu_offsetof(struct rt_arb_internal, pt[5][X]), BU_STRUCTPARSE_FUNC_NULL },
00147     { "%f", 3, "V7", bu_offsetof(struct rt_arb_internal, pt[6][X]), BU_STRUCTPARSE_FUNC_NULL },
00148     { "%f", 3, "V8", bu_offsetof(struct rt_arb_internal, pt[7][X]), BU_STRUCTPARSE_FUNC_NULL },
00149     { {'\0','\0','\0','\0'}, 0, (char *)NULL, 0, BU_STRUCTPARSE_FUNC_NULL }
00150 };
00151 
00152 /* face definitions for each arb type */
00153 const int rt_arb_faces[5][24] = {
00154     {0,1,2,3, 0,1,4,5, 1,2,4,5, 0,2,4,5, -1,-1,-1,-1, -1,-1,-1,-1},     /* ARB4 */
00155     {0,1,2,3, 4,0,1,5, 4,1,2,5, 4,2,3,5, 4,3,0,5, -1,-1,-1,-1},         /* ARB5 */
00156     {0,1,2,3, 1,2,4,6, 0,4,6,3, 4,1,0,5, 6,2,3,7, -1,-1,-1,-1},         /* ARB6 */
00157     {0,1,2,3, 4,5,6,7, 0,3,4,7, 1,2,6,5, 0,1,5,4, 3,2,6,4},             /* ARB7 */
00158     {0,1,2,3, 4,5,6,7, 0,4,7,3, 1,2,6,5, 0,1,5,4, 3,2,6,7},             /* ARB8 */
00159 };
00160 
00161 /*  rt_arb_get_cgtype(), rt_arb_std_type(), and rt_arb_centroid()
00162  *  stolen from mged/arbs.c */
00163 #define NO      0
00164 #define YES     1
00165 
00166 /**
00167  *                      R T _ A R B _ G E T _ C G T Y P E
00168  *
00169  * C G A R B S :   determines COMGEOM arb types from GED general arbs
00170  *
00171  *  Inputs -
00172  *
00173  *  Returns -
00174  *      #       Number of distinct edge vectors
00175  *              (Number of entries in uvec array)
00176  *
00177  *  Implicit returns -
00178  *      *cgtype         Comgeom type (number range 4..8;  ARB4 .. ARB8).
00179  *      uvec[8]
00180  *      svec[11]
00181  *                      Entries [0] and [1] are special
00182  */
00183 int
00184 rt_arb_get_cgtype(
00185         int                     *cgtype,
00186         struct rt_arb_internal  *arb,
00187         const struct bn_tol     *tol,
00188         register int *uvec,     /* array of unique points */
00189         register int *svec)     /* array of like points */
00190 {
00191         register int i,j;
00192         int     numuvec, unique, done;
00193         int     si;
00194 
00195         RT_ARB_CK_MAGIC(arb);
00196         BN_CK_TOL(tol);
00197 
00198         done = NO;              /* done checking for like vectors */
00199 
00200         svec[0] = svec[1] = 0;
00201         si = 2;
00202 
00203         for(i=0; i<7; i++) {
00204                 unique = YES;
00205                 if(done == NO) {
00206                         svec[si] = i;
00207                 }
00208                 for(j=i+1; j<8; j++) {
00209                         int tmp;
00210                         vect_t vtmp;
00211 
00212                         VSUB2( vtmp, arb->pt[i], arb->pt[j] );
00213 
00214                         if( fabs(vtmp[0]) > tol->dist) tmp = 0;
00215                         else    if( fabs(vtmp[1]) > tol->dist) tmp = 0;
00216                         else    if( fabs(vtmp[2]) > tol->dist) tmp = 0;
00217                         else tmp = 1;
00218 
00219                         if( tmp ) {
00220                                 if( done == NO )
00221                                         svec[++si] = j;
00222                                 unique = NO;
00223                         }
00224                 }
00225                 if( unique == NO ) {    /* point i not unique */
00226                         if( si > 2 && si < 6 ) {
00227                                 svec[0] = si - 1;
00228                                 if(si == 5 && svec[5] >= 6)
00229                                         done = YES;
00230                                 si = 6;
00231                         }
00232                         if( si > 6 ) {
00233                                 svec[1] = si - 5;
00234                                 done = YES;
00235                         }
00236                 }
00237         }
00238 
00239         if( si > 2 && si < 6 ) {
00240                 svec[0] = si - 1;
00241         }
00242         if( si > 6 ) {
00243                 svec[1] = si - 5;
00244         }
00245         for(i=1; i<=svec[1]; i++) {
00246                 svec[svec[0]+1+i] = svec[5+i];
00247         }
00248         for(i=svec[0]+svec[1]+2; i<11; i++) {
00249                 svec[i] = -1;
00250         }
00251 
00252         /* find the unique points */
00253         numuvec = 0;
00254         for(j=0; j<8; j++) {
00255                 unique = YES;
00256                 for(i=2; i<svec[0]+svec[1]+2; i++) {
00257                         if( j == svec[i] ) {
00258                                 unique = NO;
00259                                 break;
00260                         }
00261                 }
00262                 if( unique == YES ) {
00263                         uvec[numuvec++] = j;
00264                 }
00265         }
00266 
00267         /* Figure out what kind of ARB this is */
00268         switch( numuvec ) {
00269 
00270         case 8:
00271                 *cgtype = ARB8;         /* ARB8 */
00272                 break;
00273 
00274         case 6:
00275                 *cgtype = ARB7;         /* ARB7 */
00276                 break;
00277 
00278         case 4:
00279                 if(svec[0] == 2)
00280                         *cgtype = ARB6; /* ARB6 */
00281                 else
00282                         *cgtype = ARB5; /* ARB5 */
00283                 break;
00284 
00285         case 2:
00286                 *cgtype = ARB4;         /* ARB4 */
00287                 break;
00288 
00289         default:
00290                 bu_log( "rt_arb_get_cgtype: bad number of unique vectors (%d)\n",
00291                           numuvec);
00292 
00293                 return(0);
00294         }
00295 #if 0
00296         bu_log("uvec: ");
00297         for(j=0; j<8; j++) bu_log("%d, ", uvec[j]);
00298         bu_log("\nsvec: ");
00299         for(j=0; j<11; j++ ) bu_log("%d, ", svec[j]);
00300         bu_log("\n");
00301 #endif
00302         return( numuvec );
00303 }
00304 
00305 /**
00306  *                      R T _ A R B _ S T D _ T Y P E
00307  *
00308  *  Given an ARB in internal form, return it's specific ARB type.
00309  *
00310  *  Set tol.dist = 0.0001 to obtain past behavior.
00311  *
00312  *  Returns -
00313  *      0       Error in input ARB
00314  *      4       ARB4
00315  *      5       ARB5
00316  *      6       ARB6
00317  *      7       ARB7
00318  *      8       ARB8
00319  *
00320  *  Implicit return -
00321  *      rt_arb_internal pt[] array reorganized into GIFT "standard" order.
00322  */
00323 int
00324 rt_arb_std_type( const struct rt_db_internal *ip, const struct bn_tol *tol )
00325 {
00326         struct rt_arb_internal  *arb;
00327         int uvec[8], svec[11];
00328         int     cgtype = 0;
00329 
00330         RT_CK_DB_INTERNAL(ip);
00331         BN_CK_TOL(tol);
00332 
00333         if( ip->idb_type != ID_ARB8 )  bu_bomb("rt_arb_std_type: not ARB!\n");
00334 
00335         arb = (struct rt_arb_internal *)ip->idb_ptr;
00336         RT_ARB_CK_MAGIC(arb);
00337 
00338         if( rt_arb_get_cgtype( &cgtype, arb, tol, uvec, svec ) == 0 )
00339                 return(0);
00340 
00341         return( cgtype );
00342 }
00343 
00344 
00345 /**
00346  *                      R T _ A R B _ C E N T R O I D
00347  *
00348  * Find the center point for the arb whose values are in the s array,
00349  * with the given number of verticies.  Return the point in center_pt.
00350  */
00351 void
00352 rt_arb_centroid( point_t center_pt, const struct rt_arb_internal *arb, int npoints )
00353 {
00354         register int    j;
00355         fastf_t         div;
00356         point_t         sum;
00357 
00358         RT_ARB_CK_MAGIC(arb);
00359 
00360         VSETALL(sum, 0);
00361 
00362         for( j=0; j < npoints; j++ )  {
00363                 VADD2( sum, sum, arb->pt[j] );
00364         }
00365         div = 1.0 / npoints;
00366         VSCALE( center_pt, sum, div );
00367 }
00368 
00369 /**
00370  *                      R T _ A R B _ A D D _ P T
00371  *
00372  *  Add another point to a struct arb_specific, checking for unique pts.
00373  *  The first two points are easy.  The third one triggers most of the
00374  *  plane calculations, and forth and subsequent ones are merely
00375  *  checked for validity.
00376  *
00377  *  Returns -
00378  *       0      point was accepted
00379  *      -1      point was rejected
00380  */
00381 HIDDEN int
00382 rt_arb_add_pt(register pointp_t point, const char *title, struct prep_arb *pap, int ptno, const char *name)
00383 
00384 
00385 
00386                         /* current point # on face */
00387 
00388 {
00389         LOCAL vect_t    work;
00390         LOCAL vect_t    P_A;            /* new point minus A */
00391         FAST fastf_t    f;
00392         register struct aface   *afp;
00393         register struct oface   *ofp;
00394 
00395         afp = &pap->pa_face[pap->pa_faces];
00396         ofp = &pap->pa_opt[pap->pa_faces];
00397 
00398         /* The first 3 points are treated differently */
00399         switch( ptno )  {
00400         case 0:
00401                 VMOVE( afp->A, point );
00402                 if( pap->pa_doopt )  {
00403                         VMOVE( ofp->arb_UVorig, point );
00404                 }
00405                 return(0);                              /* OK */
00406         case 1:
00407                 VSUB2( ofp->arb_U, point, afp->A );     /* B-A */
00408                 f = MAGNITUDE( ofp->arb_U );
00409                 if( NEAR_ZERO( f, SQRT_SMALL_FASTF ) )  {
00410                         return(-1);                     /* BAD */
00411                 }
00412                 ofp->arb_Ulen = f;
00413                 f = 1/f;
00414                 VSCALE( ofp->arb_U, ofp->arb_U, f );
00415                 /* Note that arb_U is used to build N, below */
00416                 return(0);                              /* OK */
00417         case 2:
00418                 VSUB2( P_A, point, afp->A );    /* C-A */
00419                 /* Pts are given clockwise, so reverse terms of cross prod. */
00420                 /* peqn = (C-A)x(B-A), which points inwards */
00421                 VCROSS( afp->peqn, P_A, ofp->arb_U );
00422                 /* Check for co-linear, ie, |(B-A)x(C-A)| ~= 0 */
00423                 f = MAGNITUDE( afp->peqn );
00424                 if( NEAR_ZERO(f,RT_SLOPPY_DOT_TOL) )  {
00425                         return(-1);                     /* BAD */
00426                 }
00427                 f = 1/f;
00428                 VSCALE( afp->peqn, afp->peqn, f );
00429 
00430                 if( pap->pa_doopt )  {
00431                         /*
00432                          * Get vector perp. to AB in face of plane ABC.
00433                          * Scale by projection of AC, make this V.
00434                          */
00435                         VCROSS( work, afp->peqn, ofp->arb_U );
00436                         VUNITIZE( work );
00437                         f = VDOT( work, P_A );
00438                         VSCALE( ofp->arb_V, work, f );
00439                         f = MAGNITUDE( ofp->arb_V );
00440                         ofp->arb_Vlen = f;
00441                         f = 1/f;
00442                         VSCALE( ofp->arb_V, ofp->arb_V, f );
00443 
00444                         /* Check for new Ulen */
00445                         VSUB2( P_A, point, ofp->arb_UVorig );
00446                         f = VDOT( P_A, ofp->arb_U );
00447                         if( f > ofp->arb_Ulen ) {
00448                                 ofp->arb_Ulen = f;
00449                         } else if( f < 0.0 ) {
00450                                 VJOIN1( ofp->arb_UVorig, ofp->arb_UVorig, f,
00451                                         ofp->arb_U );
00452                                 ofp->arb_Ulen += (-f);
00453                         }
00454                 }
00455 
00456                 /*
00457                  *  If C-A is clockwise from B-A, then the normal
00458                  *  points inwards, so we need to fix it here.
00459                  *  Build a vector from the centroid to vertex A.
00460                  *  If the surface normal points in the same direction,
00461                  *  then the vertcies were given in CCW order;
00462                  *  otherwise, vertices were given in CW order, and
00463                  *  the normal needs to be flipped.
00464                  */
00465                 VSUB2( work, afp->A, pap->pa_center );
00466                 f = VDOT( work, afp->peqn );
00467                 if( f < 0.0 )  {
00468                         VREVERSE(afp->peqn, afp->peqn); /* "fix" normal */
00469                         pap->pa_clockwise[pap->pa_faces] = 1;
00470                 } else {
00471                         pap->pa_clockwise[pap->pa_faces] = 0;
00472                 }
00473                 afp->peqn[3] = VDOT( afp->peqn, afp->A );
00474                 return(0);                              /* OK */
00475         default:
00476                 /* Merely validate 4th and subsequent points */
00477                 if( pap->pa_doopt )  {
00478                         VSUB2( P_A, point, ofp->arb_UVorig );
00479                         /* Check for new Ulen, Vlen */
00480                         f = VDOT( P_A, ofp->arb_U );
00481                         if( f > ofp->arb_Ulen ) {
00482                                 ofp->arb_Ulen = f;
00483                         } else if( f < 0.0 ) {
00484                                 VJOIN1( ofp->arb_UVorig, ofp->arb_UVorig, f,
00485                                         ofp->arb_U );
00486                                 ofp->arb_Ulen += (-f);
00487                         }
00488                         f = VDOT( P_A, ofp->arb_V );
00489                         if( f > ofp->arb_Vlen ) {
00490                                 ofp->arb_Vlen = f;
00491                         } else if( f < 0.0 ) {
00492                                 VJOIN1( ofp->arb_UVorig, ofp->arb_UVorig, f,
00493                                         ofp->arb_V );
00494                                 ofp->arb_Vlen += (-f);
00495                         }
00496                 }
00497 
00498                 VSUB2( P_A, point, afp->A );
00499                 VUNITIZE( P_A );                /* Checking direction only */
00500                 f = VDOT( afp->peqn, P_A );
00501                 if( ! NEAR_ZERO(f,RT_SLOPPY_DOT_TOL) )  {
00502                         /* Non-planar face */
00503                         bu_log("arb(%s): face %s[%d] non-planar, dot=%g\n",
00504                                 name, title, ptno, f );
00505 #ifdef CONSERVATIVE
00506                         return(-1);                     /* BAD */
00507 #endif
00508                 }
00509                 return(0);                              /* OK */
00510         }
00511         /* NOTREACHED */
00512 }
00513 
00514 /**
00515  *                      R T _ A R B _ M K _ P L A N E S
00516  *
00517  *  Given an rt_arb_internal structure with 8 points in it,
00518  *  compute the face information.
00519  *
00520  *  Returns -
00521  *       0      OK
00522  *      <0      failure
00523  */
00524 HIDDEN int
00525 rt_arb_mk_planes(register struct prep_arb *pap, struct rt_arb_internal *aip, const char *name)
00526 {
00527         LOCAL vect_t    sum;            /* Sum of all endpoints */
00528         register int    i;
00529         register int    j;
00530         register int    k;
00531         int             equiv_pts[8];
00532 
00533         /*
00534          *  Determine a point which is guaranteed to be within the solid.
00535          *  This is done by averaging all the vertices.  This center is
00536          *  needed for rt_arb_add_pt, which demands a point inside the solid.
00537          *  The center of the enclosing RPP strategy used for the bounding
00538          *  sphere can be tricked by thin plates which are non-axis aligned,
00539          *  so this dual-strategy is required.  (What a bug hunt!).
00540          */
00541         VSETALL( sum, 0 );
00542 #       include "noalias.h"
00543         for( i=0; i<8; i++ )  {
00544                 VADD2( sum, sum, aip->pt[i] );
00545         }
00546         VSCALE( pap->pa_center, sum, 0.125 );   /* sum/8 */
00547 
00548         /*
00549          *  Find all points that are equivalent, within the specified tol.
00550          *  Build the array equiv_pts[] so that it is indexed by
00551          *  vertex number, and returns the lowest numbered equivalent
00552          *  vertex (or its own vertex number, if non-equivalent).
00553          */
00554         equiv_pts[0] = 0;
00555         for( i=1; i<8; i++ )  {
00556                 for( j = i-1; j >= 0; j-- )  {
00557                         /* Compare vertices I and J */
00558                         LOCAL vect_t            work;
00559 
00560                         VSUB2( work, aip->pt[i], aip->pt[j] );
00561                         if( MAGSQ( work ) < pap->pa_tol_sq )  {
00562                                 /* Points I and J are the same, J is lower */
00563                                 equiv_pts[i] = equiv_pts[j];
00564                                 goto next_point;
00565                         }
00566                 }
00567                 equiv_pts[i] = i;
00568         next_point: ;
00569         }
00570         if( RT_G_DEBUG & DEBUG_ARB8 )  {
00571                 bu_log("arb(%s) equiv_pts[] = %d %d %d %d %d %d %d %d\n",
00572                         name,
00573                         equiv_pts[0], equiv_pts[1], equiv_pts[2], equiv_pts[3],
00574                         equiv_pts[4], equiv_pts[5], equiv_pts[6], equiv_pts[7]);
00575         }
00576 
00577         pap->pa_faces = 0;
00578         for( i=0; i<6; i++ )  {
00579                 int             npts;
00580 
00581                 npts = 0;
00582                 for( j=0; j<4; j++ )  {
00583                         int     pt_index;
00584 
00585                         pt_index = rt_arb_info[i].ai_sub[j];
00586                         if( RT_G_DEBUG & DEBUG_ARB8 )  {
00587                                 bu_log("face %d, j=%d, npts=%d, orig_vert=%d, vert=%d\n",
00588                                         i, j, npts,
00589                                         pt_index, equiv_pts[pt_index] );
00590                         }
00591                         pt_index = equiv_pts[pt_index];
00592 
00593                         /* Verify that this point is not the same
00594                          * as an earlier point, by checking point indices
00595                          */
00596 #                       include "noalias.h"
00597                         for( k = npts-1; k >= 0; k-- )  {
00598                                 if( pap->pa_pindex[k][pap->pa_faces] == pt_index )  {
00599                                         /* Point is the same -- skip it */
00600                                         goto skip_pt;
00601                                 }
00602                         }
00603                         if( rt_arb_add_pt( aip->pt[pt_index],
00604                             rt_arb_info[i].ai_title, pap, npts, name ) == 0 )  {
00605                                 /* Point was accepted */
00606                                 pap->pa_pindex[npts][pap->pa_faces] = pt_index;
00607                                 npts++;
00608                         }
00609 
00610 skip_pt:                ;
00611                 }
00612 
00613                 if( npts < 3 )  {
00614                         /* This face is BAD */
00615                         continue;
00616                 }
00617 
00618                 if( pap->pa_doopt )  {
00619                         register struct oface   *ofp;
00620 
00621                         ofp = &pap->pa_opt[pap->pa_faces];
00622                         /* Scale U and V basis vectors by
00623                          * the inverse of Ulen and Vlen
00624                          */
00625                         ofp->arb_Ulen = 1.0 / ofp->arb_Ulen;
00626                         ofp->arb_Vlen = 1.0 / ofp->arb_Vlen;
00627                         VSCALE( ofp->arb_U, ofp->arb_U, ofp->arb_Ulen );
00628                         VSCALE( ofp->arb_V, ofp->arb_V, ofp->arb_Vlen );
00629                 }
00630 
00631                 pap->pa_npts[pap->pa_faces] = npts;
00632                 pap->pa_faces++;
00633         }
00634         if( pap->pa_faces < 4  || pap->pa_faces > 6 )  {
00635                 bu_log("arb(%s):  only %d faces present\n",
00636                         name, pap->pa_faces);
00637                 return(-1);                     /* Error */
00638         }
00639         return(0);                      /* OK */
00640 }
00641 
00642 /**
00643  *                      R T _ A R B _ S E T U P
00644  *
00645  *  This is packaged as a separate function, so that it can also be
00646  *  called "on the fly" from the UV mapper.
00647  *
00648  *  Returns -
00649  *       0      OK
00650  *      !0      failure
00651  */
00652 HIDDEN int
00653 rt_arb_setup(struct soltab *stp, struct rt_arb_internal *aip, struct rt_i *rtip, int uv_wanted)
00654 {
00655         register int            i;
00656         struct prep_arb         pa;
00657 
00658         RT_ARB_CK_MAGIC(aip);
00659 
00660         pa.pa_doopt = uv_wanted;
00661         pa.pa_tol_sq = rtip->rti_tol.dist_sq;
00662 
00663         if( rt_arb_mk_planes( &pa, aip, stp->st_dp->d_namep ) < 0 )  {
00664                 return(-2);             /* Error */
00665         }
00666 
00667         /*
00668          *  Allocate a private copy of the accumulated parameters
00669          *  of exactly the right size.
00670          *  The size to malloc is chosen based upon the
00671          *  exact number of faces.
00672          */
00673         {
00674                 register struct arb_specific    *arbp;
00675                 if( (arbp = (struct arb_specific *)stp->st_specific) == 0 )  {
00676                         arbp = (struct arb_specific *)bu_malloc(
00677                                 sizeof(struct arb_specific) +
00678                                 sizeof(struct aface) * (pa.pa_faces - 4),
00679                                 "arb_specific" );
00680                         stp->st_specific = (genptr_t)arbp;
00681                 }
00682                 arbp->arb_nmfaces = pa.pa_faces;
00683                 bcopy( (char *)pa.pa_face, (char *)arbp->arb_face,
00684                         pa.pa_faces * sizeof(struct aface) );
00685 
00686                 if( uv_wanted )  {
00687                         register struct oface   *ofp;
00688 
00689                         /*
00690                          * To avoid a multi-processor race here,
00691                          * copy the data first, THEN update arb_opt,
00692                          * because arb_opt doubles as the "UV avail" flag.
00693                          */
00694                         ofp = (struct oface *)bu_malloc(
00695                                 pa.pa_faces * sizeof(struct oface), "arb_opt");
00696                         bcopy( (char *)pa.pa_opt, (char *)ofp,
00697                                 pa.pa_faces * sizeof(struct oface) );
00698                         arbp->arb_opt = ofp;
00699                 } else {
00700                         arbp->arb_opt = (struct oface *)0;
00701                 }
00702         }
00703 
00704         /*
00705          * Compute bounding sphere which contains the bounding RPP.
00706          * Find min and max of the point co-ordinates to find the
00707          * bounding RPP.  Note that this center is NOT guaranteed
00708          * to be contained within the solid!
00709          */
00710         {
00711                 LOCAL vect_t            work;
00712                 register fastf_t        f;
00713 
00714 #               include "noalias.h"
00715                 for( i=0; i< 8; i++ ) {
00716                         VMINMAX( stp->st_min, stp->st_max, aip->pt[i] );
00717                 }
00718                 VADD2SCALE( stp->st_center, stp->st_min, stp->st_max, 0.5 );
00719                 VSUB2SCALE( work, stp->st_max, stp->st_min, 0.5 );
00720 
00721                 f = work[X];
00722                 if( work[Y] > f )  f = work[Y];
00723                 if( work[Z] > f )  f = work[Z];
00724                 stp->st_aradius = f;
00725                 stp->st_bradius = MAGNITUDE(work);
00726         }
00727         return(0);              /* OK */
00728 }
00729 
00730 /**
00731  *                      R T _ A R B _ P R E P
00732  *
00733  *  This is the actual LIBRT "prep" interface.
00734  *
00735  *  Returns -
00736  *       0      OK
00737  *      !0      failure
00738  */
00739 int
00740 rt_arb_prep(struct soltab *stp, struct rt_db_internal *ip, struct rt_i *rtip)
00741 {
00742         struct rt_arb_internal  *aip;
00743 
00744         aip = (struct rt_arb_internal *)ip->idb_ptr;
00745         RT_ARB_CK_MAGIC(aip);
00746 
00747         return( rt_arb_setup( stp, aip, rtip, 0 ) );
00748 }
00749 
00750 /**
00751  *                      R T _ A R B _ P R I N T
00752  */
00753 void
00754 rt_arb_print(register const struct soltab *stp)
00755 {
00756         register struct arb_specific *arbp =
00757                 (struct arb_specific *)stp->st_specific;
00758         register struct aface   *afp;
00759         register int i;
00760 
00761         if( arbp == (struct arb_specific *)0 )  {
00762                 bu_log("arb(%s):  no faces\n", stp->st_name);
00763                 return;
00764         }
00765         bu_log("%d faces:\n", arbp->arb_nmfaces);
00766         for( i=0; i < arbp->arb_nmfaces; i++ )  {
00767                 afp = &(arbp->arb_face[i]);
00768                 VPRINT( "A", afp->A );
00769                 HPRINT( "Peqn", afp->peqn );
00770                 if( arbp->arb_opt )  {
00771                         register struct oface   *op;
00772                         op = &(arbp->arb_opt[i]);
00773                         VPRINT( "UVorig", op->arb_UVorig );
00774                         VPRINT( "U", op->arb_U );
00775                         VPRINT( "V", op->arb_V );
00776                         bu_log( "Ulen = %g, Vlen = %g\n",
00777                                 op->arb_Ulen, op->arb_Vlen);
00778                 }
00779         }
00780 }
00781 
00782 /**
00783  *                      R T _ A R B _ S H O T
00784  *
00785  * Function -
00786  *      Shoot a ray at an ARB8.
00787  *
00788  * Algorithm -
00789  *      The intersection distance is computed for each face.
00790  *  The largest IN distance and the smallest OUT distance are
00791  *  used as the entry and exit points.
00792  *
00793  * Returns -
00794  *      0       MISS
00795  *      >0      HIT
00796  */
00797 int
00798 rt_arb_shot(struct soltab *stp, register struct xray *rp, struct application *ap, struct seg *seghead)
00799 {
00800         struct arb_specific *arbp = (struct arb_specific *)stp->st_specific;
00801         LOCAL int               iplane, oplane;
00802         LOCAL fastf_t           in, out;        /* ray in/out distances */
00803         register struct aface   *afp;
00804         register int            j;
00805 
00806         in = -INFINITY;
00807         out = INFINITY;
00808         iplane = oplane = -1;
00809 
00810         if (RT_G_DEBUG & DEBUG_ARB8) {
00811                 bu_log("\n\n------------\n arb: ray point %g %g %g -> %g %g %g\n",
00812                         V3ARGS(rp->r_pt),
00813                         V3ARGS(rp->r_dir));
00814         }
00815 
00816         /* consider each face */
00817         for( afp = &arbp->arb_face[j=arbp->arb_nmfaces-1]; j >= 0; j--, afp-- )  {
00818                 FAST fastf_t    dn;             /* Direction dot Normal */
00819                 FAST fastf_t    dxbdn;
00820                 FAST fastf_t    s;
00821 
00822                 /* XXX some of this math should be prep work
00823                  * (including computing dxbdn/dn ?) *$*/
00824                 dxbdn = VDOT( afp->peqn, rp->r_pt ) - afp->peqn[3];
00825                 dn = -VDOT( afp->peqn, rp->r_dir );
00826 
00827                 if (RT_G_DEBUG & DEBUG_ARB8) {
00828                         HPRINT("arb: Plane Equation", afp->peqn);
00829                         bu_log("arb: dn=%g dxbdn=%g s=%g\n", dn, dxbdn, dxbdn/dn);
00830                 }
00831 
00832                 if( dn < -SQRT_SMALL_FASTF )  {
00833                         /* exit point, when dir.N < 0.  out = min(out,s) */
00834                         if( out > (s = dxbdn/dn) )  {
00835                                 out = s;
00836                                 oplane = j;
00837                         }
00838                 } else if ( dn > SQRT_SMALL_FASTF )  {
00839                         /* entry point, when dir.N > 0.  in = max(in,s) */
00840                         if( in < (s = dxbdn/dn) )  {
00841                                 in = s;
00842                                 iplane = j;
00843                         }
00844                 }  else  {
00845                         /* ray is parallel to plane when dir.N == 0.
00846                          * If it is outside the solid, stop now.
00847                          * Allow very small amount of slop, to catch
00848                          * rays that lie very nearly in the plane of a face.
00849                          */
00850                         if( dxbdn > SQRT_SMALL_FASTF )
00851                                 return( 0 );    /* MISS */
00852                 }
00853                 if( in > out )
00854                         return( 0 );    /* MISS */
00855         }
00856         /* Validate */
00857         if( iplane == -1 || oplane == -1 )  {
00858                 bu_log("rt_arb_shoot(%s): 1 hit => MISS\n",
00859                         stp->st_name);
00860                 return( 0 );    /* MISS */
00861         }
00862         if( in >= out || out >= INFINITY )
00863                 return( 0 );    /* MISS */
00864 
00865         {
00866                 register struct seg *segp;
00867 
00868                 RT_GET_SEG( segp, ap->a_resource );
00869                 segp->seg_stp = stp;
00870                 segp->seg_in.hit_dist = in;
00871                 segp->seg_in.hit_surfno = iplane;
00872 
00873                 segp->seg_out.hit_dist = out;
00874                 segp->seg_out.hit_surfno = oplane;
00875                 BU_LIST_INSERT( &(seghead->l), &(segp->l) );
00876         }
00877         return(2);                      /* HIT */
00878 }
00879 
00880 #define SEG_MISS(SEG)           (SEG).seg_stp=(struct soltab *) 0;
00881 /**
00882  *                      R T _ A R B _ V S H O T
00883  *
00884  *  This is the Becker vector version
00885  */
00886 void
00887 rt_arb_vshot(struct soltab **stp, struct xray **rp, struct seg *segp, int n, struct application *ap)
00888                                /* An array of solid pointers */
00889                                /* An array of ray pointers */
00890                                /* array of segs (results returned) */
00891                                /* Number of ray/object pairs */
00892 
00893 {
00894         register int    j, i;
00895         register struct arb_specific *arbp;
00896         FAST fastf_t    dn;             /* Direction dot Normal */
00897         FAST fastf_t    dxbdn;
00898         FAST fastf_t    s;
00899 
00900         /* Intialize return values */
00901 #       include "noalias.h"
00902         for(i = 0; i < n; i++){
00903                 segp[i].seg_stp = stp[i];       /* Assume hit, if 0 then miss */
00904                 segp[i].seg_in.hit_dist = -INFINITY;    /* used as in */
00905                 segp[i].seg_in.hit_surfno = -1;         /* used as iplane */
00906                 segp[i].seg_out.hit_dist = INFINITY;    /* used as out */
00907                 segp[i].seg_out.hit_surfno = -1;        /* used as oplane */
00908 /**                segp[i].seg_next = SEG_NULL;**/
00909         }
00910 
00911         /* consider each face */
00912         for(j = 0; j < 6; j++)  {
00913                 /* for each ray/arb_face pair */
00914 #               include "noalias.h"
00915                 for(i = 0; i < n; i++)  {
00916                         if (stp[i] == 0) continue;      /* skip this ray */
00917                         if ( segp[i].seg_stp == 0 ) continue;   /* miss */
00918 
00919                         arbp= (struct arb_specific *) stp[i]->st_specific;
00920                         if ( arbp->arb_nmfaces <= j )
00921                                 continue; /* faces of this ARB are done */
00922 
00923                         dxbdn = VDOT( arbp->arb_face[j].peqn, rp[i]->r_pt ) -
00924                                 arbp->arb_face[j].peqn[3];
00925                         if( (dn = -VDOT( arbp->arb_face[j].peqn, rp[i]->r_dir )) <
00926                                                         -SQRT_SMALL_FASTF )  {
00927                            /* exit point, when dir.N < 0.  out = min(out,s) */
00928                            if( segp[i].seg_out.hit_dist > (s = dxbdn/dn) )  {
00929                                    segp[i].seg_out.hit_dist = s;
00930                                    segp[i].seg_out.hit_surfno = j;
00931                            }
00932                         } else if ( dn > SQRT_SMALL_FASTF )  {
00933                            /* entry point, when dir.N > 0.  in = max(in,s) */
00934                            if( segp[i].seg_in.hit_dist < (s = dxbdn/dn) )  {
00935                                    segp[i].seg_in.hit_dist = s;
00936                                    segp[i].seg_in.hit_surfno = j;
00937                            }
00938                         }  else  {
00939                            /* ray is parallel to plane when dir.N == 0.
00940                             * If it is outside the solid, stop now */
00941                            if( dxbdn > SQRT_SMALL_FASTF ) {
00942                                 SEG_MISS(segp[i]);              /* MISS */
00943                            }
00944                         }
00945                         if(segp[i].seg_in.hit_dist > segp[i].seg_out.hit_dist) {
00946                            SEG_MISS(segp[i]);           /* MISS */
00947                         }
00948                 } /* for each ray/arb_face pair */
00949         } /* for each arb_face */
00950 
00951         /*
00952          *  Validate for each ray/arb_face pair
00953          *  Segment was initialized as "good" (seg_stp set valid);
00954          *  that is revoked here on misses.
00955          */
00956 #       include "noalias.h"
00957         for(i = 0; i < n; i++){
00958                 if (stp[i] == 0) continue;              /* skip this ray */
00959                 if ( segp[i].seg_stp == 0 ) continue;   /* missed */
00960 
00961                 if( segp[i].seg_in.hit_surfno == -1 ||
00962                     segp[i].seg_out.hit_surfno == -1 )  {
00963                         SEG_MISS(segp[i]);              /* MISS */
00964                 }
00965                 else if(segp[i].seg_in.hit_dist >= segp[i].seg_out.hit_dist ||
00966                         segp[i].seg_out.hit_dist >= INFINITY ) {
00967                         SEG_MISS(segp[i]);              /* MISS */
00968                 }
00969         }
00970 }
00971 
00972 /**
00973  *                      R T _ A R B _ N O R M
00974  *
00975  *  Given ONE ray distance, return the normal and entry/exit point.
00976  */
00977 void
00978 rt_arb_norm(register struct hit *hitp, struct soltab *stp, register struct xray *rp)
00979 {
00980         register struct arb_specific *arbp =
00981                 (struct arb_specific *)stp->st_specific;
00982         register int    h;
00983 
00984         VJOIN1( hitp->hit_point, rp->r_pt, hitp->hit_dist, rp->r_dir );
00985         h = hitp->hit_surfno;
00986         VMOVE( hitp->hit_normal, arbp->arb_face[h].peqn );
00987 }
00988 
00989 /**
00990  *                      R T _ A R B _ C U R V E
00991  *
00992  *  Return the "curvature" of the ARB face.
00993  *  Pick a principle direction orthogonal to normal, and
00994  *  indicate no curvature.
00995  */
00996 void
00997 rt_arb_curve(register struct curvature *cvp, register struct hit *hitp, struct soltab *stp)
00998 {
00999 
01000         bn_vec_ortho( cvp->crv_pdir, hitp->hit_normal );
01001         cvp->crv_c1 = cvp->crv_c2 = 0;
01002 }
01003 
01004 /**
01005  *                      R T _ A R B _ U V
01006  *
01007  *  For a hit on a face of an ARB, return the (u,v) coordinates
01008  *  of the hit point.  0 <= u,v <= 1.
01009  *  u extends along the arb_U direction defined by B-A,
01010  *  v extends along the arb_V direction defined by Nx(B-A).
01011  */
01012 void
01013 rt_arb_uv(struct application *ap, struct soltab *stp, register struct hit *hitp, register struct uvcoord *uvp)
01014 {
01015         register struct arb_specific *arbp =
01016                 (struct arb_specific *)stp->st_specific;
01017         struct oface    *ofp;
01018         LOCAL vect_t    P_A;
01019         LOCAL fastf_t   r;
01020         LOCAL vect_t    rev_dir;
01021         LOCAL fastf_t   dot_N;
01022         LOCAL vect_t    UV_dir;
01023         LOCAL fastf_t   *norm;
01024         LOCAL fastf_t   min_r_U, min_r_V;
01025 
01026         if( arbp->arb_opt == (struct oface *)0 )  {
01027                 register int            ret = 0;
01028                 struct rt_db_internal   intern;
01029                 struct rt_arb_internal  *aip;
01030 
01031                 if( rt_db_get_internal( &intern, stp->st_dp, ap->a_rt_i->rti_dbip, stp->st_matp, ap->a_resource ) < 0 )  {
01032                         bu_log("rt_arb_uv(%s) rt_db_get_internal failure\n",
01033                                 stp->st_name);
01034                         return;
01035                 }
01036                 RT_CK_DB_INTERNAL( &intern );
01037                 aip = (struct rt_arb_internal *)intern.idb_ptr;
01038                 RT_ARB_CK_MAGIC(aip);
01039 
01040                 /*
01041                  *  The double check of arb_opt is to avoid the case
01042                  *  where another processor did the UV setup while
01043                  *  this processor was waiting in bu_semaphore_acquire().
01044                  */
01045                 bu_semaphore_acquire( RT_SEM_MODEL );
01046                 if( arbp->arb_opt == (struct oface *)0 )  {
01047                         ret = rt_arb_setup(stp, aip, ap->a_rt_i, 1 );
01048                 }
01049                 bu_semaphore_release( RT_SEM_MODEL );
01050 
01051                 rt_db_free_internal( &intern, ap->a_resource );
01052 
01053                 if( ret != 0 || arbp->arb_opt == (struct oface *)0 )  {
01054                         bu_log("rt_arb_uv(%s) dyanmic setup failure st_specific=x%x, optp=x%x\n",
01055                                 stp->st_name,
01056                                 stp->st_specific, arbp->arb_opt );
01057                         return;
01058                 }
01059                 if(RT_G_DEBUG&DEBUG_SOLIDS)  rt_pr_soltab( stp );
01060         }
01061 
01062         ofp = &arbp->arb_opt[hitp->hit_surfno];
01063 
01064         VSUB2( P_A, hitp->hit_point, ofp->arb_UVorig );
01065         /* Flipping v is an artifact of how the faces are built */
01066         uvp->uv_u = VDOT( P_A, ofp->arb_U );
01067         uvp->uv_v = 1.0 - VDOT( P_A, ofp->arb_V );
01068         if( uvp->uv_u < 0 || uvp->uv_v < 0 || uvp->uv_u > 1 || uvp->uv_v > 1 )  {
01069                 bu_log("arb_uv: bad uv=%g,%g\n", uvp->uv_u, uvp->uv_v);
01070                 /* Fix it up */
01071                 if( uvp->uv_u < 0 )  uvp->uv_u = (-uvp->uv_u);
01072                 if( uvp->uv_v < 0 )  uvp->uv_v = (-uvp->uv_v);
01073         }
01074         r = ap->a_rbeam + ap->a_diverge * hitp->hit_dist;
01075         min_r_U = r * ofp->arb_Ulen;
01076         min_r_V = r * ofp->arb_Vlen;
01077         VREVERSE( rev_dir, ap->a_ray.r_dir )
01078         norm = &arbp->arb_face[hitp->hit_surfno].peqn[0];
01079         dot_N = VDOT( rev_dir, norm );
01080         VJOIN1( UV_dir, rev_dir, -dot_N, norm )
01081         VUNITIZE( UV_dir )
01082         uvp->uv_du = r * VDOT( UV_dir, ofp->arb_U ) / dot_N;
01083         uvp->uv_dv = r * VDOT( UV_dir, ofp->arb_V ) / dot_N;
01084         if( uvp->uv_du < 0.0 )
01085                 uvp->uv_du = -uvp->uv_du;
01086         if( uvp->uv_du < min_r_U )
01087                 uvp->uv_du = min_r_U;
01088         if( uvp->uv_dv < 0.0 )
01089                 uvp->uv_dv = -uvp->uv_dv;
01090         if( uvp->uv_dv < min_r_V )
01091                 uvp->uv_dv = min_r_V;
01092 }
01093 
01094 /**
01095  *                      R T _ A R B _ F R E E
01096  */
01097 void
01098 rt_arb_free(register struct soltab *stp)
01099 {
01100         register struct arb_specific *arbp =
01101                 (struct arb_specific *)stp->st_specific;
01102 
01103         if( arbp->arb_opt )
01104                 bu_free( (char *)arbp->arb_opt, "arb_opt" );
01105         bu_free( (char *)arbp, "arb_specific" );
01106 }
01107 
01108 #define ARB_FACE( valp, a, b, c, d ) \
01109         RT_ADD_VLIST( vhead, valp[a], BN_VLIST_LINE_MOVE ); \
01110         RT_ADD_VLIST( vhead, valp[b], BN_VLIST_LINE_DRAW ); \
01111         RT_ADD_VLIST( vhead, valp[c], BN_VLIST_LINE_DRAW ); \
01112         RT_ADD_VLIST( vhead, valp[d], BN_VLIST_LINE_DRAW );
01113 
01114 /**
01115  *                      R T _ A R B _ P L O T
01116  *
01117  *  Plot an ARB by tracing out four "U" shaped contours
01118  *  This draws each edge only once.
01119  *  XXX No checking for degenerate faces is done, but probably should be.
01120  */
01121 int
01122 rt_arb_plot(struct bu_list *vhead, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01123 {
01124         struct rt_arb_internal  *aip;
01125 
01126         RT_CK_DB_INTERNAL(ip);
01127         aip = (struct rt_arb_internal *)ip->idb_ptr;
01128         RT_ARB_CK_MAGIC(aip);
01129 
01130         ARB_FACE( aip->pt, 0, 1, 2, 3 );
01131         ARB_FACE( aip->pt, 4, 0, 3, 7 );
01132         ARB_FACE( aip->pt, 5, 4, 7, 6 );
01133         ARB_FACE( aip->pt, 1, 5, 6, 2 );
01134         return(0);
01135 }
01136 
01137 /**
01138  *                      R T _ A R B _ C L A S S
01139  */
01140 int
01141 rt_arb_class(const struct soltab *stp, const fastf_t *min, const fastf_t *max, const struct bn_tol *tol)
01142 {
01143         register struct arb_specific *arbp =
01144                 (struct arb_specific *)stp->st_specific;
01145         register int i;
01146 
01147         if( arbp == (struct arb_specific *)0 ) {
01148                 bu_log("arb(%s): no faces\n", stp->st_name);
01149                 return RT_CLASSIFY_UNIMPLEMENTED;
01150         }
01151 
01152         for( i=0; i<arbp->arb_nmfaces; i++ ) {
01153                 if( bn_hlf_class( arbp->arb_face[i].peqn, min, max, tol ) ==
01154                     BN_CLASSIFY_OUTSIDE )
01155                         return RT_CLASSIFY_OUTSIDE;
01156         }
01157 
01158         /* We need to test for RT_CLASSIFY_INSIDE vs. RT_CLASSIFY_OVERLAPPING!
01159            XXX Do this soon */
01160         return RT_CLASSIFY_UNIMPLEMENTED; /* let the caller assume the worst */
01161 }
01162 
01163 /**
01164  *                      R T _ A R B _ I M P O R T
01165  *
01166  *  Import an ARB8 from the database format to the internal format.
01167  *  There are two parts to this:  First, the database is presently
01168  *  single precision binary floating point.
01169  *  Second, the ARB in the database is represented as a vector
01170  *  from the origin to the first point, and 7 vectors
01171  *  from the first point to the remaining points.  In 1979 it seemed
01172  *  like a good idea...
01173  *
01174  *  Convert from vector to point notation
01175  *  by rotating each vector and adding in the base vector.
01176  */
01177 int
01178 rt_arb_import(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01179 {
01180         struct rt_arb_internal  *aip;
01181         union record            *rp;
01182         register int            i;
01183         LOCAL vect_t            work;
01184         LOCAL fastf_t           vec[3*8];
01185 
01186         BU_CK_EXTERNAL( ep );
01187         rp = (union record *)ep->ext_buf;
01188         /* Check record type */
01189         if( rp->u_id != ID_SOLID )  {
01190                 bu_log("rt_arb_import: defective record, id=x%x\n", rp->u_id);
01191                 return(-1);
01192         }
01193 
01194         RT_CK_DB_INTERNAL( ip );
01195         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01196         ip->idb_type = ID_ARB8;
01197         ip->idb_meth = &rt_functab[ID_ARB8];
01198         ip->idb_ptr = bu_malloc( sizeof(struct rt_arb_internal), "rt_arb_internal");
01199         aip = (struct rt_arb_internal *)ip->idb_ptr;
01200         aip->magic = RT_ARB_INTERNAL_MAGIC;
01201 
01202         /* Convert from database to internal format */
01203         rt_fastf_float( vec, rp->s.s_values, 8 );
01204 
01205         /*
01206          * Convert from vector notation (in database) to point notation.
01207          */
01208         MAT4X3PNT( aip->pt[0], mat, &vec[0] );
01209 
01210 #       include "noalias.h"
01211         for( i=1; i<8; i++ )  {
01212                 VADD2( work, &vec[0*3], &vec[i*3] );
01213                 MAT4X3PNT( aip->pt[i], mat, work );
01214         }
01215         return(0);                      /* OK */
01216 }
01217 
01218 /**
01219  *                      R T _ A R B _ E X P O R T
01220  */
01221 int
01222 rt_arb_export(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01223 {
01224         struct rt_arb_internal  *aip;
01225         union record            *rec;
01226         register int            i;
01227 
01228         RT_CK_DB_INTERNAL(ip);
01229         if( ip->idb_type != ID_ARB8 )  return(-1);
01230         aip = (struct rt_arb_internal *)ip->idb_ptr;
01231         RT_ARB_CK_MAGIC(aip);
01232 
01233         BU_CK_EXTERNAL(ep);
01234         ep->ext_nbytes = sizeof(union record);
01235         ep->ext_buf = (genptr_t)bu_calloc( 1, ep->ext_nbytes, "arb external");
01236         rec = (union record *)ep->ext_buf;
01237 
01238         rec->s.s_id = ID_SOLID;
01239         rec->s.s_type = GENARB8;
01240 
01241         /* NOTE: This also converts to dbfloat_t */
01242         VSCALE( &rec->s.s_values[3*0], aip->pt[0], local2mm );
01243         for( i=1; i < 8; i++ )  {
01244                 VSUB2SCALE( &rec->s.s_values[3*i],
01245                         aip->pt[i], aip->pt[0], local2mm );
01246         }
01247         return(0);
01248 }
01249 
01250 /**
01251  *                      R T _ A R B _ I M P O R T 5
01252  *
01253  * Import an arb from the db5 format and convert to the internal structure.
01254  * Code duplicated from rt_arb_import() with db5 help from g_ell.c
01255  */
01256 int
01257 rt_arb_import5(struct rt_db_internal *ip, const struct bu_external *ep, register const fastf_t *mat, const struct db_i *dbip)
01258 {
01259         struct rt_arb_internal *aip;
01260         register int            i;
01261         fastf_t                 vec[3*8];
01262 
01263         BU_CK_EXTERNAL( ep );
01264         BU_ASSERT_LONG( ep->ext_nbytes, ==, SIZEOF_NETWORK_DOUBLE * 3*8);
01265         RT_CK_DB_INTERNAL( ip );
01266         ip->idb_major_type = DB5_MAJORTYPE_BRLCAD;
01267         ip->idb_type = ID_ARB8;
01268         ip->idb_meth = &rt_functab[ID_ARB8];
01269         ip->idb_ptr = bu_malloc( sizeof(struct rt_arb_internal), "rt_arb_internal");
01270 
01271         aip = (struct rt_arb_internal *)ip->idb_ptr;
01272         aip->magic = RT_ARB_INTERNAL_MAGIC;
01273 
01274         /* Convert from database (network) to internal (host) format */
01275         ntohd( (unsigned char *)vec, ep->ext_buf, 8*3);
01276         for (i=0; i<8; i++) {
01277                 MAT4X3PNT( aip->pt[i], mat, &vec[i*3]);
01278         }
01279         return 0;       /* OK */
01280 }
01281 /**
01282  *                      R T _ A R B _ E X P O R T 5
01283  */
01284 int
01285 rt_arb_export5(struct bu_external *ep, const struct rt_db_internal *ip, double local2mm, const struct db_i *dbip)
01286 {
01287         struct rt_arb_internal  *aip;
01288         fastf_t                 vec[3*8];
01289         register int            i;
01290 
01291         RT_CK_DB_INTERNAL(ip);
01292         if (ip->idb_type != ID_ARB8) return -1;
01293         aip = (struct rt_arb_internal *)ip->idb_ptr;
01294         RT_ARB_CK_MAGIC(aip);
01295 
01296         BU_CK_EXTERNAL(ep);
01297         ep->ext_nbytes = SIZEOF_NETWORK_DOUBLE * 8 * 3;
01298         ep->ext_buf = (genptr_t)bu_malloc( ep->ext_nbytes, "arb external");
01299         for (i=0; i<8; i++) {
01300                 VSCALE( &vec[i*3], aip->pt[i], local2mm );
01301         }
01302         htond( ep->ext_buf, (unsigned char *)vec, 8*3);
01303         return 0;
01304 }
01305 /**
01306  *                      R T _ A R B _ D E S C R I B E
01307  *
01308  *  Make human-readable formatted presentation of this solid.
01309  *  First line describes type of solid.
01310  *  Additional lines are indented one tab, and give parameter values.
01311  */
01312 int
01313 rt_arb_describe(struct bu_vls *str, const struct rt_db_internal *ip, int verbose, double mm2local)
01314 {
01315     register struct rt_arb_internal *aip = (struct rt_arb_internal *)ip->idb_ptr;
01316     char buf[256];
01317     int i;
01318     int arb_type;
01319     struct bn_tol tmp_tol;      /* temporay tolerance */
01320     
01321     RT_ARB_CK_MAGIC(aip);
01322     
01323     tmp_tol.magic = BN_TOL_MAGIC;
01324     tmp_tol.dist = 0.0001; /* to get old behavior of rt_arb_std_type() */
01325     tmp_tol.dist_sq = tmp_tol.dist * tmp_tol.dist;
01326     tmp_tol.perp = 1e-5;
01327     tmp_tol.para = 1 - tmp_tol.perp;
01328     
01329     arb_type = rt_arb_std_type( ip, &tmp_tol );
01330     
01331     if( !arb_type ) {
01332         
01333         bu_vls_strcat( str, "ARB8\n");
01334         
01335         /* Use 1-based numbering, to match vertex labels in MGED */             
01336         sprintf( buf, "\t1 (%g, %g, %g)\n",
01337                  INTCLAMP(aip->pt[0][X] * mm2local),
01338                  INTCLAMP(aip->pt[0][Y] * mm2local),
01339                  INTCLAMP(aip->pt[0][Z] * mm2local) );
01340         bu_vls_strcat( str, buf );
01341         
01342         if( !verbose )  return(0);
01343         
01344         for( i=1; i < 8; i++ )  {
01345             sprintf( buf, "\t%d (%g, %g, %g)\n", i+1,
01346                      INTCLAMP(aip->pt[i][X] * mm2local),
01347                      INTCLAMP(aip->pt[i][Y] * mm2local),
01348                      INTCLAMP(aip->pt[i][Z] * mm2local) );
01349             bu_vls_strcat( str, buf );
01350         }
01351     } else {
01352         sprintf( buf, "ARB%d\n", arb_type );
01353         bu_vls_strcat( str, buf );
01354         switch( arb_type ) {
01355             case ARB8:
01356                 for( i=0 ; i<8 ; i++ ) {
01357                     sprintf( buf, "\t%d (%g, %g, %g)\n", i+1, 
01358                              INTCLAMP(aip->pt[i][X] * mm2local),
01359                              INTCLAMP(aip->pt[i][Y] * mm2local),
01360                              INTCLAMP(aip->pt[i][Z] * mm2local) );
01361                     bu_vls_strcat( str, buf );
01362                 }
01363                 break;
01364             case ARB7:
01365                 for( i=0 ; i<7 ; i++ ) {
01366                     sprintf( buf, "\t%d (%g, %g, %g)\n", i+1,
01367                              INTCLAMP(aip->pt[i][X] * mm2local),
01368                              INTCLAMP(aip->pt[i][Y] * mm2local),
01369                              INTCLAMP(aip->pt[i][Z] * mm2local) );
01370                     bu_vls_strcat( str, buf );
01371                 }
01372                 break;
01373             case ARB6:
01374                 for( i=0 ; i<5 ; i++ ) {
01375                     sprintf( buf, "\t%d (%g, %g, %g)\n", i+1,
01376                              INTCLAMP(aip->pt[i][X] * mm2local),
01377                              INTCLAMP(aip->pt[i][Y] * mm2local),
01378                              INTCLAMP(aip->pt[i][Z] * mm2local) );
01379                     bu_vls_strcat( str, buf );
01380                 }
01381                 sprintf( buf, "\t6 (%g, %g, %g)\n",
01382                          INTCLAMP(aip->pt[6][X] * mm2local),
01383                          INTCLAMP(aip->pt[6][Y] * mm2local),
01384                          INTCLAMP(aip->pt[6][Z] * mm2local) );
01385                 bu_vls_strcat( str, buf );
01386                 break;
01387             case ARB5:
01388                 for( i=0 ; i<5 ; i++ ) {
01389                     sprintf( buf, "\t%d (%g, %g, %g)\n", i+1,
01390                              INTCLAMP(aip->pt[i][X] * mm2local),
01391                              INTCLAMP(aip->pt[i][Y] * mm2local),
01392                              INTCLAMP(aip->pt[i][Z] * mm2local) );
01393                     bu_vls_strcat( str, buf );
01394                 }
01395                 break;
01396             case ARB4:
01397                 for( i=0 ; i<3 ; i++ ) {
01398                     sprintf( buf, "\t%d (%g, %g, %g)\n", i+1,
01399                              INTCLAMP(aip->pt[i][X] * mm2local),
01400                              INTCLAMP(aip->pt[i][Y] * mm2local),
01401                              INTCLAMP(aip->pt[i][Z] * mm2local) );
01402                     bu_vls_strcat( str, buf );
01403                 }
01404                 sprintf( buf, "\t4 (%g, %g, %g)\n",
01405                          INTCLAMP(aip->pt[4][X] * mm2local),
01406                          INTCLAMP(aip->pt[4][Y] * mm2local),
01407                          INTCLAMP(aip->pt[4][Z] * mm2local) );
01408                 bu_vls_strcat( str, buf );
01409                 break;
01410         }
01411     }
01412     return(0);
01413 }
01414 
01415 /**
01416  *                      R T _ A R B _ I F R E E
01417  *
01418  *  Free the storage associated with the rt_db_internal version of this solid.
01419  */
01420 void
01421 rt_arb_ifree(struct rt_db_internal *ip)
01422 {
01423         RT_CK_DB_INTERNAL(ip);
01424         bu_free( ip->idb_ptr, "arb ifree" );
01425         ip->idb_ptr = (genptr_t)NULL;
01426 }
01427 
01428 /**
01429  *                      R T _ A R B _ T E S S
01430  *
01431  *  "Tessellate" an ARB into an NMG data structure.
01432  *  Purely a mechanical transformation of one faceted object
01433  *  into another.
01434  *
01435  *  Returns -
01436  *      -1      failure
01437  *       0      OK.  *r points to nmgregion that holds this tessellation.
01438  */
01439 int
01440 rt_arb_tess(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct rt_tess_tol *ttol, const struct bn_tol *tol)
01441 {
01442         LOCAL struct rt_arb_internal    *aip;
01443         struct shell            *s;
01444         struct prep_arb         pa;
01445         register int            i;
01446         struct faceuse          *fu[6];
01447         struct vertex           *verts[8];
01448         struct vertex           **vertp[4];
01449 
01450         RT_CK_DB_INTERNAL(ip);
01451         aip = (struct rt_arb_internal *)ip->idb_ptr;
01452         RT_ARB_CK_MAGIC(aip);
01453 
01454         bzero( (char *)&pa, sizeof(pa) );
01455         pa.pa_doopt = 0;                /* no UV stuff */
01456         pa.pa_tol_sq = tol->dist_sq;
01457         if( rt_arb_mk_planes( &pa, aip, "(tess)" ) < 0 )  return(-2);
01458 
01459         for( i=0; i<8; i++ )  verts[i] = (struct vertex *)0;
01460 
01461         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01462         s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01463 
01464         /* Process each face */
01465         for( i=0; i < pa.pa_faces; i++ )  {
01466                 if( pa.pa_clockwise[i] != 0 )  {
01467                         /* Counter-Clockwise orientation (CCW) */
01468                         vertp[0] = &verts[pa.pa_pindex[0][i]];
01469                         vertp[1] = &verts[pa.pa_pindex[1][i]];
01470                         vertp[2] = &verts[pa.pa_pindex[2][i]];
01471                         if( pa.pa_npts[i] > 3 ) {
01472                                 vertp[3] = &verts[pa.pa_pindex[3][i]];
01473                         }
01474                 } else {
01475                         register struct vertex  ***vertpp = vertp;
01476                         /* Clockwise orientation (CW) */
01477                         if( pa.pa_npts[i] > 3 ) {
01478                                 *vertpp++ = &verts[pa.pa_pindex[3][i]];
01479                         }
01480                         *vertpp++ = &verts[pa.pa_pindex[2][i]];
01481                         *vertpp++ = &verts[pa.pa_pindex[1][i]];
01482                         *vertpp++ = &verts[pa.pa_pindex[0][i]];
01483                 }
01484                 if( RT_G_DEBUG & DEBUG_ARB8 )  {
01485                         bu_log("face %d, npts=%d, verts %d %d %d %d\n",
01486                                 i, pa.pa_npts[i],
01487                                 pa.pa_pindex[0][i], pa.pa_pindex[1][i],
01488                                 pa.pa_pindex[2][i], pa.pa_pindex[3][i] );
01489                 }
01490                 if( (fu[i] = nmg_cmface( s, vertp, pa.pa_npts[i] )) == 0 )  {
01491                         bu_log("rt_arb_tess(%s): nmg_cmface() fail on face %d\n", i);
01492                         continue;
01493                 }
01494         }
01495 
01496         /* Associate vertex geometry */
01497         for( i=0; i<8; i++ )
01498                 if(verts[i]) nmg_vertex_gv(verts[i], aip->pt[i]);
01499 
01500         /* Associate face geometry */
01501         for( i=0; i < pa.pa_faces; i++ )  {
01502 #if 1
01503                 /* We already know the plane equations, this is fast */
01504                 nmg_face_g( fu[i], pa.pa_face[i].peqn );
01505 #else
01506                 /* For the cautious, ensure topology and geometry match */
01507                 if( nmg_fu_planeeqn( fu[i], tol ) < 0 )
01508                         return -1;              /* FAIL */
01509 #endif
01510         }
01511 
01512         /* Mark edges as real */
01513         (void)nmg_mark_edges_real( &s->l.magic );
01514 
01515         /* Compute "geometry" for region and shell */
01516         nmg_region_a( *r, tol );
01517 
01518         /* Some arbs may not be within tolerance, so triangulate faces where needed */
01519         nmg_make_faces_within_tol( s, tol );
01520 
01521         return(0);
01522 }
01523 
01524 static const fastf_t rt_arb_uvw[5*3] = {
01525         0, 0, 0,
01526         1, 0, 0,
01527         1, 1, 0,
01528         0, 1, 0,
01529         0, 0, 0
01530 };
01531 static const int rt_arb_vert_index_scramble[4] = { 0, 1, 3, 2 };
01532 
01533 /**
01534  *                      R T _ A R B _ T N U R B
01535  *
01536  *  "Tessellate" an ARB into a trimmed-NURB-NMG data structure.
01537  *  Purely a mechanical transformation of one faceted object
01538  *  into another.
01539  *
01540  *  Depending on the application, it might be beneficial to keep ARBs
01541  *  as planar-NMG objects; there is no real benefit to using B-splines
01542  *  here, other than uniformity of the conversion for all solids.
01543  *
01544  *  Returns -
01545  *      -1      failure
01546  *       0      OK.  *r points to nmgregion that holds this tessellation.
01547  */
01548 int
01549 rt_arb_tnurb(struct nmgregion **r, struct model *m, struct rt_db_internal *ip, const struct bn_tol *tol)
01550 {
01551         LOCAL struct rt_arb_internal    *aip;
01552         struct shell            *s;
01553         struct prep_arb         pa;
01554         register int            i;
01555         struct faceuse          *fu[6];
01556         struct vertex           *verts[8];
01557         struct vertex           **vertp[4];
01558         struct edgeuse          *eu;
01559         struct loopuse          *lu;
01560 
01561         RT_CK_DB_INTERNAL(ip);
01562         aip = (struct rt_arb_internal *)ip->idb_ptr;
01563         RT_ARB_CK_MAGIC(aip);
01564 
01565         bzero( (char *)&pa, sizeof(pa) );
01566         pa.pa_doopt = 0;                /* no UV stuff */
01567         pa.pa_tol_sq = tol->dist_sq;
01568         if( rt_arb_mk_planes( &pa, aip, "(tnurb)" ) < 0 )  return(-2);
01569 
01570         for( i=0; i<8; i++ )  verts[i] = (struct vertex *)0;
01571 
01572         *r = nmg_mrsv( m );     /* Make region, empty shell, vertex */
01573         s = BU_LIST_FIRST(shell, &(*r)->s_hd);
01574 
01575         /* Process each face */
01576         for( i=0; i < pa.pa_faces; i++ )  {
01577                 if( pa.pa_clockwise[i] != 0 )  {
01578                         /* Counter-Clockwise orientation (CCW) */
01579                         vertp[0] = &verts[pa.pa_pindex[0][i]];
01580                         vertp[1] = &verts[pa.pa_pindex[1][i]];
01581                         vertp[2] = &verts[pa.pa_pindex[2][i]];
01582                         if( pa.pa_npts[i] > 3 ) {
01583                                 vertp[3] = &verts[pa.pa_pindex[3][i]];
01584                         }
01585                 } else {
01586                         register struct vertex  ***vertpp = vertp;
01587                         /* Clockwise orientation (CW) */
01588                         if( pa.pa_npts[i] > 3 ) {
01589                                 *vertpp++ = &verts[pa.pa_pindex[3][i]];
01590                         }
01591                         *vertpp++ = &verts[pa.pa_pindex[2][i]];
01592                         *vertpp++ = &verts[pa.pa_pindex[1][i]];
01593                         *vertpp++ = &verts[pa.pa_pindex[0][i]];
01594                 }
01595                 if( RT_G_DEBUG & DEBUG_ARB8 )  {
01596                         bu_log("face %d, npts=%d, verts %d %d %d %d\n",
01597                                 i, pa.pa_npts[i],
01598                                 pa.pa_pindex[0][i], pa.pa_pindex[1][i],
01599                                 pa.pa_pindex[2][i], pa.pa_pindex[3][i] );
01600                 }
01601                 /* The edges created will be linear, in parameter space...,
01602                  * but need to have edge_g_cnurb geometry. */
01603                 if( (fu[i] = nmg_cmface( s, vertp, pa.pa_npts[i] )) == 0 )  {
01604                         bu_log("rt_arb_tnurb(%s): nmg_cmface() fail on face %d\n", i);
01605                         continue;
01606                 }
01607                 /* March around the fu's loop assigning uv parameter values */
01608                 lu = BU_LIST_FIRST( loopuse, &fu[i]->lu_hd );
01609                 NMG_CK_LOOPUSE(lu);
01610                 eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
01611                 NMG_CK_EDGEUSE(eu);
01612 
01613                 /* Loop always has Counter-Clockwise orientation (CCW) */
01614                 nmg_vertexuse_a_cnurb( eu->vu_p, &rt_arb_uvw[0*3] );
01615                 nmg_vertexuse_a_cnurb( eu->eumate_p->vu_p, &rt_arb_uvw[1*3] );
01616                 eu = BU_LIST_NEXT( edgeuse, &eu->l );
01617 
01618                 nmg_vertexuse_a_cnurb( eu->vu_p, &rt_arb_uvw[1*3] );
01619                 nmg_vertexuse_a_cnurb( eu->eumate_p->vu_p, &rt_arb_uvw[2*3] );
01620                 eu = BU_LIST_NEXT( edgeuse, &eu->l );
01621 
01622                 nmg_vertexuse_a_cnurb( eu->vu_p, &rt_arb_uvw[2*3] );
01623                 if( pa.pa_npts[i] > 3 ) {
01624                         nmg_vertexuse_a_cnurb( eu->eumate_p->vu_p, &rt_arb_uvw[3*3] );
01625 
01626                         eu = BU_LIST_NEXT( edgeuse, &eu->l );
01627                         nmg_vertexuse_a_cnurb( eu->vu_p, &rt_arb_uvw[3*3] );
01628                 }
01629                 /* Final eu must end back at the beginning */
01630                 nmg_vertexuse_a_cnurb( eu->eumate_p->vu_p, &rt_arb_uvw[0*3] );
01631         }
01632 
01633         /* Associate vertex geometry */
01634         for( i=0; i<8; i++ )
01635                 if(verts[i]) nmg_vertex_gv(verts[i], aip->pt[i]);
01636 
01637         /* Associate face geometry */
01638         for( i=0; i < pa.pa_faces; i++ )  {
01639                 struct face_g_snurb     *fg;
01640                 int     j;
01641 
01642                 /* Let the library allocate all the storage */
01643                 nmg_face_g_snurb( fu[i],
01644                         2, 2,           /* u,v order */
01645                         4, 4,           /* Number of knots, u,v */
01646                         NULL, NULL,     /* initial u,v knot vectors */
01647                         2, 2,           /* n_rows, n_cols */
01648                         RT_NURB_MAKE_PT_TYPE( 3, RT_NURB_PT_XYZ, RT_NURB_PT_NONRAT ),
01649                         NULL );         /* initial mesh */
01650 
01651                 fg = fu[i]->f_p->g.snurb_p;
01652                 NMG_CK_FACE_G_SNURB(fg);
01653 
01654                 /* Assign surface knot vectors as 0, 0, 1, 1 */
01655                 fg->u.knots[0] = fg->u.knots[1] = 0;
01656                 fg->u.knots[2] = fg->u.knots[3] = 1;
01657                 fg->v.knots[0] = fg->v.knots[1] = 0;
01658                 fg->v.knots[2] = fg->v.knots[3] = 1;
01659 
01660                 /* Assign surface control points from the corners */
01661                 lu = BU_LIST_FIRST( loopuse, &fu[i]->lu_hd );
01662                 NMG_CK_LOOPUSE(lu);
01663                 eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
01664                 NMG_CK_EDGEUSE(eu);
01665 
01666                 /* For ctl_points, need 4 verts in order 0, 1, 3, 2 */
01667                 for( j=0; j < pa.pa_npts[i]; j++ )  {
01668                         VMOVE( &fg->ctl_points[rt_arb_vert_index_scramble[j]*3],
01669                                 eu->vu_p->v_p->vg_p->coord );
01670 
01671                         /* Also associate edge geometry (trimming curve) */
01672                         nmg_edge_g_cnurb_plinear(eu);
01673                         eu = BU_LIST_NEXT( edgeuse, &eu->l );
01674                 }
01675                 if( pa.pa_npts[i] == 3 ) {
01676                         vect_t  c_b;
01677                         /*  Trimming curve describes a triangle ABC on face,
01678                          *  generate a phantom fourth corner at A + (C-B)
01679                          *  [3] = [0] + [2] - [1]
01680                          */
01681                         VSUB2( c_b,
01682                                 &fg->ctl_points[rt_arb_vert_index_scramble[2]*3],
01683                                 &fg->ctl_points[rt_arb_vert_index_scramble[1]*3] );
01684                         VADD2( &fg->ctl_points[rt_arb_vert_index_scramble[3]*3],
01685                                 &fg->ctl_points[rt_arb_vert_index_scramble[0]*3],
01686                                 c_b );
01687                 }
01688         }
01689 
01690 
01691         /* Mark edges as real */
01692         (void)nmg_mark_edges_real( &s->l.magic );
01693 
01694         /* Compute "geometry" for region and shell */
01695         nmg_region_a( *r, tol );
01696         return(0);
01697 }
01698 
01699 /* --- General ARB8 utility routines --- */
01700 
01701 /**
01702  *                      R T _ A R B _ C A L C _ P O I N T S
01703  *
01704  * Takes the planes[] array and intersects the planes to find the vertices
01705  * of a GENARB8.  The vertices are stored into arb->pt[].
01706  * This is an analog of rt_arb_calc_planes().
01707  */
01708 int
01709 rt_arb_calc_points(
01710         struct rt_arb_internal  *arb,           /* needs wdb.h */
01711         int                     cgtype,
01712         const plane_t           planes[6],
01713         const struct bn_tol     *tol)
01714 {
01715         int     i;
01716         point_t pt[8];
01717 
01718         RT_ARB_CK_MAGIC(arb);
01719 
01720         /* find new points for entire solid */
01721         for(i=0; i<8; i++){
01722                 if( rt_arb_3face_intersect( pt[i], planes, cgtype, i*3 ) < 0 )  {
01723                   bu_log("rt_arb_calc_points: Intersection of planes fails %d\n", i);
01724                   return -1;                    /* FAIL */
01725                 }
01726         }
01727 
01728         /* Move new points to arb */
01729         for( i=0; i<8; i++ )  {
01730                 VMOVE( arb->pt[i], pt[i] );
01731         }
01732         return 0;                                       /* success */
01733 }
01734 
01735 /* planes to define ARB vertices */
01736 const int rt_arb_planes[5][24] = {
01737         {0,1,3, 0,1,2, 0,2,3, 0,1,3, 1,2,3, 1,2,3, 1,2,3, 1,2,3},       /* ARB4 */
01738         {0,1,4, 0,1,2, 0,2,3, 0,3,4, 1,2,4, 1,2,4, 1,2,4, 1,2,4},       /* ARB5 */
01739         {0,2,3, 0,1,3, 0,1,4, 0,2,4, 1,2,3, 1,2,3, 1,2,4, 1,2,4},       /* ARB6 */
01740         {0,2,4, 0,3,4, 0,3,5, 0,2,5, 1,4,5, 1,3,4, 1,3,5, 1,2,4},       /* ARB7 */
01741         {0,2,4, 0,3,4, 0,3,5, 0,2,5, 1,2,4, 1,3,4, 1,3,5, 1,2,5},       /* ARB8 */
01742 };
01743 
01744 /**
01745  *                      R T _ A R B _ 3 F A C E _ I N T E R S E C T
01746  *
01747  *      Finds the intersection point of three faces of an ARB.
01748  *
01749  *  Returns -
01750  *        0     success, value is in 'point'
01751  *       -1     failure
01752  */
01753 int
01754 rt_arb_3face_intersect(
01755         point_t                 point,
01756         const plane_t           planes[6],
01757         int                     type,           /* 4..8 */
01758         int                     loc)
01759 {
01760         int     j;
01761         int     i1, i2, i3;
01762 
01763         j = type - 4;
01764 
01765         i1 = rt_arb_planes[j][loc];
01766         i2 = rt_arb_planes[j][loc+1];
01767         i3 = rt_arb_planes[j][loc+2];
01768 
01769         return bn_mkpoint_3planes( point, planes[i1], planes[i2], planes[i3] );
01770 }
01771 
01772 
01773 /**
01774  *                      R T _ A R B _ C A L C _ P L A N E S
01775  *
01776  *      Calculate the plane (face) equations for an arb
01777  *      output previously went to es_peqn[i].
01778  *
01779  *  Returns -
01780  *      -1      Failure
01781  *       0      OK
01782  *
01783  *  Note -
01784  *       This function migrated from mged/edsol.c.
01785  */
01786 int
01787 rt_arb_calc_planes(Tcl_Interp                   *interp,
01788                    struct rt_arb_internal       *arb,
01789                    int                          type,
01790                    plane_t                      planes[6],
01791                    const struct bn_tol          *tol)
01792 {
01793     register int i, p1, p2, p3;
01794 
01795     RT_ARB_CK_MAGIC(arb);
01796     BN_CK_TOL(tol);
01797 
01798     type -= 4;  /* ARB4 at location 0, ARB5 at 1, etc */
01799 
01800     for (i=0; i<6; i++) {
01801         if(rt_arb_faces[type][i*4] == -1)
01802             break;      /* faces are done */
01803 
01804         p1 = rt_arb_faces[type][i*4];
01805         p2 = rt_arb_faces[type][i*4+1];
01806         p3 = rt_arb_faces[type][i*4+2];
01807 
01808         if (bn_mk_plane_3pts(planes[i],
01809                              arb->pt[p1],
01810                              arb->pt[p2],
01811                              arb->pt[p3],
01812                              tol) < 0) {
01813             struct bu_vls tmp_vls;
01814 
01815             bu_vls_init(&tmp_vls);
01816             bu_vls_printf(&tmp_vls, "%d %d%d%d%d (bad face)\n",
01817                           i+1, p1+1, p2+1, p3+1, rt_arb_faces[type][i*4+3]+1);
01818             Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
01819             return -1;
01820         }
01821     }
01822 
01823     return 0;
01824 }
01825 
01826 
01827 /**  MV_EDGE:
01828  *      Moves an arb edge (end1,end2) with bounding
01829  *      planes bp1 and bp2 through point "thru".
01830  *      The edge has (non-unit) slope "dir".
01831  *      Note that the fact that the normals here point in rather than
01832  *      out makes no difference for computing the correct intercepts.
01833  *      After the intercepts are found, they should be checked against
01834  *      the other faces to make sure that they are always "inside".
01835  */
01836 int
01837 rt_arb_move_edge(Tcl_Interp             *interp,
01838                  struct rt_arb_internal *arb,
01839                  vect_t                 thru,
01840                  int                    bp1,
01841                  int                    bp2,
01842                  int                    end1,
01843                  int                    end2,
01844                  const vect_t           dir,
01845                  plane_t                planes[6],
01846                  const struct bn_tol    *tol)
01847 {
01848     fastf_t     t1, t2;
01849 
01850     if (bn_isect_line3_plane(&t1, thru, dir, planes[bp1], tol) < 0 ||
01851         bn_isect_line3_plane(&t2, thru, dir, planes[bp2], tol) < 0) {
01852         Tcl_AppendResult(interp, "edge (direction) parallel to face normal\n", (char *)NULL);
01853         return (1);
01854     }
01855 
01856     RT_ARB_CK_MAGIC(arb);
01857 
01858     VJOIN1(arb->pt[end1], thru, t1, dir);
01859     VJOIN1(arb->pt[end2], thru, t2, dir);
01860 
01861     return(0);
01862 }
01863 
01864 /**
01865  *                      E D I T A R B
01866  *
01867  *  An ARB edge is moved by finding the direction of
01868  *  the line containing the edge and the 2 "bounding"
01869  *  planes.  The new edge is found by intersecting the
01870  *  new line location with the bounding planes.  The
01871  *  two "new" planes thus defined are calculated and the
01872  *  affected points are calculated by intersecting planes.
01873  *  This keeps ALL faces planar.
01874  *
01875  *  Note -
01876  *       This code came from mged/edarb.c (written mostly by Keith Applin)
01877  *       and was modified to live here.
01878  *
01879  */
01880 
01881 /*  The storage for the "specific" ARB types is :
01882  *
01883  *      ARB4    0 1 2 0 3 3 3 3
01884  *      ARB5    0 1 2 3 4 4 4 4
01885  *      ARB6    0 1 2 3 4 4 5 5
01886  *      ARB7    0 1 2 3 4 5 6 4
01887  *      ARB8    0 1 2 3 4 5 6 7
01888  */
01889 
01890 /*
01891  *      ARB6    0 1 2 3 4 5 5 4
01892  */
01893 
01894 /* Another summary of how the vertices of ARBs are stored:
01895  *
01896  * Vertices:    1       2       3       4       5       6       7       8
01897  * Location----------------------------------------------------------------
01898  *      ARB8    0       1       2       3       4       5       6       7
01899  *      ARB7    0       1       2       3       4,7     5       6
01900  *      ARB6    0       1       2       3       4,5     6,7
01901  *      ARB5    0       1       2       3       4,5,6,7
01902  *      ARB4    0,3     1       2       4,5,6,7
01903  */
01904 
01905 /* The following arb editing arrays generally contain the following:
01906  *
01907  *      location        comments
01908  *------------------------------------------------------------------------
01909  *      0,1             edge end points
01910  *      2,3             bounding planes 1 and 2
01911  *      4, 5,6,7        plane 1 to recalculate, using next 3 points
01912  *      8, 9,10,11      plane 2 to recalculate, using next 3 points
01913  *      12, 13,14,15    plane 3 to recalculate, using next 3 points
01914  *      16,17           points (vertices) to recalculate
01915  *
01916  *
01917  * Each line is repeated for each edge (or point) to move
01918 */
01919 
01920 /* edit array for arb8's */
01921 short earb8[12][18] = {
01922         {0,1, 2,3, 0,0,1,2, 4,0,1,4, -1,0,0,0, 3,5},    /* edge 12 */
01923         {1,2, 4,5, 0,0,1,2, 3,1,2,5, -1,0,0,0, 3,6},    /* edge 23 */
01924         {2,3, 3,2, 0,0,2,3, 5,2,3,6, -1,0,0,0, 1,7},    /* edge 34 */
01925         {0,3, 4,5, 0,0,1,3, 2,0,3,4, -1,0,0,0, 2,7},    /* edge 14 */
01926         {0,4, 0,1, 2,0,4,3, 4,0,1,4, -1,0,0,0, 7,5},    /* edge 15 */
01927         {1,5, 0,1, 4,0,1,5, 3,1,2,5, -1,0,0,0, 4,6},    /* edge 26 */
01928         {4,5, 2,3, 4,0,5,4, 1,4,5,6, -1,0,0,0, 1,7},    /* edge 56 */
01929         {5,6, 4,5, 3,1,5,6, 1,4,5,6, -1,0,0,0, 2,7},    /* edge 67 */
01930         {6,7, 3,2, 5,2,7,6, 1,4,6,7, -1,0,0,0, 3,4},    /* edge 78 */
01931         {4,7, 4,5, 2,0,7,4, 1,4,5,7, -1,0,0,0, 3,6},    /* edge 58 */
01932         {2,6, 0,1, 3,1,2,6, 5,2,3,6, -1,0,0,0, 5,7},    /* edge 37 */
01933         {3,7, 0,1, 2,0,3,7, 5,2,3,7, -1,0,0,0, 4,6},    /* edge 48 */
01934 };
01935 
01936 /* edit array for arb7's */
01937 short earb7[12][18] = {
01938         {0,1, 2,3, 0,0,1,2, 4,0,1,4, -1,0,0,0, 3,5},    /* edge 12 */
01939         {1,2, 4,5, 0,0,1,2, 3,1,2,5, -1,0,0,0, 3,6},    /* edge 23 */
01940         {2,3, 3,2, 0,0,2,3, 5,2,3,6, -1,0,0,0, 1,4},    /* edge 34 */
01941         {0,3, 4,5, 0,0,1,3, 2,0,3,4, -1,0,0,0, 2,-1},   /* edge 41 */
01942         {0,4, 0,5, 4,0,5,4, 2,0,3,4, 1,4,5,6, 1,-1},    /* edge 15 */
01943         {1,5, 0,1, 4,0,1,5, 3,1,2,5, -1,0,0,0, 4,6},    /* edge 26 */
01944         {4,5, 5,3, 2,0,3,4, 4,0,5,4, 1,4,5,6, 1,-1},    /* edge 56 */
01945         {5,6, 4,5, 3,1,6,5, 1,4,5,6, -1,0,0,0, 2, -1},  /* edge 67 */
01946         {2,6, 0,1, 5,2,3,6, 3,1,2,6, -1,0,0,0, 4,5},    /* edge 37 */
01947         {4,6, 4,3, 2,0,3,4, 5,3,4,6, 1,4,5,6, 2,-1},    /* edge 57 */
01948         {3,4, 0,1, 4,0,1,4, 2,0,3,4, 5,2,3,4, 5,6},     /* edge 45 */
01949         {-1,-1, -1,-1, 5,2,3,4, 4,0,1,4, 8,2,1,-1, 6,5},        /* point 5 */
01950 };
01951 
01952 /* edit array for arb6's */
01953 short earb6[10][18] = {
01954         {0,1, 2,1, 3,0,1,4, 0,0,1,2, -1,0,0,0, 3,-1},   /* edge 12 */
01955         {1,2, 3,4, 1,1,2,5, 0,0,1,2, -1,0,0,0, 3,4},    /* edge 23 */
01956         {2,3, 1,2, 4,2,3,5, 0,0,2,3, -1,0,0,0, 1,-1},   /* edge 34 */
01957         {0,3, 3,4, 2,0,3,5, 0,0,1,3, -1,0,0,0, 4,2},    /* edge 14 */
01958         {0,4, 0,1, 3,0,1,4, 2,0,3,4, -1,0,0,0, 6,-1},   /* edge 15 */
01959         {1,4, 0,2, 3,0,1,4, 1,1,2,4, -1,0,0,0, 6,-1},   /* edge 25 */
01960         {2,6, 0,2, 4,6,2,3, 1,1,2,6, -1,0,0,0, 4,-1},   /* edge 36 */
01961         {3,6, 0,1, 4,6,2,3, 2,0,3,6, -1,0,0,0, 4,-1},   /* edge 46 */
01962         {-1,-1, -1,-1, 2,0,3,4, 1,1,2,4, 3,0,1,4, 6,-1},/* point 5 */
01963         {-1,-1, -1,-1, 2,0,3,6, 1,1,2,6, 4,2,3,6, 4,-1},/* point 6 */
01964 };
01965 
01966 /* edit array for arb5's */
01967 short earb5[9][18] = {
01968         {0,1, 4,2, 0,0,1,2, 1,0,1,4, -1,0,0,0, 3,-1},   /* edge 12 */
01969         {1,2, 1,3, 0,0,1,2, 2,1,2,4, -1,0,0,0, 3,-1},   /* edge 23 */
01970         {2,3, 2,4, 0,0,2,3, 3,2,3,4, -1,0,0,0, 1,-1},   /* edge 34 */
01971         {0,3, 1,3, 0,0,1,3, 4,0,3,4, -1,0,0,0, 2,-1},   /* edge 14 */
01972         {0,4, 0,2, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},   /* edge 15 */
01973         {1,4, 0,3, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},   /* edge 25 */
01974         {2,4, 0,4, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},   /* edge 35 */
01975         {3,4, 0,1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},   /* edge 45 */
01976         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* point 5 */
01977 };
01978 
01979 /* edit array for arb4's */
01980 short earb4[5][18] = {
01981         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* point 1 */
01982         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* point 2 */
01983         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* point 3 */
01984         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* dummy */
01985         {-1,-1, -1,-1, 9,0,0,0, 9,0,0,0, 9,0,0,0, -1,-1},       /* point 4 */
01986 };
01987 
01988 #define RT_ARB_EDIT_EDGE 0
01989 #define RT_ARB_EDIT_POINT 1
01990 #define RT_ARB7_MOVE_POINT_5 11
01991 #define RT_ARB6_MOVE_POINT_5 8
01992 #define RT_ARB6_MOVE_POINT_6 9
01993 #define RT_ARB5_MOVE_POINT_5 8
01994 #define RT_ARB4_MOVE_POINT_4 3
01995 
01996 int
01997 rt_arb_edit(Tcl_Interp                  *interp,
01998             struct rt_arb_internal      *arb,
01999             int                         arb_type,
02000             int                         edit_type,
02001             vect_t                      pos_model,
02002             plane_t                     planes[6],
02003             const struct bn_tol         *tol)
02004 {
02005     int pt1, pt2, bp1, bp2, newp, p1, p2, p3;
02006     short *edptr;               /* pointer to arb edit array */
02007     short *final;               /* location of points to redo */
02008     int i;
02009     const int *iptr;
02010     int edit_class = RT_ARB_EDIT_EDGE;
02011 
02012     RT_ARB_CK_MAGIC(arb);
02013 
02014     /* set the pointer */
02015     switch (arb_type) {
02016     case ARB4:
02017         edptr = &earb4[edit_type][0];
02018         final = &earb4[edit_type][16];
02019 
02020         if (edit_type == RT_ARB4_MOVE_POINT_4)
02021             edit_type = 4;
02022 
02023         edit_class = RT_ARB_EDIT_POINT;
02024 
02025         break;
02026     case ARB5:
02027         edptr = &earb5[edit_type][0];
02028         final = &earb5[edit_type][16];
02029 
02030         if (edit_type == RT_ARB5_MOVE_POINT_5) {
02031             edit_class = RT_ARB_EDIT_POINT;
02032             edit_type = 4;
02033         }
02034 
02035         if (edit_class == RT_ARB_EDIT_POINT) {
02036             edptr = &earb5[8][0];
02037             final = &earb5[8][16];
02038         }
02039 
02040         break;
02041     case ARB6:
02042         edptr = &earb6[edit_type][0];
02043         final = &earb6[edit_type][16];
02044 
02045         if (edit_type == RT_ARB6_MOVE_POINT_5) {
02046             edit_class = RT_ARB_EDIT_POINT;
02047             edit_type = 4;
02048         } else if (edit_type == RT_ARB6_MOVE_POINT_6) {
02049             edit_class = RT_ARB_EDIT_POINT;
02050             edit_type = 6;
02051         }
02052 
02053         if (edit_class == RT_ARB_EDIT_POINT) {
02054             i = 9;
02055             if(edit_type == 4)
02056                 i = 8;
02057             edptr = &earb6[i][0];
02058             final = &earb6[i][16];
02059         }
02060 
02061         break;
02062     case ARB7:
02063         edptr = &earb7[edit_type][0];
02064         final = &earb7[edit_type][16];
02065 
02066         if (edit_type == RT_ARB7_MOVE_POINT_5) {
02067             edit_class = RT_ARB_EDIT_POINT;
02068             edit_type = 4;
02069         }
02070 
02071         if (edit_class == RT_ARB_EDIT_POINT) {
02072             edptr = &earb7[11][0];
02073             final = &earb7[11][16];
02074         }
02075 
02076         break;
02077     case ARB8:
02078         edptr = &earb8[edit_type][0];
02079         final = &earb8[edit_type][16];
02080 
02081         break;
02082     default:
02083         Tcl_AppendResult(interp, "rt_arb_edit: unknown ARB type\n", (char *)NULL);
02084 
02085         return(1);
02086     }
02087 
02088     /* do the arb editing */
02089     if (edit_class == RT_ARB_EDIT_POINT) {
02090         /* moving a point - not an edge */
02091         VMOVE(arb->pt[edit_type] , pos_model);
02092         edptr += 4;
02093     } else if (edit_class == RT_ARB_EDIT_EDGE) {
02094         vect_t  edge_dir;
02095 
02096         /* moving an edge */
02097         pt1 = *edptr++;
02098         pt2 = *edptr++;
02099 
02100         /* calculate edge direction */
02101         VSUB2(edge_dir, arb->pt[pt2], arb->pt[pt1]);
02102 
02103         if (MAGNITUDE(edge_dir) == 0.0)
02104             goto err;
02105 
02106         /* bounding planes bp1,bp2 */
02107         bp1 = *edptr++;
02108         bp2 = *edptr++;
02109 
02110         /* move the edge */
02111         if (rt_arb_move_edge(interp, arb, pos_model, bp1, bp2, pt1, pt2,
02112                              edge_dir, planes, tol))
02113             goto err;
02114     }
02115 
02116     /* editing is done - insure planar faces */
02117     /* redo plane eqns that changed */
02118     newp = *edptr++;    /* plane to redo */
02119 
02120     if (newp == 9)      /* special flag --> redo all the planes */
02121         if (rt_arb_calc_planes(interp, arb, arb_type, planes, tol))
02122             goto err;
02123 
02124     if (newp >= 0 && newp < 6) {
02125         for (i=0; i<3; i++) {
02126             /* redo this plane (newp), use points p1,p2,p3 */
02127             p1 = *edptr++;
02128             p2 = *edptr++;
02129             p3 = *edptr++;
02130 
02131             if (bn_mk_plane_3pts(planes[newp], arb->pt[p1], arb->pt[p2],
02132                                  arb->pt[p3], tol))
02133                 goto err;
02134 
02135             /* next plane */
02136             if ((newp = *edptr++) == -1 || newp == 8)
02137                 break;
02138         }
02139     }
02140 
02141     if (newp == 8) {
02142         /* special...redo next planes using pts defined in faces */
02143         for (i=0; i<3; i++) {
02144             if ((newp = *edptr++) == -1)
02145                 break;
02146 
02147             iptr = &rt_arb_faces[arb_type-4][4*newp];
02148             p1 = *iptr++;
02149             p2 = *iptr++;
02150             p3 = *iptr++;
02151 
02152             if (bn_mk_plane_3pts(planes[newp], arb->pt[p1], arb->pt[p2],
02153                                  arb->pt[p3], tol))
02154                 goto err;
02155         }
02156     }
02157 
02158     /* the changed planes are all redone
02159      *  push necessary points back into the planes
02160      */
02161     edptr = final;      /* point to the correct location */
02162     for (i=0; i<2; i++) {
02163         if ((p1 = *edptr++) == -1)
02164             break;
02165 
02166         /* intersect proper planes to define vertex p1 */
02167 
02168         if (rt_arb_3face_intersect(arb->pt[p1], (const plane_t *)planes, arb_type, p1*3))
02169             goto err;
02170     }
02171 
02172     /* Special case for ARB7: move point 5 .... must
02173      *  recalculate plane 2 = 456
02174      */
02175     if (arb_type == ARB7 && edit_class == RT_ARB_EDIT_POINT) {
02176         if (bn_mk_plane_3pts( planes[2], arb->pt[4], arb->pt[5], arb->pt[6], tol))
02177             goto err;
02178     }
02179 
02180     /* carry along any like points */
02181     switch (arb_type) {
02182     case ARB8:
02183         break;
02184     case ARB7:
02185         VMOVE(arb->pt[7], arb->pt[4]);
02186         break;
02187     case ARB6:
02188         VMOVE(arb->pt[5], arb->pt[4]);
02189         VMOVE(arb->pt[7], arb->pt[6]);
02190         break;
02191     case ARB5:
02192         for (i=5; i<8; i++)
02193             VMOVE(arb->pt[i], arb->pt[4]);
02194         break;
02195     case ARB4:
02196         VMOVE(arb->pt[3] , arb->pt[0]);
02197         for(i=5; i<8; i++)
02198             VMOVE(arb->pt[i], arb->pt[4])
02199                 break;
02200     }
02201 
02202     return(0);          /* OK */
02203 
02204 err:
02205     /* Error handling */
02206     {
02207         struct bu_vls tmp_vls;
02208 
02209         bu_vls_init(&tmp_vls);
02210         bu_vls_printf(&tmp_vls, "cannot move edge: %d%d\n", pt1+1,pt2+1);
02211         Tcl_AppendResult(interp, bu_vls_addr(&tmp_vls), (char *)NULL);
02212         bu_vls_free(&tmp_vls);
02213     }
02214 
02215     return(1);          /* BAD */
02216 }
02217 /*@}*/
02218 
02219 /*
02220  * Local Variables:
02221  * mode: C
02222  * tab-width: 8
02223  * c-basic-offset: 4
02224  * indent-tabs-mode: t
02225  * End:
02226  * ex: shiftwidth=4 tabstop=8
02227  */

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