nmg_class.c

Go to the documentation of this file.
00001 /*                     N M G _ C L A S S . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1993-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 nmg */
00023 
00024 /*@{*/
00025 /** @file nmg_class.c
00026  *  Subroutines to classify one object with respect to another.
00027  *  Possible classifications are AinB, AoutB, AinBshared, AinBanti.
00028  *
00029  *  The first set of routines (nmg_class_pt_xxx) are used to classify
00030  *  an arbitrary point specified by it's Cartesian coordinates,
00031  *  against various kinds of NMG elements.
00032  *  nmg_class_pt_f() and nmg_class_pt_s() are available to
00033  *  applications programmers for direct use, and have no side effects.
00034  *
00035  *  The second set of routines (class_xxx_vs_s) are used only to support
00036  *  the routine nmg_class_shells() mid-way through the NMG Boolean
00037  *  algorithm.  These routines operate with special knowledge about
00038  *  the state of the data structures after the intersector has been called,
00039  *  and depends on all geometric equivalences to have been converted into
00040  *  shared topology.
00041  *
00042  *  Authors -
00043  *      Lee A. Butler
00044  *      Michael John Muuss
00045  *
00046  *  Source -
00047  *      The U. S. Army Research Laboratory
00048  *      Aberdeen Proving Ground, Maryland  21005-5068  USA
00049  */
00050 /*@}*/
00051 
00052 #ifndef lint
00053 static const char RCSid[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/nmg_class.c,v 14.13 2006/09/16 02:04:25 lbutler Exp $ (ARL)";
00054 #endif
00055 
00056 #include "common.h"
00057 
00058 
00059 #include <stdio.h>
00060 #include <math.h>
00061 #include "machine.h"
00062 #include "vmath.h"
00063 #include "nmg.h"
00064 #include "raytrace.h"
00065 #include "./debug.h"
00066 #include "plot3.h"
00067 
00068 extern int nmg_class_nothing_broken;
00069 
00070 /* XXX These should go the way of the dodo bird. */
00071 #define INSIDE  32
00072 #define ON_SURF 64
00073 #define OUTSIDE 128
00074 
00075 /*      Structure for keeping track of how close a point/vertex is to
00076  *      its potential neighbors.
00077  */
00078 struct neighbor {
00079         union {
00080                 const struct vertexuse *vu;
00081                 const struct edgeuse *eu;
00082         } p;
00083         /* XXX For efficiency, this should have been dist_sq */
00084         fastf_t dist;   /* distance from point to neighbor */
00085         int     class;  /* Classification of this neighbor */
00086 };
00087 
00088 static void     joint_hitmiss2 BU_ARGS( (struct neighbor *closest,
00089                         const struct edgeuse *eu, const point_t pt,
00090                         int code) );
00091 static void     nmg_class_pt_e BU_ARGS( (struct neighbor *closest,
00092                         const point_t pt, const struct edgeuse *eu,
00093                         const struct bn_tol *tol) );
00094 static void     nmg_class_pt_l BU_ARGS( (struct neighbor *closest,
00095                         const point_t pt, const struct loopuse *lu,
00096                         const struct bn_tol *tol) );
00097 static int      class_vu_vs_s BU_ARGS( (struct vertexuse *vu, struct shell *sB,
00098                         long *classlist[4], const struct bn_tol *tol) );
00099 static int      class_eu_vs_s BU_ARGS( (struct edgeuse *eu, struct shell *s,
00100                         long *classlist[4], const struct bn_tol *tol) );
00101 static int      class_lu_vs_s BU_ARGS( (struct loopuse *lu, struct shell *s,
00102                         long *classlist[4], const struct bn_tol *tol) );
00103 static void     class_fu_vs_s BU_ARGS( (struct faceuse *fu, struct shell *s,
00104                         long *classlist[4], const struct bn_tol *tol) );
00105 
00106 /**
00107  *                      N M G _ C L A S S _ S T A T U S
00108  *
00109  *  Convert classification status to string.
00110  */
00111 const char *
00112 nmg_class_status(int status)
00113 {
00114         switch(status)  {
00115         case INSIDE:
00116                 return "INSIDE";
00117         case OUTSIDE:
00118                 return "OUTSIDE";
00119         case ON_SURF:
00120                 return "ON_SURF";
00121         }
00122         return "BOGUS_CLASS_STATUS";
00123 }
00124 
00125 /**
00126  *                      N M G _ P R _ C L A S S _ S T A T U S
00127  */
00128 void
00129 nmg_pr_class_status(char *prefix, int status)
00130 {
00131         bu_log("%s has classification status %s\n",
00132                 prefix, nmg_class_status(status) );
00133 }
00134 
00135 /**
00136  *                      J O I N T _ H I T M I S S 2
00137  *
00138  *      The ray hit an edge.  We have to decide whether it hit the
00139  *      edge, or missed it.
00140  *
00141  *  XXX DANGER:  This routine does not work well.
00142  *
00143  *  Called by -
00144  *      nmg_class_pt_e
00145  */
00146 static void
00147 joint_hitmiss2(struct neighbor *closest, const struct edgeuse *eu, const fastf_t *pt, int code)
00148 {
00149         const struct edgeuse *eu_rinf;
00150 
00151         eu_rinf = nmg_faceradial(eu);
00152 
00153         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00154                 bu_log("joint_hitmiss2\n");
00155         }
00156         if( eu_rinf == eu )  {
00157                 rt_bomb("joint_hitmiss2: radial eu is me?\n");
00158         }
00159         /* If eu_rinf == eu->eumate_p, thats OK, this is a dangling face,
00160          * or a face that has not been fully hooked up yet.
00161          * It's OK as long the the orientations both match.
00162          */
00163         if( eu->up.lu_p->orientation == eu_rinf->up.lu_p->orientation ) {
00164                 if (eu->up.lu_p->orientation == OT_SAME) {
00165                         closest->class = NMG_CLASS_AonBshared;
00166                 } else if (eu->up.lu_p->orientation == OT_OPPOSITE) {
00167                         closest->class = NMG_CLASS_AoutB;
00168                 } else {
00169                         nmg_pr_lu_briefly(eu->up.lu_p, (char *)0);
00170                         rt_bomb("joint_hitmiss2: bad loop orientation\n");
00171                 }
00172                 closest->dist = 0.0;
00173                 switch(code)  {
00174                 case 0:
00175                         /* Hit the edge */
00176                         closest->p.eu = eu;
00177                         break;
00178                 case 1:
00179                         /* Hit first vertex */
00180                         closest->p.vu = eu->vu_p;
00181                         break;
00182                 case 2:
00183                         /* Hit second vertex */
00184                         closest->p.vu = BU_LIST_PNEXT_CIRC(edgeuse,eu)->vu_p;
00185                         break;
00186                 }
00187                 if (rt_g.NMG_debug & DEBUG_CLASSIFY) bu_log("\t\t%s\n", nmg_class_name(closest->class) );
00188                 return;
00189         }
00190 
00191         /* XXX What goes here? */
00192         rt_bomb("nmg_class.c/joint_hitmiss2() unable to resolve ray/edge hit\n");
00193         bu_log("joint_hitmiss2: NO CODE HERE, assuming miss\n");
00194 
00195         if (rt_g.NMG_debug & (DEBUG_CLASSIFY|DEBUG_NMGRT) )  {
00196                 nmg_euprint("Hard question time", eu);
00197                 bu_log(" eu_rinf=x%x, eu->eumate_p=x%x, eu=x%x\n", eu_rinf, eu->eumate_p, eu);
00198                 bu_log(" eu lu orient=%s, eu_rinf lu orient=%s\n",
00199                         nmg_orientation(eu->up.lu_p->orientation),
00200                         nmg_orientation(eu_rinf->up.lu_p->orientation) );
00201         }
00202 }
00203 
00204 /**
00205  *                      N M G _ C L A S S _ P T _ E
00206  *
00207  *  XXX DANGER:  This routine does not work well.
00208  *
00209  *  Given the Cartesian coordinates of a point, determine if the point
00210  *  is closer to this edgeuse than the previous neighbor(s) as given
00211  *  in the "closest" structure.
00212  *  If it is, record how close the point is, and whether it is IN, ON, or OUT.
00213  *  The neighor's "p" element will indicate the edgeuse or vertexuse closest.
00214  *
00215  *  This routine should print everything indented two tab stops.
00216  *
00217  *  Implicit Return -
00218  *      Updated "closest" structure if appropriate.
00219  *
00220  *  Called by -
00221  *      nmg_class_pt_l
00222  */
00223 static void
00224 nmg_class_pt_e(struct neighbor *closest, const fastf_t *pt, const struct edgeuse *eu, const struct bn_tol *tol)
00225 {
00226         vect_t  ptvec;  /* vector from lseg to pt */
00227         vect_t  left;   /* vector left of edge -- into inside of loop */
00228         const fastf_t   *eupt;
00229         const fastf_t   *matept;
00230         point_t pca;    /* point of closest approach from pt to edge lseg */
00231         fastf_t dist;   /* distance from pca to pt */
00232         fastf_t dot;
00233         int     code;
00234 
00235         NMG_CK_EDGEUSE(eu);
00236         NMG_CK_EDGEUSE(eu->eumate_p);
00237         NMG_CK_VERTEX_G(eu->vu_p->v_p->vg_p);
00238         NMG_CK_VERTEX_G(eu->eumate_p->vu_p->v_p->vg_p);
00239         BN_CK_TOL(tol);
00240         VSETALL(left, 0);
00241 
00242         eupt = eu->vu_p->v_p->vg_p->coord;
00243         matept = eu->eumate_p->vu_p->v_p->vg_p->coord;
00244 
00245         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00246                 VPRINT("nmg_class_pt_e\tPt", pt);
00247                 nmg_euprint("          \tvs. eu", eu);
00248         }
00249         /*
00250          * Note that "pca" can be one of the edge endpoints,
00251          * even if "pt" is far, far away.  This can be confusing.
00252          *
00253          * Some compilers don't get that fastf_t * and point_t are related
00254          * So we have to pass the whole bloody mess for the point arguments.
00255          */
00256         code = bn_dist_pt3_lseg3( &dist, pca, eu->vu_p->v_p->vg_p->coord,
00257                 eu->eumate_p->vu_p->v_p->vg_p->coord,
00258                 pt, tol);
00259         if( code <= 0 )  dist = 0;
00260         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00261                 bu_log("          \tcode=%d, dist: %g\n", code, dist);
00262                 VPRINT("          \tpca:", pca);
00263         }
00264 
00265         if (dist >= closest->dist + tol->dist ) {
00266                 if(rt_g.NMG_debug & DEBUG_CLASSIFY) {
00267                         bu_log("\t\tskipping, earlier eu is closer (%g)\n", closest->dist);
00268                 }
00269                 return;
00270         }
00271         if( dist >= closest->dist - tol->dist )  {
00272                 /*
00273                  *  Distances are very nearly the same.
00274                  *
00275                  *  If closest eu and this eu are from the same lu,
00276                  *  and the earlier result was OUT, that's all it takes
00277                  *  to decide things.
00278                  */
00279                 if( closest->p.eu && closest->p.eu->up.lu_p == eu->up.lu_p )  {
00280                         if( closest->class == NMG_CLASS_AoutB ||
00281                             closest->class == NMG_CLASS_AonBshared )  {
00282                                 if(rt_g.NMG_debug & DEBUG_CLASSIFY)
00283                                         bu_log("\t\tSkipping, earlier eu from same lu at same dist, is OUT or ON.\n");
00284                                 return;
00285                         }
00286                         if(rt_g.NMG_debug & DEBUG_CLASSIFY)
00287                                 bu_log("\t\tEarlier eu from same lu at same dist, is IN, continue processing.\n");
00288                 } else {
00289                         /*
00290                          *  If this is now a new lu, it is more complicated.
00291                          *  If "closest" result so far was a NMG_CLASS_AinB or
00292                          *  or NMG_CLASS_AonB, then keep it,
00293                          *  otherwise, replace that result with whatever we find
00294                          *  here.  Logic:  Two touching loops, one concave ("A")
00295                          *  which wraps around part of the other ("B"), with the
00296                          *  point inside A near the contact with B.  If loop B is
00297                          *  processed first, the closest result will be NMG_CLASS_AoutB,
00298                          *  and when loop A is visited the distances will be exactly
00299                          *  equal, not giving A a chance to claim it's hit.
00300                          */
00301                         if( closest->class == NMG_CLASS_AinB ||
00302                             closest->class == NMG_CLASS_AonBshared )  {
00303                                 if(rt_g.NMG_debug & DEBUG_CLASSIFY)
00304                                         bu_log("\t\tSkipping, earlier eu from other another lu at same dist, is IN or ON\n");
00305                                 return;
00306                         }
00307                         if(rt_g.NMG_debug & DEBUG_CLASSIFY)
00308                                 bu_log("\t\tEarlier eu from other lu at same dist, is OUT, continue processing.\n");
00309                 }
00310         }
00311 
00312         /* Plane hit point is closer to this edgeuse than previous one(s) */
00313         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00314                 bu_log("\t\tCLOSER dist=%g (closest=%g), tol=%g\n",
00315                         dist, closest->dist, tol->dist);
00316         }
00317 
00318         if (*eu->up.magic_p != NMG_LOOPUSE_MAGIC ||
00319             *eu->up.lu_p->up.magic_p != NMG_FACEUSE_MAGIC) {
00320                 bu_log("Trying to classify a pt (%g, %g, %g)\n\tvs a wire edge? (%g, %g, %g -> %g, %g, %g)\n",
00321                         V3ARGS(pt),
00322                         V3ARGS(eupt),
00323                         V3ARGS(matept)
00324                 );
00325                 return;
00326         }
00327 
00328         if( code <= 2 )  {
00329                 /* code==0:  The point is ON the edge! */
00330                 /* code==1 or 2:  The point is ON a vertex! */
00331                 if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00332                         bu_log("\t\tThe point is ON the edge, calling joint_hitmiss2()\n");
00333                 }
00334                 joint_hitmiss2(closest, eu, pt, code);
00335                 return;
00336         } else {
00337                 if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00338                         bu_log("\t\tThe point is not on the edge\n");
00339                 }
00340         }
00341 
00342         /* The point did not lie exactly ON the edge, calculate in/out */
00343 
00344         /* Get vector which lies on the plane, and points
00345          * left, towards the interior of the loop, regardless of
00346          * whether it's an interior (OT_OPPOSITE) or exterior (OT_SAME) loop.
00347          */
00348         if( nmg_find_eu_leftvec( left, eu ) < 0 )  rt_bomb("nmg_class_pt_e() bad LEFT vector\n");
00349 
00350         VSUB2(ptvec, pt, pca);          /* pt - pca */
00351         if (rt_g.NMG_debug & DEBUG_CLASSIFY)  {
00352                 VPRINT("\t\tptvec unnorm", ptvec);
00353                 VPRINT("\t\tleft", left);
00354         }
00355         VUNITIZE( ptvec );
00356 
00357         dot = VDOT(left, ptvec);
00358         if( NEAR_ZERO( dot, RT_DOT_TOL )  )  {
00359                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00360                         bu_log("\t\tpt lies on line of edge, outside verts. Skipping this edge\n");
00361                 goto out;
00362         }
00363 
00364         if (dot >= 0.0) {
00365                 closest->dist = dist;
00366                 closest->p.eu = eu;
00367                 closest->class = NMG_CLASS_AinB;
00368                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00369                         bu_log("\t\tpt is left of edge, INSIDE loop, dot=%g\n", dot);
00370         } else {
00371                 closest->dist = dist;
00372                 closest->p.eu = eu;
00373                 closest->class = NMG_CLASS_AoutB;
00374                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00375                         bu_log("\t\tpt is right of edge, OUTSIDE loop\n");
00376         }
00377 
00378 out:
00379         /* XXX Temporary addition for chasing bug in Bradley r5 */
00380         /* XXX Should at least add DEBUG_PLOTEM check, later */
00381         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00382                 struct faceuse  *fu;
00383                 char    buf[128];
00384                 static int      num;
00385                 FILE    *fp;
00386                 long    *bits;
00387                 point_t mid_pt;
00388                 point_t left_pt;
00389                 fu = eu->up.lu_p->up.fu_p;
00390                 bits = (long *)bu_calloc( nmg_find_model(&fu->l.magic)->maxindex, sizeof(long), "bits[]");
00391                 sprintf(buf,"faceclass%d.pl", num++);
00392                 if( (fp = fopen(buf, "w")) == NULL) rt_bomb(buf);
00393                 nmg_pl_fu( fp, fu, bits, 0, 0, 255 );   /* blue */
00394                 pl_color( fp, 0, 255, 0 );      /* green */
00395                 pdv_3line( fp, pca, pt );
00396                 pl_color( fp, 255, 0, 0 );      /* red */
00397                 VADD2SCALE( mid_pt, eupt, matept, 0.5 );
00398                 VJOIN1( left_pt, mid_pt, 500, left);
00399                 pdv_3line( fp, mid_pt, left_pt );
00400                 fclose(fp);
00401                 bu_free( (char *)bits, "bits[]");
00402                 bu_log("wrote %s\n", buf);
00403         }
00404 }
00405 
00406 
00407 /**
00408  *                      N M G _ C L A S S _ P T _ L
00409  *
00410  *  XXX DANGER:  This routine does not work well.
00411  *
00412  *  Given the coordinates of a point which lies on the plane of the face
00413  *  containing a loopuse, determine if the point is IN, ON, or OUT of the
00414  *  area enclosed by the loop.
00415  *
00416  *  Implicit Return -
00417  *      Updated "closest" structure if appropriate.
00418  *
00419  *  Called by -
00420  *      nmg_class_pt_loop()
00421  *              from: nmg_extrude.c / nmg_fix_overlapping_loops()
00422  *      nmg_classify_lu_lu()
00423  *              from: nmg_misc.c / nmg_split_loops_handler()
00424  */
00425 static void
00426 nmg_class_pt_l(struct neighbor *closest, const fastf_t *pt, const struct loopuse *lu, const struct bn_tol *tol)
00427 {
00428         vect_t          delta;
00429         pointp_t        lu_pt;
00430         fastf_t         dist;
00431         struct edgeuse  *eu;
00432         struct loop_g   *lg;
00433 
00434         NMG_CK_LOOPUSE(lu);
00435         NMG_CK_LOOP(lu->l_p);
00436         lg = lu->l_p->lg_p;
00437         NMG_CK_LOOP_G(lg);
00438 
00439         if (rt_g.NMG_debug & DEBUG_CLASSIFY)  {
00440                 VPRINT("nmg_class_pt_l\tPt:", pt);
00441         }
00442 
00443         if (*lu->up.magic_p != NMG_FACEUSE_MAGIC)
00444                 return;
00445 
00446         if (rt_g.NMG_debug & DEBUG_CLASSIFY)  {
00447                 plane_t         peqn;
00448                 nmg_pr_lu_briefly(lu, 0);
00449                 NMG_GET_FU_PLANE( peqn, lu->up.fu_p );
00450                 HPRINT("\tplane eqn", peqn);
00451         }
00452 
00453         if( !V3PT_IN_RPP_TOL( pt, lg->min_pt, lg->max_pt, tol ) )  {
00454                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00455                         bu_log("\tPoint is outside loop RPP\n");
00456                 return;
00457         }
00458         if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_EDGEUSE_MAGIC) {
00459                 for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
00460                         nmg_class_pt_e(closest, pt, eu, tol);
00461                         /* If point lies ON edge, we are done */
00462                         if( closest->class == NMG_CLASS_AonBshared )  break;
00463                 }
00464         } else if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC) {
00465                 register struct vertexuse *vu;
00466                 vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
00467                 lu_pt = vu->v_p->vg_p->coord;
00468                 VSUB2(delta, pt, lu_pt);
00469                 if ( (dist = MAGNITUDE(delta)) < closest->dist) {
00470                         if (lu->orientation == OT_OPPOSITE) {
00471                                 closest->class = NMG_CLASS_AoutB;
00472                         } else if (lu->orientation == OT_SAME) {
00473                                 closest->class = NMG_CLASS_AonBshared;
00474                         } else {
00475                                 nmg_pr_orient(lu->orientation, "\t");
00476                                 rt_bomb("nmg_class_pt_l: bad orientation for face loopuse\n");
00477                         }
00478                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00479                                 bu_log("\t\t closer to loop pt (%g, %g, %g)\n",
00480                                         V3ARGS(lu_pt) );
00481 
00482                         closest->dist = dist;
00483                         closest->p.vu = vu;
00484                 }
00485         } else {
00486                 rt_bomb("nmg_class_pt_l() bad child of loopuse\n");
00487         }
00488         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00489                 bu_log("nmg_class_pt_l\treturning, closest=%g %s\n",
00490                         closest->dist, nmg_class_name(closest->class) );
00491 }
00492 
00493 /**
00494  *                      N M G _ C L A S S _ L U _ F U
00495  *
00496  *  This is intended as an internal routine to support nmg_lu_reorient().
00497  *
00498  *  Given a loopuse in a face, pick one of it's vertexuses, and classify
00499  *  that point with respect to all the rest of the loopuses in the face.
00500  *  The containment status of that point is the status of the loopuse.
00501  *
00502  *  If the first vertex chosen is "ON" another loop boundary,
00503  *  choose the next vertex and try again.  Only return an "ON"
00504  *  status if _all_ the vertices are ON.
00505  *
00506  *  The point is "A", and the face is "B".
00507  *
00508  *  Returns -
00509  *      NMG_CLASS_AinB          lu is INSIDE the area of the face.
00510  *      NMG_CLASS_AonBshared    ALL of lu is ON other loop boundaries.
00511  *      NMG_CLASS_AoutB         lu is OUTSIDE the area of the face.
00512  *
00513  *  Called by -
00514  *      nmg_mod.c, nmg_lu_reorient()
00515  */
00516 int
00517 nmg_class_lu_fu(const struct loopuse *lu, const struct bn_tol *tol)
00518 {
00519         const struct faceuse    *fu;
00520         struct vertexuse        *vu;
00521         const struct vertex_g   *vg;
00522         struct edgeuse          *eu;
00523         struct edgeuse          *eu_first;
00524         fastf_t                 dist;
00525         plane_t                 n;
00526         int                     class;
00527 
00528         NMG_CK_LOOPUSE(lu);
00529         BN_CK_TOL(tol);
00530 
00531         fu = lu->up.fu_p;
00532         NMG_CK_FACEUSE(fu);
00533         NMG_CK_FACE(fu->f_p);
00534         NMG_CK_FACE_G_PLANE(fu->f_p->g.plane_p);
00535 
00536         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00537                 bu_log("nmg_class_lu_fu(lu=x%x) START\n", lu);
00538 
00539         /* Pick first vertex in loopuse, for point */
00540         if( BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC )  {
00541                 vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
00542                 eu = (struct edgeuse *)NULL;
00543         } else {
00544                 eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
00545                 NMG_CK_EDGEUSE(eu);
00546                 vu = eu->vu_p;
00547         }
00548         eu_first = eu;
00549 again:
00550         NMG_CK_VERTEXUSE(vu);
00551         NMG_CK_VERTEX(vu->v_p);
00552         vg = vu->v_p->vg_p;
00553         NMG_CK_VERTEX_G(vg);
00554 
00555         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00556                 VPRINT("nmg_class_lu_fu\tPt:", vg->coord);
00557         }
00558 
00559         /* Validate distance from point to plane */
00560         NMG_GET_FU_PLANE( n, fu );
00561         if( (dist=fabs(DIST_PT_PLANE( vg->coord, n ))) > tol->dist )  {
00562                 bu_log("nmg_class_lu_fu() ERROR, point (%g,%g,%g) not on face, dist=%g\n",
00563                         V3ARGS(vg->coord), dist );
00564         }
00565 
00566         /* find the closest approach in this face to the projected point */
00567         class = nmg_class_pt_fu_except( vg->coord, fu, lu,
00568                 NULL, NULL, NULL, 0, 0, tol );
00569 
00570         /* If this vertex lies ON loop edge, must check all others. */
00571         if( class == NMG_CLASS_AonBshared )  {
00572                 if( !eu_first )  {
00573                         /* was self-loop, nothing more to test */
00574                 } else {
00575                         eu = BU_LIST_PNEXT_CIRC(edgeuse, &eu->l);
00576                         if( eu != eu_first )  {
00577                                 vu = eu->vu_p;
00578                                 goto again;
00579                         }
00580                         /* all match, call it "ON" */
00581                 }
00582         }
00583 
00584         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00585                 bu_log("nmg_class_lu_fu(lu=x%x) END, ret=%s\n",
00586                         lu,
00587                         nmg_class_name(class) );
00588         }
00589         return class;
00590 }
00591 
00592 /* Ray direction vectors for Jordan curve algorithm */
00593 static const point_t nmg_good_dirs[10] = {
00594 #if 1
00595         {3, 2, 1},      /* Normally the first dir */
00596 #else
00597         {1, 0, 0},      /* Make this first dir to wring out ray-tracer XXX */
00598 #endif
00599         {1, 0, 0},
00600         {0, 1, 0},
00601         {0, 0, 1},
00602         {1, 1, 1},
00603         {-3,-2,-1},
00604         {-1,0, 0},
00605         {0,-1, 0},
00606         {0, 0,-1},
00607         {-1,-1,-1}
00608 };
00609 
00610 /**
00611  *                      N M G _ C L A S S _ P T _ S
00612  *
00613  *  This is intended as a general user interface routine.
00614  *  Given the Cartesian coordinates for a point in space,
00615  *  return the classification for that point with respect to a shell.
00616  *
00617  *  The algorithm used is to fire a ray from the point, and count
00618  *  the number of times it crosses a face.
00619  *
00620  *  The flag "in_or_out_only" specifies that the point is known to not
00621  *  be on the shell, therfore only returns of NMG_CLASS_AinB or
00622  *  NMG_CLASS_AoutB are acceptable.
00623  *
00624  *  The point is "A", and the face is "B".
00625  *
00626  *  Returns -
00627  *      NMG_CLASS_AinB          pt is INSIDE the volume of the shell.
00628  *      NMG_CLASS_AonBshared    pt is ON the shell boundary.
00629  *      NMG_CLASS_AoutB         pt is OUTSIDE the volume of the shell.
00630  */
00631 int
00632 nmg_class_pt_s(const fastf_t *pt, const struct shell *s, const int in_or_out_only, const struct bn_tol *tol)
00633 {
00634         const struct faceuse    *fu;
00635         struct model    *m;
00636         long            *faces_seen = NULL;
00637         vect_t          region_diagonal;
00638         fastf_t         region_diameter;
00639         int             class;
00640         vect_t          projection_dir;
00641         int             try=0;
00642         struct xray     rp;
00643 
00644         NMG_CK_SHELL(s);
00645         BN_CK_TOL(tol);
00646 
00647         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00648                 bu_log("nmg_class_pt_s:\tpt=(%g, %g, %g), s=x%x\n",
00649                         V3ARGS(pt), s );
00650 
00651         if( !V3PT_IN_RPP_TOL(pt, s->sa_p->min_pt, s->sa_p->max_pt, tol) )  {
00652                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00653                         bu_log("        OUT, point not in RPP\n");
00654                 return NMG_CLASS_AoutB;
00655         }
00656 
00657         m = s->r_p->m_p;
00658         NMG_CK_MODEL(m);
00659         if( !in_or_out_only )
00660         {
00661                 faces_seen = (long *)bu_calloc( m->maxindex, sizeof(long), "nmg_class_pt_s faces_seen[]" );
00662 
00663                 /*
00664                  *  First pass:  Try hard to see if point is ON a face.
00665                  */
00666                 for( BU_LIST_FOR(fu, faceuse, &s->fu_hd) )  {
00667                         plane_t n;
00668 
00669                         /* If this face processed before, skip on */
00670                         if( NMG_INDEX_TEST( faces_seen, fu->f_p ) )  continue;
00671 
00672                         /* Only consider the outward pointing faceuses */
00673                         if( fu->orientation != OT_SAME )  continue;
00674 
00675                         /* See if this point lies on this face */
00676                         NMG_GET_FU_PLANE( n, fu );
00677                         if( (fabs(DIST_PT_PLANE(pt, n))) < tol->dist)  {
00678                                 /* Point lies on this plane, it may be possible to
00679                                  * short circuit everything.
00680                                  */
00681                                 class = nmg_class_pt_fu_except(pt, fu, (struct loopuse *)0,
00682                                         (void (*)())NULL, (void (*)())NULL, (char *)NULL, 0,
00683                                         0, tol);
00684                                 if( class == NMG_CLASS_AonBshared )  {
00685                                         /* Point is ON face, therefore it must be
00686                                          * ON the shell also.
00687                                          */
00688                                         class = NMG_CLASS_AonBshared;
00689                                         goto out;
00690                                 }
00691                                 if( class == NMG_CLASS_AinB )  {
00692                                         /* Point is IN face, therefor it must be
00693                                          * ON the shell also.
00694                                          */
00695                                         class = NMG_CLASS_AonBshared;
00696                                         goto out;
00697                                 }
00698                                 /* Point is OUTside face, its undecided. */
00699                         }
00700 
00701                         /* Mark this face as having been processed */
00702                         NMG_INDEX_SET(faces_seen, fu->f_p);
00703                 }
00704         }
00705 
00706         /* If we got here, the point isn't ON any of the faces.
00707          * Time to do the Jordan Curve Theorem.  We fire an arbitrary
00708          * ray and count the number of crossings (in the positive direction)
00709          * If that number is even, we're outside the shell, otherwise we're
00710          * inside the shell.
00711          */
00712         NMG_CK_REGION_A(s->r_p->ra_p);
00713         VSUB2( region_diagonal, s->r_p->ra_p->max_pt, s->r_p->ra_p->min_pt );
00714         region_diameter = MAGNITUDE(region_diagonal);
00715 
00716         /* Choose an unlikely direction */
00717         try = 0;
00718 retry:
00719         VMOVE( projection_dir, nmg_good_dirs[try] );
00720         if( ++try > 10 )  rt_bomb("nmg_class_pt_s() retry count exhausted\n");
00721         VUNITIZE(projection_dir);
00722 
00723         if (rt_g.NMG_debug & DEBUG_CLASSIFY )
00724                 bu_log("\tPt=(%g, %g, %g) dir=(%g, %g, %g), reg_diam=%g\n",
00725                         V3ARGS(pt), V3ARGS(projection_dir), region_diameter);
00726 
00727         VMOVE(rp.r_pt, pt);
00728         VMOVE(rp.r_dir, projection_dir);
00729 
00730 
00731         /* get NMG ray-tracer to tell us if start point is inside or outside */
00732         class = nmg_class_ray_vs_shell(&rp, s, in_or_out_only, tol);
00733         if( class == NMG_CLASS_Unknown )  goto retry;
00734 
00735 out:
00736         if( !in_or_out_only )
00737                 bu_free( (char *)faces_seen, "nmg_class_pt_s faces_seen[]" );
00738 
00739         if (rt_g.NMG_debug & DEBUG_CLASSIFY )
00740                 bu_log("nmg_class_pt_s: returning %s, s=x%x, try=%d\n",
00741                         nmg_class_name(class), s, try );
00742         return class;
00743 }
00744 
00745 
00746 /**
00747  *                      C L A S S _ V U _ V S _ S
00748  *
00749  *      Classify a loopuse/vertexuse from shell A WRT shell B.
00750  */
00751 static int
00752 class_vu_vs_s(struct vertexuse *vu, struct shell *sB, long int **classlist, const struct bn_tol *tol)
00753 {
00754         struct vertexuse *vup;
00755         pointp_t pt;
00756         char    *reason;
00757         int     status = 0;
00758         int     class;
00759 
00760         NMG_CK_VERTEXUSE(vu);
00761         NMG_CK_SHELL(sB);
00762         BN_CK_TOL(tol);
00763 
00764         pt = vu->v_p->vg_p->coord;
00765 
00766         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00767                 bu_log("class_vu_vs_s(vu=x%x, v=x%x) pt=(%g,%g,%g)\n", vu, vu->v_p, V3ARGS(pt) );
00768 
00769         /* As a mandatory consistency measure, check for cached classification */
00770         reason = "of cached classification";
00771         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], vu->v_p) )  {
00772                 status = INSIDE;
00773                 goto out;
00774         }
00775         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], vu->v_p) )  {
00776                 status = ON_SURF;
00777                 goto out;
00778         }
00779         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], vu->v_p) )  {
00780                 status = ON_SURF;
00781                 goto out;
00782         }
00783         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], vu->v_p) )  {
00784                 status = OUTSIDE;
00785                 goto out;
00786         }
00787 
00788         /* we use topology to determing if the vertex is "ON" the
00789          * other shell.
00790          */
00791         for(BU_LIST_FOR(vup, vertexuse, &vu->v_p->vu_hd)) {
00792 
00793                 if (*vup->up.magic_p == NMG_LOOPUSE_MAGIC )  {
00794                         if( nmg_find_s_of_lu(vup->up.lu_p) != sB) continue;
00795                         NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
00796                                 vu->v_p );
00797                         reason = "a loopuse of vertex is on shell";
00798                         status = ON_SURF;
00799                         goto out;
00800                 } else if (*vup->up.magic_p == NMG_EDGEUSE_MAGIC )  {
00801                         if( nmg_find_s_of_eu(vup->up.eu_p) != sB) continue;
00802                         NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
00803                                 vu->v_p );
00804                         reason = "an edgeuse of vertex is on shell";
00805                         status = ON_SURF;
00806                         goto out;
00807                 } else if( *vup->up.magic_p == NMG_SHELL_MAGIC )  {
00808                         if( vup->up.s_p != sB ) continue;
00809                         NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
00810                                 vu->v_p );
00811                         reason = "vertex is shell's lone vertex";
00812                         status = ON_SURF;
00813                         goto out;
00814                 } else {
00815                         rt_bomb("class_vu_vs_s() bad vertex UP pointer\n");
00816                 }
00817         }
00818 
00819         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00820                 bu_log("\tCan't classify vertex via topology\n");
00821 
00822         /* The topology doesn't tell us about the vertex being "on" the shell
00823          * so now it's time to look at the geometry to determine the vertex
00824          * relationship to the shell.
00825          *
00826          * The vertex should *not* lie ON any of the faces or
00827          * edges, since the intersection algorithm would have combined the
00828          * topology if that had been the case.
00829          */
00830         /* XXX eventually, make this conditional on DEBUG_CLASSIFY */
00831         {
00832                 /* Verify this assertion */
00833                 struct vertex   *sv;
00834                 if( (sv = nmg_find_pt_in_shell( sB, pt, tol ) ) )  {
00835                         bu_log("vu=x%x, v=x%x, sv=x%x, pt=(%g,%g,%g)\n",
00836                                 vu, vu->v_p, sv, V3ARGS(pt) );
00837                         rt_bomb("nmg_class_pt_s() vertex topology not shared properly\n");
00838                 }
00839         }
00840 
00841         reason = "of nmg_class_pt_s()";
00842         class = nmg_class_pt_s(pt, sB, 1, tol);
00843 
00844         if( class == NMG_CLASS_AoutB )  {
00845                 NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], vu->v_p);
00846                 status = OUTSIDE;
00847         }  else if( class == NMG_CLASS_AinB )  {
00848                 NMG_INDEX_SET(classlist[NMG_CLASS_AinB], vu->v_p);
00849                 status = INSIDE;
00850         }  else if( class == NMG_CLASS_AonBshared )  {
00851                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], vu->v_p);
00852                 status = ON_SURF;
00853         }  else  {
00854                 bu_log("class=%s\n", nmg_class_name(class) );
00855                 VPRINT("pt", pt);
00856                 rt_bomb("class_vu_vs_s: nmg_class_pt_s() failure\n");
00857         }
00858 
00859 out:
00860         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
00861                 bu_log("class_vu_vs_s(vu=x%x) return %s because %s\n",
00862                         vu, nmg_class_status(status), reason );
00863         }
00864         return(status);
00865 }
00866 
00867 /**
00868  *                      C L A S S _ E U _ V S _ S
00869  */
00870 static int
00871 class_eu_vs_s(struct edgeuse *eu, struct shell *s, long int **classlist, const struct bn_tol *tol)
00872 {
00873         int euv_cl, matev_cl;
00874         int     status = 0;
00875         struct edgeuse *eup;
00876         point_t pt;
00877         pointp_t eupt, matept;
00878         char    *reason = "Unknown";
00879         int     class;
00880 
00881         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
00882         {
00883                 bu_log( "class_eu_vs_s( eu=x%x (e_p=x%x, lu=x%x), s=x%x )\n", eu, eu->e_p, eu->up.lu_p, s );
00884                 nmg_euprint("class_eu_vs_s\t", eu);
00885         }
00886 
00887         NMG_CK_EDGEUSE(eu);
00888         NMG_CK_SHELL(s);
00889         BN_CK_TOL(tol);
00890 
00891         /* As a mandatory consistency measure, check for cached classification */
00892         reason = "of cached classification";
00893         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], eu->e_p) )  {
00894                 status = INSIDE;
00895                 goto out;
00896         }
00897         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], eu->e_p) )  {
00898                 status = ON_SURF;
00899                 goto out;
00900         }
00901         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], eu->e_p) )  {
00902                 status = ON_SURF;
00903                 goto out;
00904         }
00905         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], eu->e_p) )  {
00906                 status = OUTSIDE;
00907                 goto out;
00908         }
00909 
00910         euv_cl = class_vu_vs_s(eu->vu_p, s, classlist, tol);
00911         matev_cl = class_vu_vs_s(eu->eumate_p->vu_p, s, classlist, tol);
00912 
00913         /* sanity check */
00914         if ((euv_cl == INSIDE && matev_cl == OUTSIDE) ||
00915             (euv_cl == OUTSIDE && matev_cl == INSIDE)) {
00916                 static int      num=0;
00917                 char    buf[128];
00918                 FILE    *fp;
00919 
00920                 sprintf(buf, "class%d.pl", num++ );
00921                 if( (fp = fopen(buf, "w")) == NULL ) rt_bomb(buf);
00922                 nmg_pl_s( fp, s );
00923                 /* A yellow line for the angry edge */
00924                 pl_color(fp, 255, 255, 0);
00925                 pdv_3line(fp, eu->vu_p->v_p->vg_p->coord,
00926                         eu->eumate_p->vu_p->v_p->vg_p->coord );
00927                 fclose(fp);
00928 
00929                 nmg_pr_class_status("eu vu", euv_cl);
00930                 nmg_pr_class_status("eumate vu", matev_cl);
00931                 if( RT_G_DEBUG || rt_g.NMG_debug )  {
00932                         /* Do them over, so we can watch */
00933                         bu_log("Edge not cut, doing it over\n");
00934                         NMG_INDEX_CLEAR( classlist[NMG_CLASS_AinB], eu->vu_p);
00935                         NMG_INDEX_CLEAR( classlist[NMG_CLASS_AoutB], eu->vu_p);
00936                         NMG_INDEX_CLEAR( classlist[NMG_CLASS_AinB], eu->eumate_p->vu_p);
00937                         NMG_INDEX_CLEAR( classlist[NMG_CLASS_AoutB], eu->eumate_p->vu_p);
00938 /**                     rt_g.debug |= DEBUG_MATH; **/
00939                         rt_g.NMG_debug |= DEBUG_CLASSIFY;
00940                         (void)class_vu_vs_s(eu->vu_p, s, classlist, tol);
00941                         (void)class_vu_vs_s(eu->eumate_p->vu_p, s, classlist, tol);
00942                         nmg_euprint("didn't this edge get cut?", eu);
00943                         nmg_pr_eu(eu, "  ");
00944                 }
00945 
00946                 bu_log("wrote %s\n", buf);
00947                 rt_bomb("class_eu_vs_s:  edge didn't get cut\n");
00948         }
00949 
00950         if (euv_cl == ON_SURF && matev_cl == ON_SURF) {
00951                 vect_t eu_dir;
00952                 int try;
00953 
00954                 /* check for radial uses of this edge by the shell */
00955                 eup = eu->radial_p->eumate_p;
00956                 do {
00957                         NMG_CK_EDGEUSE(eup);
00958                         if (nmg_find_s_of_eu(eup) == s) {
00959                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
00960                                         eu->e_p );
00961                                 reason = "a radial edgeuse is on shell";
00962                                 status = ON_SURF;
00963                                 goto out;
00964                         }
00965                         eup = eup->radial_p->eumate_p;
00966                 } while (eup != eu->radial_p->eumate_p);
00967 
00968                 /* look for another eu between these two vertices */
00969                 if( nmg_find_matching_eu_in_s( eu, s ) )
00970                 {
00971                         NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
00972                                 eu->e_p );
00973                         reason = "another eu between same vertices is on shell";
00974                         status = ON_SURF;
00975                         goto out;
00976                 }
00977 
00978                 /* although the two endpoints are "on" the shell,
00979                  * the edge would appear to be either "inside" or "outside",
00980                  * since there are no uses of this edge in the shell.
00981                  *
00982                  * So we classify the midpoint of the line WRT the shell.
00983                  *
00984                  *  Consider AE as being "eu", with M as the geometric midpoint.
00985                  *  Consider AD as being a side view of a perpendicular plane
00986                  *  in the other shell, which AE _almost_ lies in.
00987                  *  A problem occurs when the angle DAE is very small.
00988                  *  Point A is ON because of shared topology.
00989                  *  Point E is marked ON because it is within tolerance of
00990                  *  the face, even though there is no corresponding vertex.
00991                  *  Naturally, point M is also going to be found to be ON
00992                  *  because it is also within tolerance.
00993                  *
00994                  *         D
00995                  *        /|
00996                  *       / |
00997                  *      B  |
00998                  *     /   |
00999                  *    /    |
01000                  *   A--M--E
01001                  *
01002                  *  This would seem to be an intersector problem.
01003                  */
01004                 class = NMG_CLASS_Unknown;
01005                 eupt = eu->vu_p->v_p->vg_p->coord;
01006                 matept = eu->eumate_p->vu_p->v_p->vg_p->coord;
01007 #if 0
01008                 {
01009                         /* XXXX This topological algorithm assumes that the intersector is perfect */
01010 
01011                         int in=0,out=0,on=0,unk=0;
01012                         struct edgeuse *eu_loop;
01013                         struct edgeuse *eu_on=(struct edgeuse *)NULL;
01014 
01015                         class_topo = NMG_CLASS_Unknown;
01016 
01017                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01018                                 bu_log( "\tclass_eu_vs_s: attempting topological classification\n" );
01019 
01020                         /* look at other EU's in the loop
01021                          * counting in/on/out/unknown EU classes
01022                          * if we find an ON, keep it handy
01023                          */
01024                         eu_loop = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
01025                         while( eu_loop != eu )
01026                         {
01027                                 if( NMG_INDEX_TEST( classlist[NMG_CLASS_AinB], eu_loop->e_p ) )
01028                                         in++;
01029                                 else if( NMG_INDEX_TEST( classlist[NMG_CLASS_AoutB], eu_loop->e_p ) )
01030                                         out++;
01031                                 else if( NMG_INDEX_TEST( classlist[NMG_CLASS_AonBshared], eu_loop->e_p ) )
01032                                 {
01033                                         eu_on = eu_loop;
01034                                         on++;
01035                                 }
01036                                 else if( NMG_INDEX_TEST( classlist[NMG_CLASS_AonBanti], eu_loop->e_p ) )
01037                                 {
01038                                         eu_on = eu_loop;
01039                                         on++;
01040                                 }
01041                                 else
01042                                         unk++;
01043 
01044                                 eu_loop = BU_LIST_PNEXT_CIRC( edgeuse, &eu_loop->l );
01045                         }
01046 
01047                         if(in && out )
01048                         {
01049                                 /* This is obviously a GOOF!!! */
01050                                 bu_log( "loop crosses shell boundary!!!\n" );
01051                                 nmg_pr_fu_briefly( nmg_find_fu_of_eu( eu ), "" );
01052                                 rt_bomb( "loop crosses shell boundary!!!" );
01053                         }
01054 
01055                         if( on )
01056                         {
01057                                 struct edgeuse *eu_rad;
01058                                 struct edgeuse *eu_rad_too;
01059                                 struct faceuse *fu_rad;
01060                                 struct faceuse *fu_rad_too;
01061                                 struct faceuse *fu_eu;
01062 
01063                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01064                                         bu_log( "\tclass_eu_vs_s: found other eu (x%x) in loop on shell\n", eu_on );
01065 
01066                                 /* Look radially around this "ON" edgeuse.
01067                                  * The first eu we encounter from the shell "s"
01068                                  * tells us if we are in or out
01069                                  */
01070 
01071                                 fu_eu = nmg_find_fu_of_eu( eu );
01072                                 eu_rad = eu_on->radial_p;
01073                                 while( nmg_find_s_of_eu( eu_rad ) != s &&
01074                                         eu_rad != eu_on &&
01075                                         eu_rad != eu_on->eumate_p &&
01076                                         ( fu_rad = nmg_find_fu_of_eu( eu_rad )) != NULL )
01077                                                 eu_rad = eu_rad->eumate_p->radial_p;
01078 
01079                                 /* find the nearest in the other direction also */
01080                                 eu_rad_too = eu_on->eumate_p->radial_p;
01081                                 while( nmg_find_s_of_eu( eu_rad_too ) != s &&
01082                                         eu_rad_too != eu_on &&
01083                                         eu_rad_too != eu_on->eumate_p &&
01084                                         ( fu_rad_too = nmg_find_fu_of_eu( eu_rad_too )) != NULL )
01085                                                 eu_rad_too = eu_rad_too->eumate_p->radial_p;
01086 
01087                                 fu_rad = nmg_find_fu_of_eu( eu_rad );
01088 
01089                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01090                                 {
01091                                         bu_log( "\tclass_eu_vs_s: eu_rad=x%x, eu_rad_too=x%x\n",
01092                                                 eu_rad, eu_rad_too );
01093                                         bu_log( "\t\tfu_rad=x%x, fu_rad_too=x%x, fu_eu=x%x, fu_eu->fumate_p=x%x\n",
01094                                                 fu_rad, fu_rad_too, fu_eu, fu_eu->fumate_p );
01095                                         bu_log( "\t\ts=x%x, fu_rad->s_p=x%x\n", s, fu_rad->s_p );
01096                                 }
01097 
01098                                 if( fu_rad && fu_rad->s_p == s && fu_rad != fu_eu &&
01099                                         fu_rad != fu_eu->fumate_p )
01100                                 {
01101                                         vect_t left_eu;
01102                                         vect_t left_rad;
01103                                         vect_t left_rad_too;
01104                                         double dot;
01105                                         double dot_too;
01106 
01107                                         nmg_find_eu_leftvec( left_eu, eu_on );
01108                                         nmg_find_eu_leftvec( left_rad, eu_rad );
01109                                         nmg_find_eu_leftvec( left_rad_too, eu_rad_too );
01110 
01111                                         dot = VDOT( left_eu, left_rad );
01112                                         dot_too = VDOT( left_eu, left_rad_too );
01113 
01114                                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01115                                         {
01116                                                 bu_log( "\tclass_eu_vs_s eu_rad=x%x, eu_rad_too=x%x, dot=%f, dot_too=%f\n",
01117                                                 eu_rad, eu_rad_too, dot, dot_too );
01118                                         }
01119 
01120                                         /* if left vectors of left vectors are parallel,
01121                                          * then we can't classify this way
01122                                          */
01123                                         if( dot > tol->para )
01124                                                 class_topo = NMG_CLASS_Unknown;
01125                                         else if( dot_too > tol->para )
01126                                                 class_topo = NMG_CLASS_Unknown;
01127                                         else if( fu_rad->orientation == OT_SAME )
01128                                         {
01129                                                 class_topo = NMG_CLASS_AoutB;
01130                                                 reason = "nearest radial face is OT_SAME";
01131                                         }
01132                                         else if( fu_rad->orientation == OT_OPPOSITE )
01133                                         {
01134                                                 class_topo = NMG_CLASS_AinB;
01135                                                 reason = "nearest radial face is OT_OPPOSITE";
01136                                         }
01137                                         else
01138                                         {
01139                                                 bu_log( "FU (x%x) has bad orientation (%s)\n", fu_rad, nmg_orientation( fu_rad->orientation ) );
01140                                                 rt_bomb( "FU has bad orientation" );
01141                                         }
01142                                         if (rt_g.NMG_debug & DEBUG_CLASSIFY &&
01143                                                 class_topo != NMG_CLASS_Unknown )
01144                                         {
01145                                                 bu_log( "\tClassifiying EU using on eu (x%x) from fu (x%x)\n", eu_on, nmg_find_fu_of_eu( eu_on) );
01146                                                 nmg_pr_fu_around_eu( eu_on, tol );
01147                                         }
01148                                 }
01149                         }
01150                 }
01151 
01152                 class = class_topo;
01153 #endif
01154                 if( class == NMG_CLASS_Unknown )
01155                 {
01156                         VSUB2( eu_dir, matept, eupt );
01157                         VUNITIZE( eu_dir );
01158                 }
01159 
01160                 try = 0;
01161                 while( class == NMG_CLASS_Unknown && try < 3 )
01162                 {
01163                         /* Must resort to ray trace */
01164                         switch( try )
01165                         {
01166                                 case 0 :
01167                                         VADD2SCALE(pt, eupt, matept, 0.5);
01168                                         reason = "midpoint classification (both verts ON)";
01169                                         break;
01170                                 case 1:
01171                                         VJOIN1( pt, eupt, 1.05*tol->dist, eu_dir );
01172                                         reason = "point near EU start classification (both verts ON)";
01173                                         break;
01174                                 case 2:
01175                                         VJOIN1( pt, matept, -1.05*tol->dist, eu_dir );
01176                                         reason = "point near EU end classification (both verts ON)";
01177                                         break;
01178                         }
01179                         class = nmg_class_pt_s(pt, s, 1, tol);
01180                         try++;
01181                 }
01182 
01183                 if( class == NMG_CLASS_AoutB )  {
01184                         NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->e_p);
01185                         status = OUTSIDE;
01186                 }  else if( class == NMG_CLASS_AinB )  {
01187                         NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
01188                         status = INSIDE;
01189                 } else if( class == NMG_CLASS_AonBshared )  {
01190                         FILE    *fp;
01191 #if 0
01192                         /* XXX bug hunt helper */
01193                         rt_g.NMG_debug |= DEBUG_FINDEU;
01194                 /*      rt_g.NMG_debug |= DEBUG_MESH;    */
01195                         rt_g.NMG_debug |= DEBUG_BASIC;
01196                         eup = nmg_findeu( eu->vu_p->v_p, eu->eumate_p->vu_p->v_p, s, eu, 0 );
01197                         if(!eup) bu_log("Unable to find it\n");
01198                         nmg_model_fuse( nmg_find_model((long*)eu), tol );
01199 #endif
01200                         nmg_pr_fu_around_eu( eu, tol );
01201                         VPRINT("class_eu_vs_s: midpoint of edge", pt);
01202                         if( (fp = fopen("shell1.pl", "w")) )  {
01203                                 nmg_pl_s(fp, s);
01204                                 fclose(fp);
01205                                 bu_log("wrote shell1.pl\n");
01206                         }
01207                         if( (fp = fopen("shell2.pl", "w")) )  {
01208                                 nmg_pl_shell(fp, eu->up.lu_p->up.fu_p->s_p, 1 );
01209                                 fclose(fp);
01210                                 bu_log("wrote shell2.pl\n");
01211                         }
01212 #if 0
01213                         /* Another bug hunt helper -- re-run face/face isect */
01214                         rt_g.NMG_debug |= DEBUG_POLYSECT;
01215                         rt_g.NMG_debug |= DEBUG_PLOTEM;
01216                         rt_g.NMG_debug |= DEBUG_BASIC;
01217                         bu_log("class_eu_vs_s:  classifier found edge midpoint ON, edge topology should have been shared\n\n################ re-run face/face isect ############\n\n");
01218                 {
01219                         struct faceuse  *fu1 = eu->up.lu_p->up.fu_p;
01220                         struct faceuse  *fu2;
01221 
01222                         NMG_CK_FACEUSE(fu1);
01223                         for( BU_LIST_FOR( fu2, faceuse, &s->fu_hd ) )  {
01224                                 NMG_CK_FACEUSE(fu2);
01225                                 if( fu2->orientation != OT_SAME )  continue;
01226                                 nmg_isect_two_generic_faces( fu1, fu2, tol );
01227                         }
01228                         eup = nmg_findeu( eu->vu_p->v_p, eu->eumate_p->vu_p->v_p, s, eu, 0 );
01229                         if(!eup) bu_log("Unable to find it\n");
01230                 }
01231 #endif
01232                         rt_bomb("class_eu_vs_s:  classifier found edge midpoint ON, edge topology should have been shared\n");
01233                 }  else  {
01234                         bu_log("class=%s\n", nmg_class_name(class) );
01235                         nmg_euprint("Why wasn't this edge in or out?", eu);
01236                         rt_bomb("class_eu_vs_s: nmg_class_pt_s() midpoint failure\n");
01237                 }
01238                 goto out;
01239         }
01240 
01241         if (euv_cl == OUTSIDE || matev_cl == OUTSIDE) {
01242                 NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], eu->e_p);
01243                 reason = "at least one vert OUT";
01244                 status = OUTSIDE;
01245                 goto out;
01246         }
01247         if( euv_cl == INSIDE && matev_cl == INSIDE )  {
01248                 NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
01249                 reason = "both verts IN";
01250                 status = INSIDE;
01251                 goto out;
01252         }
01253         if( (euv_cl == INSIDE && matev_cl == ON_SURF) ||
01254             (euv_cl == ON_SURF && matev_cl == INSIDE) )  {
01255                 NMG_INDEX_SET(classlist[NMG_CLASS_AinB], eu->e_p);
01256                 reason = "one vert IN, one ON";
01257                 status = INSIDE;
01258                 goto out;
01259         }
01260         bu_log("eu's vert is %s, mate's vert is %s\n",
01261                 nmg_class_status(euv_cl), nmg_class_status(matev_cl) );
01262         rt_bomb("class_eu_vs_s() inconsistent edgeuse\n");
01263 out:
01264         if (rt_g.NMG_debug & DEBUG_GRAPHCL)
01265                 nmg_show_broken_classifier_stuff((long *)eu, classlist, nmg_class_nothing_broken, 0, (char *)NULL);
01266         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
01267                 bu_log("class_eu_vs_s(eu=x%x) return %s because %s\n",
01268                         eu, nmg_class_status(status), reason );
01269         }
01270         return(status);
01271 }
01272 
01273 /* XXX move to nmg_info.c */
01274 /**
01275  *                      N M G _ 2 L U _ I D E N T I C A L
01276  *
01277  *  Given two edgeuses in different faces that share a common edge,
01278  *  determine if they are from identical loops or not.
01279  *
01280  *  Note that two identical loops in an anti-shared pair of faces
01281  *  (faces with opposite orientations) will also have opposite orientations.
01282  *
01283  *  Returns -
01284  *      0       Loops not identical
01285  *      1       Loops identical, faces are ON-shared
01286  *      2       Loops identical, faces are ON-anti-shared
01287  *      3       Loops identical, at least one is a wire loop.
01288  */
01289 int
01290 nmg_2lu_identical(const struct edgeuse *eu1, const struct edgeuse *eu2)
01291 {
01292         const struct loopuse    *lu1;
01293         const struct loopuse    *lu2;
01294         const struct edgeuse    *eu1_first;
01295         const struct faceuse    *fu1;
01296         const struct faceuse    *fu2;
01297         int                     ret;
01298 
01299         NMG_CK_EDGEUSE(eu1);
01300         NMG_CK_EDGEUSE(eu2);
01301 
01302         if( eu1->e_p != eu2->e_p )  rt_bomb("nmg_2lu_identical() differing edges?\n");
01303 
01304         /* get the starting vertex of each edgeuse to be the same. */
01305         if (eu2->vu_p->v_p != eu1->vu_p->v_p) {
01306                 eu2 = eu2->eumate_p;
01307                 if (eu2->vu_p->v_p != eu1->vu_p->v_p)
01308                         rt_bomb("nmg_2lu_identical() radial edgeuse doesn't share verticies\n");
01309         }
01310 
01311         lu1 = eu1->up.lu_p;
01312         lu2 = eu2->up.lu_p;
01313 
01314         NMG_CK_LOOPUSE(lu1);
01315         NMG_CK_LOOPUSE(lu2);
01316 
01317         /* march around the two loops to see if they
01318          * are the same all the way around.
01319          */
01320         eu1_first = eu1;
01321         do {
01322                 if( eu1->vu_p->v_p != eu2->vu_p->v_p )  {
01323                         ret = 0;
01324                         goto out;
01325                 }
01326                 eu1 = BU_LIST_PNEXT_CIRC(edgeuse, &eu1->l);
01327                 eu2 = BU_LIST_PNEXT_CIRC(edgeuse, &eu2->l);
01328         } while ( eu1 != eu1_first );
01329 
01330         if( *lu1->up.magic_p != NMG_FACEUSE_MAGIC ||
01331             *lu2->up.magic_p != NMG_FACEUSE_MAGIC )  {
01332                 ret = 3;        /* one is a wire loop */
01333                 goto out;
01334             }
01335 
01336         fu1 = lu1->up.fu_p;
01337         fu2 = lu2->up.fu_p;
01338 
01339         if( fu1->f_p->g.plane_p != fu2->f_p->g.plane_p )  {
01340                 vect_t fu1_norm,fu2_norm;
01341 #if 0
01342                 bu_log("nmg_2lu_identical() loops lu1=x%x lu2=x%x are shared, face geometry is not? fg1=x%x, fg2=x%x\n",
01343                         lu1, lu2, fu1->f_p->g.plane_p, fu2->f_p->g.plane_p);
01344                 bu_log("---- fu1, f=x%x, flip=%d\n", fu1->f_p, fu1->f_p->flip);
01345                 nmg_pr_fg(fu1->f_p->g.magic_p, 0);
01346                 nmg_pr_fu_briefly(fu1, 0);
01347 
01348                 bu_log("---- fu2, f=x%x, flip=%d\n", fu2->f_p, fu2->f_p->flip);
01349                 nmg_pr_fg(fu2->f_p->g.magic_p, 0);
01350                 nmg_pr_fu_briefly(fu2, 0);
01351 #endif
01352 
01353                 /* Drop back to using a geometric calculation */
01354                 if( fu1->orientation != fu2->orientation )
01355                         NMG_GET_FU_NORMAL( fu2_norm, fu2->fumate_p )
01356                 else
01357                         NMG_GET_FU_NORMAL( fu2_norm, fu2 )
01358 
01359                 NMG_GET_FU_NORMAL( fu1_norm, fu1 );
01360                 if( VDOT( fu1_norm, fu2_norm ) < 0.0 )
01361                         ret = 2;        /* ON anti-shared */
01362                 else
01363                         ret = 1;        /* ON shared */
01364                 goto out;
01365         }
01366 
01367         if( rt_g.NMG_debug & DEBUG_BASIC )  {
01368                 bu_log("---- fu1, f=x%x, flip=%d\n", fu1->f_p, fu1->f_p->flip);
01369                 nmg_pr_fu_briefly(fu1, 0);
01370                 bu_log("---- fu2, f=x%x, flip=%d\n", fu2->f_p, fu2->f_p->flip);
01371                 nmg_pr_fu_briefly(fu2, 0);
01372         }
01373 
01374         /*
01375          *  The two loops are identical, compare the two faces.
01376          *  Only raw face orientations count here.
01377          *  Loopuse and faceuse orientations do not matter.
01378          */
01379         if( fu1->f_p->flip != fu2->f_p->flip )
01380                 ret = 2;                /* ON anti-shared */
01381         else
01382                 ret = 1;                /* ON shared */
01383 out:
01384         if( rt_g.NMG_debug & DEBUG_BASIC )  {
01385                 bu_log("nmg_2lu_identical(eu1=x%x, eu2=x%x) ret=%d\n",
01386                         eu1, eu2, ret);
01387         }
01388         return ret;
01389 }
01390 
01391 /**
01392  *                      N M G _ R E C L A S S I F Y _ L U _ E U
01393  *
01394  *  Make all the edges and vertices of a loop carry the same classification
01395  *  as the loop.
01396  *  There is no intrinsic way to tell if an edge is "shared" or
01397  *  "antishared", except by reference to it's loopuse, but the heritage
01398  *  of the edgeuse makes a difference to the boolean evaluator.
01399  *
01400  *  "newclass" should only be AonBshared or AonBanti.
01401  */
01402 void
01403 nmg_reclassify_lu_eu(struct loopuse *lu, long int **classlist, int newclass)
01404 {
01405         struct vertexuse        *vu;
01406         struct edgeuse          *eu;
01407         struct vertex           *v;
01408 
01409         NMG_CK_LOOPUSE(lu);
01410 
01411         if (rt_g.NMG_debug & DEBUG_BASIC)  {
01412                 bu_log("nmg_reclassify_lu_eu(lu=x%x, classlist=x%x, newclass=%s)\n",
01413                         lu, classlist, nmg_class_name(newclass) );
01414         }
01415 
01416         if( newclass != NMG_CLASS_AonBshared && newclass != NMG_CLASS_AonBanti )
01417                 rt_bomb("nmg_reclassify_lu_eu() bad newclass\n");
01418 
01419         if (BU_LIST_FIRST_MAGIC(&lu->down_hd) == NMG_VERTEXUSE_MAGIC)  {
01420                 vu = BU_LIST_FIRST(vertexuse, &lu->down_hd);
01421                 NMG_CK_VERTEXUSE(vu);
01422                 v = vu->v_p;
01423                 NMG_CK_VERTEX(v);
01424 
01425                 if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], v) ||
01426                     NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], v) )
01427                         rt_bomb("nmg_reclassify_lu_eu() changing in/out vert of lone vu loop to ON?\n");
01428 
01429                 /* Clear out other classifications */
01430                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], v);
01431                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], v);
01432                 /* Establish new classification */
01433                 NMG_INDEX_SET(classlist[newclass], v);
01434                 return;
01435         }
01436 
01437         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )  {
01438                 struct edge     *e;
01439                 NMG_CK_EDGEUSE(eu);
01440                 e = eu->e_p;
01441                 NMG_CK_EDGE(e);
01442 
01443                 if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], e) ||
01444                     NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], e) )
01445                         rt_bomb("nmg_reclassify_lu_eu() changing in/out edge to ON?\n");
01446 
01447                 /* Clear out other edge classifications */
01448                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], e);
01449                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], e);
01450                 /* Establish new edge classification */
01451                 NMG_INDEX_SET(classlist[newclass], e);
01452 
01453                 /* Same thing for the vertices.  Only need to do start vert here. */
01454                 vu = eu->vu_p;
01455                 NMG_CK_VERTEXUSE(vu);
01456                 v = vu->v_p;
01457                 NMG_CK_VERTEX(v);
01458 
01459                 if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], v) ||
01460                     NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], v) )
01461                         rt_bomb("nmg_reclassify_lu_eu() changing in/out vert to ON?\n");
01462 
01463                 /* Clear out other classifications */
01464                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBshared], v);
01465                 NMG_INDEX_CLEAR(classlist[NMG_CLASS_AonBanti], v);
01466                 /* Establish new classification */
01467                 NMG_INDEX_SET(classlist[newclass], v);
01468         }
01469 }
01470 
01471 /**                     C L A S S _ S H A R E D _ L U
01472  *
01473  *      Classify LU w.r.t. LU_REF.
01474  *
01475  *      Pre-requisites, required (but not checked for here ):
01476  *              LU shares all its edges with LU_REF
01477  *              LU and LU_REF are loopuses from faceuses
01478  *              LU and LU_REF are from different shells
01479  *              LU and LU_REF must both be loops of edges
01480  *
01481  *      LU is classified w.r.t. LU_REF by the following algorithm:
01482  *              1. Select any EU from LU.
01483  *              2. Find EU_REF from LU_REF that is shared with EU.
01484  *              3. If left vector of EU is opposite left vector of EU_REF,
01485  *                      then classification is either NMG_CLASS_AoutB or
01486  *                      NMG_CLASS_AinB.
01487  *                      (This is the case where one lu exactly fills a hole
01488  *                       that the other lu creates in a face).
01489  *              4. If the left vectors agree, then the loops are shared -
01490  *                      If the direction of EU agrees with the direction
01491  *                      of EU_REF, then classify as NMG_CLASS_AonBshared.
01492  *                      Otherwise classify as NMG_CLASS_AonBanti.
01493  *
01494  *      returns:
01495  *              NMG_CLASS_AonBshared
01496  *              NMG_CLASS_AonBanti
01497  *              NMG_CLASS_AoutB
01498  */
01499 static int
01500 class_shared_lu(const struct loopuse *lu, const struct loopuse *lu_ref, const struct bn_tol *tol)
01501 {
01502         struct shell *s_ref;
01503         struct edgeuse *eu;
01504         struct edgeuse *eu_ref;
01505         struct edgeuse *eu_start,*eu_tmp;
01506         struct faceuse *fu_of_lu;
01507         vect_t left;
01508         vect_t left_ref;
01509 
01510         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01511                 bu_log( "class_shared_lu: classifying lu x%x w.r.t. lu_ref x%x\n",
01512                                 lu, lu_ref );
01513 
01514         NMG_CK_LOOPUSE( lu );
01515         NMG_CK_LOOPUSE( lu_ref );
01516         BN_CK_TOL( tol );
01517 
01518         eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
01519         for( BU_LIST_FOR( eu_ref, edgeuse, &lu_ref->down_hd ) )
01520         {
01521                 if( eu_ref->e_p == eu->e_p )
01522                         break;
01523         }
01524 
01525         if( eu_ref->e_p != eu->e_p )
01526         {
01527                 bu_log( "class_shared_lu() couldn't find a shared EU between LU's x%x and x%x\n",
01528                         lu, lu_ref );
01529                 rt_bomb( "class_shared_lu() couldn't find a shared EU between LU's\n" );
01530         }
01531 
01532         if( nmg_find_eu_leftvec( left, eu ) )
01533         {
01534                 bu_log( "class_shared_lu() couldn't get a left vector for EU x%x\n", eu );
01535                 rt_bomb( "class_shared_lu() couldn't get a left vector for EU\n" );
01536         }
01537         if( nmg_find_eu_leftvec( left_ref, eu_ref ) )
01538         {
01539                 bu_log( "class_shared_lu() couldn't get a left vector for EU x%x\n", eu_ref );
01540                 rt_bomb( "class_shared_lu() couldn't get a left vector for EU\n" );
01541         }
01542 
01543         if( VDOT( left, left_ref ) > 0.0 )
01544         {
01545                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01546                 {
01547                         bu_log( "eu x%x goes form v x%x to v x%x\n", eu, eu->vu_p->v_p, eu->eumate_p->vu_p->v_p );
01548                         bu_log( "eu_ref x%x goes form v x%x to v x%x\n", eu_ref, eu_ref->vu_p->v_p, eu_ref->eumate_p->vu_p->v_p );
01549                 }
01550 
01551                 /* loop is either shared or anti */
01552                 if( eu->vu_p->v_p == eu_ref->vu_p->v_p )
01553                 {
01554                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01555                                 bu_log( "class_shared_lu returning NMG_CLASS_AonBshared\n" );
01556                         return( NMG_CLASS_AonBshared );
01557                 }
01558                 else
01559                 {
01560                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01561                                 bu_log( "class_shared_lu returning NMG_CLASS_AonBanti\n" );
01562                         return( NMG_CLASS_AonBanti );
01563                 }
01564         }
01565 
01566         /* loop is either in or out
01567          * look at next radial edge from lu_ref shell if that faceuse
01568          * is OT_OPPOSITE, then we are "in", else "out"
01569          */
01570         s_ref = nmg_find_s_of_eu( eu_ref );
01571         fu_of_lu = nmg_find_fu_of_lu( lu );
01572 
01573         for( BU_LIST_FOR( eu_start, edgeuse, &lu->down_hd ) )
01574         {
01575                 int use_this_eu=1;
01576 
01577                 eu_tmp = eu_start->eumate_p->radial_p;
01578                 while( eu_tmp != eu_start )
01579                 {
01580                         struct faceuse *fu_tmp;
01581                         struct loopuse *lu_tmp;
01582                         int class;
01583 
01584                         fu_tmp = nmg_find_fu_of_eu( eu_tmp );
01585                         if( fu_tmp != fu_of_lu && fu_tmp->fumate_p != fu_of_lu )
01586                         {
01587                                 eu_tmp = eu_tmp->eumate_p->radial_p;
01588                                 continue;
01589                         }
01590 
01591                         /* radial edge is part of same face
01592                          * make sure it is part of a loop */
01593                         if( *eu_tmp->up.magic_p != NMG_LOOPUSE_MAGIC )
01594                         {
01595                                 eu_tmp = eu_tmp->eumate_p->radial_p;
01596                                 continue;
01597                         }
01598                         lu_tmp = eu_tmp->up.lu_p;
01599                         if( fu_tmp->fumate_p == fu_of_lu )
01600                                 lu_tmp = lu_tmp->lumate_p;
01601 
01602                         if( lu_tmp == lu )
01603                         {
01604                                 /* cannot classify based on this edgeuse */
01605                                 use_this_eu = 0;
01606                                 break;
01607                         }
01608 
01609                         class = nmg_classify_lu_lu( lu_tmp, lu, tol );
01610                         if( lu->orientation == OT_OPPOSITE && class == NMG_CLASS_AoutB )
01611                         {
01612                                 /* cannot classify based on this edgeuse */
01613                                 use_this_eu = 0;
01614                                 break;
01615                         }
01616                         else if( lu->orientation == OT_SAME && class == NMG_CLASS_AinB )
01617                         {
01618                                 /* cannot classify based on this edgeuse */
01619                                 use_this_eu = 0;
01620                                 break;
01621                         }
01622 
01623                         eu_tmp = eu_tmp->eumate_p->radial_p;
01624                 }
01625                 if( !use_this_eu )
01626                         continue;
01627 
01628                 /* O.K. we can classify using this eu, look radially for a faceuse
01629                  * from the reference shell
01630                  */
01631                 eu_tmp = eu_start->eumate_p->radial_p;
01632                 while( eu_tmp != eu_start )
01633                 {
01634                         struct faceuse *fu_tmp;
01635 
01636                         if( nmg_find_s_of_eu( eu_tmp ) == s_ref )
01637                         {
01638                                 fu_tmp = nmg_find_fu_of_eu( eu_tmp );
01639                                 if( fu_tmp->orientation == OT_SAME )
01640                                         return( NMG_CLASS_AoutB );
01641                                 else
01642                                         return( NMG_CLASS_AinB );
01643                         }
01644 
01645                         eu_tmp = eu_tmp->eumate_p->radial_p;
01646                 }
01647         }
01648 
01649         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01650                 bu_log( "class_shared_lu returning NMG_CLASS_Unknown at end\n" );
01651         return( NMG_CLASS_Unknown );
01652 }
01653 
01654 /**
01655  *                      C L A S S _ L U _ V S _ S
01656  *
01657  *  Called by -
01658  *      class_fu_vs_s
01659  */
01660 static int
01661 class_lu_vs_s(struct loopuse *lu, struct shell *s, long int **classlist, const struct bn_tol *tol)
01662 {
01663         int class;
01664         unsigned int    in, outside, on;
01665         struct edgeuse *eu, *p;
01666         struct loopuse *q_lu;
01667         struct vertexuse *vu;
01668         long            magic1;
01669         char            *reason = "Unknown";
01670         int             seen_error = 0;
01671         int             status = 0;
01672 
01673         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01674                 bu_log( "class_lu_vs_s( lu=x%x, s=x%x )\n", lu, s );
01675 
01676         NMG_CK_LOOPUSE(lu);
01677         NMG_CK_SHELL(s);
01678         BN_CK_TOL(tol);
01679 
01680         if( nmg_find_s_of_lu( lu ) == s )
01681         {
01682                 bu_log( "class_lu_vs_s() is trying to classify lu x%x vs its own shell (x%x)\n",
01683                         lu, s );
01684                 rt_bomb( "class_lu_vs_s() is trying to classify lu vs its own shell" );
01685         }
01686 
01687         /* check to see if loop is already in one of the lists */
01688         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], lu->l_p) )  {
01689                 reason = "of classlist";
01690                 class = NMG_CLASS_AinB;
01691                 status = INSIDE;
01692                 goto out;
01693         }
01694 
01695         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], lu->l_p) ) {
01696                 reason = "of classlist";
01697                 class = NMG_CLASS_AonBshared;
01698                 status = ON_SURF;
01699                 goto out;
01700         }
01701         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], lu->l_p) )  {
01702                 reason = "of classlist";
01703                 class = NMG_CLASS_AonBanti;
01704                 status = ON_SURF;
01705                 goto out;
01706         }
01707 
01708         if( NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], lu->l_p) )  {
01709                 reason = "of classlist";
01710                 class = NMG_CLASS_AoutB;
01711                 status = OUTSIDE;
01712                 goto out;
01713         }
01714 
01715         magic1 = BU_LIST_FIRST_MAGIC( &lu->down_hd );
01716         if (magic1 == NMG_VERTEXUSE_MAGIC) {
01717                 /* Loop of a single vertex */
01718                 reason = "of vertex classification";
01719                 vu = BU_LIST_PNEXT( vertexuse, &lu->down_hd );
01720                 NMG_CK_VERTEXUSE(vu);
01721                 class = class_vu_vs_s(vu, s, classlist, tol);
01722                 switch (class) {
01723                 case INSIDE:
01724                         NMG_INDEX_SET(classlist[NMG_CLASS_AinB], lu->l_p);
01725                         break;
01726                 case OUTSIDE:
01727                         NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
01728                          break;
01729                 case ON_SURF:
01730                         NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], lu->l_p);
01731                         break;
01732                 default:
01733                         rt_bomb("class_lu_vs_s: bad vertexloop classification\n");
01734                 }
01735                 status = class;
01736                 goto out;
01737         } else if (magic1 != NMG_EDGEUSE_MAGIC) {
01738                 rt_bomb("class_lu_vs_s: bad child of loopuse\n");
01739         }
01740 
01741         /* loop is collection of edgeuses */
01742 retry:
01743         in = outside = on = 0;
01744         for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
01745                 /* Classify each edgeuse */
01746                 class = class_eu_vs_s(eu, s, classlist, tol);
01747                 switch (class) {
01748                 case INSIDE     : ++in;
01749                                 break;
01750                 case OUTSIDE    : ++outside;
01751                                 break;
01752                 case ON_SURF    : ++on;
01753                                 break;
01754                 default         : rt_bomb("class_lu_vs_s: bad class for edgeuse\n");
01755                 }
01756         }
01757 
01758         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01759                 bu_log("class_lu_vs_s: Loopuse edges in:%d on:%d out:%d\n", in, on, outside);
01760 
01761         if (in > 0 && outside > 0) {
01762                 FILE *fp;
01763 
01764                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )  {
01765                         char            buf[128];
01766                         static int      num;
01767                         long            *b;
01768                         struct model    *m;
01769 
01770                         m = nmg_find_model(lu->up.magic_p);
01771                         b = (long *)bu_calloc(m->maxindex, sizeof(long), "nmg_pl_lu flag[]");
01772 
01773                         for(BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
01774                                 if (NMG_INDEX_TEST(classlist[NMG_CLASS_AinB], eu->e_p))
01775                                         nmg_euprint("In:  edgeuse", eu);
01776                                 else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AoutB], eu->e_p))
01777                                         nmg_euprint("Out: edgeuse", eu);
01778                                 else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBshared], eu->e_p))
01779                                         nmg_euprint("OnShare:  edgeuse", eu);
01780                                 else if (NMG_INDEX_TEST(classlist[NMG_CLASS_AonBanti], eu->e_p))
01781                                         nmg_euprint("OnAnti:  edgeuse", eu);
01782                                 else
01783                                         nmg_euprint("BAD: edgeuse", eu);
01784                         }
01785 
01786                         sprintf(buf, "badloop%d.pl", num++);
01787                         if ((fp=fopen(buf, "w")) != NULL) {
01788                                 nmg_pl_lu(fp, lu, b, 255, 255, 255);
01789                                 nmg_pl_s(fp, s);
01790                                 fclose(fp);
01791                                 bu_log("wrote %s\n", buf);
01792                         }
01793                         nmg_pr_lu(lu, "");
01794                         nmg_stash_model_to_file( "class.g", nmg_find_model((long *)lu), "class_ls_vs_s: loop transits plane of shell/face?");
01795                         bu_free( (char *)b, "nmg_pl_lu flag[]" );
01796                 }
01797                 if(seen_error)
01798                         rt_bomb("class_lu_vs_s: loop transits plane of shell/face?\n");
01799                 seen_error = 1;
01800                 goto retry;
01801         }
01802         if (outside > 0) {
01803                 NMG_INDEX_SET(classlist[NMG_CLASS_AoutB], lu->l_p);
01804                 reason = "edgeuses were OUT and ON";
01805                 class = NMG_CLASS_AoutB;
01806                 status = OUTSIDE;
01807                 goto out;
01808         } else if (in > 0) {
01809                 NMG_INDEX_SET(classlist[NMG_CLASS_AinB], lu->l_p);
01810                 reason = "edgeuses were IN and ON";
01811                 class = NMG_CLASS_AinB;
01812                 status = INSIDE;
01813                 goto out;
01814         } else if (on == 0)
01815                 rt_bomb("class_lu_vs_s: alright, who's the wiseguy that stole my edgeuses?\n");
01816 
01817 
01818         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01819                 bu_log("\tAll edgeuses of loop are ON\n");
01820 
01821         /* since all of the edgeuses of this loop are "on" the other shell,
01822          * we need to see if this loop is "on" the other shell
01823          *
01824          * if we're a wire edge loop, simply having all edges "on" is
01825          *      sufficient.
01826          *
01827          * foreach radial edgeuse
01828          *      if edgeuse vertex is same and edgeuse parent shell is the one
01829          *              desired, then....
01830          *
01831          *              p = edgeuse, q = radial edgeuse
01832          *              while p's vertex equals q's vertex and we
01833          *                      haven't come full circle
01834          *                      move p and q forward
01835          *              if we made it full circle, loop is on
01836          */
01837 
01838         if (*lu->up.magic_p == NMG_SHELL_MAGIC) {
01839                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared], lu->l_p);
01840                 reason = "loop is a wire loop in the shell";
01841                 class = NMG_CLASS_AonBshared;
01842                 status = ON_SURF;
01843                 goto out;
01844         }
01845 
01846         NMG_CK_FACEUSE(lu->up.fu_p);
01847 
01848 #if 0
01849         eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
01850         for(
01851             eu = eu->radial_p->eumate_p;
01852             eu != BU_LIST_FIRST(edgeuse, &lu->down_hd);
01853             eu = eu->radial_p->eumate_p
01854         )  {
01855                 int     code;
01856 
01857                 /* if the radial edge is a part of a loop which is part of
01858                  * a face, then it's one that we might be "on"
01859                  */
01860                 if( *eu->up.magic_p != NMG_LOOPUSE_MAGIC ) continue;
01861                 q_lu = eu->up.lu_p;
01862                 if( *q_lu->up.magic_p != NMG_FACEUSE_MAGIC ) continue;
01863 
01864                 if( q_lu == lu )  continue;
01865 
01866                 /* Only consider faces from shell 's' */
01867                 if( q_lu->up.fu_p->s_p != s )  continue;
01868 
01869                 code = nmg_2lu_identical( eu,
01870                         BU_LIST_FIRST(edgeuse, &lu->down_hd) );
01871                 switch(code)  {
01872                 default:
01873                 case 0:
01874                         /* Not identical */
01875                         break;
01876                 case 1:
01877                         /* ON-shared */
01878                         if( lu->orientation == OT_OPPOSITE )  {
01879                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBanti],
01880                                         lu->l_p );
01881                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01882                                         bu_log("Loop is on-antishared (lu orient is OT_OPPOSITE)\n");
01883                                 nmg_reclassify_lu_eu( lu, classlist, NMG_CLASS_AonBanti );
01884                         }  else {
01885                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
01886                                         lu->l_p );
01887                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01888                                         bu_log("Loop is on-shared\n");
01889                                 /* no need to reclassify, edges were previously marked as AonBshared */
01890                         }
01891                         reason = "edges identical with radial face, normals colinear";
01892                         status = ON_SURF;
01893                         goto out;
01894                 case 2:
01895                         /* ON-antishared */
01896                         if( lu->orientation == OT_OPPOSITE )  {
01897                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBshared],
01898                                         lu->l_p );
01899                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01900                                         bu_log("Loop is on-shared (lu orient is OT_OPPOSITE)\n");
01901                                 /* no need to reclassify, edges were previously marked as AonBshared */
01902                         }  else  {
01903                                 NMG_INDEX_SET(classlist[NMG_CLASS_AonBanti],
01904                                         lu->l_p );
01905                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01906                                         bu_log("Loop is on-antishared\n");
01907                                 nmg_reclassify_lu_eu( lu, classlist, NMG_CLASS_AonBanti );
01908                         }
01909                         reason = "edges identical with radial face, normals opposite";
01910                         status = ON_SURF;
01911                         goto out;
01912                 case 3:
01913                         rt_bomb("class_lu_vs_s() unexpected wire ON\n");
01914                 }
01915         }
01916 #else
01917         class = NMG_CLASS_Unknown;
01918         eu = BU_LIST_FIRST(edgeuse, &lu->down_hd);
01919         for(
01920             eu = eu->radial_p->eumate_p;
01921             eu != BU_LIST_FIRST(edgeuse, &lu->down_hd) ;
01922             eu = eu->radial_p->eumate_p )
01923         {
01924                 struct faceuse *fu_qlu;
01925                 struct edgeuse *eu1;
01926                 struct edgeuse *eu2;
01927                 int found_match;
01928 
01929                 /* if the radial edge is a part of a loop which is part of
01930                  * a face, then it's one that we might be "on"
01931                  */
01932                 if( *eu->up.magic_p != NMG_LOOPUSE_MAGIC ) continue;
01933                 q_lu = eu->up.lu_p;
01934                 if( *q_lu->up.magic_p != NMG_FACEUSE_MAGIC ) continue;
01935                 fu_qlu = q_lu->up.fu_p;
01936                 NMG_CK_FACEUSE( fu_qlu );
01937 
01938                 if( q_lu == lu )  continue;
01939 
01940                 /* Only consider faces from shell 's' */
01941                 if( q_lu->up.fu_p->s_p != s )  continue;
01942 
01943                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01944                         bu_log( "\tfound radial lu (x%x), check for match\n", q_lu );
01945 
01946                 /* now check if eu's match in both LU's */
01947                 eu1 = BU_LIST_FIRST(edgeuse, &lu->down_hd);
01948                 if( eu1->vu_p->v_p == eu->vu_p->v_p )
01949                         eu2 = eu;
01950                 else if( eu1->vu_p->v_p == eu->eumate_p->vu_p->v_p )
01951                         eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
01952                 else
01953                         eu2 = BU_LIST_PPREV_CIRC( edgeuse, &eu->l );
01954                 found_match = 1;
01955                 do
01956                 {
01957                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01958                                 bu_log( "\t\tcompare vertex x%x to vertex x%x\n", eu1->vu_p->v_p, eu2->vu_p->v_p );
01959                         if( eu1->vu_p->v_p != eu2->vu_p->v_p )
01960                         {
01961                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01962                                         bu_log( "\t\t\tnot a match\n" );
01963                                 found_match = 0;
01964                                 break;
01965                         }
01966                         eu1 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
01967                         eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu2->l );
01968                 } while( eu1 != BU_LIST_FIRST(edgeuse, &lu->down_hd));
01969 
01970                 if( !found_match )
01971                 {
01972                         /* check opposite direction */
01973                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01974                                 bu_log( "\tChecking for match in opposite direction\n" );
01975                         eu1 = BU_LIST_FIRST(edgeuse, &lu->down_hd);
01976                         if( eu1->vu_p->v_p == eu->vu_p->v_p )
01977                                 eu2 = eu;
01978                         else if( eu1->vu_p->v_p == eu->eumate_p->vu_p->v_p )
01979                                 eu2 = BU_LIST_PNEXT_CIRC( edgeuse, &eu->l );
01980                         else
01981                                 eu2 = BU_LIST_PPREV_CIRC( edgeuse, &eu->l );
01982                         found_match = 1;
01983                         do
01984                         {
01985                                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01986                                         bu_log( "\t\tcompare vertex x%x to vertex x%x\n", eu1->vu_p->v_p, eu2->vu_p->v_p );
01987                                 if( eu1->vu_p->v_p != eu2->vu_p->v_p )
01988                                 {
01989                                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
01990                                                 bu_log( "\t\t\tnot a match\n" );
01991                                         found_match = 0;
01992                                         break;
01993                                 }
01994                                 eu1 = BU_LIST_PNEXT_CIRC( edgeuse, &eu1->l );
01995                                 eu2 = BU_LIST_PPREV_CIRC( edgeuse, &eu2->l );
01996                         } while( eu1 != BU_LIST_FIRST(edgeuse, &lu->down_hd));
01997                 }
01998 
01999                 if( found_match )
02000                 {
02001                         int test_class = NMG_CLASS_Unknown;
02002 
02003                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02004                                 bu_log( "\tFound a matching LU's x%x and x%x\n", lu, q_lu );
02005 
02006                         if( fu_qlu->orientation == OT_SAME )
02007                                 test_class = class_shared_lu( lu, q_lu, tol );
02008                         else if( fu_qlu->orientation == OT_OPPOSITE )
02009                                 test_class = class_shared_lu( lu, q_lu->lumate_p, tol );
02010                         else
02011                         {
02012                                 bu_log( "class_lu_vs_s: FU x%x for lu x%x matching lu x%x has bad orientation (%s)\n",
02013                                         fu_qlu, lu, q_lu, nmg_orientation( fu_qlu->orientation ) );
02014                                 rt_bomb( "class_lu_vs_s: FU has bad orientation\n" );
02015                         }
02016 
02017                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02018                                 bu_log( "\tclass_shared_lu says %s\n", nmg_class_name( test_class ) );
02019 
02020                         if( class == NMG_CLASS_Unknown )
02021                                 class = test_class;
02022                         else if( test_class == NMG_CLASS_AonBshared || test_class == NMG_CLASS_AonBanti )
02023                                 class = test_class;
02024 
02025                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02026                                 bu_log( "\tclass set to %s\n",  nmg_class_name( class ) );
02027 
02028                         if( class == NMG_CLASS_AonBshared )
02029                                 break;
02030                 }
02031 
02032         }
02033 
02034         if( class != NMG_CLASS_Unknown )
02035         {
02036                 if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02037                         bu_log( "Final class = %s\n", nmg_class_name( class ) );
02038                 NMG_INDEX_SET( classlist[class],lu->l_p );
02039                 if( class == NMG_CLASS_AonBanti )
02040                         nmg_reclassify_lu_eu( lu, classlist, NMG_CLASS_AonBanti );
02041 
02042                 reason = "edges identical with radial face";
02043                 status = ON_SURF;
02044                 goto out;
02045         }
02046 #endif
02047 
02048         /* OK, the edgeuses are all "on", but the loop isn't.  Time to
02049          * decide if the loop is "out" or "in".  To do this, we look for
02050          * an edgeuse radial to one of the edgeuses in the loop which is
02051          * a part of a face in the other shell.  If/when we find such a
02052          * radial edge, we check the direction (in/out) of the faceuse normal.
02053          * if the faceuse normal is pointing out of the shell, we are outside.
02054          * if the faceuse normal is pointing into the shell, we are inside.
02055          */
02056 
02057         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02058         {
02059                 bu_log( "Checking radial faces:\n" );
02060                 nmg_pr_fu_around_eu( eu, tol );
02061         }
02062 
02063         for (BU_LIST_FOR(eu, edgeuse, &lu->down_hd)) {
02064 
02065                 p = eu->radial_p;
02066                 do {
02067                         if (*p->up.magic_p == NMG_LOOPUSE_MAGIC &&
02068                             *p->up.lu_p->up.magic_p == NMG_FACEUSE_MAGIC &&
02069                             p->up.lu_p->up.fu_p->s_p == s) {
02070 
02071                                 if (p->up.lu_p->up.fu_p->orientation == OT_OPPOSITE) {
02072                                         NMG_INDEX_SET(classlist[NMG_CLASS_AinB],
02073                                                 lu->l_p );
02074                                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02075                                                 bu_log("Loop is INSIDE of fu x%x\n", p->up.lu_p->up.fu_p);
02076                                         reason = "radial faceuse is OT_OPPOSITE";
02077                                         class = NMG_CLASS_AinB;
02078                                         status = INSIDE;
02079                                         goto out;
02080                                 } else if (p->up.lu_p->up.fu_p->orientation == OT_SAME) {
02081                                         NMG_INDEX_SET(classlist[NMG_CLASS_AoutB],
02082                                                 lu->l_p );
02083                                         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02084                                                 bu_log("Loop is OUTSIDEof fu x%x\n", p->up.lu_p->up.fu_p);
02085                                         reason = "radial faceuse is OT_SAME";
02086                                         class = NMG_CLASS_AoutB;
02087                                         status = OUTSIDE;
02088                                         goto out;
02089                                 } else {
02090                                         rt_bomb("class_lu_vs_s() bad fu orientation\n");
02091                                 }
02092                         }
02093                         p = p->eumate_p->radial_p;
02094                 } while (p != eu->eumate_p);
02095 
02096         }
02097 
02098         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02099                 bu_log("Loop is OUTSIDE 'cause it isn't anything else\n");
02100 
02101         /* Since we didn't find any radial faces to classify ourselves against
02102          * and we already know that the edges are all "on" that must mean that
02103          * the loopuse is "on" a wireframe portion of the shell.
02104          */
02105         NMG_INDEX_SET( classlist[NMG_CLASS_AoutB], lu->l_p );
02106         reason = "loopuse is ON a wire loop in the shell";
02107         class = NMG_CLASS_AoutB;
02108         status = OUTSIDE;
02109 out:
02110         if (rt_g.NMG_debug & DEBUG_CLASSIFY) {
02111                 bu_log("class_lu_vs_s(lu=x%x) return %s (%s) because %s\n",
02112                         lu, nmg_class_status(status), nmg_class_name( class ), reason );
02113         }
02114         return status;
02115 }
02116 
02117 /**
02118  *                      C L A S S _ F U _ V S _ S
02119  *
02120  *  Called by -
02121  *      nmg_class_shells()
02122  */
02123 static void
02124 class_fu_vs_s(struct faceuse *fu, struct shell *s, long int **classlist, const struct bn_tol *tol)
02125 {
02126         struct loopuse *lu;
02127         plane_t         n;
02128 
02129         NMG_CK_FACEUSE(fu);
02130         NMG_CK_SHELL(s);
02131 
02132         NMG_GET_FU_PLANE( n, fu );
02133 
02134         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02135                 PLPRINT("\nclass_fu_vs_s plane equation:", n);
02136 
02137         for (BU_LIST_FOR(lu, loopuse, &fu->lu_hd))
02138                 (void)class_lu_vs_s(lu, s, classlist, tol);
02139 
02140         if (rt_g.NMG_debug & DEBUG_CLASSIFY)
02141                 bu_log("class_fu_vs_s() END\n");
02142 }
02143 
02144 /**
02145  *                      N M G _ C L A S S _ S H E L L S
02146  *
02147  *      Classify one shell WRT the other shell
02148  *
02149  *  Implicit return -
02150  *      Each element's classification will be represented by a
02151  *      SET entry in the appropriate classlist[] array.
02152  *
02153  *  Called by -
02154  *      nmg_bool.c
02155  */
02156 void
02157 nmg_class_shells(struct shell *sA, struct shell *sB, long int **classlist, const struct bn_tol *tol)
02158 {
02159         struct faceuse *fu;
02160         struct loopuse *lu;
02161         struct edgeuse *eu;
02162 
02163         NMG_CK_SHELL(sA);
02164         NMG_CK_SHELL(sB);
02165         BN_CK_TOL(tol);
02166 
02167         if (rt_g.NMG_debug & DEBUG_CLASSIFY &&
02168             BU_LIST_NON_EMPTY(&sA->fu_hd))
02169                 bu_log("nmg_class_shells - doing faces\n");
02170 
02171         fu = BU_LIST_FIRST(faceuse, &sA->fu_hd);
02172         while (BU_LIST_NOT_HEAD(fu, &sA->fu_hd)) {
02173 
02174                 class_fu_vs_s(fu, sB, classlist, tol);
02175 
02176                 if (BU_LIST_PNEXT(faceuse, fu) == fu->fumate_p)
02177                         fu = BU_LIST_PNEXT_PNEXT(faceuse, fu);
02178                 else
02179                         fu = BU_LIST_PNEXT(faceuse, fu);
02180         }
02181 
02182         if (rt_g.NMG_debug & DEBUG_CLASSIFY &&
02183             BU_LIST_NON_EMPTY(&sA->lu_hd))
02184                 bu_log("nmg_class_shells - doing loops\n");
02185 
02186         lu = BU_LIST_FIRST(loopuse, &sA->lu_hd);
02187         while (BU_LIST_NOT_HEAD(lu, &sA->lu_hd)) {
02188 
02189                 (void)class_lu_vs_s(lu, sB, classlist, tol);
02190 
02191                 if (BU_LIST_PNEXT(loopuse, lu) == lu->lumate_p)
02192                         lu = BU_LIST_PNEXT_PNEXT(loopuse, lu);
02193                 else
02194                         lu = BU_LIST_PNEXT(loopuse, lu);
02195         }
02196 
02197         if (rt_g.NMG_debug & DEBUG_CLASSIFY &&
02198             BU_LIST_NON_EMPTY(&sA->eu_hd))
02199                 bu_log("nmg_class_shells - doing edges\n");
02200 
02201         eu = BU_LIST_FIRST(edgeuse, &sA->eu_hd);
02202         while (BU_LIST_NOT_HEAD(eu, &sA->eu_hd)) {
02203 
02204                 (void)class_eu_vs_s(eu, sB, classlist, tol);
02205 
02206                 if (BU_LIST_PNEXT(edgeuse, eu) == eu->eumate_p)
02207                         eu = BU_LIST_PNEXT_PNEXT(edgeuse, eu);
02208                 else
02209                         eu = BU_LIST_PNEXT(edgeuse, eu);
02210         }
02211 
02212         if (sA->vu_p) {
02213                 if (rt_g.NMG_debug)
02214                         bu_log("nmg_class_shells - doing vertex\n");
02215                 (void)class_vu_vs_s(sA->vu_p, sB, classlist, tol);
02216         }
02217 }
02218 
02219 /**     N M G _ C L A S S I F Y _ P T _ L O O P
02220  *
02221  *      A generally available interface to nmg_class_pt_l
02222  *
02223  *      returns the classification from nmg_class_pt_l
02224  *      or a (-1) on error
02225  *
02226  *  Called by -
02227  *      nmg_extrude.c / nmg_fix_overlapping_loops()
02228  *
02229  *  XXX DANGER:  Calls nmg_class_pt_l(), which does not work well.
02230  */
02231 int
02232 nmg_classify_pt_loop(const point_t pt,
02233                      const struct loopuse *lu,
02234                      const struct bn_tol *tol)
02235 {
02236         struct neighbor closest;
02237         struct faceuse *fu;
02238         plane_t n;
02239         fastf_t dist;
02240 
02241         NMG_CK_LOOPUSE( lu );
02242         BN_CK_TOL( tol );
02243 
02244 bu_log("DANGER: nmg_classify_pt_loop() is calling nmg_class_pt_l(), which does not work well\n");
02245         if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
02246         {
02247                 bu_log( "nmg_classify_pt_loop: lu not part of a faceuse!!\n" );
02248                 return( -1 );
02249         }
02250 
02251         fu = lu->up.fu_p;
02252 
02253         /* Validate distance from point to plane */
02254         NMG_GET_FU_PLANE( n, fu );
02255         if( (dist=fabs(DIST_PT_PLANE( pt, n ))) > tol->dist )  {
02256                 bu_log("nmg_classify_pt_l() ERROR, point (%g,%g,%g) not on face, dist=%g\n",
02257                         V3ARGS(pt), dist );
02258                 return( -1 );
02259         }
02260 
02261 
02262         /* find the closest approach in this face to the projected point */
02263         closest.dist = MAX_FASTF;
02264         closest.p.eu = (struct edgeuse *)NULL;
02265         closest.class = NMG_CLASS_AoutB;        /* default return */
02266 
02267         nmg_class_pt_l( &closest , pt , lu , tol );
02268 
02269         return( closest.class );
02270 }
02271 
02272 /**             N M G _ G E T _ I N T E R I O R _ P T
02273  *
02274  *      Find any point that is interior to LU
02275  *
02276  *      Returns:
02277  *              0 - All is well
02278  *              1 - Loop is not part of a faceuse
02279  *              2 - Loop is a single vertexuse
02280  *              3 - Loop is a crack
02281  *              4 - Just plain can't find an interior point
02282  */
02283 int
02284 nmg_get_interior_pt(fastf_t *pt, const struct loopuse *lu, const struct bn_tol *tol)
02285 {
02286         struct edgeuse *eu;
02287         fastf_t point_count=0.0;
02288         double one_over_count;
02289         point_t test_pt;
02290         point_t average_pt;
02291         int i;
02292 
02293         NMG_CK_LOOPUSE( lu );
02294         BN_CK_TOL( tol );
02295 
02296         if( *lu->up.magic_p != NMG_FACEUSE_MAGIC )
02297                 return( 1 );
02298 
02299         if( BU_LIST_FIRST_MAGIC( &lu->down_hd ) != NMG_EDGEUSE_MAGIC )
02300                 return( 2 );
02301 
02302         if( nmg_loop_is_a_crack( lu ) )
02303                 return( 3 );
02304 
02305         /* first try just averaging all the vertices */
02306         VSETALL( average_pt, 0.0 );
02307         for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
02308         {
02309                 struct vertex_g *vg;
02310                 NMG_CK_EDGEUSE( eu );
02311 
02312                 vg = eu->vu_p->v_p->vg_p;
02313                 NMG_CK_VERTEX_G( vg );
02314 
02315                 VADD2( average_pt, average_pt, vg->coord );
02316                 point_count++;
02317         }
02318 
02319         one_over_count = 1.0/point_count;
02320         VSCALE( average_pt, average_pt, one_over_count );
02321         VMOVE( test_pt, average_pt );
02322 
02323         if (nmg_class_pt_lu_except( test_pt, lu, (struct edge *)NULL, tol ) == NMG_CLASS_AinB )
02324         {
02325                 VMOVE( pt, test_pt );
02326                 return( 0 );
02327         }
02328 
02329         for( i=0 ; i<3 ; i++ )
02330         {
02331 
02332                 double tol_mult;
02333 
02334                 /* Try moving just a little left of an edge */
02335                 switch( i )
02336                 {
02337                         case 0:
02338                                 tol_mult = 5.0 * tol->dist;
02339                                 break;
02340                         case 1:
02341                                 tol_mult = 1.5 * tol->dist;
02342                                 break;
02343                         case 2:
02344                                 tol_mult = 1.005 * tol->dist;
02345                                 break;
02346                         default:                        /* sanity / lint */
02347                                 tol_mult = 1;
02348                                 break;
02349                 }
02350                 for( BU_LIST_FOR( eu, edgeuse, &lu->down_hd ) )
02351                 {
02352                         vect_t left;
02353                         struct vertex_g *vg1,*vg2;
02354 
02355                         (void)nmg_find_eu_leftvec( left, eu );
02356 
02357                         vg1 = eu->vu_p->v_p->vg_p;
02358                         vg2 = eu->eumate_p->vu_p->v_p->vg_p;
02359 
02360                         VADD2( test_pt, vg1->coord, vg2->coord );
02361                         VSCALE( test_pt, test_pt, 0.5 );
02362 
02363                         VJOIN1( test_pt, test_pt, tol_mult, left );
02364                         if( nmg_class_pt_lu_except( test_pt, lu, (struct edge *)NULL, tol ) == NMG_CLASS_AinB )
02365                         {
02366                                 VMOVE( pt, test_pt );
02367                                 return( 0 );
02368                         }
02369                 }
02370         }
02371 
02372         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02373         {
02374                 bu_log( "nmg_get_interior_pt: Couldn't find interior point for lu x%x\n", lu );
02375                 nmg_pr_lu_briefly( lu, "" );
02376         }
02377 
02378         VMOVE( pt, average_pt );
02379         return( 4 );
02380 }
02381 
02382 /**     N M G _ C L A S S I F Y _ L U _ L U
02383  *
02384  *      Generally available classifier for
02385  *      determining if one loop is within another
02386  *
02387  *      returns classification based on nmg_class_pt_l results
02388  *
02389  *  Called by -
02390  *      nmg_misc.c / nmg_split_loops_handler()
02391  *
02392  */
02393 int
02394 nmg_classify_lu_lu(const struct loopuse *lu1, const struct loopuse *lu2, const struct bn_tol *tol)
02395 {
02396         struct faceuse *fu1,*fu2;
02397         struct edgeuse *eu;
02398         int share_edges;
02399         int lu1_eu_count=0;
02400         int lu2_eu_count=0;
02401 
02402         NMG_CK_LOOPUSE( lu1 );
02403         NMG_CK_LOOPUSE( lu2 );
02404         BN_CK_TOL( tol );
02405 
02406         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02407                 bu_log( "nmg_classify_lu_lu( lu1=x%x , lu2=x%x )\n", lu1, lu2 );
02408 
02409         if( lu1 == lu2 || lu1 == lu2->lumate_p )
02410                 return( NMG_CLASS_AonBshared );
02411 
02412         if( *lu1->up.magic_p != NMG_FACEUSE_MAGIC )
02413         {
02414                 bu_log( "nmg_classify_lu_lu: lu1 not part of a faceuse\n" );
02415                 return( -1 );
02416         }
02417 
02418         if( *lu2->up.magic_p != NMG_FACEUSE_MAGIC )
02419         {
02420                 bu_log( "nmg_classify_lu_lu: lu2 not part of a faceuse\n" );
02421                 return( -1 );
02422         }
02423 
02424         fu1 = lu1->up.fu_p;
02425         NMG_CK_FACEUSE( fu1 );
02426         fu2 = lu2->up.fu_p;
02427         NMG_CK_FACEUSE( fu2 );
02428 
02429         if( fu1->f_p != fu2->f_p )
02430         {
02431                 bu_log( "nmg_classify_lu_lu: loops are not in same face\n" );
02432                 return( -1 );
02433         }
02434 
02435         /* do simple check for two loops of the same vertices */
02436         if( BU_LIST_FIRST_MAGIC( &lu1->down_hd ) == NMG_EDGEUSE_MAGIC &&
02437             BU_LIST_FIRST_MAGIC( &lu2->down_hd ) == NMG_EDGEUSE_MAGIC )
02438         {
02439                 struct edgeuse *eu1_start,*eu2_start;
02440                 struct edgeuse *eu1,*eu2;
02441 
02442                 /* count EU's in lu1 */
02443                 for( BU_LIST_FOR( eu, edgeuse, &lu1->down_hd ) )
02444                         lu1_eu_count++;
02445 
02446                 /* count EU's in lu2 */
02447                 for( BU_LIST_FOR( eu, edgeuse, &lu2->down_hd ) )
02448                         lu2_eu_count++;
02449 
02450                 share_edges = 1;
02451                 eu1_start = BU_LIST_FIRST( edgeuse , &lu1->down_hd );
02452                 NMG_CK_EDGEUSE( eu1_start );
02453                 eu2_start = BU_LIST_FIRST( edgeuse , &lu2->down_hd );
02454                 NMG_CK_EDGEUSE( eu2_start );
02455                 while( BU_LIST_NOT_HEAD( eu2_start , &lu2->down_hd ) &&
02456                         eu2_start->e_p != eu1_start->e_p )
02457                 {
02458                         NMG_CK_EDGEUSE( eu2_start );
02459                         eu2_start = BU_LIST_PNEXT( edgeuse , &eu2_start->l );
02460                 }
02461 
02462                 if( BU_LIST_NOT_HEAD( eu2_start , &lu2->down_hd ) &&
02463                         eu1_start->e_p == eu2_start->e_p )
02464                 {
02465                         /* check the rest of the loop */
02466                         share_edges = 1;
02467                         eu1 = eu1_start;
02468                         eu2 = eu2_start;
02469                         do
02470                         {
02471                                 if( eu1->e_p != eu2->e_p )
02472                                 {
02473                                         share_edges = 0;
02474                                         break;
02475                                 }
02476                                 eu1 = BU_LIST_PNEXT_CIRC( edgeuse , &eu1->l );
02477                                 eu2 = BU_LIST_PNEXT_CIRC( edgeuse , &eu2->l );
02478                         } while( eu1 != eu1_start );
02479 
02480                         if( !share_edges )
02481                         {
02482                                 /* maybe the other way round */
02483                                 share_edges = 1;
02484                                 eu1 = eu1_start;
02485                                 eu2 = eu2_start;
02486                                 do
02487                                 {
02488                                         if( eu1->e_p != eu2->e_p )
02489                                         {
02490                                                 share_edges = 0;
02491                                                 break;
02492                                         }
02493                                         eu1 = BU_LIST_PNEXT_CIRC( edgeuse , &eu1->l );
02494                                         eu2 = BU_LIST_PPREV_CIRC( edgeuse , &eu2->l );
02495                                 } while( eu1 != eu1_start );
02496                         }
02497 
02498                         if( share_edges && lu1_eu_count == lu2_eu_count )
02499                         {
02500                                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02501                                         bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AonBshared\n" );
02502                                 return( NMG_CLASS_AonBshared );
02503                         }
02504                         else
02505                         {
02506                                 vect_t inward1,inward2;
02507 
02508                                 /* not all edges are shared,
02509                                  * try to fnd a vertex in lu1
02510                                  * that is not in lu2
02511                                  */
02512                                 for( BU_LIST_FOR( eu , edgeuse , &lu1->down_hd ) )
02513                                 {
02514                                         struct vertex_g *vg;
02515                                         int class;
02516 
02517                                         NMG_CK_EDGEUSE( eu );
02518 
02519                                         vg = eu->vu_p->v_p->vg_p;
02520                                         NMG_CK_VERTEX_G( vg );
02521 
02522                                         if( nmg_find_vertex_in_lu( eu->vu_p->v_p, lu2 ) != NULL )
02523                                                 continue;
02524 
02525                                         class = nmg_class_pt_lu_except( vg->coord, lu2, (struct edge *)NULL, tol);
02526                                         if( class != NMG_CLASS_AonBshared && class != NMG_CLASS_AonBanti )
02527                                         {
02528                                                 if( lu2->orientation == OT_SAME )
02529                                                 {
02530                                                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02531                                                                 bu_log( "nmg_classify_lu_lu returning %s\n", nmg_class_name( class ) );
02532                                                         return( class );
02533                                                 }
02534                                                 else
02535                                                 {
02536                                                         if( class == NMG_CLASS_AinB )
02537                                                         {
02538                                                                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02539                                                                         bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AoutB\n" );
02540                                                                 return( NMG_CLASS_AoutB );
02541                                                         }
02542                                                         if( class == NMG_CLASS_AoutB )
02543                                                         {
02544                                                                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02545                                                                         bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AinB\n" );
02546                                                                 return( NMG_CLASS_AinB );
02547                                                         }
02548                                                 }
02549                                         }
02550                                 }
02551 
02552                                 /* Some of lu1 edges are on lu2, but loops are not shared.
02553                                  * Compare inward vectors of a shared edge.
02554                                  */
02555 
02556                                 nmg_find_eu_leftvec( inward1, eu1_start );
02557                                 if( lu1->orientation == OT_OPPOSITE )
02558                                         VREVERSE( inward1, inward1 );
02559 
02560                                 nmg_find_eu_leftvec( inward2, eu2_start );
02561                                 if( lu2->orientation == OT_OPPOSITE )
02562                                         VREVERSE( inward2, inward2 );
02563 
02564                                 if( VDOT( inward1, inward2 ) < 0.0 )
02565                                         return( NMG_CLASS_AoutB );
02566                                 else
02567                                         return( NMG_CLASS_AinB );
02568                         }
02569                 }
02570                 else
02571                 {
02572                         /* no matching edges, classify by vertices */
02573                         for( BU_LIST_FOR( eu , edgeuse , &lu1->down_hd ) )
02574                         {
02575                                 struct vertex_g *vg;
02576                                 int class;
02577 
02578                                 NMG_CK_EDGEUSE( eu );
02579 
02580                                 vg = eu->vu_p->v_p->vg_p;
02581                                 NMG_CK_VERTEX_G( vg );
02582 
02583                                 if( nmg_find_vertex_in_lu( eu->vu_p->v_p, lu2 ) != NULL )
02584                                         continue;
02585 
02586                                 class = nmg_class_pt_lu_except( vg->coord, lu2, (struct edge *)NULL, tol);
02587                                 if( class != NMG_CLASS_AonBshared && class != NMG_CLASS_AonBanti )
02588                                 {
02589                                         if( lu2->orientation == OT_SAME )
02590                                         {
02591                                                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02592                                                         bu_log( "nmg_classify_lu_lu returning %s\n", nmg_class_name( class ) );
02593                                                 return( class );
02594                                         }
02595                                         else
02596                                         {
02597                                                 if( class == NMG_CLASS_AinB )
02598                                                 {
02599                                                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02600                                                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AoutB\n" );
02601                                                         return( NMG_CLASS_AoutB );
02602                                                 }
02603                                                 if( class == NMG_CLASS_AoutB )
02604                                                 {
02605                                                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02606                                                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AinB\n" );
02607                                                         return( NMG_CLASS_AinB );
02608                                                 }
02609                                         }
02610                                 }
02611                         }
02612 
02613                         /* if we get here, all vertices are shared,
02614                          * but no edges are!!!!! Check the midpoint of edges.
02615                          */
02616                         for( BU_LIST_FOR( eu , edgeuse , &lu1->down_hd ) )
02617                         {
02618                                 struct vertex_g *vg1,*vg2;
02619                                 int class;
02620                                 point_t mid_pt;
02621 
02622                                 vg1 = eu->vu_p->v_p->vg_p;
02623                                 vg2 = eu->eumate_p->vu_p->v_p->vg_p;
02624 
02625                                 VADD2( mid_pt, vg1->coord, vg2->coord );
02626                                 VSCALE( mid_pt, mid_pt, 0.5 );
02627 
02628                                 class = nmg_class_pt_lu_except( mid_pt, lu2, (struct edge *)NULL, tol);
02629                                 if( class != NMG_CLASS_AonBshared && class != NMG_CLASS_AonBanti )
02630                                 {
02631                                         if( lu2->orientation == OT_SAME )
02632                                         {
02633                                                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02634                                                         bu_log( "nmg_classify_lu_lu returning %s\n", nmg_class_name( class ) );
02635                                                 return( class );
02636                                         }
02637                                         else
02638                                         {
02639                                                 if( class == NMG_CLASS_AinB )
02640                                                 {
02641                                                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02642                                                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AoutB\n" );
02643                                                         return( NMG_CLASS_AoutB );
02644                                                 }
02645                                                 if( class == NMG_CLASS_AoutB )
02646                                                 {
02647                                                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02648                                                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AinB\n" );
02649                                                         return( NMG_CLASS_AinB );
02650                                                 }
02651                                         }
02652                                 }
02653                         }
02654 
02655                         /* Should never get here */
02656                         bu_log( "nmg_classify_lu_lu: Cannot classify lu x%x w.r.t. lu x%x\n",
02657                                         lu1, lu2 );
02658                         return( NMG_CLASS_Unknown );
02659                 }
02660         }
02661         else if( BU_LIST_FIRST_MAGIC( &lu1->down_hd ) == NMG_VERTEXUSE_MAGIC &&
02662                  BU_LIST_FIRST_MAGIC( &lu2->down_hd ) == NMG_VERTEXUSE_MAGIC )
02663         {
02664                 struct vertexuse *vu1,*vu2;
02665 
02666                 vu1 = BU_LIST_FIRST( vertexuse , &lu1->down_hd );
02667                 vu2 = BU_LIST_FIRST( vertexuse , &lu2->down_hd );
02668 
02669                 if( vu1->v_p == vu2->v_p )
02670                 {
02671                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02672                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AonBshared\n" );
02673                         return( NMG_CLASS_AonBshared );
02674                 }
02675                 else
02676                 {
02677                         if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02678                                 bu_log( "nmg_classify_lu_lu returning NMG_CLASS_AoutB\n" );
02679                         return( NMG_CLASS_AoutB );
02680                 }
02681         }
02682 
02683         if( BU_LIST_FIRST_MAGIC( &lu1->down_hd ) == NMG_VERTEXUSE_MAGIC )
02684         {
02685                 struct vertexuse *vu;
02686                 struct vertex_g *vg;
02687                 int class;
02688 
02689                 vu = BU_LIST_FIRST( vertexuse , &lu1->down_hd );
02690                 NMG_CK_VERTEXUSE( vu );
02691                 vg = vu->v_p->vg_p;
02692                 NMG_CK_VERTEX_G( vg );
02693 
02694                 class = nmg_class_pt_lu_except( vg->coord, lu2,
02695                                                 (struct edge *)NULL, tol );
02696 
02697                 if( lu2->orientation == OT_OPPOSITE )
02698                 {
02699                         if( class == NMG_CLASS_AoutB )
02700                                 class = NMG_CLASS_AinB;
02701                         else if( class == NMG_CLASS_AinB )
02702                                 class = NMG_CLASS_AoutB;
02703                 }
02704                 if( rt_g.NMG_debug & DEBUG_CLASSIFY )
02705                         bu_log( "nmg_classify_lu_lu returning %s\n", nmg_class_name( class ) );
02706                 return( class );
02707         }
02708         else if( BU_LIST_FIRST_MAGIC( &lu2->down_hd ) == NMG_VERTEXUSE_MAGIC )
02709                 return( NMG_CLASS_AoutB );
02710 
02711         bu_log( "nmg_classify_lu_lu: ERROR, Should not get here!!!\n" );
02712         rt_bomb(  "nmg_classify_lu_lu: ERROR, Should not get here!!!\n" );
02713 
02714         return( -1); /* to make the compilers happy */
02715 }
02716 
02717 /**             N M G _ C L A S S I F Y _ S _ V S _S
02718  *
02719  *      Classify one shell (s2) with respect to another (s).
02720  *
02721  *      returns:
02722  *              NMG_CLASS_AinB if s2 is inside s
02723  *              NMG_CLASS_AoutB is s2 is outside s
02724  *              NMG_CLASS_Unknown if we can't tell
02725  *
02726  *      Assumes (but does not verify) that these two shells do not
02727  *      overlap, but are either entirely separate or entirely within
02728  *      one or the other.
02729  */
02730 int
02731 nmg_classify_s_vs_s(struct shell *s2, struct shell *s, const struct bn_tol *tol)
02732 {
02733         int i;
02734         int class;
02735         struct faceuse *fu;
02736         struct loopuse *lu;
02737         struct edgeuse *eu;
02738         point_t pt_in_s2;
02739         struct bu_ptbl verts;
02740 
02741         if( !V3RPP1_IN_RPP2( s2->sa_p->min_pt, s2->sa_p->max_pt, s->sa_p->min_pt, s->sa_p->max_pt ) )
02742                 return( NMG_CLASS_AoutB );
02743 
02744         /* shell s2 may be inside shell s
02745            Get a point from s2 to classify vs s */
02746 
02747         if( BU_LIST_NON_EMPTY( &s2->fu_hd ) )
02748         {
02749                 fu = BU_LIST_FIRST( faceuse, &s2->fu_hd );
02750                 lu = BU_LIST_FIRST( loopuse, &fu->lu_hd );
02751                 eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
02752                 VMOVE( pt_in_s2, eu->vu_p->v_p->vg_p->coord );
02753                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02754                 if( class == NMG_CLASS_AinB )
02755                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02756                 else if( class == NMG_CLASS_AoutB )
02757                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02758 
02759                 /* try other end of this EU */
02760                 VMOVE( pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord );
02761                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02762                 if( class == NMG_CLASS_AinB )
02763                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02764                 else if( class == NMG_CLASS_AoutB )
02765                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02766         }
02767 
02768         if( BU_LIST_NON_EMPTY( &s2->lu_hd ) )
02769         {
02770                 lu = BU_LIST_FIRST( loopuse, &s2->lu_hd );
02771                 eu = BU_LIST_FIRST( edgeuse, &lu->down_hd );
02772                 VMOVE( pt_in_s2, eu->vu_p->v_p->vg_p->coord );
02773                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02774                 if( class == NMG_CLASS_AinB )
02775                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02776                 else if( class == NMG_CLASS_AoutB )
02777                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02778 
02779                 /* try other end of this EU */
02780                 VMOVE( pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord );
02781                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02782                 if( class == NMG_CLASS_AinB )
02783                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02784                 else if( class == NMG_CLASS_AoutB )
02785                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02786         }
02787 
02788         if( BU_LIST_NON_EMPTY( &s2->eu_hd ) )
02789         {
02790                 eu = BU_LIST_FIRST( edgeuse, &s2->eu_hd );
02791                 VMOVE( pt_in_s2, eu->vu_p->v_p->vg_p->coord );
02792                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02793                 if( class == NMG_CLASS_AinB )
02794                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02795                 else if( class == NMG_CLASS_AoutB )
02796                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02797 
02798                 /* try other end of this EU */
02799                 VMOVE( pt_in_s2, eu->eumate_p->vu_p->v_p->vg_p->coord );
02800                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02801                 if( class == NMG_CLASS_AinB )
02802                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02803                 else if( class == NMG_CLASS_AoutB )
02804                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02805         }
02806 
02807         if( s2->vu_p && s2->vu_p->v_p->vg_p )
02808         {
02809                 VMOVE( pt_in_s2, s2->vu_p->v_p->vg_p->coord );
02810                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02811                 if( class == NMG_CLASS_AinB )
02812                         return( NMG_CLASS_AinB );               /* shell s2 is inside shell s */
02813                 else if( class == NMG_CLASS_AoutB )
02814                         return( NMG_CLASS_AoutB );              /* shell s2 is not inside shell s */
02815         }
02816 
02817         /* classification returned NMG_CLASS_AonB, so need to try other points */
02818         nmg_vertex_tabulate( &verts, &s2->l.magic );
02819         for( i=0 ; i<BU_PTBL_END( &verts ) ; i++ )
02820         {
02821                 struct vertex *v;
02822 
02823                 v = (struct vertex *)BU_PTBL_GET( &verts, i );
02824 
02825                 VMOVE( pt_in_s2, v->vg_p->coord );
02826                 class = nmg_class_pt_s(pt_in_s2, s, 0, tol);
02827                 if( class == NMG_CLASS_AinB )
02828                 {
02829                         bu_ptbl_free( &verts );
02830                         return( NMG_CLASS_AinB );       /* shell s2 is inside shell s */
02831                 }
02832                 else if( class == NMG_CLASS_AoutB )
02833                 {
02834                         bu_ptbl_free( &verts );
02835                         return( NMG_CLASS_AoutB );      /* shell s2 is not inside shell s */
02836                 }
02837         }
02838         bu_ptbl_free( &verts );
02839 
02840         /* every point of s2 is on s !!!!!!! */
02841         return( NMG_CLASS_Unknown );
02842 }
02843 
02844 /*
02845  * Local Variables:
02846  * mode: C
02847  * tab-width: 8
02848  * c-basic-offset: 4
02849  * indent-tabs-mode: t
02850  * End:
02851  * ex: shiftwidth=4 tabstop=8
02852  */

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